Class: OmnifocusMcp::Infrastructure::ScriptRunner
- Inherits:
-
Object
- Object
- OmnifocusMcp::Infrastructure::ScriptRunner
- Defined in:
- lib/omnifocus_mcp/infrastructure/script_runner.rb
Overview
Runs OmniFocus automation scripts (JXA, OmniJS, AppleScript) via
osascript.
Instances accept an injectable runner so unit specs can supply a fake that
returns canned [stdout, stderr, status] triples without invoking
osascript. Class methods delegate to a default singleton for compatibility
with the previous Utils::ScriptExecution API.
Constant Summary collapse
- OMNIFOCUS_SCRIPTS_DIR =
File.("../utils/omnifocus_scripts", __dir__).freeze
- STDOUT_PREVIEW_LIMIT =
200
Instance Attribute Summary collapse
Class Method Summary collapse
- .capture_osascript ⇒ Object
- .default ⇒ Object
- .escape_content(content) ⇒ Object
- .execute_applescript(source) ⇒ Object
- .execute_jxa(script) ⇒ Object
- .execute_omnifocus_script(script_path, args: nil) ⇒ Object
- .execute_omnifocus_source(source, args: nil) ⇒ Object
- .reset! ⇒ Object
- .resolve_script_path(script_path) ⇒ Object
- .runner ⇒ Object
- .runner=(runner) ⇒ Object
- .with_temp_script(content:, prefix:, ext:) ⇒ Object
Instance Method Summary collapse
-
#capture_osascript(*argv) ⇒ Object
Runs
osascript(or a test double) with an optional timeout so a hung automation call cannot block the MCP server indefinitely. -
#escape_content(content) ⇒ Object
Escape a string for safe embedding in a JXA template literal.
-
#execute_applescript(source) ⇒ Object
Execute a raw AppleScript source string via
osascript. -
#execute_jxa(script) ⇒ Object
Execute a JXA script (string source) and return Result with the parsed JSON value.
-
#execute_omnifocus_script(script_path, args: nil) ⇒ Object
Execute an OmniJS script from disk inside OmniFocus.
-
#execute_omnifocus_source(source, args: nil) ⇒ Object
Execute an OmniJS script source (string) inside OmniFocus via
app.evaluateJavascript. -
#initialize(runner: nil) ⇒ ScriptRunner
constructor
A new instance of ScriptRunner.
-
#resolve_script_path(script_path) ⇒ Object
Resolve
@scriptName.jsto an absolute path inside the gem's bundled OmniJS directory. -
#with_temp_script(content:, prefix:, ext:) ⇒ Object
Materialize
contentto a tempfile, yield its path to the block, and guarantee cleanup (even on exception).
Constructor Details
#initialize(runner: nil) ⇒ ScriptRunner
Returns a new instance of ScriptRunner.
47 48 49 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 47 def initialize(runner: nil) @runner = runner end |
Instance Attribute Details
#runner ⇒ Object
51 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 51 def runner = @runner ||= method(:capture_osascript).to_proc |
Class Method Details
.capture_osascript ⇒ Object
42 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 42 def capture_osascript(...) = default.capture_osascript(...) |
.default ⇒ Object
26 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 26 def default = @default ||= new |
.escape_content(content) ⇒ Object
40 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 40 def escape_content(content) = default.escape_content(content) |
.execute_applescript(source) ⇒ Object
38 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 38 def execute_applescript(source) = default.execute_applescript(source) |
.execute_jxa(script) ⇒ Object
35 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 35 def execute_jxa(script) = default.execute_jxa(script) |
.execute_omnifocus_script(script_path, args: nil) ⇒ Object
37 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 37 def execute_omnifocus_script(script_path, args: nil) = default.execute_omnifocus_script(script_path, args: args) |
.execute_omnifocus_source(source, args: nil) ⇒ Object
36 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 36 def execute_omnifocus_source(source, args: nil) = default.execute_omnifocus_source(source, args: args) |
.reset! ⇒ Object
27 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 27 def reset! = @default = new |
.resolve_script_path(script_path) ⇒ Object
41 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 41 def resolve_script_path(script_path) = default.resolve_script_path(script_path) |
.runner ⇒ Object
29 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 29 def runner = default.runner |
.runner=(runner) ⇒ Object
31 32 33 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 31 def runner=(runner) default.runner = runner end |
.with_temp_script(content:, prefix:, ext:) ⇒ Object
39 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 39 def with_temp_script(content:, prefix:, ext:, &) = default.with_temp_script(content:, prefix:, ext:, &) |
Instance Method Details
#capture_osascript(*argv) ⇒ Object
Runs osascript (or a test double) with an optional timeout so a hung
automation call cannot block the MCP server indefinitely.
119 120 121 122 123 124 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 119 def capture_osascript(*argv) timeout_sec = Config.script_timeout_sec return Open3.capture3(*argv) unless timeout_sec capture_osascript_with_timeout(*argv, timeout_sec:) end |
#escape_content(content) ⇒ Object
Escape a string for safe embedding in a JXA template literal.
107 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 107 def escape_content(content) = JsEmbed.template_literal(content) |
#execute_applescript(source) ⇒ Object
Execute a raw AppleScript source string via osascript.
Returns the [stdout, stderr, status] triple from the runner so callers
can surface stderr/status without re-running the script.
89 90 91 92 93 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 89 def execute_applescript(source) with_temp_script(content: source, prefix: "applescript", ext: "applescript") do |path| runner.call("osascript", path) end end |
#execute_jxa(script) ⇒ Object
Execute a JXA script (string source) and return Result with the parsed JSON value.
55 56 57 58 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 55 def execute_jxa(script) run_jxa_source_result(source: script, prefix: "jxa_script") .and_then { |stdout| parse_jxa_output(stdout) } end |
#execute_omnifocus_script(script_path, args: nil) ⇒ Object
Execute an OmniJS script from disk inside OmniFocus.
script_path may be a real filesystem path or an @scriptName.js
shorthand that resolves against OMNIFOCUS_SCRIPTS_DIR.
76 77 78 79 80 81 82 83 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 76 def execute_omnifocus_script(script_path, args: nil) # Force UTF-8: bundled OmniJS files may contain non-ASCII bytes; the # platform default of US-ASCII would otherwise raise inside the # regex-based escape pass. File.read(resolve_script_path(script_path), encoding: Encoding::UTF_8).then do |source| execute_omnifocus_source(source, args:) end end |
#execute_omnifocus_source(source, args: nil) ⇒ Object
Execute an OmniJS script source (string) inside OmniFocus via
app.evaluateJavascript. Returns Result with the parsed JSON value.
args (Arrayconst argv = [...] block
before the script body.
65 66 67 68 69 70 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 65 def execute_omnifocus_source(source, args: nil) wrap_omnifocus_source(source:, args:).then do |wrapped| run_jxa_source_result(source: wrapped, prefix: "jxa_wrapper") .and_then { |stdout| parse_omnifocus_output(stdout) } end end |
#resolve_script_path(script_path) ⇒ Object
Resolve @scriptName.js to an absolute path inside the gem's bundled
OmniJS directory. Plain paths pass through unchanged.
111 112 113 114 115 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 111 def resolve_script_path(script_path) return script_path unless script_path.start_with?("@") File.join(OMNIFOCUS_SCRIPTS_DIR, script_path[1..]) end |
#with_temp_script(content:, prefix:, ext:) ⇒ Object
Materialize content to a tempfile, yield its path to the block, and
guarantee cleanup (even on exception). Uses Tempfile.create, which
removes the file when the block exits.
98 99 100 101 102 103 104 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 98 def with_temp_script(content:, prefix:, ext:) Tempfile.create([prefix, ".#{ext}"]) do |file| file.write(content) file.flush yield file.path end end |