@agimon-ai/doompi-computer-use 0.0.1-alpha.12 → 0.0.1-alpha.2

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 (95) hide show
  1. package/README.md +3 -6
  2. package/dist/adapters/computerUseApi.cjs +1 -1
  3. package/dist/adapters/computerUseApi.cjs.map +1 -1
  4. package/dist/adapters/computerUseApi.d.cts +5 -9
  5. package/dist/adapters/computerUseApi.d.cts.map +1 -1
  6. package/dist/adapters/computerUseApi.d.mts +5 -9
  7. package/dist/adapters/computerUseApi.d.mts.map +1 -1
  8. package/dist/adapters/computerUseApi.mjs +1 -1
  9. package/dist/adapters/computerUseApi.mjs.map +1 -1
  10. package/dist/adapters/pi/extension.cjs +2 -2
  11. package/dist/adapters/pi/extension.cjs.map +1 -1
  12. package/dist/adapters/pi/extension.d.cts +4 -4
  13. package/dist/adapters/pi/extension.d.cts.map +1 -1
  14. package/dist/adapters/pi/extension.d.mts +4 -4
  15. package/dist/adapters/pi/extension.d.mts.map +1 -1
  16. package/dist/adapters/pi/extension.mjs +2 -2
  17. package/dist/adapters/pi/extension.mjs.map +1 -1
  18. package/dist/adapters/pi/sessionApiClient.d.cts +4 -3
  19. package/dist/adapters/pi/sessionApiClient.d.cts.map +1 -1
  20. package/dist/adapters/pi/sessionApiClient.d.mts +4 -3
  21. package/dist/adapters/pi/sessionApiClient.d.mts.map +1 -1
  22. package/dist/adapters/webComputerUseChannel.cjs +1 -1
  23. package/dist/adapters/webComputerUseChannel.cjs.map +1 -1
  24. package/dist/adapters/webComputerUseChannel.d.cts +3 -3
  25. package/dist/adapters/webComputerUseChannel.d.cts.map +1 -1
  26. package/dist/adapters/webComputerUseChannel.d.mts +3 -3
  27. package/dist/adapters/webComputerUseChannel.d.mts.map +1 -1
  28. package/dist/adapters/webComputerUseChannel.mjs +1 -1
  29. package/dist/adapters/webComputerUseChannel.mjs.map +1 -1
  30. package/dist/container/index.cjs +1 -1
  31. package/dist/container/index.cjs.map +1 -1
  32. package/dist/container/index.d.cts +2 -2
  33. package/dist/container/index.d.cts.map +1 -1
  34. package/dist/container/index.d.mts +2 -2
  35. package/dist/container/index.d.mts.map +1 -1
  36. package/dist/container/index.mjs +1 -1
  37. package/dist/container/index.mjs.map +1 -1
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.d.cts +2 -4
  40. package/dist/index.d.mts +2 -4
  41. package/dist/index.mjs +1 -1
  42. package/dist/services/extensionService.d.cts +2 -1
  43. package/dist/services/extensionService.d.cts.map +1 -1
  44. package/dist/services/extensionService.d.mts +2 -1
  45. package/dist/services/extensionService.d.mts.map +1 -1
  46. package/dist/services/sessionController.d.cts +3 -2
  47. package/dist/services/sessionController.d.cts.map +1 -1
  48. package/dist/services/sessionController.d.mts +3 -2
  49. package/dist/services/sessionController.d.mts.map +1 -1
  50. package/dist/services/traceRedaction.d.cts +4 -3
  51. package/dist/services/traceRedaction.d.cts.map +1 -1
  52. package/dist/services/traceRedaction.d.mts +4 -3
  53. package/dist/services/traceRedaction.d.mts.map +1 -1
  54. package/dist/types/computerUse.d.cts +14 -14
  55. package/dist/types/computerUse.d.cts.map +1 -1
  56. package/dist/types/computerUse.d.mts +14 -14
  57. package/dist/types/computerUse.d.mts.map +1 -1
  58. package/dist/types/computerUseApi.cjs.map +1 -1
  59. package/dist/types/computerUseApi.d.cts +19 -22
  60. package/dist/types/computerUseApi.d.cts.map +1 -1
  61. package/dist/types/computerUseApi.d.mts +19 -22
  62. package/dist/types/computerUseApi.d.mts.map +1 -1
  63. package/dist/types/computerUseApi.mjs.map +1 -1
  64. package/dist/types/extension.d.cts +5 -7
  65. package/dist/types/extension.d.cts.map +1 -1
  66. package/dist/types/extension.d.mts +5 -7
  67. package/dist/types/extension.d.mts.map +1 -1
  68. package/llms.txt +0 -1
  69. package/package.json +13 -17
  70. package/src/prompts/doompi-use-computer-use/SKILL.md +1 -1
  71. package/src/types/computerUse.ts +2 -3
  72. package/src/types/computerUseApi.ts +0 -1
  73. package/src/web/{components/ComputerActionToolCard.tsx → ComputerActionToolCard.tsx} +1 -1
  74. package/src/web/{components/ComputerStateToolCard.tsx → ComputerStateToolCard.tsx} +1 -1
  75. package/src/web/{components/ComputerUsePanel.tsx → ComputerUsePanel.tsx} +7 -16
  76. package/src/web/{stores/computerUseStore.ts → computerUseStore.ts} +2 -2
  77. package/src/web/index.ts +6 -12
  78. package/dist/adapters/pi/computerScriptRunner.cjs +0 -45
  79. package/dist/adapters/pi/computerScriptRunner.cjs.map +0 -1
  80. package/dist/adapters/pi/computerScriptRunner.d.cts +0 -12
  81. package/dist/adapters/pi/computerScriptRunner.d.cts.map +0 -1
  82. package/dist/adapters/pi/computerScriptRunner.d.mts +0 -12
  83. package/dist/adapters/pi/computerScriptRunner.d.mts.map +0 -1
  84. package/dist/adapters/pi/computerScriptRunner.mjs +0 -45
  85. package/dist/adapters/pi/computerScriptRunner.mjs.map +0 -1
  86. package/dist/types/computerScript.d.cts +0 -38
  87. package/dist/types/computerScript.d.cts.map +0 -1
  88. package/dist/types/computerScript.d.mts +0 -38
  89. package/dist/types/computerScript.d.mts.map +0 -1
  90. package/docs/computer-scripts.md +0 -37
  91. package/src/web/components/ComputerExecToolCard.tsx +0 -44
  92. package/src/web/lib/computerExecToolRender.ts +0 -14
  93. package/src/web/lib/computerUseSettings.ts +0 -17
  94. /package/src/web/{lib/computerActionToolRender.ts → computerActionToolRender.ts} +0 -0
  95. /package/src/web/{lib/computerStateToolRender.ts → computerStateToolRender.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../src/types/extension.ts"],"mappings":";;;YAGY;iBAEK;EACf;EACA,OAAO;;iBAGQ;EACf,WAAW,QAAQ;;iBAGJ;EACf,SAAS;EACT,SAAS;EACT,eAAe;EACf,0BAA0B"}
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../../src/types/extension.ts"],"mappings":";;KAEY;UAEK;EACf;EACA,OAAO;;UAGQ;EACf,WAAW,QAAQ;;UAGJ;EACf,SAAS;EACT,SAAS"}
@@ -1,19 +1,17 @@
1
1
  import { ComputerUseSessionClient } from "../adapters/pi/sessionApiClient.mjs";
2
- import { ComputerScriptExecutor } from "./computerScript.mjs";
3
2
  //#region src/types/extension.d.ts
4
- export type ComputerUseNotificationLevel = 'info' | 'error';
5
- export interface ComputerUseExtensionResult {
3
+ type ComputerUseNotificationLevel = 'info' | 'error';
4
+ interface ComputerUseExtensionResult {
6
5
  message: string;
7
6
  level: ComputerUseNotificationLevel;
8
7
  }
9
- export interface ComputerUseExtensionService {
8
+ interface ComputerUseExtensionService {
10
9
  execute(): Promise<ComputerUseExtensionResult>;
11
10
  }
12
- export interface ComputerUseExtensionDependencies {
11
+ interface ComputerUseExtensionDependencies {
13
12
  service: ComputerUseExtensionService;
14
13
  client?: ComputerUseSessionClient;
15
- scriptRunner?: ComputerScriptExecutor;
16
- enabled?: () => boolean | Promise<boolean>;
17
14
  }
18
15
  //#endregion
16
+ export { ComputerUseExtensionDependencies, ComputerUseExtensionResult, ComputerUseExtensionService, ComputerUseNotificationLevel };
19
17
  //# sourceMappingURL=extension.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../src/types/extension.ts"],"mappings":";;;YAGY;iBAEK;EACf;EACA,OAAO;;iBAGQ;EACf,WAAW,QAAQ;;iBAGJ;EACf,SAAS;EACT,SAAS;EACT,eAAe;EACf,0BAA0B"}
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../../src/types/extension.ts"],"mappings":";;KAEY;UAEK;EACf;EACA,OAAO;;UAGQ;EACf,WAAW,QAAQ;;UAGJ;EACf,SAAS;EACT,SAAS"}
package/llms.txt CHANGED
@@ -9,4 +9,3 @@ Session-scoped semantic computer control through DoomPi Desktop.
9
9
  ## Resources
10
10
 
11
11
  - [Package README](./README.md): Installation, activation workflow, tools, API boundaries, and development commands.
12
- - [Author computer scripts](./docs/computer-scripts.md): Trusted script authorization, runtime contract, limits, and example.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-computer-use",
3
- "version": "0.0.1-alpha.12",
3
+ "version": "0.0.1-alpha.2",
4
4
  "description": "Session scoped semantic computer control through the DoomPi Desktop capability.",
5
5
  "keywords": [
6
6
  "ai",
@@ -26,7 +26,6 @@
26
26
  },
27
27
  "files": [
28
28
  "dist",
29
- "docs",
30
29
  "src/web",
31
30
  "src/types/computerUse.ts",
32
31
  "src/types/computerUseApi.ts",
@@ -35,8 +34,7 @@
35
34
  "llms.txt",
36
35
  "README.md",
37
36
  "LICENSE",
38
- "package.json",
39
- "!src/web/**/*.stories.tsx"
37
+ "package.json"
40
38
  ],
41
39
  "type": "module",
42
40
  "main": "./dist/index.cjs",
@@ -64,28 +62,26 @@
64
62
  "access": "public"
65
63
  },
66
64
  "dependencies": {
67
- "@deepseek-ai/cordis": "4.0.2",
68
- "@agimon-ai/doompi-config": "0.0.1-alpha.67",
69
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.67",
70
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.30",
71
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.27"
65
+ "@deepseek-ai/cordis": "4.0.1",
66
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.57",
67
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.17",
68
+ "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.20"
72
69
  },
73
70
  "devDependencies": {
74
- "@earendil-works/pi-coding-agent": "0.85.1",
71
+ "@earendil-works/pi-coding-agent": "0.85.0",
75
72
  "@tanstack/react-store": "0.11.1",
76
73
  "@tanstack/store": "0.11.1",
77
- "@types/node": "26.4.1",
74
+ "@types/node": "26.2.0",
78
75
  "@types/react": "19.2.18",
79
- "@vitest/coverage-v8": "5.0.0",
76
+ "@vitest/coverage-v8": "4.1.11",
80
77
  "react": "19.2.8",
81
- "react-dom": "19.2.8",
82
- "tsdown": "0.23.0",
78
+ "tsdown": "0.22.14",
83
79
  "typescript": "7.0.2",
84
- "vitest": "5.0.0",
85
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.64"
80
+ "vitest": "4.1.11",
81
+ "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.54"
86
82
  },
87
83
  "peerDependencies": {
88
- "@earendil-works/pi-coding-agent": "0.85.1"
84
+ "@earendil-works/pi-coding-agent": "0.85.0"
89
85
  },
90
86
  "peerDependenciesMeta": {
91
87
  "@earendil-works/pi-coding-agent": {
@@ -11,7 +11,7 @@ This guidance applies only while the session's Computer Use minor mode is active
11
11
 
12
12
  - Call `computer_state` before acting and after an action that may change the interface.
13
13
  - Use only snapshot ids and element refs returned by the latest `computer_state` result.
14
- - Call `computer_action` for one semantic `press`, `focus`, `set_value`, or `scroll` at a time.
14
+ - Call `computer_action` for one semantic `press`, `set_value`, or `scroll` at a time.
15
15
  - Never infer screen coordinates, inspect another application, or bypass secure elements.
16
16
  - Treat stale snapshots, lost targets, and uncertain outcomes as terminal until a fresh observation clarifies them.
17
17
  - Do not automatically retry an uncertain action.
@@ -36,7 +36,6 @@ export interface ComputerUseElement {
36
36
  readonly ref: string;
37
37
  readonly role: string;
38
38
  readonly label?: string;
39
- readonly value?: string;
40
39
  readonly enabled: boolean;
41
40
  readonly secure: boolean;
42
41
  readonly actions: readonly ComputerUseActionKind[];
@@ -56,11 +55,11 @@ export interface ComputerUseObservation {
56
55
  };
57
56
  }
58
57
 
59
- export type ComputerUseActionKind = 'press' | 'focus' | 'set_value' | 'scroll';
58
+ export type ComputerUseActionKind = 'press' | 'set_value' | 'scroll';
60
59
 
61
60
  export type ComputerUseAction =
62
61
  | {
63
- readonly kind: 'press' | 'focus';
62
+ readonly kind: 'press';
64
63
  readonly snapshotId: string;
65
64
  readonly elementRef: string;
66
65
  }
@@ -37,7 +37,6 @@ export interface ComputerUseArtifactView {
37
37
  readonly previewUrl?: string;
38
38
  readonly actionCount?: number;
39
39
  readonly completedAt?: string;
40
- readonly failure?: { readonly code: string; readonly message: string };
41
40
  }
42
41
 
43
42
  export interface ComputerUseSessionView {
@@ -13,7 +13,7 @@ import {
13
13
  computerActionCallSummary,
14
14
  computerActionCollapsedLines,
15
15
  computerActionResultLines,
16
- } from '../lib/computerActionToolRender.ts';
16
+ } from './computerActionToolRender.ts';
17
17
 
18
18
  /**
19
19
  * The computer_action tool's timeline item: Perform one constrained semantic action in the authorized application window. The shell owns
@@ -13,7 +13,7 @@ import {
13
13
  computerStateCallSummary,
14
14
  computerStateCollapsedLines,
15
15
  computerStateResultLines,
16
- } from '../lib/computerStateToolRender.ts';
16
+ } from './computerStateToolRender.ts';
17
17
 
18
18
  /**
19
19
  * The computer_state tool's timeline item: Observe the authorized application window and semantic accessibility state. The shell owns
@@ -1,8 +1,8 @@
1
1
  import type { WebPluginSlotProps } from '@agimon-ai/doompi-web-contracts';
2
2
  import { useStore } from '@tanstack/react-store';
3
3
  import { useEffect, useState } from 'react';
4
- import { activationUrl, COMPUTER_USE_DEFAULT_DURATION_MS, computerUseChannelType } from '../../types/computerUseApi.ts';
5
- import { computerUse } from '../stores/computerUseStore.ts';
4
+ import { activationUrl, COMPUTER_USE_DEFAULT_DURATION_MS, computerUseChannelType } from '../types/computerUseApi.ts';
5
+ import { computerUse } from './computerUseStore.ts';
6
6
 
7
7
  function text(value: unknown, fallback: string): string {
8
8
  return typeof value === 'string' || typeof value === 'number' ? String(value) : fallback;
@@ -23,7 +23,7 @@ export function ComputerUsePanel({ sessionId, sendSessionFrame }: WebPluginSlotP
23
23
  sendSessionFrame(sessionId, { type: computerUseChannelType, payload: { action: 'targets' } });
24
24
  }, [sendSessionFrame, sessionId]);
25
25
 
26
- if (sessionId === null) return <p className="px-1 text-xs text-doom-faint">Select a session.</p>;
26
+ if (sessionId === null) return <p className="px-1 text-[10px] text-doom-faint">Select a session.</p>;
27
27
  const state = session.state;
28
28
  const target = session.targets[selected];
29
29
  const artifact = state.artifact;
@@ -45,7 +45,7 @@ export function ComputerUsePanel({ sessionId, sendSessionFrame }: WebPluginSlotP
45
45
  };
46
46
 
47
47
  return (
48
- <div data-testid="computer-use-panel" className="flex flex-col gap-2 text-xs text-doom-text">
48
+ <div data-testid="computer-use-panel" className="flex flex-col gap-2 text-[10px] text-doom-text">
49
49
  <header className="flex items-center justify-between gap-2">
50
50
  <span className="text-doom-faint">{state.phase.replaceAll('_', ' ')}</span>
51
51
  <button type="button" className="rounded bg-doom-panel px-2 py-1" onClick={() => send({ action: 'targets' })}>
@@ -122,22 +122,13 @@ export function ComputerUsePanel({ sessionId, sendSessionFrame }: WebPluginSlotP
122
122
  ) : null}
123
123
  {artifact ? (
124
124
  <div data-testid="computer-use-artifact" className="rounded bg-doom-panel p-2">
125
- <strong>{artifact.status === 'ready' ? 'Completed recording' : 'Recording unavailable'}</strong>
125
+ <strong>Completed artifact</strong>
126
126
  <p>ID: {artifact.artifactId}</p>
127
127
  <p>Status: {artifact.status}</p>
128
- {artifact.failure ? <p className="text-doom-error">{artifact.failure.message}</p> : null}
129
128
  {artifact.completedAt ? <p>Completed: {artifact.completedAt}</p> : null}
130
129
  {artifact.actionCount !== undefined ? <p>Actions: {artifact.actionCount}</p> : null}
131
- {artifact.status === 'ready' && artifact.previewUrl ? (
132
- <video className="mt-2 w-full" controls preload="metadata" src={artifact.previewUrl}>
133
- <track kind="captions" />
134
- </video>
135
- ) : null}
136
- {artifact.status === 'ready' && artifact.downloadUrl ? (
137
- <a className="mt-2 inline-block text-doom-accent" href={artifact.downloadUrl} download>
138
- Download recording
139
- </a>
140
- ) : null}
130
+ {artifact.previewUrl ? <a href={artifact.previewUrl}>Preview</a> : null}
131
+ {artifact.downloadUrl ? <a href={artifact.downloadUrl}>Download</a> : null}
141
132
  </div>
142
133
  ) : null}
143
134
  </div>
@@ -1,6 +1,6 @@
1
1
  import { defineSessionStore } from '@agimon-ai/doompi-web-contracts';
2
- import { computerUseChannelType } from '../../types/computerUseApi.ts';
3
- import type { ComputerUseChannelPayload } from '../../types/computerUseApi.ts';
2
+ import { computerUseChannelType } from '../types/computerUseApi.ts';
3
+ import type { ComputerUseChannelPayload } from '../types/computerUseApi.ts';
4
4
 
5
5
  export interface ComputerUseSession extends ComputerUseChannelPayload {}
6
6
 
package/src/web/index.ts CHANGED
@@ -4,15 +4,12 @@ import {
4
4
  COMPUTER_USE_MODE_STATUS_KEY,
5
5
  COMPUTER_USE_STATUS_KEY,
6
6
  } from '../types/computerUseApi.ts';
7
- import { ComputerActionToolCard } from './components/ComputerActionToolCard.tsx';
8
- import { ComputerExecToolCard } from './components/ComputerExecToolCard.tsx';
9
- import { ComputerStateToolCard } from './components/ComputerStateToolCard.tsx';
10
- import { ComputerUsePanel } from './components/ComputerUsePanel.tsx';
11
- import { computerActionToolName } from './lib/computerActionToolRender.ts';
12
- import { computerExecToolName } from './lib/computerExecToolRender.ts';
13
- import { computerStateToolName } from './lib/computerStateToolRender.ts';
14
- import { computerUseSettingsSection } from './lib/computerUseSettings.ts';
15
- import { computerUseChannel } from './stores/computerUseStore.ts';
7
+ import { ComputerActionToolCard } from './ComputerActionToolCard.tsx';
8
+ import { computerActionToolName } from './computerActionToolRender.ts';
9
+ import { ComputerStateToolCard } from './ComputerStateToolCard.tsx';
10
+ import { computerStateToolName } from './computerStateToolRender.ts';
11
+ import { ComputerUsePanel } from './ComputerUsePanel.tsx';
12
+ import { computerUseChannel } from './computerUseStore.ts';
16
13
 
17
14
  export const webPlugin = defineWebPlugin({
18
15
  id: 'computer-use',
@@ -22,7 +19,6 @@ export const webPlugin = defineWebPlugin({
22
19
  modeId: COMPUTER_USE_MODE_ID,
23
20
  keys: 'c e',
24
21
  statusKey: COMPUTER_USE_MODE_STATUS_KEY,
25
- hideWhenMissing: true,
26
22
  order: 70,
27
23
  },
28
24
  ],
@@ -36,12 +32,10 @@ export const webPlugin = defineWebPlugin({
36
32
  marksBackgroundWork: false,
37
33
  },
38
34
  ],
39
- settingsSections: [computerUseSettingsSection],
40
35
  activitySections: [{ id: 'computer-use', component: ComputerUsePanel }],
41
36
  channels: [computerUseChannel],
42
37
  toolRenderers: [
43
38
  { tools: [computerStateToolName], message: ComputerStateToolCard },
44
39
  { tools: [computerActionToolName], message: ComputerActionToolCard },
45
- { tools: [computerExecToolName], message: ComputerExecToolCard },
46
40
  ],
47
41
  });
@@ -1,45 +0,0 @@
1
- let e=require("node:child_process"),t=require("node:fs/promises");const n=String.raw`
2
- import { readFile } from 'node:fs/promises';
3
- import { stripTypeScriptTypes } from 'node:module';
4
-
5
- let nextRequestId = 0;
6
- const pending = new Map();
7
- const call = (operation, payload) => new Promise((resolve, reject) => {
8
- const id = String(++nextRequestId);
9
- pending.set(id, { resolve, reject });
10
- process.send({ type: 'program', id, operation, payload });
11
- });
12
- process.on('message', async (message) => {
13
- if (message?.type === 'response') {
14
- const request = pending.get(message.id);
15
- if (!request) return;
16
- pending.delete(message.id);
17
- if (message.error) request.reject(new Error(message.error));
18
- else request.resolve(message.value);
19
- return;
20
- }
21
- if (message?.type !== 'start') return;
22
- const controller = new AbortController();
23
- process.once('SIGTERM', () => controller.abort());
24
- const logger = Object.fromEntries(['debug', 'info', 'warn', 'error'].map((level) => [level, (value) => {
25
- process.send({ type: 'log', level, message: String(value) });
26
- }]));
27
- try {
28
- const source = await readFile(message.scriptPath, 'utf8');
29
- const compiled = stripTypeScriptTypes(source, { mode: 'strip' });
30
- const moduleUrl = 'data:text/javascript;base64,' + Buffer.from(compiled).toString('base64');
31
- const loaded = await import(moduleUrl);
32
- if (typeof loaded.run !== 'function') throw new Error('Computer script must export a named run function.');
33
- const program = {
34
- observe: () => call('observe'),
35
- act: (action) => call('act', action),
36
- };
37
- const result = await loaded.run({ context: { program }, input: message.input, logger, signal: controller.signal });
38
- const resultJson = result === undefined ? undefined : JSON.stringify(result);
39
- process.send({ type: 'complete', resultJson });
40
- } catch (error) {
41
- process.send({ type: 'failed', error: error instanceof Error ? error.message : String(error) });
42
- }
43
- });
44
- `;var r=class{client;allowedScriptPaths;timeoutMs;maxOutputBytes;constructor(e){this.client=e.client,this.allowedScriptPaths=e.allowedScriptPaths,this.timeoutMs=e.timeoutMs??3e4,this.maxOutputBytes=e.maxOutputBytes??262144}async execute(r,i,a){let o=await(0,t.realpath)(r);if(!(await Promise.all(this.allowedScriptPaths.map(async e=>(0,t.realpath)(e).catch(()=>``)))).includes(o))throw Error(`Computer script path is not explicitly allowed.`);if(!o.endsWith(`.ts`))throw Error(`Computer scripts must be TypeScript files.`);let s=JSON.stringify(i);if(s===void 0)throw Error(`Computer script input must be JSON serializable.`);if(Buffer.byteLength(s)>this.maxOutputBytes)throw Error(`Computer script input exceeds the size limit.`);return new Promise((t,r)=>{let i=(0,e.spawn)(process.execPath,[`--input-type=module`,`--eval`,n],{stdio:[`ignore`,`pipe`,`pipe`,`ipc`]}),c=new AbortController,l=[],u=0,d=!1,f=Promise.resolve(),p=()=>{c.abort(),i.kill(`SIGTERM`),setTimeout(()=>i.kill(`SIGKILL`),250).unref?.()},m=(e,n)=>{d||(d=!0,clearTimeout(_),a?.removeEventListener(`abort`,g),p(),e===void 0?t(n):r(e))},h=e=>(u+=Buffer.byteLength(e),u<=this.maxOutputBytes||(m(Error(`Computer script output exceeded the size limit.`)),!1)),g=()=>m(Error(`Computer script execution was cancelled.`)),_=setTimeout(()=>m(Error(`Computer script execution timed out.`)),this.timeoutMs);if(_.unref?.(),a?.addEventListener(`abort`,g,{once:!0}),a?.aborted===!0)return g();i.stdout?.on(`data`,e=>h(e.toString(`utf8`))),i.stderr?.on(`data`,e=>h(e.toString(`utf8`))),i.on(`error`,e=>m(e)),i.on(`exit`,(e,t)=>{d||m(Error(`Computer script worker exited before completion (${t??e??`unknown`}).`))}),i.on(`message`,e=>{if(!d){if(e.type===`log`&&typeof e.message==`string`){let t=`[${String(e.level)}] ${e.message}`;h(t)&&l.push(t);return}if(e.type===`program`&&typeof e.id==`string`&&(e.operation===`observe`||e.operation===`act`)){f=f.then(async()=>{try{let t=e.operation===`observe`?await this.client.observe(c.signal):await this.client.act(e.payload,c.signal);i.send({type:`response`,id:e.id,value:t})}catch(t){i.send({type:`response`,id:e.id,error:t instanceof Error?t.message:String(t)})}});return}if(e.type===`failed`&&typeof e.error==`string`){m(Error(e.error));return}e.type===`complete`&&(f=f.then(async()=>{let t=e.resultJson;if(t!==void 0&&typeof t!=`string`){m(Error(`Computer script returned an invalid result.`));return}if(typeof t==`string`&&!h(t))return;let n=await this.client.observe(c.signal);m(void 0,{...typeof t==`string`?{result:JSON.parse(t)}:{},observation:n,logs:l})}).catch(e=>m(e instanceof Error?e:Error(String(e)))))}}),i.send({type:`start`,scriptPath:o,input:JSON.parse(s)})})}};exports.ComputerScriptRunner=r;
45
- //# sourceMappingURL=computerScriptRunner.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"computerScriptRunner.cjs","names":["realpath","spawn"],"sources":["../../../src/adapters/pi/computerScriptRunner.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { realpath } from 'node:fs/promises';\nimport type { ComputerScriptExecutionResult, ComputerScriptRunnerOptions } from '../../types/computerScript.ts';\nimport type { ComputerUseAction } from '../../types/computerUse.ts';\nimport type { ComputerUseSessionClient } from './sessionApiClient.ts';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_MAX_OUTPUT_BYTES = 256 * 1024;\n\nconst WORKER_SOURCE = String.raw`\nimport { readFile } from 'node:fs/promises';\nimport { stripTypeScriptTypes } from 'node:module';\n\nlet nextRequestId = 0;\nconst pending = new Map();\nconst call = (operation, payload) => new Promise((resolve, reject) => {\n const id = String(++nextRequestId);\n pending.set(id, { resolve, reject });\n process.send({ type: 'program', id, operation, payload });\n});\nprocess.on('message', async (message) => {\n if (message?.type === 'response') {\n const request = pending.get(message.id);\n if (!request) return;\n pending.delete(message.id);\n if (message.error) request.reject(new Error(message.error));\n else request.resolve(message.value);\n return;\n }\n if (message?.type !== 'start') return;\n const controller = new AbortController();\n process.once('SIGTERM', () => controller.abort());\n const logger = Object.fromEntries(['debug', 'info', 'warn', 'error'].map((level) => [level, (value) => {\n process.send({ type: 'log', level, message: String(value) });\n }]));\n try {\n const source = await readFile(message.scriptPath, 'utf8');\n const compiled = stripTypeScriptTypes(source, { mode: 'strip' });\n const moduleUrl = 'data:text/javascript;base64,' + Buffer.from(compiled).toString('base64');\n const loaded = await import(moduleUrl);\n if (typeof loaded.run !== 'function') throw new Error('Computer script must export a named run function.');\n const program = {\n observe: () => call('observe'),\n act: (action) => call('act', action),\n };\n const result = await loaded.run({ context: { program }, input: message.input, logger, signal: controller.signal });\n const resultJson = result === undefined ? undefined : JSON.stringify(result);\n process.send({ type: 'complete', resultJson });\n } catch (error) {\n process.send({ type: 'failed', error: error instanceof Error ? error.message : String(error) });\n }\n});\n`;\n\ninterface WorkerMessage {\n readonly type?: unknown;\n readonly id?: unknown;\n readonly operation?: unknown;\n readonly payload?: unknown;\n readonly level?: unknown;\n readonly message?: unknown;\n readonly resultJson?: unknown;\n readonly error?: unknown;\n}\n\nexport class ComputerScriptRunner {\n private readonly client: ComputerUseSessionClient;\n private readonly allowedScriptPaths: readonly string[];\n private readonly timeoutMs: number;\n private readonly maxOutputBytes: number;\n\n public constructor(options: ComputerScriptRunnerOptions) {\n this.client = options.client;\n this.allowedScriptPaths = options.allowedScriptPaths;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n }\n\n public async execute(\n scriptPath: string,\n input: unknown,\n signal?: AbortSignal,\n ): Promise<ComputerScriptExecutionResult> {\n const resolvedPath = await realpath(scriptPath);\n const allowedPaths = await Promise.all(\n this.allowedScriptPaths.map(async (candidate) => realpath(candidate).catch(() => '')),\n );\n if (!allowedPaths.includes(resolvedPath)) throw new Error('Computer script path is not explicitly allowed.');\n if (!resolvedPath.endsWith('.ts')) throw new Error('Computer scripts must be TypeScript files.');\n\n const serializedInput = JSON.stringify(input);\n if (serializedInput === undefined) throw new Error('Computer script input must be JSON serializable.');\n if (Buffer.byteLength(serializedInput) > this.maxOutputBytes)\n throw new Error('Computer script input exceeds the size limit.');\n\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, ['--input-type=module', '--eval', WORKER_SOURCE], {\n stdio: ['ignore', 'pipe', 'pipe', 'ipc'],\n });\n const controller = new AbortController();\n const logs: string[] = [];\n let outputBytes = 0;\n let settled = false;\n let operation = Promise.resolve();\n\n const terminate = (): void => {\n controller.abort();\n child.kill('SIGTERM');\n const killTimer = setTimeout(() => child.kill('SIGKILL'), 250);\n killTimer.unref?.();\n };\n const finish = (error?: Error, value?: ComputerScriptExecutionResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n signal?.removeEventListener('abort', onAbort);\n terminate();\n if (error !== undefined) reject(error);\n else resolve(value as ComputerScriptExecutionResult);\n };\n const addOutput = (value: string): boolean => {\n outputBytes += Buffer.byteLength(value);\n if (outputBytes <= this.maxOutputBytes) return true;\n finish(new Error('Computer script output exceeded the size limit.'));\n return false;\n };\n const onAbort = () => finish(new Error('Computer script execution was cancelled.'));\n const timeout = setTimeout(() => finish(new Error('Computer script execution timed out.')), this.timeoutMs);\n timeout.unref?.();\n signal?.addEventListener('abort', onAbort, { once: true });\n if (signal?.aborted === true) return onAbort();\n\n child.stdout?.on('data', (chunk: Buffer) => addOutput(chunk.toString('utf8')));\n child.stderr?.on('data', (chunk: Buffer) => addOutput(chunk.toString('utf8')));\n child.on('error', (error) => finish(error));\n child.on('exit', (code, exitSignal) => {\n if (!settled)\n finish(new Error(`Computer script worker exited before completion (${exitSignal ?? code ?? 'unknown'}).`));\n });\n child.on('message', (rawMessage: WorkerMessage) => {\n if (settled) return;\n if (rawMessage.type === 'log' && typeof rawMessage.message === 'string') {\n const line = `[${String(rawMessage.level)}] ${rawMessage.message}`;\n if (addOutput(line)) logs.push(line);\n return;\n }\n if (\n rawMessage.type === 'program' &&\n typeof rawMessage.id === 'string' &&\n (rawMessage.operation === 'observe' || rawMessage.operation === 'act')\n ) {\n operation = operation.then(async () => {\n try {\n const value =\n rawMessage.operation === 'observe'\n ? await this.client.observe(controller.signal)\n : await this.client.act(rawMessage.payload as ComputerUseAction, controller.signal);\n child.send({ type: 'response', id: rawMessage.id, value });\n } catch (error) {\n child.send({\n type: 'response',\n id: rawMessage.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n });\n return;\n }\n if (rawMessage.type === 'failed' && typeof rawMessage.error === 'string') {\n finish(new Error(rawMessage.error));\n return;\n }\n if (rawMessage.type === 'complete') {\n operation = operation\n .then(async () => {\n const resultJson = rawMessage.resultJson;\n if (resultJson !== undefined && typeof resultJson !== 'string') {\n finish(new Error('Computer script returned an invalid result.'));\n return;\n }\n if (typeof resultJson === 'string' && !addOutput(resultJson)) return;\n const observation = await this.client.observe(controller.signal);\n finish(undefined, {\n ...(typeof resultJson === 'string' ? { result: JSON.parse(resultJson) as unknown } : {}),\n observation,\n logs,\n });\n })\n .catch((error: unknown) => finish(error instanceof Error ? error : new Error(String(error))));\n }\n });\n child.send({ type: 'start', scriptPath: resolvedPath, input: JSON.parse(serializedInput) as unknown });\n });\n }\n}\n"],"mappings":"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"}
@@ -1,12 +0,0 @@
1
- import { ComputerScriptExecutionResult, ComputerScriptRunnerOptions } from "../../types/computerScript.cjs";
2
- //#region src/adapters/pi/computerScriptRunner.d.ts
3
- export declare class ComputerScriptRunner {
4
- private readonly client;
5
- private readonly allowedScriptPaths;
6
- private readonly timeoutMs;
7
- private readonly maxOutputBytes;
8
- constructor(options: ComputerScriptRunnerOptions);
9
- execute(scriptPath: string, input: unknown, signal?: AbortSignal): Promise<ComputerScriptExecutionResult>;
10
- }
11
- //#endregion
12
- //# sourceMappingURL=computerScriptRunner.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"computerScriptRunner.d.cts","names":[],"sources":["../../../src/adapters/pi/computerScriptRunner.ts"],"mappings":";;qBAiEa;mBACM;mBACA;mBACA;mBACA;EAEjB,YAAmB,SAAS;EAOf,QACX,oBACA,gBACA,SAAS,cACR,QAAQ"}
@@ -1,12 +0,0 @@
1
- import { ComputerScriptExecutionResult, ComputerScriptRunnerOptions } from "../../types/computerScript.mjs";
2
- //#region src/adapters/pi/computerScriptRunner.d.ts
3
- export declare class ComputerScriptRunner {
4
- private readonly client;
5
- private readonly allowedScriptPaths;
6
- private readonly timeoutMs;
7
- private readonly maxOutputBytes;
8
- constructor(options: ComputerScriptRunnerOptions);
9
- execute(scriptPath: string, input: unknown, signal?: AbortSignal): Promise<ComputerScriptExecutionResult>;
10
- }
11
- //#endregion
12
- //# sourceMappingURL=computerScriptRunner.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"computerScriptRunner.d.mts","names":[],"sources":["../../../src/adapters/pi/computerScriptRunner.ts"],"mappings":";;qBAiEa;mBACM;mBACA;mBACA;mBACA;EAEjB,YAAmB,SAAS;EAOf,QACX,oBACA,gBACA,SAAS,cACR,QAAQ"}
@@ -1,45 +0,0 @@
1
- import{spawn as e}from"node:child_process";import{realpath as t}from"node:fs/promises";const n=String.raw`
2
- import { readFile } from 'node:fs/promises';
3
- import { stripTypeScriptTypes } from 'node:module';
4
-
5
- let nextRequestId = 0;
6
- const pending = new Map();
7
- const call = (operation, payload) => new Promise((resolve, reject) => {
8
- const id = String(++nextRequestId);
9
- pending.set(id, { resolve, reject });
10
- process.send({ type: 'program', id, operation, payload });
11
- });
12
- process.on('message', async (message) => {
13
- if (message?.type === 'response') {
14
- const request = pending.get(message.id);
15
- if (!request) return;
16
- pending.delete(message.id);
17
- if (message.error) request.reject(new Error(message.error));
18
- else request.resolve(message.value);
19
- return;
20
- }
21
- if (message?.type !== 'start') return;
22
- const controller = new AbortController();
23
- process.once('SIGTERM', () => controller.abort());
24
- const logger = Object.fromEntries(['debug', 'info', 'warn', 'error'].map((level) => [level, (value) => {
25
- process.send({ type: 'log', level, message: String(value) });
26
- }]));
27
- try {
28
- const source = await readFile(message.scriptPath, 'utf8');
29
- const compiled = stripTypeScriptTypes(source, { mode: 'strip' });
30
- const moduleUrl = 'data:text/javascript;base64,' + Buffer.from(compiled).toString('base64');
31
- const loaded = await import(moduleUrl);
32
- if (typeof loaded.run !== 'function') throw new Error('Computer script must export a named run function.');
33
- const program = {
34
- observe: () => call('observe'),
35
- act: (action) => call('act', action),
36
- };
37
- const result = await loaded.run({ context: { program }, input: message.input, logger, signal: controller.signal });
38
- const resultJson = result === undefined ? undefined : JSON.stringify(result);
39
- process.send({ type: 'complete', resultJson });
40
- } catch (error) {
41
- process.send({ type: 'failed', error: error instanceof Error ? error.message : String(error) });
42
- }
43
- });
44
- `;var r=class{client;allowedScriptPaths;timeoutMs;maxOutputBytes;constructor(e){this.client=e.client,this.allowedScriptPaths=e.allowedScriptPaths,this.timeoutMs=e.timeoutMs??3e4,this.maxOutputBytes=e.maxOutputBytes??262144}async execute(r,i,a){let o=await t(r);if(!(await Promise.all(this.allowedScriptPaths.map(async e=>t(e).catch(()=>``)))).includes(o))throw Error(`Computer script path is not explicitly allowed.`);if(!o.endsWith(`.ts`))throw Error(`Computer scripts must be TypeScript files.`);let s=JSON.stringify(i);if(s===void 0)throw Error(`Computer script input must be JSON serializable.`);if(Buffer.byteLength(s)>this.maxOutputBytes)throw Error(`Computer script input exceeds the size limit.`);return new Promise((t,r)=>{let i=e(process.execPath,[`--input-type=module`,`--eval`,n],{stdio:[`ignore`,`pipe`,`pipe`,`ipc`]}),c=new AbortController,l=[],u=0,d=!1,f=Promise.resolve(),p=()=>{c.abort(),i.kill(`SIGTERM`),setTimeout(()=>i.kill(`SIGKILL`),250).unref?.()},m=(e,n)=>{d||(d=!0,clearTimeout(_),a?.removeEventListener(`abort`,g),p(),e===void 0?t(n):r(e))},h=e=>(u+=Buffer.byteLength(e),u<=this.maxOutputBytes||(m(Error(`Computer script output exceeded the size limit.`)),!1)),g=()=>m(Error(`Computer script execution was cancelled.`)),_=setTimeout(()=>m(Error(`Computer script execution timed out.`)),this.timeoutMs);if(_.unref?.(),a?.addEventListener(`abort`,g,{once:!0}),a?.aborted===!0)return g();i.stdout?.on(`data`,e=>h(e.toString(`utf8`))),i.stderr?.on(`data`,e=>h(e.toString(`utf8`))),i.on(`error`,e=>m(e)),i.on(`exit`,(e,t)=>{d||m(Error(`Computer script worker exited before completion (${t??e??`unknown`}).`))}),i.on(`message`,e=>{if(!d){if(e.type===`log`&&typeof e.message==`string`){let t=`[${String(e.level)}] ${e.message}`;h(t)&&l.push(t);return}if(e.type===`program`&&typeof e.id==`string`&&(e.operation===`observe`||e.operation===`act`)){f=f.then(async()=>{try{let t=e.operation===`observe`?await this.client.observe(c.signal):await this.client.act(e.payload,c.signal);i.send({type:`response`,id:e.id,value:t})}catch(t){i.send({type:`response`,id:e.id,error:t instanceof Error?t.message:String(t)})}});return}if(e.type===`failed`&&typeof e.error==`string`){m(Error(e.error));return}e.type===`complete`&&(f=f.then(async()=>{let t=e.resultJson;if(t!==void 0&&typeof t!=`string`){m(Error(`Computer script returned an invalid result.`));return}if(typeof t==`string`&&!h(t))return;let n=await this.client.observe(c.signal);m(void 0,{...typeof t==`string`?{result:JSON.parse(t)}:{},observation:n,logs:l})}).catch(e=>m(e instanceof Error?e:Error(String(e)))))}}),i.send({type:`start`,scriptPath:o,input:JSON.parse(s)})})}};export{r as ComputerScriptRunner};
45
- //# sourceMappingURL=computerScriptRunner.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"computerScriptRunner.mjs","names":[],"sources":["../../../src/adapters/pi/computerScriptRunner.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { realpath } from 'node:fs/promises';\nimport type { ComputerScriptExecutionResult, ComputerScriptRunnerOptions } from '../../types/computerScript.ts';\nimport type { ComputerUseAction } from '../../types/computerUse.ts';\nimport type { ComputerUseSessionClient } from './sessionApiClient.ts';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_MAX_OUTPUT_BYTES = 256 * 1024;\n\nconst WORKER_SOURCE = String.raw`\nimport { readFile } from 'node:fs/promises';\nimport { stripTypeScriptTypes } from 'node:module';\n\nlet nextRequestId = 0;\nconst pending = new Map();\nconst call = (operation, payload) => new Promise((resolve, reject) => {\n const id = String(++nextRequestId);\n pending.set(id, { resolve, reject });\n process.send({ type: 'program', id, operation, payload });\n});\nprocess.on('message', async (message) => {\n if (message?.type === 'response') {\n const request = pending.get(message.id);\n if (!request) return;\n pending.delete(message.id);\n if (message.error) request.reject(new Error(message.error));\n else request.resolve(message.value);\n return;\n }\n if (message?.type !== 'start') return;\n const controller = new AbortController();\n process.once('SIGTERM', () => controller.abort());\n const logger = Object.fromEntries(['debug', 'info', 'warn', 'error'].map((level) => [level, (value) => {\n process.send({ type: 'log', level, message: String(value) });\n }]));\n try {\n const source = await readFile(message.scriptPath, 'utf8');\n const compiled = stripTypeScriptTypes(source, { mode: 'strip' });\n const moduleUrl = 'data:text/javascript;base64,' + Buffer.from(compiled).toString('base64');\n const loaded = await import(moduleUrl);\n if (typeof loaded.run !== 'function') throw new Error('Computer script must export a named run function.');\n const program = {\n observe: () => call('observe'),\n act: (action) => call('act', action),\n };\n const result = await loaded.run({ context: { program }, input: message.input, logger, signal: controller.signal });\n const resultJson = result === undefined ? undefined : JSON.stringify(result);\n process.send({ type: 'complete', resultJson });\n } catch (error) {\n process.send({ type: 'failed', error: error instanceof Error ? error.message : String(error) });\n }\n});\n`;\n\ninterface WorkerMessage {\n readonly type?: unknown;\n readonly id?: unknown;\n readonly operation?: unknown;\n readonly payload?: unknown;\n readonly level?: unknown;\n readonly message?: unknown;\n readonly resultJson?: unknown;\n readonly error?: unknown;\n}\n\nexport class ComputerScriptRunner {\n private readonly client: ComputerUseSessionClient;\n private readonly allowedScriptPaths: readonly string[];\n private readonly timeoutMs: number;\n private readonly maxOutputBytes: number;\n\n public constructor(options: ComputerScriptRunnerOptions) {\n this.client = options.client;\n this.allowedScriptPaths = options.allowedScriptPaths;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n }\n\n public async execute(\n scriptPath: string,\n input: unknown,\n signal?: AbortSignal,\n ): Promise<ComputerScriptExecutionResult> {\n const resolvedPath = await realpath(scriptPath);\n const allowedPaths = await Promise.all(\n this.allowedScriptPaths.map(async (candidate) => realpath(candidate).catch(() => '')),\n );\n if (!allowedPaths.includes(resolvedPath)) throw new Error('Computer script path is not explicitly allowed.');\n if (!resolvedPath.endsWith('.ts')) throw new Error('Computer scripts must be TypeScript files.');\n\n const serializedInput = JSON.stringify(input);\n if (serializedInput === undefined) throw new Error('Computer script input must be JSON serializable.');\n if (Buffer.byteLength(serializedInput) > this.maxOutputBytes)\n throw new Error('Computer script input exceeds the size limit.');\n\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, ['--input-type=module', '--eval', WORKER_SOURCE], {\n stdio: ['ignore', 'pipe', 'pipe', 'ipc'],\n });\n const controller = new AbortController();\n const logs: string[] = [];\n let outputBytes = 0;\n let settled = false;\n let operation = Promise.resolve();\n\n const terminate = (): void => {\n controller.abort();\n child.kill('SIGTERM');\n const killTimer = setTimeout(() => child.kill('SIGKILL'), 250);\n killTimer.unref?.();\n };\n const finish = (error?: Error, value?: ComputerScriptExecutionResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n signal?.removeEventListener('abort', onAbort);\n terminate();\n if (error !== undefined) reject(error);\n else resolve(value as ComputerScriptExecutionResult);\n };\n const addOutput = (value: string): boolean => {\n outputBytes += Buffer.byteLength(value);\n if (outputBytes <= this.maxOutputBytes) return true;\n finish(new Error('Computer script output exceeded the size limit.'));\n return false;\n };\n const onAbort = () => finish(new Error('Computer script execution was cancelled.'));\n const timeout = setTimeout(() => finish(new Error('Computer script execution timed out.')), this.timeoutMs);\n timeout.unref?.();\n signal?.addEventListener('abort', onAbort, { once: true });\n if (signal?.aborted === true) return onAbort();\n\n child.stdout?.on('data', (chunk: Buffer) => addOutput(chunk.toString('utf8')));\n child.stderr?.on('data', (chunk: Buffer) => addOutput(chunk.toString('utf8')));\n child.on('error', (error) => finish(error));\n child.on('exit', (code, exitSignal) => {\n if (!settled)\n finish(new Error(`Computer script worker exited before completion (${exitSignal ?? code ?? 'unknown'}).`));\n });\n child.on('message', (rawMessage: WorkerMessage) => {\n if (settled) return;\n if (rawMessage.type === 'log' && typeof rawMessage.message === 'string') {\n const line = `[${String(rawMessage.level)}] ${rawMessage.message}`;\n if (addOutput(line)) logs.push(line);\n return;\n }\n if (\n rawMessage.type === 'program' &&\n typeof rawMessage.id === 'string' &&\n (rawMessage.operation === 'observe' || rawMessage.operation === 'act')\n ) {\n operation = operation.then(async () => {\n try {\n const value =\n rawMessage.operation === 'observe'\n ? await this.client.observe(controller.signal)\n : await this.client.act(rawMessage.payload as ComputerUseAction, controller.signal);\n child.send({ type: 'response', id: rawMessage.id, value });\n } catch (error) {\n child.send({\n type: 'response',\n id: rawMessage.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n });\n return;\n }\n if (rawMessage.type === 'failed' && typeof rawMessage.error === 'string') {\n finish(new Error(rawMessage.error));\n return;\n }\n if (rawMessage.type === 'complete') {\n operation = operation\n .then(async () => {\n const resultJson = rawMessage.resultJson;\n if (resultJson !== undefined && typeof resultJson !== 'string') {\n finish(new Error('Computer script returned an invalid result.'));\n return;\n }\n if (typeof resultJson === 'string' && !addOutput(resultJson)) return;\n const observation = await this.client.observe(controller.signal);\n finish(undefined, {\n ...(typeof resultJson === 'string' ? { result: JSON.parse(resultJson) as unknown } : {}),\n observation,\n logs,\n });\n })\n .catch((error: unknown) => finish(error instanceof Error ? error : new Error(String(error))));\n }\n });\n child.send({ type: 'start', scriptPath: resolvedPath, input: JSON.parse(serializedInput) as unknown });\n });\n }\n}\n"],"mappings":"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"}
@@ -1,38 +0,0 @@
1
- import { ComputerUseAction, ComputerUseObservation } from "./computerUse.cjs";
2
- import { ComputerUseSessionClient } from "../adapters/pi/sessionApiClient.cjs";
3
- //#region src/types/computerScript.d.ts
4
- export interface ComputerScriptProgram {
5
- observe(): Promise<ComputerUseObservation>;
6
- act(action: ComputerUseAction): Promise<unknown>;
7
- }
8
- export interface ComputerScriptLogger {
9
- debug(message: string): void;
10
- info(message: string): void;
11
- warn(message: string): void;
12
- error(message: string): void;
13
- }
14
- export interface ComputerScriptRunContext {
15
- context: {
16
- program: ComputerScriptProgram;
17
- };
18
- input: unknown;
19
- logger: ComputerScriptLogger;
20
- signal: AbortSignal;
21
- }
22
- export type ComputerScriptRun = (options: ComputerScriptRunContext) => Promise<unknown>;
23
- export interface ComputerScriptExecutionResult {
24
- result?: unknown;
25
- observation: ComputerUseObservation;
26
- logs: readonly string[];
27
- }
28
- export interface ComputerScriptExecutor {
29
- execute(scriptPath: string, input: unknown, signal?: AbortSignal): Promise<ComputerScriptExecutionResult>;
30
- }
31
- export interface ComputerScriptRunnerOptions {
32
- readonly client: ComputerUseSessionClient;
33
- readonly allowedScriptPaths: readonly string[];
34
- readonly timeoutMs?: number;
35
- readonly maxOutputBytes?: number;
36
- }
37
- //#endregion
38
- //# sourceMappingURL=computerScript.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"computerScript.d.cts","names":[],"sources":["../../src/types/computerScript.ts"],"mappings":";;;iBAGiB;EACf,WAAW,QAAQ;EACnB,IAAI,QAAQ,oBAAoB;;iBAGjB;EACf,MAAM;EACN,KAAK;EACL,KAAK;EACL,MAAM;;iBAGS;EACf;IAAW,SAAS;;EACpB;EACA,QAAQ;EACR,QAAQ;;YAGE,qBAAqB,SAAS,6BAA6B;iBAEtD;EACf;EACA,aAAa;EACb;;iBAGe;EACf,QAAQ,oBAAoB,gBAAgB,SAAS,cAAc,QAAQ;;iBAG5D;WACN,QAAQ;WACR;WACA;WACA"}
@@ -1,38 +0,0 @@
1
- import { ComputerUseAction, ComputerUseObservation } from "./computerUse.mjs";
2
- import { ComputerUseSessionClient } from "../adapters/pi/sessionApiClient.mjs";
3
- //#region src/types/computerScript.d.ts
4
- export interface ComputerScriptProgram {
5
- observe(): Promise<ComputerUseObservation>;
6
- act(action: ComputerUseAction): Promise<unknown>;
7
- }
8
- export interface ComputerScriptLogger {
9
- debug(message: string): void;
10
- info(message: string): void;
11
- warn(message: string): void;
12
- error(message: string): void;
13
- }
14
- export interface ComputerScriptRunContext {
15
- context: {
16
- program: ComputerScriptProgram;
17
- };
18
- input: unknown;
19
- logger: ComputerScriptLogger;
20
- signal: AbortSignal;
21
- }
22
- export type ComputerScriptRun = (options: ComputerScriptRunContext) => Promise<unknown>;
23
- export interface ComputerScriptExecutionResult {
24
- result?: unknown;
25
- observation: ComputerUseObservation;
26
- logs: readonly string[];
27
- }
28
- export interface ComputerScriptExecutor {
29
- execute(scriptPath: string, input: unknown, signal?: AbortSignal): Promise<ComputerScriptExecutionResult>;
30
- }
31
- export interface ComputerScriptRunnerOptions {
32
- readonly client: ComputerUseSessionClient;
33
- readonly allowedScriptPaths: readonly string[];
34
- readonly timeoutMs?: number;
35
- readonly maxOutputBytes?: number;
36
- }
37
- //#endregion
38
- //# sourceMappingURL=computerScript.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"computerScript.d.mts","names":[],"sources":["../../src/types/computerScript.ts"],"mappings":";;;iBAGiB;EACf,WAAW,QAAQ;EACnB,IAAI,QAAQ,oBAAoB;;iBAGjB;EACf,MAAM;EACN,KAAK;EACL,KAAK;EACL,MAAM;;iBAGS;EACf;IAAW,SAAS;;EACpB;EACA,QAAQ;EACR,QAAQ;;YAGE,qBAAqB,SAAS,6BAA6B;iBAEtD;EACf;EACA,aAAa;EACb;;iBAGe;EACf,QAAQ,oBAAoB,gBAAgB,SAAS,cAAc,QAAQ;;iBAG5D;WACN,QAAQ;WACR;WACA;WACA"}
@@ -1,37 +0,0 @@
1
- # Author computer scripts
2
-
3
- `computer_exec` runs reusable local TypeScript against the application that the current Desktop grant authorized. Scripts are trusted Node.js programs. They are not sandboxed and can use the full permissions of the Pi process. Only allow scripts you have reviewed.
4
-
5
- ## Allow a script
6
-
7
- Set `DOOMPI_COMPUTER_USE_SCRIPT_PATHS` before starting Pi. Its value is a platform-delimited list of exact script paths (`:` on macOS and Linux). Symlinks are resolved before the exact-path check.
8
-
9
- ```bash
10
- export DOOMPI_COMPUTER_USE_SCRIPT_PATHS="$PWD/scripts/fill-form.ts:$PWD/scripts/read-summary.ts"
11
- ```
12
-
13
- The tool does not accept a directory, glob, inline source, or a path that was not listed. Scripts must use the `.ts` extension.
14
-
15
- ## Script contract
16
-
17
- Export a named `run` function. It receives only the active authorized program handle in `context.program`, JSON input, a bounded logger, and an abort signal:
18
-
19
- ```ts
20
- import type { ComputerScriptRun } from '@agimon-ai/doompi-computer-use';
21
-
22
- export const run: ComputerScriptRun = async ({ context: { program }, input, logger, signal }) => {
23
- if (signal.aborted) throw signal.reason;
24
-
25
- const options = input as { snapshotId: string; elementRef: string };
26
- logger.info('Pressing the requested element');
27
- return program.act({
28
- kind: 'press',
29
- snapshotId: options.snapshotId,
30
- elementRef: options.elementRef,
31
- });
32
- };
33
- ```
34
-
35
- `program.observe()` returns a semantic observation. `program.act(action)` applies one semantic action. Calls are serialized through the existing session API. A successful run keeps the session active and `computer_exec` returns a fresh observation for the next turn.
36
-
37
- Scripts are loaded with Node.js type stripping. Use erasable TypeScript syntax. The runner enforces a 30 second execution timeout and a combined 256 KiB bound for input, logs, process output, and the serialized result. Cancellation or a limit breach terminates the worker process. A cancelled in-flight Desktop request can make the session stop because its outcome is uncertain.