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.js` to an absolute path inside the gem’s bundled OmniJS directory.
-
#with_temp_script(content:, prefix:, ext:) ⇒ Object
Materialize ‘content` to 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.
50 51 52 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 50 def initialize(runner: nil) @runner = runner end |
Instance Attribute Details
#runner ⇒ Object
54 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 54 def runner = @runner ||= method(:capture_osascript).to_proc |
Class Method Details
.capture_osascript ⇒ Object
45 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 45 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
43 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 43 def escape_content(content) = default.escape_content(content) |
.execute_applescript(source) ⇒ Object
41 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 41 def execute_applescript(source) = default.execute_applescript(source) |
.execute_jxa(script) ⇒ Object
38 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 38 def execute_jxa(script) = default.execute_jxa(script) |
.execute_omnifocus_script(script_path, args: nil) ⇒ Object
40 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 40 def execute_omnifocus_script(script_path, args: nil) = default.execute_omnifocus_script(script_path, args: args) |
.execute_omnifocus_source(source, args: nil) ⇒ Object
39 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 39 def execute_omnifocus_source(source, args: nil) = default.execute_omnifocus_source(source, args: args) |
.reset! ⇒ Object
34 35 36 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 34 def reset! @default = new end |
.resolve_script_path(script_path) ⇒ Object
44 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 44 def resolve_script_path(script_path) = default.resolve_script_path(script_path) |
.runner ⇒ Object
28 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 28 def runner = default.runner |
.runner=(runner) ⇒ Object
30 31 32 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 30 def runner=(runner) default.runner = runner end |
.with_temp_script(content:, prefix:, ext:) ⇒ Object
42 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 42 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.
122 123 124 125 126 127 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 122 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.
110 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 110 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.
92 93 94 95 96 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 92 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.
58 59 60 61 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 58 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`.
79 80 81 82 83 84 85 86 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 79 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` (Array<String>) is prepended as a `const argv = […]` block before the script body.
68 69 70 71 72 73 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 68 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.
114 115 116 117 118 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 114 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.
101 102 103 104 105 106 107 |
# File 'lib/omnifocus_mcp/infrastructure/script_runner.rb', line 101 def with_temp_script(content:, prefix:, ext:) Tempfile.create([prefix, ".#{ext}"]) do |file| file.write(content) file.flush yield file.path end end |