open-multi-agent-kit 0.98.3 → 0.98.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +11 -2
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +14 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/help.d.ts.map +1 -1
  8. package/dist/cli/help.js +3 -0
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/mcp-attach.d.ts +3 -3
  11. package/dist/cli/mcp-attach.d.ts.map +1 -1
  12. package/dist/cli/mcp-attach.js +5 -4
  13. package/dist/cli/mcp-attach.js.map +1 -1
  14. package/dist/cli/model-contract.d.ts +5 -0
  15. package/dist/cli/model-contract.d.ts.map +1 -0
  16. package/dist/cli/model-contract.js +45 -0
  17. package/dist/cli/model-contract.js.map +1 -0
  18. package/dist/commands/adaptorch-doctor-cli.d.ts.map +1 -1
  19. package/dist/commands/adaptorch-doctor-cli.js +32 -10
  20. package/dist/commands/adaptorch-doctor-cli.js.map +1 -1
  21. package/dist/commands/init-cli.d.ts +12 -0
  22. package/dist/commands/init-cli.d.ts.map +1 -0
  23. package/dist/commands/init-cli.js +76 -0
  24. package/dist/commands/init-cli.js.map +1 -0
  25. package/dist/commands/provider-sync-cli.d.ts +27 -0
  26. package/dist/commands/provider-sync-cli.d.ts.map +1 -0
  27. package/dist/commands/provider-sync-cli.js +177 -0
  28. package/dist/commands/provider-sync-cli.js.map +1 -0
  29. package/dist/commands/run-command.d.ts +8 -0
  30. package/dist/commands/run-command.d.ts.map +1 -0
  31. package/dist/commands/run-command.js +26 -0
  32. package/dist/commands/run-command.js.map +1 -0
  33. package/dist/core/active-skill-state.d.ts +7 -0
  34. package/dist/core/active-skill-state.d.ts.map +1 -0
  35. package/dist/core/active-skill-state.js +25 -0
  36. package/dist/core/active-skill-state.js.map +1 -0
  37. package/dist/core/agent-session-services.d.ts +2 -0
  38. package/dist/core/agent-session-services.d.ts.map +1 -1
  39. package/dist/core/agent-session-services.js +2 -0
  40. package/dist/core/agent-session-services.js.map +1 -1
  41. package/dist/core/agent-session.d.ts +0 -1
  42. package/dist/core/agent-session.d.ts.map +1 -1
  43. package/dist/core/agent-session.js +30 -85
  44. package/dist/core/agent-session.js.map +1 -1
  45. package/dist/core/codex-chatgpt-web-bridge.d.ts +99 -0
  46. package/dist/core/codex-chatgpt-web-bridge.d.ts.map +1 -0
  47. package/dist/core/codex-chatgpt-web-bridge.js +192 -0
  48. package/dist/core/codex-chatgpt-web-bridge.js.map +1 -0
  49. package/dist/core/codex-chatgpt-web-sync.d.ts +50 -0
  50. package/dist/core/codex-chatgpt-web-sync.d.ts.map +1 -0
  51. package/dist/core/codex-chatgpt-web-sync.js +119 -0
  52. package/dist/core/codex-chatgpt-web-sync.js.map +1 -0
  53. package/dist/core/compaction/compaction-headroom.d.ts +30 -0
  54. package/dist/core/compaction/compaction-headroom.d.ts.map +1 -0
  55. package/dist/core/compaction/compaction-headroom.js +48 -0
  56. package/dist/core/compaction/compaction-headroom.js.map +1 -0
  57. package/dist/core/compaction/compaction.d.ts +2 -29
  58. package/dist/core/compaction/compaction.d.ts.map +1 -1
  59. package/dist/core/compaction/compaction.js +19 -47
  60. package/dist/core/compaction/compaction.js.map +1 -1
  61. package/dist/core/compaction/overflow-retry-guard.d.ts +20 -0
  62. package/dist/core/compaction/overflow-retry-guard.d.ts.map +1 -0
  63. package/dist/core/compaction/overflow-retry-guard.js +38 -0
  64. package/dist/core/compaction/overflow-retry-guard.js.map +1 -0
  65. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  66. package/dist/core/context-budget-headroom-types.js +1 -1
  67. package/dist/core/context-budget-headroom-types.js.map +1 -1
  68. package/dist/core/context-budget-token-counter-types.d.ts +27 -0
  69. package/dist/core/context-budget-token-counter-types.d.ts.map +1 -0
  70. package/dist/core/context-budget-token-counter-types.js +2 -0
  71. package/dist/core/context-budget-token-counter-types.js.map +1 -0
  72. package/dist/core/context-budget-token-counter.d.ts +2 -26
  73. package/dist/core/context-budget-token-counter.d.ts.map +1 -1
  74. package/dist/core/context-budget-token-counter.js +2 -0
  75. package/dist/core/context-budget-token-counter.js.map +1 -1
  76. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  77. package/dist/core/context-budget-v2-scoring.js +14 -10
  78. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  79. package/dist/core/domain-router.d.ts.map +1 -1
  80. package/dist/core/domain-router.js +1 -1
  81. package/dist/core/domain-router.js.map +1 -1
  82. package/dist/core/grok-harness-dispatch.d.ts +4 -0
  83. package/dist/core/grok-harness-dispatch.d.ts.map +1 -1
  84. package/dist/core/grok-harness-dispatch.js +20 -3
  85. package/dist/core/grok-harness-dispatch.js.map +1 -1
  86. package/dist/core/grok-harness.d.ts +15 -9
  87. package/dist/core/grok-harness.d.ts.map +1 -1
  88. package/dist/core/grok-harness.js +34 -8
  89. package/dist/core/grok-harness.js.map +1 -1
  90. package/dist/core/mcp/tools.d.ts +2 -2
  91. package/dist/core/mcp/tools.d.ts.map +1 -1
  92. package/dist/core/mcp/tools.js +29 -3
  93. package/dist/core/mcp/tools.js.map +1 -1
  94. package/dist/core/model-registry-schema.d.ts +1081 -0
  95. package/dist/core/model-registry-schema.d.ts.map +1 -0
  96. package/dist/core/model-registry-schema.js +148 -0
  97. package/dist/core/model-registry-schema.js.map +1 -0
  98. package/dist/core/model-registry.d.ts.map +1 -1
  99. package/dist/core/model-registry.js +1 -146
  100. package/dist/core/model-registry.js.map +1 -1
  101. package/dist/core/model-resolver.d.ts +2 -1
  102. package/dist/core/model-resolver.d.ts.map +1 -1
  103. package/dist/core/model-resolver.js +2 -1
  104. package/dist/core/model-resolver.js.map +1 -1
  105. package/dist/core/prompt-preset.d.ts +1 -1
  106. package/dist/core/prompt-preset.d.ts.map +1 -1
  107. package/dist/core/prompt-preset.js +16 -1
  108. package/dist/core/prompt-preset.js.map +1 -1
  109. package/dist/core/provider-display-names.d.ts.map +1 -1
  110. package/dist/core/provider-display-names.js +1 -0
  111. package/dist/core/provider-display-names.js.map +1 -1
  112. package/dist/core/provider-resilience.d.ts +8 -0
  113. package/dist/core/provider-resilience.d.ts.map +1 -1
  114. package/dist/core/provider-resilience.js +20 -3
  115. package/dist/core/provider-resilience.js.map +1 -1
  116. package/dist/core/provider-usage-types.d.ts +42 -0
  117. package/dist/core/provider-usage-types.d.ts.map +1 -0
  118. package/dist/core/provider-usage-types.js +2 -0
  119. package/dist/core/provider-usage-types.js.map +1 -0
  120. package/dist/core/provider-usage.d.ts +2 -31
  121. package/dist/core/provider-usage.d.ts.map +1 -1
  122. package/dist/core/provider-usage.js +4 -0
  123. package/dist/core/provider-usage.js.map +1 -1
  124. package/dist/core/redaction.d.ts.map +1 -1
  125. package/dist/core/redaction.js +18 -2
  126. package/dist/core/redaction.js.map +1 -1
  127. package/dist/core/sandbox/policy-merge.d.ts +6 -0
  128. package/dist/core/sandbox/policy-merge.d.ts.map +1 -0
  129. package/dist/core/sandbox/policy-merge.js +74 -0
  130. package/dist/core/sandbox/policy-merge.js.map +1 -0
  131. package/dist/core/sandbox/policy-paths.d.ts +4 -0
  132. package/dist/core/sandbox/policy-paths.d.ts.map +1 -0
  133. package/dist/core/sandbox/policy-paths.js +21 -0
  134. package/dist/core/sandbox/policy-paths.js.map +1 -0
  135. package/dist/core/sandbox/policy-types.d.ts +75 -0
  136. package/dist/core/sandbox/policy-types.d.ts.map +1 -0
  137. package/dist/core/sandbox/policy-types.js +2 -0
  138. package/dist/core/sandbox/policy-types.js.map +1 -0
  139. package/dist/core/sandbox/policy.d.ts +3 -77
  140. package/dist/core/sandbox/policy.d.ts.map +1 -1
  141. package/dist/core/sandbox/policy.js +3 -98
  142. package/dist/core/sandbox/policy.js.map +1 -1
  143. package/dist/core/sdk-provider-stream.d.ts +14 -0
  144. package/dist/core/sdk-provider-stream.d.ts.map +1 -0
  145. package/dist/core/sdk-provider-stream.js +38 -0
  146. package/dist/core/sdk-provider-stream.js.map +1 -0
  147. package/dist/core/sdk.d.ts +5 -1
  148. package/dist/core/sdk.d.ts.map +1 -1
  149. package/dist/core/sdk.js +17 -33
  150. package/dist/core/sdk.js.map +1 -1
  151. package/dist/core/session-failure-cause.d.ts.map +1 -1
  152. package/dist/core/session-failure-cause.js +10 -3
  153. package/dist/core/session-failure-cause.js.map +1 -1
  154. package/dist/core/session-run-termination.d.ts +18 -0
  155. package/dist/core/session-run-termination.d.ts.map +1 -0
  156. package/dist/core/session-run-termination.js +61 -0
  157. package/dist/core/session-run-termination.js.map +1 -0
  158. package/dist/core/session-termination.d.ts.map +1 -1
  159. package/dist/core/session-termination.js +1 -1
  160. package/dist/core/session-termination.js.map +1 -1
  161. package/dist/core/skill-selector.d.ts +40 -0
  162. package/dist/core/skill-selector.d.ts.map +1 -0
  163. package/dist/core/skill-selector.js +102 -0
  164. package/dist/core/skill-selector.js.map +1 -0
  165. package/dist/core/tools/render-utils.d.ts.map +1 -1
  166. package/dist/core/tools/render-utils.js +2 -4
  167. package/dist/core/tools/render-utils.js.map +1 -1
  168. package/dist/core/turn-metrics-record.d.ts +55 -0
  169. package/dist/core/turn-metrics-record.d.ts.map +1 -0
  170. package/dist/core/turn-metrics-record.js +149 -0
  171. package/dist/core/turn-metrics-record.js.map +1 -0
  172. package/dist/core/turn-metrics.d.ts +4 -56
  173. package/dist/core/turn-metrics.d.ts.map +1 -1
  174. package/dist/core/turn-metrics.js +12 -60
  175. package/dist/core/turn-metrics.js.map +1 -1
  176. package/dist/guardrails/evidence-system.d.ts +1 -1
  177. package/dist/guardrails/evidence-system.d.ts.map +1 -1
  178. package/dist/guardrails/evidence-system.js +18 -29
  179. package/dist/guardrails/evidence-system.js.map +1 -1
  180. package/dist/guardrails/merge-gate-result.d.ts +4 -0
  181. package/dist/guardrails/merge-gate-result.d.ts.map +1 -0
  182. package/dist/guardrails/merge-gate-result.js +32 -0
  183. package/dist/guardrails/merge-gate-result.js.map +1 -0
  184. package/dist/main.d.ts.map +1 -1
  185. package/dist/main.js +22 -22
  186. package/dist/main.js.map +1 -1
  187. package/dist/modes/interactive/interactive-login-options.d.ts +27 -0
  188. package/dist/modes/interactive/interactive-login-options.d.ts.map +1 -0
  189. package/dist/modes/interactive/interactive-login-options.js +54 -0
  190. package/dist/modes/interactive/interactive-login-options.js.map +1 -0
  191. package/dist/modes/interactive/interactive-mode.d.ts +7 -4
  192. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  193. package/dist/modes/interactive/interactive-mode.js +138 -140
  194. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  195. package/dist/modes/interactive/interactive-resume-command.d.ts +3 -0
  196. package/dist/modes/interactive/interactive-resume-command.d.ts.map +1 -0
  197. package/dist/modes/interactive/interactive-resume-command.js +24 -0
  198. package/dist/modes/interactive/interactive-resume-command.js.map +1 -0
  199. package/dist/modes/interactive/interactive-tool-result.d.ts +32 -0
  200. package/dist/modes/interactive/interactive-tool-result.d.ts.map +1 -0
  201. package/dist/modes/interactive/interactive-tool-result.js +59 -0
  202. package/dist/modes/interactive/interactive-tool-result.js.map +1 -0
  203. package/dist/modes/print-mode.d.ts.map +1 -1
  204. package/dist/modes/print-mode.js +22 -0
  205. package/dist/modes/print-mode.js.map +1 -1
  206. package/dist/utils/clipboard-image.d.ts.map +1 -1
  207. package/dist/utils/clipboard-image.js +10 -60
  208. package/dist/utils/clipboard-image.js.map +1 -1
  209. package/dist/utils/terminal-links.d.ts +13 -0
  210. package/dist/utils/terminal-links.d.ts.map +1 -0
  211. package/dist/utils/terminal-links.js +68 -0
  212. package/dist/utils/terminal-links.js.map +1 -0
  213. package/dist/utils/windows-clipboard-image.d.ts +16 -0
  214. package/dist/utils/windows-clipboard-image.d.ts.map +1 -0
  215. package/dist/utils/windows-clipboard-image.js +87 -0
  216. package/dist/utils/windows-clipboard-image.js.map +1 -0
  217. package/docs/adaptorch-onboarding.md +80 -0
  218. package/docs/containerization.md +12 -0
  219. package/docs/context-files.md +97 -0
  220. package/docs/correctness-wall.md +8 -0
  221. package/docs/custom-provider.md +35 -0
  222. package/docs/development.md +18 -3
  223. package/docs/environment-variables.md +2 -0
  224. package/docs/grok-harness.md +4 -0
  225. package/docs/harness-boundaries.md +154 -0
  226. package/docs/harness-improvements.md +90 -0
  227. package/docs/json.md +19 -0
  228. package/docs/keybindings.md +1 -1
  229. package/docs/mcp.md +22 -2
  230. package/docs/metrics.md +66 -16
  231. package/docs/model-catalog-refresh.md +190 -0
  232. package/docs/model-contract.md +168 -0
  233. package/docs/models.md +21 -4
  234. package/docs/provider-resilience.md +5 -3
  235. package/docs/providers.md +68 -1
  236. package/docs/quickstart.md +4 -0
  237. package/docs/release-audit-0.98.4.md +112 -0
  238. package/docs/review-bundle-followup.md +195 -0
  239. package/docs/runtime-algorithms.md +19 -0
  240. package/docs/sdk.md +11 -2
  241. package/docs/sessions.md +1 -1
  242. package/docs/settings.md +17 -1
  243. package/docs/skills.md +2 -0
  244. package/docs/tb21-audit.md +131 -0
  245. package/docs/terminal-setup.md +20 -0
  246. package/docs/usage.md +23 -1
  247. package/docs/windows.md +48 -0
  248. package/examples/README.md +4 -0
  249. package/examples/context/AGENTS.md +64 -0
  250. package/examples/context/CLAUDE.md +17 -0
  251. package/examples/context/INTERNET.md +42 -0
  252. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  253. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  254. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  255. package/examples/extensions/gondolin/package-lock.json +2 -2
  256. package/examples/extensions/gondolin/package.json +1 -1
  257. package/examples/extensions/sandbox/package-lock.json +2 -2
  258. package/examples/extensions/sandbox/package.json +1 -1
  259. package/examples/extensions/with-deps/package-lock.json +2 -2
  260. package/examples/extensions/with-deps/package.json +1 -1
  261. package/npm-shrinkwrap.json +18 -18
  262. package/package.json +6 -6
@@ -0,0 +1,3 @@
1
+ import type { SessionManager } from "../../core/session-manager.ts";
2
+ export declare function formatResumeCommand(sessionManager: SessionManager): string | undefined;
3
+ //# sourceMappingURL=interactive-resume-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-resume-command.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-resume-command.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AASpE,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAatF","sourcesContent":["import * as fs from \"node:fs\";\nimport { APP_NAME } from \"../../config.ts\";\nimport type { SessionManager } from \"../../core/session-manager.ts\";\n\nfunction quoteIfNeeded(value: string): string {\n\tif (value.length > 0 && !/[^a-zA-Z0-9_\\-./~:@]/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport function formatResumeCommand(sessionManager: SessionManager): string | undefined {\n\tif (!process.stdout.isTTY) return undefined;\n\tif (!sessionManager.isPersisted()) return undefined;\n\n\tconst sessionFile = sessionManager.getSessionFile();\n\tif (!sessionFile || !fs.existsSync(sessionFile)) return undefined;\n\n\tconst args = [APP_NAME];\n\tif (!sessionManager.usesDefaultSessionDir()) {\n\t\targs.push(\"--session-dir\", quoteIfNeeded(sessionManager.getSessionDir()));\n\t}\n\targs.push(\"--session\", sessionManager.getSessionId());\n\treturn args.join(\" \");\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import * as fs from "node:fs";
2
+ import { APP_NAME } from "../../config.js";
3
+ function quoteIfNeeded(value) {
4
+ if (value.length > 0 && !/[^a-zA-Z0-9_\-./~:@]/.test(value)) {
5
+ return value;
6
+ }
7
+ return `'${value.replace(/'/g, `'\\''`)}'`;
8
+ }
9
+ export function formatResumeCommand(sessionManager) {
10
+ if (!process.stdout.isTTY)
11
+ return undefined;
12
+ if (!sessionManager.isPersisted())
13
+ return undefined;
14
+ const sessionFile = sessionManager.getSessionFile();
15
+ if (!sessionFile || !fs.existsSync(sessionFile))
16
+ return undefined;
17
+ const args = [APP_NAME];
18
+ if (!sessionManager.usesDefaultSessionDir()) {
19
+ args.push("--session-dir", quoteIfNeeded(sessionManager.getSessionDir()));
20
+ }
21
+ args.push("--session", sessionManager.getSessionId());
22
+ return args.join(" ");
23
+ }
24
+ //# sourceMappingURL=interactive-resume-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-resume-command.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-resume-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,SAAS,aAAa,CAAC,KAAa,EAAU;IAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7D,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAAA,CAC3C;AAED,MAAM,UAAU,mBAAmB,CAAC,cAA8B,EAAsB;IACvF,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC5C,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE;QAAE,OAAO,SAAS,CAAC;IAEpD,MAAM,WAAW,GAAG,cAAc,CAAC,cAAc,EAAE,CAAC;IACpD,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAC;IAElE,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,IAAI,CAAC,cAAc,CAAC,qBAAqB,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,YAAY,EAAE,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACtB","sourcesContent":["import * as fs from \"node:fs\";\nimport { APP_NAME } from \"../../config.ts\";\nimport type { SessionManager } from \"../../core/session-manager.ts\";\n\nfunction quoteIfNeeded(value: string): string {\n\tif (value.length > 0 && !/[^a-zA-Z0-9_\\-./~:@]/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport function formatResumeCommand(sessionManager: SessionManager): string | undefined {\n\tif (!process.stdout.isTTY) return undefined;\n\tif (!sessionManager.isPersisted()) return undefined;\n\n\tconst sessionFile = sessionManager.getSessionFile();\n\tif (!sessionFile || !fs.existsSync(sessionFile)) return undefined;\n\n\tconst args = [APP_NAME];\n\tif (!sessionManager.usesDefaultSessionDir()) {\n\t\targs.push(\"--session-dir\", quoteIfNeeded(sessionManager.getSessionDir()));\n\t}\n\targs.push(\"--session\", sessionManager.getSessionId());\n\treturn args.join(\" \");\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { Text } from "omk-tui";
2
+ /** Interface for components that can be expanded/collapsed */
3
+ interface Expandable {
4
+ setExpanded(expanded: boolean): void;
5
+ }
6
+ export declare function isExpandable(obj: unknown): obj is Expandable;
7
+ export declare function disposeComponent(component: unknown): void;
8
+ type ToolExecutionContent = {
9
+ type: "text";
10
+ text: string;
11
+ } | {
12
+ type: "image";
13
+ data: string;
14
+ mimeType: string;
15
+ };
16
+ type ToolExecutionResult = {
17
+ content: ToolExecutionContent[];
18
+ details?: unknown;
19
+ };
20
+ export declare function normalizeToolExecutionResult(result: unknown, isError: boolean): ToolExecutionResult & {
21
+ isError: boolean;
22
+ };
23
+ export declare class ExpandableText extends Text implements Expandable {
24
+ private readonly getCollapsedText;
25
+ private readonly getExpandedText;
26
+ private expanded;
27
+ constructor(getCollapsedText: () => string, getExpandedText: () => string, expanded?: boolean, paddingX?: number, paddingY?: number);
28
+ setExpanded(expanded: boolean): void;
29
+ invalidate(): void;
30
+ }
31
+ export {};
32
+ //# sourceMappingURL=interactive-tool-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-tool-result.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-tool-result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,8DAA8D;AAC9D,UAAU,UAAU;IACnB,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;CACrC;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CASzD;AACD,KAAK,oBAAoB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/G,KAAK,mBAAmB,GAAG;IAC1B,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAgBF,wBAAgB,4BAA4B,CAC3C,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,OAAO,GACd,mBAAmB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAa5C;AAED,qBAAa,cAAe,SAAQ,IAAK,YAAW,UAAU;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAU;IAE1B,YACC,gBAAgB,EAAE,MAAM,MAAM,EAC9B,eAAe,EAAE,MAAM,MAAM,EAC7B,QAAQ,UAAQ,EAChB,QAAQ,SAAI,EACZ,QAAQ,SAAI,EAMZ;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;CACD","sourcesContent":["import { Text } from \"omk-tui\";\n\n/** Interface for components that can be expanded/collapsed */\ninterface Expandable {\n\tsetExpanded(expanded: boolean): void;\n}\n\nexport function isExpandable(obj: unknown): obj is Expandable {\n\treturn typeof obj === \"object\" && obj !== null && \"setExpanded\" in obj && typeof obj.setExpanded === \"function\";\n}\n\nexport function disposeComponent(component: unknown): void {\n\tif (\n\t\ttypeof component === \"object\" &&\n\t\tcomponent !== null &&\n\t\t\"dispose\" in component &&\n\t\ttypeof component.dispose === \"function\"\n\t) {\n\t\tcomponent.dispose();\n\t}\n}\ntype ToolExecutionContent = { type: \"text\"; text: string } | { type: \"image\"; data: string; mimeType: string };\n\ntype ToolExecutionResult = {\n\tcontent: ToolExecutionContent[];\n\tdetails?: unknown;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isToolExecutionContent(value: unknown): value is ToolExecutionContent {\n\tif (!isRecord(value)) return false;\n\tif (value.type === \"text\") return typeof value.text === \"string\";\n\treturn value.type === \"image\" && typeof value.data === \"string\" && typeof value.mimeType === \"string\";\n}\n\nfunction isToolExecutionResult(value: unknown): value is ToolExecutionResult {\n\treturn isRecord(value) && Array.isArray(value.content) && value.content.every(isToolExecutionContent);\n}\n\nexport function normalizeToolExecutionResult(\n\tresult: unknown,\n\tisError: boolean,\n): ToolExecutionResult & { isError: boolean } {\n\ttry {\n\t\tif (isToolExecutionResult(result)) {\n\t\t\treturn { content: result.content, details: result.details, isError };\n\t\t}\n\t} catch {\n\t\t// Treat inaccessible or malformed extension payloads as invalid results.\n\t}\n\n\treturn {\n\t\tcontent: [{ type: \"text\", text: \"Tool returned an invalid result.\" }],\n\t\tisError: true,\n\t};\n}\n\nexport class ExpandableText extends Text implements Expandable {\n\tprivate readonly getCollapsedText: () => string;\n\tprivate readonly getExpandedText: () => string;\n\tprivate expanded: boolean;\n\n\tconstructor(\n\t\tgetCollapsedText: () => string,\n\t\tgetExpandedText: () => string,\n\t\texpanded = false,\n\t\tpaddingX = 0,\n\t\tpaddingY = 0,\n\t) {\n\t\tsuper(expanded ? getExpandedText() : getCollapsedText(), paddingX, paddingY);\n\t\tthis.getCollapsedText = getCollapsedText;\n\t\tthis.getExpandedText = getExpandedText;\n\t\tthis.expanded = expanded;\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.setText(expanded ? this.getExpandedText() : this.getCollapsedText());\n\t}\n\n\toverride invalidate(): void {\n\t\tthis.setText(this.expanded ? this.getExpandedText() : this.getCollapsedText());\n\t\tsuper.invalidate();\n\t}\n}\n"]}
@@ -0,0 +1,59 @@
1
+ import { Text } from "omk-tui";
2
+ export function isExpandable(obj) {
3
+ return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
4
+ }
5
+ export function disposeComponent(component) {
6
+ if (typeof component === "object" &&
7
+ component !== null &&
8
+ "dispose" in component &&
9
+ typeof component.dispose === "function") {
10
+ component.dispose();
11
+ }
12
+ }
13
+ function isRecord(value) {
14
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15
+ }
16
+ function isToolExecutionContent(value) {
17
+ if (!isRecord(value))
18
+ return false;
19
+ if (value.type === "text")
20
+ return typeof value.text === "string";
21
+ return value.type === "image" && typeof value.data === "string" && typeof value.mimeType === "string";
22
+ }
23
+ function isToolExecutionResult(value) {
24
+ return isRecord(value) && Array.isArray(value.content) && value.content.every(isToolExecutionContent);
25
+ }
26
+ export function normalizeToolExecutionResult(result, isError) {
27
+ try {
28
+ if (isToolExecutionResult(result)) {
29
+ return { content: result.content, details: result.details, isError };
30
+ }
31
+ }
32
+ catch {
33
+ // Treat inaccessible or malformed extension payloads as invalid results.
34
+ }
35
+ return {
36
+ content: [{ type: "text", text: "Tool returned an invalid result." }],
37
+ isError: true,
38
+ };
39
+ }
40
+ export class ExpandableText extends Text {
41
+ getCollapsedText;
42
+ getExpandedText;
43
+ expanded;
44
+ constructor(getCollapsedText, getExpandedText, expanded = false, paddingX = 0, paddingY = 0) {
45
+ super(expanded ? getExpandedText() : getCollapsedText(), paddingX, paddingY);
46
+ this.getCollapsedText = getCollapsedText;
47
+ this.getExpandedText = getExpandedText;
48
+ this.expanded = expanded;
49
+ }
50
+ setExpanded(expanded) {
51
+ this.expanded = expanded;
52
+ this.setText(expanded ? this.getExpandedText() : this.getCollapsedText());
53
+ }
54
+ invalidate() {
55
+ this.setText(this.expanded ? this.getExpandedText() : this.getCollapsedText());
56
+ super.invalidate();
57
+ }
58
+ }
59
+ //# sourceMappingURL=interactive-tool-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-tool-result.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-tool-result.ts"],"names":[],"mappings":"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","sourcesContent":["import { Text } from \"omk-tui\";\n\n/** Interface for components that can be expanded/collapsed */\ninterface Expandable {\n\tsetExpanded(expanded: boolean): void;\n}\n\nexport function isExpandable(obj: unknown): obj is Expandable {\n\treturn typeof obj === \"object\" && obj !== null && \"setExpanded\" in obj && typeof obj.setExpanded === \"function\";\n}\n\nexport function disposeComponent(component: unknown): void {\n\tif (\n\t\ttypeof component === \"object\" &&\n\t\tcomponent !== null &&\n\t\t\"dispose\" in component &&\n\t\ttypeof component.dispose === \"function\"\n\t) {\n\t\tcomponent.dispose();\n\t}\n}\ntype ToolExecutionContent = { type: \"text\"; text: string } | { type: \"image\"; data: string; mimeType: string };\n\ntype ToolExecutionResult = {\n\tcontent: ToolExecutionContent[];\n\tdetails?: unknown;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isToolExecutionContent(value: unknown): value is ToolExecutionContent {\n\tif (!isRecord(value)) return false;\n\tif (value.type === \"text\") return typeof value.text === \"string\";\n\treturn value.type === \"image\" && typeof value.data === \"string\" && typeof value.mimeType === \"string\";\n}\n\nfunction isToolExecutionResult(value: unknown): value is ToolExecutionResult {\n\treturn isRecord(value) && Array.isArray(value.content) && value.content.every(isToolExecutionContent);\n}\n\nexport function normalizeToolExecutionResult(\n\tresult: unknown,\n\tisError: boolean,\n): ToolExecutionResult & { isError: boolean } {\n\ttry {\n\t\tif (isToolExecutionResult(result)) {\n\t\t\treturn { content: result.content, details: result.details, isError };\n\t\t}\n\t} catch {\n\t\t// Treat inaccessible or malformed extension payloads as invalid results.\n\t}\n\n\treturn {\n\t\tcontent: [{ type: \"text\", text: \"Tool returned an invalid result.\" }],\n\t\tisError: true,\n\t};\n}\n\nexport class ExpandableText extends Text implements Expandable {\n\tprivate readonly getCollapsedText: () => string;\n\tprivate readonly getExpandedText: () => string;\n\tprivate expanded: boolean;\n\n\tconstructor(\n\t\tgetCollapsedText: () => string,\n\t\tgetExpandedText: () => string,\n\t\texpanded = false,\n\t\tpaddingX = 0,\n\t\tpaddingY = 0,\n\t) {\n\t\tsuper(expanded ? getExpandedText() : getCollapsedText(), paddingX, paddingY);\n\t\tthis.getCollapsedText = getCollapsedText;\n\t\tthis.getExpandedText = getExpandedText;\n\t\tthis.expanded = expanded;\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.setText(expanded ? this.getExpandedText() : this.getCollapsedText());\n\t}\n\n\toverride invalidate(): void {\n\t\tthis.setText(this.expanded ? this.getExpandedText() : this.getCollapsedText());\n\t\tsuper.invalidate();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAK5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkL/G","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `omk -p \"prompt\"` - text output\n * - `omk --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"omk-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport { formatSessionTermination, type SessionTermination } from \"../core/session-termination.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tlet promptStarted = false;\n\tlet latestTermination: SessionTermination | undefined;\n\tlet renderedTermination: SessionTermination | undefined;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst renderTermination = (termination: SessionTermination): void => {\n\t\tif (termination.kind === \"completed\" || renderedTermination === termination) return;\n\t\trenderedTermination = termination;\n\t\tif (mode === \"json\") {\n\t\t\twriteRawStdout(`${JSON.stringify({ type: \"session_termination\", termination })}\\n`);\n\t\t} else {\n\t\t\tconsole.error(formatSessionTermination(termination));\n\t\t}\n\t};\n\n\tconst renderFailure = (fallback: string): void => {\n\t\tconst termination = latestTermination ?? (!promptStarted ? session.lastTermination : undefined);\n\t\tif (termination && termination.kind !== \"completed\") {\n\t\t\trenderTermination(termination);\n\t\t\treturn;\n\t\t}\n\t\tconsole.error(fallback);\n\t};\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: Array<\"SIGTERM\" | \"SIGHUP\"> = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tsession.recordProcessSignal(signal);\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tpromptStarted = false;\n\t\tlatestTermination = undefined;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (event.type === \"session_termination\") {\n\t\t\t\tlatestTermination = event.termination;\n\t\t\t\tif (mode === \"text\") {\n\t\t\t\t\trenderTermination(event.termination);\n\t\t\t\t} else {\n\t\t\t\t\trenderedTermination = event.termination;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\n\t\tconst startupTermination = session.lastTermination;\n\t\tif (startupTermination?.kind === \"process_crash\" && startupTermination.source === \"inferred_on_resume\") {\n\t\t\trenderTermination(startupTermination);\n\t\t}\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tconst hasPrompt =\n\t\t\t!!initialMessage?.trim() ||\n\t\t\tmessages.some((m) => typeof m === \"string\" && m.trim().length > 0) ||\n\t\t\t!!(initialImages && initialImages.length > 0);\n\t\tif (!hasPrompt) {\n\t\t\t// Bare `omk` with no prompt used to exit silently in print mode.\n\t\t\tconsole.error('No prompt provided. Use: omk -p \"...\" or run `omk` in an interactive terminal.');\n\t\t\texitCode = 2;\n\t\t\treturn exitCode;\n\t\t}\n\n\t\tif (initialMessage) {\n\t\t\tpromptStarted = true;\n\t\t\tawait session.prompt(initialMessage, { images: initialImages });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tpromptStarted = true;\n\t\t\tawait session.prompt(message);\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\trenderFailure(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\trenderFailure(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
1
+ {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAM5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0M/G","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `omk -p \"prompt\"` - text output\n * - `omk --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"omk-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport type { PromptSettlementOutcome } from \"../core/prompt-settlement.ts\";\nimport { formatSessionTermination, type SessionTermination } from \"../core/session-termination.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tlet promptStarted = false;\n\tlet latestTermination: SessionTermination | undefined;\n\tlet latestOutcome: PromptSettlementOutcome | undefined;\n\tlet renderedTermination: SessionTermination | undefined;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst renderTermination = (termination: SessionTermination): void => {\n\t\tif (termination.kind === \"completed\" || renderedTermination === termination) return;\n\t\trenderedTermination = termination;\n\t\tif (mode === \"json\") {\n\t\t\twriteRawStdout(`${JSON.stringify({ type: \"session_termination\", termination })}\\n`);\n\t\t} else {\n\t\t\tconsole.error(formatSessionTermination(termination));\n\t\t}\n\t};\n\n\tconst renderFailure = (fallback: string): void => {\n\t\tconst termination = latestTermination ?? (!promptStarted ? session.lastTermination : undefined);\n\t\tif (termination && termination.kind !== \"completed\") {\n\t\t\trenderTermination(termination);\n\t\t\treturn;\n\t\t}\n\t\tconsole.error(fallback);\n\t};\n\n\tconst promptFailed = (): boolean => {\n\t\tconst last = session.state.messages.at(-1);\n\t\tconst assistantFailed =\n\t\t\tlast?.role === \"assistant\" && (last.stopReason === \"error\" || last.stopReason === \"aborted\");\n\t\tconst failed =\n\t\t\tlatestOutcome !== undefined\n\t\t\t\t? latestOutcome !== \"completed\"\n\t\t\t\t: assistantFailed || (latestTermination !== undefined && latestTermination.kind !== \"completed\");\n\t\tif (failed)\n\t\t\trenderFailure(\n\t\t\t\tassistantFailed ? last.errorMessage || `Request ${last.stopReason}` : \"Prompt did not complete.\",\n\t\t\t);\n\t\treturn failed;\n\t};\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: Array<\"SIGTERM\" | \"SIGHUP\"> = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tsession.recordProcessSignal(signal);\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tpromptStarted = false;\n\t\tlatestTermination = undefined;\n\t\tlatestOutcome = undefined;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (event.type === \"prompt_settled\") latestOutcome = event.outcome;\n\t\t\tif (event.type === \"session_termination\") {\n\t\t\t\tlatestTermination = event.termination;\n\t\t\t\tif (mode === \"text\") {\n\t\t\t\t\trenderTermination(event.termination);\n\t\t\t\t} else {\n\t\t\t\t\trenderedTermination = event.termination;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\n\t\tconst startupTermination = session.lastTermination;\n\t\tif (startupTermination?.kind === \"process_crash\" && startupTermination.source === \"inferred_on_resume\") {\n\t\t\trenderTermination(startupTermination);\n\t\t}\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tconst hasPrompt =\n\t\t\t!!initialMessage?.trim() ||\n\t\t\tmessages.some((m) => typeof m === \"string\" && m.trim().length > 0) ||\n\t\t\t!!(initialImages && initialImages.length > 0);\n\t\tif (!hasPrompt) {\n\t\t\t// Bare `omk` with no prompt used to exit silently in print mode.\n\t\t\tconsole.error('No prompt provided. Use: omk -p \"...\" or run `omk` in an interactive terminal.');\n\t\t\texitCode = 2;\n\t\t\treturn exitCode;\n\t\t}\n\n\t\tif (initialMessage) {\n\t\t\tpromptStarted = true;\n\t\t\tlatestTermination = undefined;\n\t\t\tlatestOutcome = undefined;\n\t\t\tawait session.prompt(initialMessage, { images: initialImages });\n\t\t\tif (promptFailed()) return 1;\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tpromptStarted = true;\n\t\t\tlatestTermination = undefined;\n\t\t\tlatestOutcome = undefined;\n\t\t\tawait session.prompt(message);\n\t\t\tif (promptFailed()) return 1;\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\trenderFailure(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\trenderFailure(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
@@ -20,6 +20,7 @@ export async function runPrintMode(runtimeHost, options) {
20
20
  let disposed = false;
21
21
  let promptStarted = false;
22
22
  let latestTermination;
23
+ let latestOutcome;
23
24
  let renderedTermination;
24
25
  const signalCleanupHandlers = [];
25
26
  const renderTermination = (termination) => {
@@ -41,6 +42,16 @@ export async function runPrintMode(runtimeHost, options) {
41
42
  }
42
43
  console.error(fallback);
43
44
  };
45
+ const promptFailed = () => {
46
+ const last = session.state.messages.at(-1);
47
+ const assistantFailed = last?.role === "assistant" && (last.stopReason === "error" || last.stopReason === "aborted");
48
+ const failed = latestOutcome !== undefined
49
+ ? latestOutcome !== "completed"
50
+ : assistantFailed || (latestTermination !== undefined && latestTermination.kind !== "completed");
51
+ if (failed)
52
+ renderFailure(assistantFailed ? last.errorMessage || `Request ${last.stopReason}` : "Prompt did not complete.");
53
+ return failed;
54
+ };
44
55
  const disposeRuntime = async () => {
45
56
  if (disposed)
46
57
  return;
@@ -73,6 +84,7 @@ export async function runPrintMode(runtimeHost, options) {
73
84
  session = runtimeHost.session;
74
85
  promptStarted = false;
75
86
  latestTermination = undefined;
87
+ latestOutcome = undefined;
76
88
  await session.bindExtensions({
77
89
  mode: mode === "json" ? "json" : "print",
78
90
  commandContextActions: {
@@ -104,6 +116,8 @@ export async function runPrintMode(runtimeHost, options) {
104
116
  });
105
117
  unsubscribe?.();
106
118
  unsubscribe = session.subscribe((event) => {
119
+ if (event.type === "prompt_settled")
120
+ latestOutcome = event.outcome;
107
121
  if (event.type === "session_termination") {
108
122
  latestTermination = event.termination;
109
123
  if (mode === "text") {
@@ -141,11 +155,19 @@ export async function runPrintMode(runtimeHost, options) {
141
155
  }
142
156
  if (initialMessage) {
143
157
  promptStarted = true;
158
+ latestTermination = undefined;
159
+ latestOutcome = undefined;
144
160
  await session.prompt(initialMessage, { images: initialImages });
161
+ if (promptFailed())
162
+ return 1;
145
163
  }
146
164
  for (const message of messages) {
147
165
  promptStarted = true;
166
+ latestTermination = undefined;
167
+ latestOutcome = undefined;
148
168
  await session.prompt(message);
169
+ if (promptFailed())
170
+ return 1;
149
171
  }
150
172
  if (mode === "text") {
151
173
  const state = session.state;
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAA2B,MAAM,gCAAgC,CAAC;AACnG,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAgBhE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAgC,EAAE,OAAyB,EAAmB;IAChH,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IACvE,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;IAClC,IAAI,WAAqC,CAAC;IAC1C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,iBAAiD,CAAC;IACtD,IAAI,mBAAmD,CAAC;IACxD,MAAM,qBAAqB,GAAsB,EAAE,CAAC;IAEpD,MAAM,iBAAiB,GAAG,CAAC,WAA+B,EAAQ,EAAE,CAAC;QACpE,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,IAAI,mBAAmB,KAAK,WAAW;YAAE,OAAO;QACpF,mBAAmB,GAAG,WAAW,CAAC;QAClC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC;QACrF,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,WAAW,CAAC,CAAC,CAAC;QACtD,CAAC;IAAA,CACD,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAQ,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,iBAAiB,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAChG,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACrD,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAC/B,OAAO;QACR,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAAA,CACxB,CAAC;IAEF,MAAM,cAAc,GAAG,KAAK,IAAmB,EAAE,CAAC;QACjD,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,WAAW,EAAE,EAAE,CAAC;QAChB,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IAAA,CAC5B,CAAC;IAEF,MAAM,sBAAsB,GAAG,GAAS,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAgC,CAAC,SAAS,CAAC,CAAC;QACzD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;gBACrB,OAAO,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;gBACpC,2BAA2B,EAAE,CAAC;gBAC9B,KAAK,cAAc,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;oBACnC,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAAA,CAC9C,CAAC,CAAC;YAAA,CACH,CAAC;YACF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC5B,qBAAqB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAChE,CAAC;IAAA,CACD,CAAC;IAEF,sBAAsB,EAAE,CAAC;IAEzB,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,aAAa,EAAE,CAAC;IAAA,CACtB,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE,CAAC;QAChD,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;QAC9B,aAAa,GAAG,KAAK,CAAC;QACtB,iBAAiB,GAAG,SAAS,CAAC;QAC9B,MAAM,OAAO,CAAC,cAAc,CAAC;YAC5B,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;YACxC,qBAAqB,EAAE;gBACtB,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE;gBAC9C,UAAU,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,iBAAiB,CAAC;gBAClF,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC;oBACrC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;oBAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;gBAAA,CACvC;gBACD,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,CAAC;oBAClD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE;wBACnD,SAAS,EAAE,eAAe,EAAE,SAAS;wBACrC,kBAAkB,EAAE,eAAe,EAAE,kBAAkB;wBACvD,mBAAmB,EAAE,eAAe,EAAE,mBAAmB;wBACzD,KAAK,EAAE,eAAe,EAAE,KAAK;qBAC7B,CAAC,CAAC;oBACH,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;gBAAA,CACvC;gBACD,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE,CAAC;oBACpD,OAAO,WAAW,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;gBAAA,CAC7D;gBACD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;oBACnB,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;gBAAA,CACvB;aACD;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,aAAa,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;YAAA,CACtE;SACD,CAAC,CAAC;QAEH,WAAW,EAAE,EAAE,CAAC;QAChB,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;gBAC1C,iBAAiB,GAAG,KAAK,CAAC,WAAW,CAAC;gBACtC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACrB,iBAAiB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;gBACtC,CAAC;qBAAM,CAAC;oBACP,mBAAmB,GAAG,KAAK,CAAC,WAAW,CAAC;gBACzC,CAAC;YACF,CAAC;YACD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACrB,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9C,CAAC;QAAA,CACD,CAAC,CAAC;QAEH,MAAM,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC;QACnD,IAAI,kBAAkB,EAAE,IAAI,KAAK,eAAe,IAAI,kBAAkB,CAAC,MAAM,KAAK,oBAAoB,EAAE,CAAC;YACxG,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;QACvC,CAAC;IAAA,CACD,CAAC;IAEF,IAAI,CAAC;QACJ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;YAClD,IAAI,MAAM,EAAE,CAAC;gBACZ,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;QACF,CAAC;QAED,MAAM,aAAa,EAAE,CAAC;QAEtB,MAAM,SAAS,GACd,CAAC,CAAC,cAAc,EAAE,IAAI,EAAE;YACxB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/C,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,iEAAiE;YACjE,OAAO,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;YACjG,QAAQ,GAAG,CAAC,CAAC;YACb,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,IAAI,cAAc,EAAE,CAAC;YACpB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAChC,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;YAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAE9D,IAAI,WAAW,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBACvC,MAAM,YAAY,GAAG,WAA+B,CAAC;gBACrD,IAAI,YAAY,CAAC,UAAU,KAAK,OAAO,IAAI,YAAY,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBAClF,aAAa,CAAC,YAAY,CAAC,YAAY,IAAI,WAAW,YAAY,CAAC,UAAU,EAAE,CAAC,CAAC;oBACjF,QAAQ,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACP,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;wBAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;4BAC7B,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;wBACrC,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,OAAO,QAAQ,CAAC;IACjB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,aAAa,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACV,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;YAC7C,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,cAAc,EAAE,CAAC;QACvB,MAAM,cAAc,EAAE,CAAC;IACxB,CAAC;AAAA,CACD","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `omk -p \"prompt\"` - text output\n * - `omk --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"omk-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport { formatSessionTermination, type SessionTermination } from \"../core/session-termination.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tlet promptStarted = false;\n\tlet latestTermination: SessionTermination | undefined;\n\tlet renderedTermination: SessionTermination | undefined;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst renderTermination = (termination: SessionTermination): void => {\n\t\tif (termination.kind === \"completed\" || renderedTermination === termination) return;\n\t\trenderedTermination = termination;\n\t\tif (mode === \"json\") {\n\t\t\twriteRawStdout(`${JSON.stringify({ type: \"session_termination\", termination })}\\n`);\n\t\t} else {\n\t\t\tconsole.error(formatSessionTermination(termination));\n\t\t}\n\t};\n\n\tconst renderFailure = (fallback: string): void => {\n\t\tconst termination = latestTermination ?? (!promptStarted ? session.lastTermination : undefined);\n\t\tif (termination && termination.kind !== \"completed\") {\n\t\t\trenderTermination(termination);\n\t\t\treturn;\n\t\t}\n\t\tconsole.error(fallback);\n\t};\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: Array<\"SIGTERM\" | \"SIGHUP\"> = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tsession.recordProcessSignal(signal);\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tpromptStarted = false;\n\t\tlatestTermination = undefined;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (event.type === \"session_termination\") {\n\t\t\t\tlatestTermination = event.termination;\n\t\t\t\tif (mode === \"text\") {\n\t\t\t\t\trenderTermination(event.termination);\n\t\t\t\t} else {\n\t\t\t\t\trenderedTermination = event.termination;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\n\t\tconst startupTermination = session.lastTermination;\n\t\tif (startupTermination?.kind === \"process_crash\" && startupTermination.source === \"inferred_on_resume\") {\n\t\t\trenderTermination(startupTermination);\n\t\t}\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tconst hasPrompt =\n\t\t\t!!initialMessage?.trim() ||\n\t\t\tmessages.some((m) => typeof m === \"string\" && m.trim().length > 0) ||\n\t\t\t!!(initialImages && initialImages.length > 0);\n\t\tif (!hasPrompt) {\n\t\t\t// Bare `omk` with no prompt used to exit silently in print mode.\n\t\t\tconsole.error('No prompt provided. Use: omk -p \"...\" or run `omk` in an interactive terminal.');\n\t\t\texitCode = 2;\n\t\t\treturn exitCode;\n\t\t}\n\n\t\tif (initialMessage) {\n\t\t\tpromptStarted = true;\n\t\t\tawait session.prompt(initialMessage, { images: initialImages });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tpromptStarted = true;\n\t\t\tawait session.prompt(message);\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\trenderFailure(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\trenderFailure(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
1
+ {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `omk -p \"prompt\"` - text output\n * - `omk --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"omk-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.ts\";\nimport type { PromptSettlementOutcome } from \"../core/prompt-settlement.ts\";\nimport { formatSessionTermination, type SessionTermination } from \"../core/session-termination.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tlet promptStarted = false;\n\tlet latestTermination: SessionTermination | undefined;\n\tlet latestOutcome: PromptSettlementOutcome | undefined;\n\tlet renderedTermination: SessionTermination | undefined;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst renderTermination = (termination: SessionTermination): void => {\n\t\tif (termination.kind === \"completed\" || renderedTermination === termination) return;\n\t\trenderedTermination = termination;\n\t\tif (mode === \"json\") {\n\t\t\twriteRawStdout(`${JSON.stringify({ type: \"session_termination\", termination })}\\n`);\n\t\t} else {\n\t\t\tconsole.error(formatSessionTermination(termination));\n\t\t}\n\t};\n\n\tconst renderFailure = (fallback: string): void => {\n\t\tconst termination = latestTermination ?? (!promptStarted ? session.lastTermination : undefined);\n\t\tif (termination && termination.kind !== \"completed\") {\n\t\t\trenderTermination(termination);\n\t\t\treturn;\n\t\t}\n\t\tconsole.error(fallback);\n\t};\n\n\tconst promptFailed = (): boolean => {\n\t\tconst last = session.state.messages.at(-1);\n\t\tconst assistantFailed =\n\t\t\tlast?.role === \"assistant\" && (last.stopReason === \"error\" || last.stopReason === \"aborted\");\n\t\tconst failed =\n\t\t\tlatestOutcome !== undefined\n\t\t\t\t? latestOutcome !== \"completed\"\n\t\t\t\t: assistantFailed || (latestTermination !== undefined && latestTermination.kind !== \"completed\");\n\t\tif (failed)\n\t\t\trenderFailure(\n\t\t\t\tassistantFailed ? last.errorMessage || `Request ${last.stopReason}` : \"Prompt did not complete.\",\n\t\t\t);\n\t\treturn failed;\n\t};\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: Array<\"SIGTERM\" | \"SIGHUP\"> = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tsession.recordProcessSignal(signal);\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tpromptStarted = false;\n\t\tlatestTermination = undefined;\n\t\tlatestOutcome = undefined;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (event.type === \"prompt_settled\") latestOutcome = event.outcome;\n\t\t\tif (event.type === \"session_termination\") {\n\t\t\t\tlatestTermination = event.termination;\n\t\t\t\tif (mode === \"text\") {\n\t\t\t\t\trenderTermination(event.termination);\n\t\t\t\t} else {\n\t\t\t\t\trenderedTermination = event.termination;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\n\t\tconst startupTermination = session.lastTermination;\n\t\tif (startupTermination?.kind === \"process_crash\" && startupTermination.source === \"inferred_on_resume\") {\n\t\t\trenderTermination(startupTermination);\n\t\t}\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tconst hasPrompt =\n\t\t\t!!initialMessage?.trim() ||\n\t\t\tmessages.some((m) => typeof m === \"string\" && m.trim().length > 0) ||\n\t\t\t!!(initialImages && initialImages.length > 0);\n\t\tif (!hasPrompt) {\n\t\t\t// Bare `omk` with no prompt used to exit silently in print mode.\n\t\t\tconsole.error('No prompt provided. Use: omk -p \"...\" or run `omk` in an interactive terminal.');\n\t\t\texitCode = 2;\n\t\t\treturn exitCode;\n\t\t}\n\n\t\tif (initialMessage) {\n\t\t\tpromptStarted = true;\n\t\t\tlatestTermination = undefined;\n\t\t\tlatestOutcome = undefined;\n\t\t\tawait session.prompt(initialMessage, { images: initialImages });\n\t\t\tif (promptFailed()) return 1;\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tpromptStarted = true;\n\t\t\tlatestTermination = undefined;\n\t\t\tlatestOutcome = undefined;\n\t\t\tawait session.prompt(message);\n\t\t\tif (promptFailed()) return 1;\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\trenderFailure(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\trenderFailure(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard-image.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard-image.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AASF,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE9E;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAazE;AA6OD,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA+BjC","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { readFileSync, unlinkSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { clipboard } from \"./clipboard-native.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport type ClipboardImage = {\n\tbytes: Uint8Array;\n\tmimeType: string;\n};\n\nconst SUPPORTED_IMAGE_MIME_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n\nconst DEFAULT_LIST_TIMEOUT_MS = 1000;\nconst DEFAULT_READ_TIMEOUT_MS = 3000;\nconst DEFAULT_POWERSHELL_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\nexport function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === \"wayland\";\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nexport function extensionForImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"png\";\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction selectPreferredImageMimeType(mimeTypes: string[]): string | null {\n\tconst normalized = mimeTypes\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean)\n\t\t.map((t) => ({ raw: t, base: baseMimeType(t) }));\n\n\tfor (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {\n\t\tconst match = normalized.find((t) => t.base === preferred);\n\t\tif (match) {\n\t\t\treturn match.raw;\n\t\t}\n\t}\n\n\tconst anyImage = normalized.find((t) => t.base.startsWith(\"image/\"));\n\treturn anyImage?.raw ?? null;\n}\n\nfunction isSupportedImageMimeType(mimeType: string): boolean {\n\tconst base = baseMimeType(mimeType);\n\treturn SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);\n}\n\n/**\n * Convert unsupported image formats to PNG using Photon.\n * Returns null if conversion is unavailable or fails.\n */\nasync function convertToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst image = photon.PhotonImage.new_from_byteslice(bytes);\n\t\ttry {\n\t\t\treturn image.get_bytes();\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction runCommand(\n\tcommand: string,\n\targs: string[],\n\toptions?: { timeoutMs?: number; maxBufferBytes?: number; env?: NodeJS.ProcessEnv },\n): { stdout: Buffer; ok: boolean } {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;\n\tconst maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n\n\tconst result = spawnSync(command, args, {\n\t\ttimeout: timeoutMs,\n\t\tmaxBuffer: maxBufferBytes,\n\t\tenv: options?.env,\n\t});\n\n\tif (result.error) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (result.status !== 0) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (Buffer.isBuffer(result.stdout)) {\n\t\treturn { ok: true, stdout: result.stdout };\n\t}\n\n\tconst encoding = typeof result.stdout === \"string\" ? \"utf-8\" : undefined;\n\treturn { ok: true, stdout: Buffer.from(result.stdout ?? \"\", encoding) };\n}\n\nfunction readClipboardImageViaWlPaste(): ClipboardImage | null {\n\tconst list = runCommand(\"wl-paste\", [\"--list-types\"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\tif (!list.ok) {\n\t\treturn null;\n\t}\n\n\tconst types = list.stdout\n\t\t.toString(\"utf-8\")\n\t\t.split(/\\r?\\n/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\n\tconst selectedType = selectPreferredImageMimeType(types);\n\tif (!selectedType) {\n\t\treturn null;\n\t}\n\n\tconst data = runCommand(\"wl-paste\", [\"--type\", selectedType, \"--no-newline\"]);\n\tif (!data.ok || data.stdout.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn { bytes: data.stdout, mimeType: baseMimeType(selectedType) };\n}\n\nfunction isWSL(env: NodeJS.ProcessEnv = process.env, inspectProcVersion = true): boolean {\n\tif (env.WSL_DISTRO_NAME || env.WSLENV) {\n\t\treturn true;\n\t}\n\n\tif (!inspectProcVersion) {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst release = readFileSync(\"/proc/version\", \"utf-8\");\n\t\treturn /microsoft|wsl/i.test(release);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * On WSL, the Linux clipboard (Wayland/X11) does not receive image data from\n * Windows screenshots (Win+Shift+S). PowerShell can access the Windows clipboard\n * directly, so we use it as a fallback.\n */\nfunction readClipboardImageViaPowerShell(): ClipboardImage | null {\n\tconst tmpFile = join(tmpdir(), `omk-wsl-clip-${randomUUID()}.png`);\n\n\ttry {\n\t\tconst winPathResult = runCommand(\"wslpath\", [\"-w\", tmpFile], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\t\tif (!winPathResult.ok) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst winPath = winPathResult.stdout.toString(\"utf-8\").trim();\n\t\tif (!winPath) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst psQuotedWinPath = winPath.replaceAll(\"'\", \"''\");\n\t\tconst psScript = [\n\t\t\t\"Add-Type -AssemblyName System.Windows.Forms\",\n\t\t\t\"Add-Type -AssemblyName System.Drawing\",\n\t\t\t`$path = '${psQuotedWinPath}'`,\n\t\t\t\"$img = [System.Windows.Forms.Clipboard]::GetImage()\",\n\t\t\t\"if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }\",\n\t\t].join(\"; \");\n\n\t\tconst result = runCommand(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], {\n\t\t\ttimeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,\n\t\t});\n\t\tif (!result.ok) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst output = result.stdout.toString(\"utf-8\").trim();\n\t\tif (output !== \"ok\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst bytes = readFileSync(tmpFile);\n\t\tif (bytes.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn { bytes: new Uint8Array(bytes), mimeType: \"image/png\" };\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\ttry {\n\t\t\tunlinkSync(tmpFile);\n\t\t} catch {\n\t\t\t// Ignore cleanup errors.\n\t\t}\n\t}\n}\n\nfunction readClipboardImageViaXclip(): ClipboardImage | null {\n\tconst targets = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"], {\n\t\ttimeoutMs: DEFAULT_LIST_TIMEOUT_MS,\n\t});\n\n\tlet candidateTypes: string[] = [];\n\tif (targets.ok) {\n\t\tcandidateTypes = targets.stdout\n\t\t\t.toString(\"utf-8\")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tconst preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;\n\tconst tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];\n\n\tfor (const mimeType of tryTypes) {\n\t\tconst data = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", mimeType, \"-o\"]);\n\t\tif (data.ok && data.stdout.length > 0) {\n\t\t\treturn { bytes: data.stdout, mimeType: baseMimeType(mimeType) };\n\t\t}\n\t}\n\n\treturn null;\n}\n\nasync function readClipboardImageViaNativeClipboard(): Promise<ClipboardImage | null> {\n\tif (!clipboard || !clipboard.hasImage()) {\n\t\treturn null;\n\t}\n\n\tconst imageData = await clipboard.getImageBinary();\n\tif (!imageData || imageData.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);\n\treturn { bytes, mimeType: \"image/png\" };\n}\n\n/**\n * Normalize one source's read into a supported format, or reject that read.\n *\n * Applied per source rather than once after the chain, because a source can\n * succeed at reading bytes yet still be unusable: WSLg publishes a Windows\n * screenshot as `image/bmp`, which needs conversion. Folding the conversion\n * into the source attempt keeps an unconvertible read from disqualifying the\n * sources behind it — notably the PowerShell reader, which returns PNG\n * directly and needs no converter at all.\n */\nasync function toSupportedImage(image: ClipboardImage | null): Promise<ClipboardImage | null> {\n\tif (!image) {\n\t\treturn null;\n\t}\n\tif (isSupportedImageMimeType(image.mimeType)) {\n\t\treturn image;\n\t}\n\tconst pngBytes = await convertToPng(image.bytes);\n\treturn pngBytes ? { bytes: pngBytes, mimeType: \"image/png\" } : null;\n}\n\nexport async function readClipboardImage(options?: {\n\tenv?: NodeJS.ProcessEnv;\n\tplatform?: NodeJS.Platform;\n}): Promise<ClipboardImage | null> {\n\tconst env = options?.env ?? process.env;\n\tconst platform = options?.platform ?? process.platform;\n\tconst hasExplicitEnv = options?.env !== undefined;\n\n\tif (env.TERMUX_VERSION) {\n\t\treturn null;\n\t}\n\n\tif (platform !== \"linux\") {\n\t\treturn toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\tconst wsl = isWSL(env, !hasExplicitEnv);\n\tconst wayland = isWaylandSession(env);\n\tlet image: ClipboardImage | null = null;\n\n\tif (wayland || wsl) {\n\t\timage = await toSupportedImage(readClipboardImageViaWlPaste());\n\t\timage ??= await toSupportedImage(readClipboardImageViaXclip());\n\t}\n\n\tif (!image && wsl) {\n\t\timage = await toSupportedImage(readClipboardImageViaPowerShell());\n\t}\n\n\tif (!image && !wayland) {\n\t\timage = await toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\treturn image;\n}\n"]}
1
+ {"version":3,"file":"clipboard-image.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard-image.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAQF,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE9E;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAazE;AAoLD,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmCjC","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\n\nimport { clipboard } from \"./clipboard-native.ts\";\nimport { loadPhoton } from \"./photon.ts\";\nimport { readWindowsClipboardImage, WindowsClipboardError } from \"./windows-clipboard-image.ts\";\n\nexport type ClipboardImage = {\n\tbytes: Uint8Array;\n\tmimeType: string;\n};\n\nconst SUPPORTED_IMAGE_MIME_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n\nconst DEFAULT_LIST_TIMEOUT_MS = 1000;\nconst DEFAULT_READ_TIMEOUT_MS = 3000;\nconst DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\nexport function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === \"wayland\";\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nexport function extensionForImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"png\";\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction selectPreferredImageMimeType(mimeTypes: string[]): string | null {\n\tconst normalized = mimeTypes\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean)\n\t\t.map((t) => ({ raw: t, base: baseMimeType(t) }));\n\n\tfor (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {\n\t\tconst match = normalized.find((t) => t.base === preferred);\n\t\tif (match) {\n\t\t\treturn match.raw;\n\t\t}\n\t}\n\n\tconst anyImage = normalized.find((t) => t.base.startsWith(\"image/\"));\n\treturn anyImage?.raw ?? null;\n}\n\nfunction isSupportedImageMimeType(mimeType: string): boolean {\n\tconst base = baseMimeType(mimeType);\n\treturn SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);\n}\n\n/**\n * Convert unsupported image formats to PNG using Photon.\n * Returns null if conversion is unavailable or fails.\n */\nasync function convertToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst image = photon.PhotonImage.new_from_byteslice(bytes);\n\t\ttry {\n\t\t\treturn image.get_bytes();\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction runCommand(\n\tcommand: string,\n\targs: string[],\n\toptions?: { timeoutMs?: number; maxBufferBytes?: number; env?: NodeJS.ProcessEnv },\n): { stdout: Buffer; ok: boolean } {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;\n\tconst maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n\n\tconst result = spawnSync(command, args, {\n\t\ttimeout: timeoutMs,\n\t\tmaxBuffer: maxBufferBytes,\n\t\tenv: options?.env,\n\t});\n\n\tif (result.error) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (result.status !== 0) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (Buffer.isBuffer(result.stdout)) {\n\t\treturn { ok: true, stdout: result.stdout };\n\t}\n\n\tconst encoding = typeof result.stdout === \"string\" ? \"utf-8\" : undefined;\n\treturn { ok: true, stdout: Buffer.from(result.stdout ?? \"\", encoding) };\n}\n\nfunction readClipboardImageViaWlPaste(): ClipboardImage | null {\n\tconst list = runCommand(\"wl-paste\", [\"--list-types\"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\tif (!list.ok) {\n\t\treturn null;\n\t}\n\n\tconst types = list.stdout\n\t\t.toString(\"utf-8\")\n\t\t.split(/\\r?\\n/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\n\tconst selectedType = selectPreferredImageMimeType(types);\n\tif (!selectedType) {\n\t\treturn null;\n\t}\n\n\tconst data = runCommand(\"wl-paste\", [\"--type\", selectedType, \"--no-newline\"]);\n\tif (!data.ok || data.stdout.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn { bytes: data.stdout, mimeType: baseMimeType(selectedType) };\n}\n\nfunction isWSL(env: NodeJS.ProcessEnv = process.env, inspectProcVersion = true): boolean {\n\tif (env.WSL_DISTRO_NAME || env.WSLENV) {\n\t\treturn true;\n\t}\n\n\tif (!inspectProcVersion) {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst release = readFileSync(\"/proc/version\", \"utf-8\");\n\t\treturn /microsoft|wsl/i.test(release);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction readClipboardImageViaXclip(): ClipboardImage | null {\n\tconst targets = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"], {\n\t\ttimeoutMs: DEFAULT_LIST_TIMEOUT_MS,\n\t});\n\n\tlet candidateTypes: string[] = [];\n\tif (targets.ok) {\n\t\tcandidateTypes = targets.stdout\n\t\t\t.toString(\"utf-8\")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tconst preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;\n\tconst tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];\n\n\tfor (const mimeType of tryTypes) {\n\t\tconst data = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", mimeType, \"-o\"]);\n\t\tif (data.ok && data.stdout.length > 0) {\n\t\t\treturn { bytes: data.stdout, mimeType: baseMimeType(mimeType) };\n\t\t}\n\t}\n\n\treturn null;\n}\n\nasync function readClipboardImageViaNativeClipboard(): Promise<ClipboardImage | null> {\n\tif (!clipboard || !clipboard.hasImage()) {\n\t\treturn null;\n\t}\n\n\tconst imageData = await clipboard.getImageBinary();\n\tif (!imageData || imageData.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);\n\treturn { bytes, mimeType: \"image/png\" };\n}\n\n/**\n * Normalize one source's read into a supported format, or reject that read.\n *\n * Applied per source rather than once after the chain, because a source can\n * succeed at reading bytes yet still be unusable: WSLg publishes a Windows\n * screenshot as `image/bmp`, which needs conversion. Folding the conversion\n * into the source attempt keeps an unconvertible read from disqualifying the\n * sources behind it — notably the PowerShell reader, which returns PNG\n * directly and needs no converter at all.\n */\nasync function toSupportedImage(image: ClipboardImage | null): Promise<ClipboardImage | null> {\n\tif (!image) {\n\t\treturn null;\n\t}\n\tif (isSupportedImageMimeType(image.mimeType)) {\n\t\treturn image;\n\t}\n\tconst pngBytes = await convertToPng(image.bytes);\n\treturn pngBytes ? { bytes: pngBytes, mimeType: \"image/png\" } : null;\n}\n\nexport async function readClipboardImage(options?: {\n\tenv?: NodeJS.ProcessEnv;\n\tplatform?: NodeJS.Platform;\n}): Promise<ClipboardImage | null> {\n\tconst env = options?.env ?? process.env;\n\tconst platform = options?.platform ?? process.platform;\n\tconst hasExplicitEnv = options?.env !== undefined;\n\n\tif (env.TERMUX_VERSION) {\n\t\treturn null;\n\t}\n\n\tif (platform !== \"linux\") {\n\t\treturn toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\tconst wsl = isWSL(env, !hasExplicitEnv);\n\tconst windows = wsl ? await readWindowsClipboardImage(env) : undefined;\n\tif (windows?.kind === \"image\") return { bytes: windows.bytes, mimeType: windows.mimeType };\n\tif (windows?.kind === \"empty\") return null;\n\tconst wayland = isWaylandSession(env);\n\tlet image: ClipboardImage | null = null;\n\n\tif (wayland || wsl) {\n\t\timage = await toSupportedImage(readClipboardImageViaWlPaste());\n\t\timage ??= await toSupportedImage(readClipboardImageViaXclip());\n\t}\n\n\tif (!image && !wayland) {\n\t\timage = await toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\tif (!image && windows?.kind === \"unavailable\") {\n\t\tthrow new WindowsClipboardError(\n\t\t\t`Windows clipboard unavailable (${windows.reason}). Check WSL interop and Windows PowerShell, or drop an image file.`,\n\t\t);\n\t}\n\treturn image;\n}\n"]}
@@ -1,14 +1,11 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { randomUUID } from "node:crypto";
3
- import { readFileSync, unlinkSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
2
+ import { readFileSync } from "node:fs";
6
3
  import { clipboard } from "./clipboard-native.js";
7
4
  import { loadPhoton } from "./photon.js";
5
+ import { readWindowsClipboardImage, WindowsClipboardError } from "./windows-clipboard-image.js";
8
6
  const SUPPORTED_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
9
7
  const DEFAULT_LIST_TIMEOUT_MS = 1000;
10
8
  const DEFAULT_READ_TIMEOUT_MS = 3000;
11
- const DEFAULT_POWERSHELL_TIMEOUT_MS = 5000;
12
9
  const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
13
10
  export function isWaylandSession(env = process.env) {
14
11
  return Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === "wayland";
@@ -125,58 +122,6 @@ function isWSL(env = process.env, inspectProcVersion = true) {
125
122
  return false;
126
123
  }
127
124
  }
128
- /**
129
- * On WSL, the Linux clipboard (Wayland/X11) does not receive image data from
130
- * Windows screenshots (Win+Shift+S). PowerShell can access the Windows clipboard
131
- * directly, so we use it as a fallback.
132
- */
133
- function readClipboardImageViaPowerShell() {
134
- const tmpFile = join(tmpdir(), `omk-wsl-clip-${randomUUID()}.png`);
135
- try {
136
- const winPathResult = runCommand("wslpath", ["-w", tmpFile], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
137
- if (!winPathResult.ok) {
138
- return null;
139
- }
140
- const winPath = winPathResult.stdout.toString("utf-8").trim();
141
- if (!winPath) {
142
- return null;
143
- }
144
- const psQuotedWinPath = winPath.replaceAll("'", "''");
145
- const psScript = [
146
- "Add-Type -AssemblyName System.Windows.Forms",
147
- "Add-Type -AssemblyName System.Drawing",
148
- `$path = '${psQuotedWinPath}'`,
149
- "$img = [System.Windows.Forms.Clipboard]::GetImage()",
150
- "if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }",
151
- ].join("; ");
152
- const result = runCommand("powershell.exe", ["-NoProfile", "-Command", psScript], {
153
- timeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,
154
- });
155
- if (!result.ok) {
156
- return null;
157
- }
158
- const output = result.stdout.toString("utf-8").trim();
159
- if (output !== "ok") {
160
- return null;
161
- }
162
- const bytes = readFileSync(tmpFile);
163
- if (bytes.length === 0) {
164
- return null;
165
- }
166
- return { bytes: new Uint8Array(bytes), mimeType: "image/png" };
167
- }
168
- catch {
169
- return null;
170
- }
171
- finally {
172
- try {
173
- unlinkSync(tmpFile);
174
- }
175
- catch {
176
- // Ignore cleanup errors.
177
- }
178
- }
179
- }
180
125
  function readClipboardImageViaXclip() {
181
126
  const targets = runCommand("xclip", ["-selection", "clipboard", "-t", "TARGETS", "-o"], {
182
127
  timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
@@ -241,18 +186,23 @@ export async function readClipboardImage(options) {
241
186
  return toSupportedImage(await readClipboardImageViaNativeClipboard());
242
187
  }
243
188
  const wsl = isWSL(env, !hasExplicitEnv);
189
+ const windows = wsl ? await readWindowsClipboardImage(env) : undefined;
190
+ if (windows?.kind === "image")
191
+ return { bytes: windows.bytes, mimeType: windows.mimeType };
192
+ if (windows?.kind === "empty")
193
+ return null;
244
194
  const wayland = isWaylandSession(env);
245
195
  let image = null;
246
196
  if (wayland || wsl) {
247
197
  image = await toSupportedImage(readClipboardImageViaWlPaste());
248
198
  image ??= await toSupportedImage(readClipboardImageViaXclip());
249
199
  }
250
- if (!image && wsl) {
251
- image = await toSupportedImage(readClipboardImageViaPowerShell());
252
- }
253
200
  if (!image && !wayland) {
254
201
  image = await toSupportedImage(await readClipboardImageViaNativeClipboard());
255
202
  }
203
+ if (!image && windows?.kind === "unavailable") {
204
+ throw new WindowsClipboardError(`Windows clipboard unavailable (${windows.reason}). Check WSL interop and Windows PowerShell, or drop an image file.`);
205
+ }
256
206
  return image;
257
207
  }
258
208
  //# sourceMappingURL=clipboard-image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/utils/clipboard-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { readFileSync, unlinkSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { clipboard } from \"./clipboard-native.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport type ClipboardImage = {\n\tbytes: Uint8Array;\n\tmimeType: string;\n};\n\nconst SUPPORTED_IMAGE_MIME_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n\nconst DEFAULT_LIST_TIMEOUT_MS = 1000;\nconst DEFAULT_READ_TIMEOUT_MS = 3000;\nconst DEFAULT_POWERSHELL_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\nexport function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === \"wayland\";\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nexport function extensionForImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"png\";\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction selectPreferredImageMimeType(mimeTypes: string[]): string | null {\n\tconst normalized = mimeTypes\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean)\n\t\t.map((t) => ({ raw: t, base: baseMimeType(t) }));\n\n\tfor (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {\n\t\tconst match = normalized.find((t) => t.base === preferred);\n\t\tif (match) {\n\t\t\treturn match.raw;\n\t\t}\n\t}\n\n\tconst anyImage = normalized.find((t) => t.base.startsWith(\"image/\"));\n\treturn anyImage?.raw ?? null;\n}\n\nfunction isSupportedImageMimeType(mimeType: string): boolean {\n\tconst base = baseMimeType(mimeType);\n\treturn SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);\n}\n\n/**\n * Convert unsupported image formats to PNG using Photon.\n * Returns null if conversion is unavailable or fails.\n */\nasync function convertToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst image = photon.PhotonImage.new_from_byteslice(bytes);\n\t\ttry {\n\t\t\treturn image.get_bytes();\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction runCommand(\n\tcommand: string,\n\targs: string[],\n\toptions?: { timeoutMs?: number; maxBufferBytes?: number; env?: NodeJS.ProcessEnv },\n): { stdout: Buffer; ok: boolean } {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;\n\tconst maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n\n\tconst result = spawnSync(command, args, {\n\t\ttimeout: timeoutMs,\n\t\tmaxBuffer: maxBufferBytes,\n\t\tenv: options?.env,\n\t});\n\n\tif (result.error) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (result.status !== 0) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (Buffer.isBuffer(result.stdout)) {\n\t\treturn { ok: true, stdout: result.stdout };\n\t}\n\n\tconst encoding = typeof result.stdout === \"string\" ? \"utf-8\" : undefined;\n\treturn { ok: true, stdout: Buffer.from(result.stdout ?? \"\", encoding) };\n}\n\nfunction readClipboardImageViaWlPaste(): ClipboardImage | null {\n\tconst list = runCommand(\"wl-paste\", [\"--list-types\"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\tif (!list.ok) {\n\t\treturn null;\n\t}\n\n\tconst types = list.stdout\n\t\t.toString(\"utf-8\")\n\t\t.split(/\\r?\\n/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\n\tconst selectedType = selectPreferredImageMimeType(types);\n\tif (!selectedType) {\n\t\treturn null;\n\t}\n\n\tconst data = runCommand(\"wl-paste\", [\"--type\", selectedType, \"--no-newline\"]);\n\tif (!data.ok || data.stdout.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn { bytes: data.stdout, mimeType: baseMimeType(selectedType) };\n}\n\nfunction isWSL(env: NodeJS.ProcessEnv = process.env, inspectProcVersion = true): boolean {\n\tif (env.WSL_DISTRO_NAME || env.WSLENV) {\n\t\treturn true;\n\t}\n\n\tif (!inspectProcVersion) {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst release = readFileSync(\"/proc/version\", \"utf-8\");\n\t\treturn /microsoft|wsl/i.test(release);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * On WSL, the Linux clipboard (Wayland/X11) does not receive image data from\n * Windows screenshots (Win+Shift+S). PowerShell can access the Windows clipboard\n * directly, so we use it as a fallback.\n */\nfunction readClipboardImageViaPowerShell(): ClipboardImage | null {\n\tconst tmpFile = join(tmpdir(), `omk-wsl-clip-${randomUUID()}.png`);\n\n\ttry {\n\t\tconst winPathResult = runCommand(\"wslpath\", [\"-w\", tmpFile], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\t\tif (!winPathResult.ok) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst winPath = winPathResult.stdout.toString(\"utf-8\").trim();\n\t\tif (!winPath) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst psQuotedWinPath = winPath.replaceAll(\"'\", \"''\");\n\t\tconst psScript = [\n\t\t\t\"Add-Type -AssemblyName System.Windows.Forms\",\n\t\t\t\"Add-Type -AssemblyName System.Drawing\",\n\t\t\t`$path = '${psQuotedWinPath}'`,\n\t\t\t\"$img = [System.Windows.Forms.Clipboard]::GetImage()\",\n\t\t\t\"if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }\",\n\t\t].join(\"; \");\n\n\t\tconst result = runCommand(\"powershell.exe\", [\"-NoProfile\", \"-Command\", psScript], {\n\t\t\ttimeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,\n\t\t});\n\t\tif (!result.ok) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst output = result.stdout.toString(\"utf-8\").trim();\n\t\tif (output !== \"ok\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst bytes = readFileSync(tmpFile);\n\t\tif (bytes.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn { bytes: new Uint8Array(bytes), mimeType: \"image/png\" };\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\ttry {\n\t\t\tunlinkSync(tmpFile);\n\t\t} catch {\n\t\t\t// Ignore cleanup errors.\n\t\t}\n\t}\n}\n\nfunction readClipboardImageViaXclip(): ClipboardImage | null {\n\tconst targets = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"], {\n\t\ttimeoutMs: DEFAULT_LIST_TIMEOUT_MS,\n\t});\n\n\tlet candidateTypes: string[] = [];\n\tif (targets.ok) {\n\t\tcandidateTypes = targets.stdout\n\t\t\t.toString(\"utf-8\")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tconst preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;\n\tconst tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];\n\n\tfor (const mimeType of tryTypes) {\n\t\tconst data = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", mimeType, \"-o\"]);\n\t\tif (data.ok && data.stdout.length > 0) {\n\t\t\treturn { bytes: data.stdout, mimeType: baseMimeType(mimeType) };\n\t\t}\n\t}\n\n\treturn null;\n}\n\nasync function readClipboardImageViaNativeClipboard(): Promise<ClipboardImage | null> {\n\tif (!clipboard || !clipboard.hasImage()) {\n\t\treturn null;\n\t}\n\n\tconst imageData = await clipboard.getImageBinary();\n\tif (!imageData || imageData.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);\n\treturn { bytes, mimeType: \"image/png\" };\n}\n\n/**\n * Normalize one source's read into a supported format, or reject that read.\n *\n * Applied per source rather than once after the chain, because a source can\n * succeed at reading bytes yet still be unusable: WSLg publishes a Windows\n * screenshot as `image/bmp`, which needs conversion. Folding the conversion\n * into the source attempt keeps an unconvertible read from disqualifying the\n * sources behind it — notably the PowerShell reader, which returns PNG\n * directly and needs no converter at all.\n */\nasync function toSupportedImage(image: ClipboardImage | null): Promise<ClipboardImage | null> {\n\tif (!image) {\n\t\treturn null;\n\t}\n\tif (isSupportedImageMimeType(image.mimeType)) {\n\t\treturn image;\n\t}\n\tconst pngBytes = await convertToPng(image.bytes);\n\treturn pngBytes ? { bytes: pngBytes, mimeType: \"image/png\" } : null;\n}\n\nexport async function readClipboardImage(options?: {\n\tenv?: NodeJS.ProcessEnv;\n\tplatform?: NodeJS.Platform;\n}): Promise<ClipboardImage | null> {\n\tconst env = options?.env ?? process.env;\n\tconst platform = options?.platform ?? process.platform;\n\tconst hasExplicitEnv = options?.env !== undefined;\n\n\tif (env.TERMUX_VERSION) {\n\t\treturn null;\n\t}\n\n\tif (platform !== \"linux\") {\n\t\treturn toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\tconst wsl = isWSL(env, !hasExplicitEnv);\n\tconst wayland = isWaylandSession(env);\n\tlet image: ClipboardImage | null = null;\n\n\tif (wayland || wsl) {\n\t\timage = await toSupportedImage(readClipboardImageViaWlPaste());\n\t\timage ??= await toSupportedImage(readClipboardImageViaXclip());\n\t}\n\n\tif (!image && wsl) {\n\t\timage = await toSupportedImage(readClipboardImageViaPowerShell());\n\t}\n\n\tif (!image && !wayland) {\n\t\timage = await toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\treturn image;\n}\n"]}
1
+ {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/utils/clipboard-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\n\nimport { clipboard } from \"./clipboard-native.ts\";\nimport { loadPhoton } from \"./photon.ts\";\nimport { readWindowsClipboardImage, WindowsClipboardError } from \"./windows-clipboard-image.ts\";\n\nexport type ClipboardImage = {\n\tbytes: Uint8Array;\n\tmimeType: string;\n};\n\nconst SUPPORTED_IMAGE_MIME_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n\nconst DEFAULT_LIST_TIMEOUT_MS = 1000;\nconst DEFAULT_READ_TIMEOUT_MS = 3000;\nconst DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\nexport function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === \"wayland\";\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nexport function extensionForImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"png\";\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction selectPreferredImageMimeType(mimeTypes: string[]): string | null {\n\tconst normalized = mimeTypes\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean)\n\t\t.map((t) => ({ raw: t, base: baseMimeType(t) }));\n\n\tfor (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {\n\t\tconst match = normalized.find((t) => t.base === preferred);\n\t\tif (match) {\n\t\t\treturn match.raw;\n\t\t}\n\t}\n\n\tconst anyImage = normalized.find((t) => t.base.startsWith(\"image/\"));\n\treturn anyImage?.raw ?? null;\n}\n\nfunction isSupportedImageMimeType(mimeType: string): boolean {\n\tconst base = baseMimeType(mimeType);\n\treturn SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);\n}\n\n/**\n * Convert unsupported image formats to PNG using Photon.\n * Returns null if conversion is unavailable or fails.\n */\nasync function convertToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst image = photon.PhotonImage.new_from_byteslice(bytes);\n\t\ttry {\n\t\t\treturn image.get_bytes();\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction runCommand(\n\tcommand: string,\n\targs: string[],\n\toptions?: { timeoutMs?: number; maxBufferBytes?: number; env?: NodeJS.ProcessEnv },\n): { stdout: Buffer; ok: boolean } {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;\n\tconst maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n\n\tconst result = spawnSync(command, args, {\n\t\ttimeout: timeoutMs,\n\t\tmaxBuffer: maxBufferBytes,\n\t\tenv: options?.env,\n\t});\n\n\tif (result.error) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (result.status !== 0) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (Buffer.isBuffer(result.stdout)) {\n\t\treturn { ok: true, stdout: result.stdout };\n\t}\n\n\tconst encoding = typeof result.stdout === \"string\" ? \"utf-8\" : undefined;\n\treturn { ok: true, stdout: Buffer.from(result.stdout ?? \"\", encoding) };\n}\n\nfunction readClipboardImageViaWlPaste(): ClipboardImage | null {\n\tconst list = runCommand(\"wl-paste\", [\"--list-types\"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\tif (!list.ok) {\n\t\treturn null;\n\t}\n\n\tconst types = list.stdout\n\t\t.toString(\"utf-8\")\n\t\t.split(/\\r?\\n/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\n\tconst selectedType = selectPreferredImageMimeType(types);\n\tif (!selectedType) {\n\t\treturn null;\n\t}\n\n\tconst data = runCommand(\"wl-paste\", [\"--type\", selectedType, \"--no-newline\"]);\n\tif (!data.ok || data.stdout.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn { bytes: data.stdout, mimeType: baseMimeType(selectedType) };\n}\n\nfunction isWSL(env: NodeJS.ProcessEnv = process.env, inspectProcVersion = true): boolean {\n\tif (env.WSL_DISTRO_NAME || env.WSLENV) {\n\t\treturn true;\n\t}\n\n\tif (!inspectProcVersion) {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst release = readFileSync(\"/proc/version\", \"utf-8\");\n\t\treturn /microsoft|wsl/i.test(release);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction readClipboardImageViaXclip(): ClipboardImage | null {\n\tconst targets = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"], {\n\t\ttimeoutMs: DEFAULT_LIST_TIMEOUT_MS,\n\t});\n\n\tlet candidateTypes: string[] = [];\n\tif (targets.ok) {\n\t\tcandidateTypes = targets.stdout\n\t\t\t.toString(\"utf-8\")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tconst preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;\n\tconst tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];\n\n\tfor (const mimeType of tryTypes) {\n\t\tconst data = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", mimeType, \"-o\"]);\n\t\tif (data.ok && data.stdout.length > 0) {\n\t\t\treturn { bytes: data.stdout, mimeType: baseMimeType(mimeType) };\n\t\t}\n\t}\n\n\treturn null;\n}\n\nasync function readClipboardImageViaNativeClipboard(): Promise<ClipboardImage | null> {\n\tif (!clipboard || !clipboard.hasImage()) {\n\t\treturn null;\n\t}\n\n\tconst imageData = await clipboard.getImageBinary();\n\tif (!imageData || imageData.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);\n\treturn { bytes, mimeType: \"image/png\" };\n}\n\n/**\n * Normalize one source's read into a supported format, or reject that read.\n *\n * Applied per source rather than once after the chain, because a source can\n * succeed at reading bytes yet still be unusable: WSLg publishes a Windows\n * screenshot as `image/bmp`, which needs conversion. Folding the conversion\n * into the source attempt keeps an unconvertible read from disqualifying the\n * sources behind it — notably the PowerShell reader, which returns PNG\n * directly and needs no converter at all.\n */\nasync function toSupportedImage(image: ClipboardImage | null): Promise<ClipboardImage | null> {\n\tif (!image) {\n\t\treturn null;\n\t}\n\tif (isSupportedImageMimeType(image.mimeType)) {\n\t\treturn image;\n\t}\n\tconst pngBytes = await convertToPng(image.bytes);\n\treturn pngBytes ? { bytes: pngBytes, mimeType: \"image/png\" } : null;\n}\n\nexport async function readClipboardImage(options?: {\n\tenv?: NodeJS.ProcessEnv;\n\tplatform?: NodeJS.Platform;\n}): Promise<ClipboardImage | null> {\n\tconst env = options?.env ?? process.env;\n\tconst platform = options?.platform ?? process.platform;\n\tconst hasExplicitEnv = options?.env !== undefined;\n\n\tif (env.TERMUX_VERSION) {\n\t\treturn null;\n\t}\n\n\tif (platform !== \"linux\") {\n\t\treturn toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\tconst wsl = isWSL(env, !hasExplicitEnv);\n\tconst windows = wsl ? await readWindowsClipboardImage(env) : undefined;\n\tif (windows?.kind === \"image\") return { bytes: windows.bytes, mimeType: windows.mimeType };\n\tif (windows?.kind === \"empty\") return null;\n\tconst wayland = isWaylandSession(env);\n\tlet image: ClipboardImage | null = null;\n\n\tif (wayland || wsl) {\n\t\timage = await toSupportedImage(readClipboardImageViaWlPaste());\n\t\timage ??= await toSupportedImage(readClipboardImageViaXclip());\n\t}\n\n\tif (!image && !wayland) {\n\t\timage = await toSupportedImage(await readClipboardImageViaNativeClipboard());\n\t}\n\n\tif (!image && windows?.kind === \"unavailable\") {\n\t\tthrow new WindowsClipboardError(\n\t\t\t`Windows clipboard unavailable (${windows.reason}). Check WSL interop and Windows PowerShell, or drop an image file.`,\n\t\t);\n\t}\n\treturn image;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ interface TerminalLinkEnvironment {
2
+ readonly env?: NodeJS.ProcessEnv;
3
+ readonly platform?: NodeJS.Platform;
4
+ }
5
+ /** File URLs are opened by the host terminal, not by a Linux process inside WSL. */
6
+ export declare function terminalFileUrl(path: string, cwd: string, options?: TerminalLinkEnvironment): string;
7
+ /** Bind destinations to the active project so resumed sessions do not use the launch cwd. */
8
+ export declare function terminalMarkdownLinks(cwd: string, options?: TerminalLinkEnvironment): {
9
+ resolveLink(href: string): string | undefined;
10
+ resolveFileLink(text: string): string | undefined;
11
+ };
12
+ export {};
13
+ //# sourceMappingURL=terminal-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-links.d.ts","sourceRoot":"","sources":["../../src/utils/terminal-links.ts"],"names":[],"mappings":"AAKA,UAAU,uBAAuB;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CACpC;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,MAAM,CAcxG;AAED,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B;;;EAkCvF","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { win32 } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { normalizePath, resolvePath } from \"./paths.ts\";\n\ninterface TerminalLinkEnvironment {\n\treadonly env?: NodeJS.ProcessEnv;\n\treadonly platform?: NodeJS.Platform;\n}\n\n/** File URLs are opened by the host terminal, not by a Linux process inside WSL. */\nexport function terminalFileUrl(path: string, cwd: string, options: TerminalLinkEnvironment = {}): string {\n\tconst env = options.env ?? process.env;\n\tconst platform = options.platform ?? process.platform;\n\tif (/^[a-z]:[\\\\/]|^\\\\\\\\/i.test(path)) return pathToFileURL(path, { windows: true }).href;\n\tconst absolute = platform === \"win32\" ? win32.resolve(cwd, normalizePath(path)) : resolvePath(path, cwd);\n\tconst distro = env.WSL_DISTRO_NAME;\n\tif (platform === \"linux\" && distro && !env.SSH_CONNECTION && !env.SSH_TTY && !/[\\\\/\\x00-\\x1f]/.test(distro)) {\n\t\tconst drive = /^\\/mnt\\/([a-z])(?:\\/|$)/i.exec(absolute);\n\t\tconst windowsPath = drive\n\t\t\t? `${drive[1].toUpperCase()}:\\\\${absolute.slice(drive[0].length).replaceAll(\"/\", \"\\\\\")}`\n\t\t\t: `\\\\\\\\wsl.localhost\\\\${distro}${absolute.replaceAll(\"/\", \"\\\\\")}`;\n\t\treturn pathToFileURL(windowsPath, { windows: true }).href;\n\t}\n\treturn pathToFileURL(absolute, { windows: platform === \"win32\" }).href;\n}\n\n/** Bind destinations to the active project so resumed sessions do not use the launch cwd. */\nexport function terminalMarkdownLinks(cwd: string, options: TerminalLinkEnvironment = {}) {\n\treturn {\n\t\tresolveLink(href: string): string | undefined {\n\t\t\tif (/[\\x00-\\x1f\\x7f]/.test(href)) return undefined;\n\t\t\tif (/^[a-z]:[\\\\/]|^\\\\\\\\/i.test(href)) return terminalFileUrl(href, cwd, options);\n\t\t\tif (href.startsWith(\"#\")) return href;\n\t\t\tif (/^file:/i.test(href)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst url = new URL(href);\n\t\t\t\t\tif (url.hostname && url.hostname !== \"localhost\") return href;\n\t\t\t\t\tconst windows = /^\\/[a-z]:\\//i.test(url.pathname);\n\t\t\t\t\treturn terminalFileUrl(fileURLToPath(url, { windows }), cwd, options) + url.search + url.hash;\n\t\t\t\t} catch {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (/^[a-z][a-z\\d+.-]*:/i.test(href) || href.startsWith(\"//\")) return href;\n\t\t\tconst boundary = href.search(/[?#]/);\n\t\t\tconst pathname = boundary < 0 ? href : href.slice(0, boundary);\n\t\t\tconst suffix = boundary < 0 ? \"\" : href.slice(boundary);\n\t\t\ttry {\n\t\t\t\treturn terminalFileUrl(decodeURIComponent(pathname), cwd, options) + suffix;\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof URIError) return terminalFileUrl(pathname, cwd, options) + suffix;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tresolveFileLink(text: string): string | undefined {\n\t\t\tif (/^[a-z][a-z\\d+.-]*:/i.test(text) && !/^[a-z]:[\\\\/]/i.test(text)) return undefined;\n\t\t\tif (/[\\x00-\\x1f\\x7f]/.test(text) || (!/[\\\\/]/.test(text) && !/\\.[a-z\\d]{1,10}$/i.test(text))) return undefined;\n\t\t\tconst path = resolvePath(text, cwd);\n\t\t\treturn existsSync(path) ? terminalFileUrl(text, cwd, options) : undefined;\n\t\t},\n\t};\n}\n"]}