@myagentroam/node 0.1.3 → 0.1.6

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 (195) hide show
  1. package/README.md +9 -1
  2. package/dist/capabilities.d.ts +2 -0
  3. package/dist/capabilities.js +15 -4
  4. package/dist/claude-agent-sdk.d.ts +4 -16
  5. package/dist/claude-agent-sdk.js +18 -79
  6. package/dist/claude-channel.js +0 -1
  7. package/dist/codex-app-server.d.ts +5 -0
  8. package/dist/codex-app-server.js +26 -5
  9. package/dist/config.d.ts +3 -0
  10. package/dist/config.js +18 -2
  11. package/dist/connector/node-connector-options.d.ts +26 -0
  12. package/dist/connector/node-connector-options.js +1 -0
  13. package/dist/connector/node-operation-router.d.ts +9 -0
  14. package/dist/connector/node-operation-router.js +22 -0
  15. package/dist/connector.d.ts +62 -356
  16. package/dist/connector.js +714 -6176
  17. package/dist/control-outbound-scheduler.d.ts +26 -0
  18. package/dist/control-outbound-scheduler.js +121 -0
  19. package/dist/database.d.ts +4 -0
  20. package/dist/database.js +15 -10
  21. package/dist/event-buffer.js +0 -1
  22. package/dist/fake-runner.js +0 -1
  23. package/dist/health.js +0 -1
  24. package/dist/main.js +6 -1
  25. package/dist/migrations/v001.js +0 -1
  26. package/dist/migrations/v002.js +0 -1
  27. package/dist/migrations/v003.d.ts +4 -0
  28. package/dist/migrations/v003.js +44 -0
  29. package/dist/native-jsonl-reader.d.ts +14 -0
  30. package/dist/native-jsonl-reader.js +97 -0
  31. package/dist/native-session-history.d.ts +41 -4
  32. package/dist/native-session-history.js +340 -159
  33. package/dist/opencode-runtime.d.ts +33 -0
  34. package/dist/opencode-runtime.js +80 -0
  35. package/dist/opencode-server.d.ts +47 -0
  36. package/dist/opencode-server.js +250 -0
  37. package/dist/operational.js +10 -3
  38. package/dist/process-tree.js +0 -1
  39. package/dist/runner/abstract-runner.d.ts +153 -0
  40. package/dist/runner/abstract-runner.js +154 -0
  41. package/dist/runner/claude/managed-run-controller.d.ts +52 -0
  42. package/dist/runner/claude/managed-run-controller.js +326 -0
  43. package/dist/runner/claude-code-runner.d.ts +70 -0
  44. package/dist/runner/claude-code-runner.js +286 -0
  45. package/dist/runner/codex/conversation-parser.d.ts +59 -0
  46. package/dist/runner/codex/conversation-parser.js +816 -0
  47. package/dist/runner/codex/managed-run-controller.d.ts +80 -0
  48. package/dist/runner/codex/managed-run-controller.js +388 -0
  49. package/dist/runner/codex-runner.d.ts +85 -0
  50. package/dist/runner/codex-runner.js +365 -0
  51. package/dist/runner/fake-test-runner.d.ts +26 -0
  52. package/dist/runner/fake-test-runner.js +47 -0
  53. package/dist/runner/opencode/conversation-parser.d.ts +27 -0
  54. package/dist/runner/opencode/conversation-parser.js +217 -0
  55. package/dist/runner/opencode/managed-run-controller.d.ts +76 -0
  56. package/dist/runner/opencode/managed-run-controller.js +451 -0
  57. package/dist/runner/opencode-runner.d.ts +60 -0
  58. package/dist/runner/opencode-runner.js +330 -0
  59. package/dist/runner/runner-registry.d.ts +19 -0
  60. package/dist/runner/runner-registry.js +75 -0
  61. package/dist/runner-command-engine.js +0 -1
  62. package/dist/runner-profiles.d.ts +4 -0
  63. package/dist/runner-profiles.js +76 -3
  64. package/dist/runner-usage.d.ts +2 -2
  65. package/dist/runner-usage.js +3 -6
  66. package/dist/runtime-state.js +26 -14
  67. package/dist/service/attachment-domain-state.d.ts +5 -0
  68. package/dist/service/attachment-domain-state.js +5 -0
  69. package/dist/service/conversation-history-service.d.ts +34 -0
  70. package/dist/service/conversation-history-service.js +158 -0
  71. package/dist/service/external-session-resume-service.d.ts +32 -0
  72. package/dist/service/external-session-resume-service.js +98 -0
  73. package/dist/service/fake-managed-run-service.d.ts +29 -0
  74. package/dist/service/fake-managed-run-service.js +62 -0
  75. package/dist/service/managed-runner-session-service.d.ts +12 -0
  76. package/dist/service/managed-runner-session-service.js +35 -0
  77. package/dist/service/native-session-projection-service.d.ts +51 -0
  78. package/dist/service/native-session-projection-service.js +271 -0
  79. package/dist/service/native-session-watch-service.d.ts +37 -0
  80. package/dist/service/native-session-watch-service.js +150 -0
  81. package/dist/service/node-connection-lifecycle-service.d.ts +37 -0
  82. package/dist/service/node-connection-lifecycle-service.js +85 -0
  83. package/dist/service/node-control-channel.d.ts +32 -0
  84. package/dist/service/node-control-channel.js +85 -0
  85. package/dist/service/node-control-message-service.d.ts +28 -0
  86. package/dist/service/node-control-message-service.js +104 -0
  87. package/dist/service/node-request-service.d.ts +14 -0
  88. package/dist/service/node-request-service.js +50 -0
  89. package/dist/service/node-run-event-bridge.d.ts +40 -0
  90. package/dist/service/node-run-event-bridge.js +83 -0
  91. package/dist/service/node-terminal-channel.d.ts +29 -0
  92. package/dist/service/node-terminal-channel.js +182 -0
  93. package/dist/service/node-upgrade-coordinator.d.ts +29 -0
  94. package/dist/service/node-upgrade-coordinator.js +137 -0
  95. package/dist/service/node-workspace-coordinator.d.ts +32 -0
  96. package/dist/service/node-workspace-coordinator.js +80 -0
  97. package/dist/service/run-attachment-service.d.ts +37 -0
  98. package/dist/service/run-attachment-service.js +160 -0
  99. package/dist/service/run-domain-state.d.ts +4 -0
  100. package/dist/service/run-domain-state.js +4 -0
  101. package/dist/service/run-event-service.d.ts +31 -0
  102. package/dist/service/run-event-service.js +102 -0
  103. package/dist/service/run-workbench-service.d.ts +27 -0
  104. package/dist/service/run-workbench-service.js +113 -0
  105. package/dist/service/runner-channel-message-service.d.ts +13 -0
  106. package/dist/service/runner-channel-message-service.js +20 -0
  107. package/dist/service/runner-interaction-service.d.ts +40 -0
  108. package/dist/service/runner-interaction-service.js +246 -0
  109. package/dist/service/runner-service.d.ts +44 -0
  110. package/dist/service/runner-service.js +68 -0
  111. package/dist/service/session-catalog-service.d.ts +45 -0
  112. package/dist/service/session-catalog-service.js +224 -0
  113. package/dist/service/session-command-service.d.ts +23 -0
  114. package/dist/service/session-command-service.js +69 -0
  115. package/dist/service/session-context-service.d.ts +34 -0
  116. package/dist/service/session-context-service.js +87 -0
  117. package/dist/service/session-domain-state.d.ts +8 -0
  118. package/dist/service/session-domain-state.js +8 -0
  119. package/dist/service/session-identity-service.d.ts +24 -0
  120. package/dist/service/session-identity-service.js +86 -0
  121. package/dist/service/session-lifecycle-service.d.ts +31 -0
  122. package/dist/service/session-lifecycle-service.js +242 -0
  123. package/dist/service/session-message-service.d.ts +33 -0
  124. package/dist/service/session-message-service.js +157 -0
  125. package/dist/service/session-presentation-service.d.ts +33 -0
  126. package/dist/service/session-presentation-service.js +36 -0
  127. package/dist/service/session-query-service.d.ts +31 -0
  128. package/dist/service/session-query-service.js +77 -0
  129. package/dist/service/terminal-relay-state.d.ts +6 -0
  130. package/dist/service/terminal-relay-state.js +3 -0
  131. package/dist/service/terminal-service.d.ts +12 -0
  132. package/dist/service/terminal-service.js +52 -0
  133. package/dist/service/workbench-event-service.d.ts +21 -0
  134. package/dist/service/workbench-event-service.js +53 -0
  135. package/dist/service/workbench-manifest-service.d.ts +89 -0
  136. package/dist/service/workbench-manifest-service.js +70 -0
  137. package/dist/service/workspace-change-service.d.ts +9 -0
  138. package/dist/service/workspace-change-service.js +86 -0
  139. package/dist/service/workspace-content-search-service.d.ts +14 -0
  140. package/dist/service/workspace-content-search-service.js +477 -0
  141. package/dist/service/workspace-domain-state.d.ts +75 -0
  142. package/dist/service/workspace-domain-state.js +40 -0
  143. package/dist/service/workspace-file-service.d.ts +17 -0
  144. package/dist/service/workspace-file-service.js +109 -0
  145. package/dist/service/workspace-queue-workbench-service.d.ts +55 -0
  146. package/dist/service/workspace-queue-workbench-service.js +226 -0
  147. package/dist/service/workspace-service.d.ts +15 -0
  148. package/dist/service/workspace-service.js +67 -0
  149. package/dist/service/workspace-session-service.d.ts +23 -0
  150. package/dist/service/workspace-session-service.js +139 -0
  151. package/dist/service/workspace-upload-service.d.ts +37 -0
  152. package/dist/service/workspace-upload-service.js +240 -0
  153. package/dist/service/workspace-watch-service.d.ts +18 -0
  154. package/dist/service/workspace-watch-service.js +120 -0
  155. package/dist/service/workspace-workbench-service.d.ts +47 -0
  156. package/dist/service/workspace-workbench-service.js +223 -0
  157. package/dist/service.js +2 -3
  158. package/dist/storage.js +0 -1
  159. package/dist/supervisor.d.ts +1 -0
  160. package/dist/supervisor.js +154 -0
  161. package/dist/terminal.d.ts +1 -0
  162. package/dist/terminal.js +1 -2
  163. package/dist/util/node-operation-parsers.d.ts +92 -0
  164. package/dist/util/node-operation-parsers.js +382 -0
  165. package/dist/util/runner-native-session-parsers.d.ts +72 -0
  166. package/dist/util/runner-native-session-parsers.js +381 -0
  167. package/dist/util/secret-environment.d.ts +1 -0
  168. package/dist/util/secret-environment.js +25 -0
  169. package/dist/workspace.d.ts +62 -2
  170. package/dist/workspace.js +539 -57
  171. package/package.json +3 -2
  172. package/dist/capabilities.js.map +0 -1
  173. package/dist/claude-agent-sdk.js.map +0 -1
  174. package/dist/claude-channel.js.map +0 -1
  175. package/dist/codex-app-server.js.map +0 -1
  176. package/dist/config.js.map +0 -1
  177. package/dist/connector.js.map +0 -1
  178. package/dist/database.js.map +0 -1
  179. package/dist/event-buffer.js.map +0 -1
  180. package/dist/fake-runner.js.map +0 -1
  181. package/dist/health.js.map +0 -1
  182. package/dist/main.js.map +0 -1
  183. package/dist/migrations/v001.js.map +0 -1
  184. package/dist/migrations/v002.js.map +0 -1
  185. package/dist/native-session-history.js.map +0 -1
  186. package/dist/operational.js.map +0 -1
  187. package/dist/process-tree.js.map +0 -1
  188. package/dist/runner-command-engine.js.map +0 -1
  189. package/dist/runner-profiles.js.map +0 -1
  190. package/dist/runner-usage.js.map +0 -1
  191. package/dist/runtime-state.js.map +0 -1
  192. package/dist/service.js.map +0 -1
  193. package/dist/storage.js.map +0 -1
  194. package/dist/terminal.js.map +0 -1
  195. package/dist/workspace.js.map +0 -1
@@ -0,0 +1,80 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { minimalRunnerEnvironment, platformCliInvocation, runRunnerProbe } from './operational.js';
3
+ const PROBE_TIMEOUT_MS = 3_000;
4
+ export function probeOpenCode(command = 'opencode') {
5
+ const invocation = platformCliInvocation(command, ['--version']);
6
+ const result = spawnSync(invocation.command, invocation.args, {
7
+ windowsHide: true,
8
+ env: minimalRunnerEnvironment(),
9
+ timeout: PROBE_TIMEOUT_MS,
10
+ encoding: 'utf8'
11
+ });
12
+ if (result.status !== 0 || result.error !== undefined)
13
+ return { available: false, compatible: false };
14
+ return openCodeProbeResult(`${result.stdout}${result.stderr}`);
15
+ }
16
+ export async function probeOpenCodeAsync(command = 'opencode') {
17
+ const invocation = platformCliInvocation(command, ['--version']);
18
+ const result = await runRunnerProbe(invocation.command, invocation.args, {
19
+ env: minimalRunnerEnvironment(),
20
+ timeoutMs: PROBE_TIMEOUT_MS
21
+ });
22
+ return result.status === 0
23
+ ? openCodeProbeResult(`${result.stdout}${result.stderr}`)
24
+ : { available: false, compatible: false };
25
+ }
26
+ function openCodeProbeResult(output) {
27
+ const version = output.trim().replace(/^v/i, '').slice(0, 128);
28
+ return {
29
+ available: version.length > 0,
30
+ compatible: /^1\.18\./.test(version),
31
+ ...(version.length === 0 ? {} : { version })
32
+ };
33
+ }
34
+ export function openCodeProfileModels(catalog) {
35
+ const connected = new Set(catalog.connected ?? []);
36
+ return (catalog.all ?? [])
37
+ .filter((provider) => typeof provider.id === 'string' &&
38
+ connected.has(provider.id) &&
39
+ provider.source === 'config')
40
+ .flatMap((provider) => {
41
+ const providerId = provider.id;
42
+ return Object.entries(provider.models ?? {}).flatMap(([key, model]) => {
43
+ const modelId = boundedString(model.id) ?? boundedString(key);
44
+ if (modelId === undefined)
45
+ return [];
46
+ const variants = Object.entries(model.variants ?? {})
47
+ .filter(([, value]) => value.disabled !== true)
48
+ .map(([id]) => id)
49
+ .filter((id) => id.length > 0 && id.length <= 64)
50
+ .slice(0, 32);
51
+ return [
52
+ {
53
+ id: `${providerId}/${modelId}`.slice(0, 128),
54
+ label: (boundedString(model.name) ?? modelId).slice(0, 128),
55
+ supportedEfforts: variants
56
+ }
57
+ ];
58
+ });
59
+ });
60
+ }
61
+ export function openCodeContextLimits(catalog) {
62
+ const connected = new Set(catalog.connected ?? []);
63
+ return new Map((catalog.all ?? [])
64
+ .filter((provider) => typeof provider.id === 'string' &&
65
+ connected.has(provider.id) &&
66
+ provider.source === 'config')
67
+ .flatMap((provider) => Object.entries(provider.models ?? {}).flatMap(([key, model]) => {
68
+ const modelId = boundedString(model.id) ?? boundedString(key);
69
+ const context = model.limit?.context;
70
+ return modelId !== undefined && Number.isSafeInteger(context) && (context ?? 0) > 0
71
+ ? [[`${provider.id}/${modelId}`, context]]
72
+ : [];
73
+ })));
74
+ }
75
+ function boundedString(value) {
76
+ if (typeof value !== 'string')
77
+ return undefined;
78
+ const trimmed = value.trim();
79
+ return trimmed.length > 0 && trimmed.length <= 4096 ? trimmed : undefined;
80
+ }
@@ -0,0 +1,47 @@
1
+ import { spawn } from 'node:child_process';
2
+ import type { OpenCodeProviderCatalog } from './opencode-runtime.js';
3
+ export interface OpenCodeServerOptions {
4
+ readonly command?: string;
5
+ readonly startTimeoutMs?: number;
6
+ readonly spawnProcess?: typeof spawn;
7
+ }
8
+ export declare class OpenCodeServerClient {
9
+ private readonly options;
10
+ private child;
11
+ private client;
12
+ private starting;
13
+ private environment;
14
+ constructor(options?: OpenCodeServerOptions);
15
+ configureEnvironment(environment: Readonly<Record<string, string>>): void;
16
+ start(): Promise<void>;
17
+ private startServer;
18
+ providers(directory: string): Promise<OpenCodeProviderCatalog>;
19
+ sessions(directory: string, limit?: number): Promise<readonly unknown[]>;
20
+ sessionStatus(directory: string): Promise<Readonly<Record<string, unknown>>>;
21
+ createSession(directory: string, title?: string): Promise<unknown>;
22
+ forkSession(directory: string, sessionID: string, messageID: string): Promise<unknown>;
23
+ messages(directory: string, sessionID: string, limit?: number): Promise<readonly unknown[]>;
24
+ updateSession(directory: string, sessionID: string, title: string): Promise<unknown>;
25
+ deleteSession(directory: string, sessionID: string): Promise<unknown>;
26
+ abort(directory: string, sessionID: string): Promise<unknown>;
27
+ summarize(directory: string, sessionID: string, model: string): Promise<unknown>;
28
+ replyPermission(directory: string, requestID: string, decision: 'APPROVED' | 'REJECTED'): Promise<unknown>;
29
+ replyQuestion(directory: string, requestID: string, answers: readonly (readonly string[])[]): Promise<unknown>;
30
+ rejectQuestion(directory: string, requestID: string): Promise<unknown>;
31
+ prompt(input: {
32
+ readonly directory: string;
33
+ readonly sessionID: string;
34
+ readonly text: string;
35
+ readonly model: string;
36
+ readonly variant?: string;
37
+ readonly agent?: 'plan' | 'build';
38
+ readonly attachments?: readonly {
39
+ readonly name: string;
40
+ readonly mime: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
41
+ readonly dataBase64: string;
42
+ }[];
43
+ }): Promise<unknown>;
44
+ subscribe(directory: string, signal: AbortSignal, onEvent: (event: unknown) => void, onReady?: () => void): Promise<void>;
45
+ stop(): Promise<void>;
46
+ private requireClient;
47
+ }
@@ -0,0 +1,250 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { createServer } from 'node:net';
4
+ import { createOpencodeClient } from '@opencode-ai/sdk/v2/client';
5
+ import { minimalRunnerEnvironment, platformCliInvocation } from './operational.js';
6
+ import { terminateProcessTree } from './process-tree.js';
7
+ const START_TIMEOUT_MS = 10_000;
8
+ export class OpenCodeServerClient {
9
+ options;
10
+ child;
11
+ client;
12
+ starting;
13
+ environment = {};
14
+ constructor(options = {}) {
15
+ this.options = options;
16
+ }
17
+ configureEnvironment(environment) {
18
+ if (this.child !== undefined)
19
+ throw new Error('OPENCODE_SERVER_ALREADY_STARTED');
20
+ this.environment = { ...environment };
21
+ }
22
+ async start() {
23
+ if (this.client !== undefined)
24
+ return;
25
+ if (this.starting !== undefined)
26
+ return this.starting;
27
+ const starting = this.startServer();
28
+ this.starting = starting;
29
+ try {
30
+ await starting;
31
+ }
32
+ finally {
33
+ if (this.starting === starting)
34
+ this.starting = undefined;
35
+ }
36
+ }
37
+ async startServer() {
38
+ const port = await availablePort();
39
+ const password = randomBytes(24).toString('base64url');
40
+ const invocation = platformCliInvocation(this.options.command ?? 'opencode', [
41
+ 'serve',
42
+ '--hostname',
43
+ '127.0.0.1',
44
+ '--port',
45
+ String(port)
46
+ ]);
47
+ const spawnProcess = this.options.spawnProcess ?? spawn;
48
+ const child = spawnProcess(invocation.command, invocation.args, {
49
+ windowsHide: true,
50
+ detached: process.platform !== 'win32',
51
+ env: {
52
+ ...minimalRunnerEnvironment(),
53
+ ...this.environment,
54
+ OPENCODE_SERVER_USERNAME: 'opencode',
55
+ OPENCODE_SERVER_PASSWORD: password,
56
+ OPENCODE_DISABLE_AUTOUPDATE: 'true'
57
+ },
58
+ stdio: ['ignore', 'pipe', 'pipe']
59
+ });
60
+ this.child = child;
61
+ const authorization = `Basic ${Buffer.from(`opencode:${password}`).toString('base64')}`;
62
+ const client = createOpencodeClient({
63
+ baseUrl: `http://127.0.0.1:${port}`,
64
+ headers: { authorization },
65
+ throwOnError: true
66
+ });
67
+ try {
68
+ await waitUntilHealthy(`http://127.0.0.1:${port}/global/health`, authorization, child, this.options.startTimeoutMs ?? START_TIMEOUT_MS);
69
+ this.client = client;
70
+ }
71
+ catch (error) {
72
+ await this.stop();
73
+ throw error;
74
+ }
75
+ }
76
+ async providers(directory) {
77
+ const client = await this.requireClient();
78
+ return unwrap(await client.provider.list({ directory }));
79
+ }
80
+ async sessions(directory, limit = 100) {
81
+ const client = await this.requireClient();
82
+ return unwrap(await client.session.list({ directory, limit }));
83
+ }
84
+ async sessionStatus(directory) {
85
+ const client = await this.requireClient();
86
+ return unwrap(await client.session.status({ directory }));
87
+ }
88
+ async createSession(directory, title) {
89
+ const client = await this.requireClient();
90
+ return unwrap(await client.session.create({ directory, ...(title === undefined ? {} : { title }) }));
91
+ }
92
+ async forkSession(directory, sessionID, messageID) {
93
+ const client = await this.requireClient();
94
+ return unwrap(await client.session.fork({ directory, sessionID, messageID }));
95
+ }
96
+ async messages(directory, sessionID, limit = 100) {
97
+ const client = await this.requireClient();
98
+ return unwrap(await client.session.messages({ directory, sessionID, limit }));
99
+ }
100
+ async updateSession(directory, sessionID, title) {
101
+ const client = await this.requireClient();
102
+ return unwrap(await client.session.update({ directory, sessionID, title }));
103
+ }
104
+ async deleteSession(directory, sessionID) {
105
+ const client = await this.requireClient();
106
+ return unwrap(await client.session.delete({ directory, sessionID }));
107
+ }
108
+ async abort(directory, sessionID) {
109
+ const client = await this.requireClient();
110
+ return unwrap(await client.session.abort({ directory, sessionID }));
111
+ }
112
+ async summarize(directory, sessionID, model) {
113
+ const client = await this.requireClient();
114
+ const separator = model.indexOf('/');
115
+ if (separator <= 0 || separator === model.length - 1)
116
+ throw new Error('OPENCODE_MODEL_INVALID');
117
+ return unwrap(await client.session.summarize({
118
+ directory,
119
+ sessionID,
120
+ providerID: model.slice(0, separator),
121
+ modelID: model.slice(separator + 1)
122
+ }));
123
+ }
124
+ async replyPermission(directory, requestID, decision) {
125
+ const client = await this.requireClient();
126
+ return unwrap(await client.permission.reply({
127
+ directory,
128
+ requestID,
129
+ reply: decision === 'APPROVED' ? 'once' : 'reject'
130
+ }));
131
+ }
132
+ async replyQuestion(directory, requestID, answers) {
133
+ const client = await this.requireClient();
134
+ return unwrap(await client.question.reply({
135
+ directory,
136
+ requestID,
137
+ answers: answers.map((answer) => [...answer])
138
+ }));
139
+ }
140
+ async rejectQuestion(directory, requestID) {
141
+ const client = await this.requireClient();
142
+ return unwrap(await client.question.reject({ directory, requestID }));
143
+ }
144
+ async prompt(input) {
145
+ const client = await this.requireClient();
146
+ const separator = input.model.indexOf('/');
147
+ if (separator <= 0 || separator === input.model.length - 1)
148
+ throw new Error('OPENCODE_MODEL_INVALID');
149
+ return unwrap(await client.session.promptAsync({
150
+ directory: input.directory,
151
+ sessionID: input.sessionID,
152
+ model: {
153
+ providerID: input.model.slice(0, separator),
154
+ modelID: input.model.slice(separator + 1)
155
+ },
156
+ ...(input.variant === undefined || input.variant.length === 0
157
+ ? {}
158
+ : { variant: input.variant }),
159
+ ...(input.agent === undefined ? {} : { agent: input.agent }),
160
+ parts: [
161
+ { type: 'text', text: input.text },
162
+ ...(input.attachments ?? []).map((attachment) => ({
163
+ type: 'file',
164
+ mime: attachment.mime,
165
+ filename: attachment.name,
166
+ url: `data:${attachment.mime};base64,${attachment.dataBase64}`
167
+ }))
168
+ ]
169
+ }));
170
+ }
171
+ async subscribe(directory, signal, onEvent, onReady) {
172
+ const client = await this.requireClient();
173
+ const stream = await client.event.subscribe({ directory }, { signal });
174
+ onReady?.();
175
+ for await (const event of stream.stream)
176
+ onEvent(event);
177
+ }
178
+ async stop() {
179
+ this.client = undefined;
180
+ const child = this.child;
181
+ this.child = undefined;
182
+ if (child === undefined)
183
+ return;
184
+ if (child.pid === undefined) {
185
+ child.kill('SIGTERM');
186
+ return;
187
+ }
188
+ try {
189
+ await terminateProcessTree({
190
+ pid: child.pid,
191
+ kill: (signal) => child.kill(signal)
192
+ });
193
+ }
194
+ catch {
195
+ child.kill('SIGTERM');
196
+ }
197
+ }
198
+ async requireClient() {
199
+ await this.start();
200
+ return this.client;
201
+ }
202
+ }
203
+ async function availablePort() {
204
+ return new Promise((resolve, reject) => {
205
+ const server = createServer();
206
+ server.once('error', reject);
207
+ server.listen(0, '127.0.0.1', () => {
208
+ const address = server.address();
209
+ const port = typeof address === 'object' && address !== null ? address.port : undefined;
210
+ server.close((error) => {
211
+ if (error !== undefined)
212
+ reject(error);
213
+ else if (port === undefined)
214
+ reject(new Error('OPENCODE_PORT_UNAVAILABLE'));
215
+ else
216
+ resolve(port);
217
+ });
218
+ });
219
+ });
220
+ }
221
+ async function waitUntilHealthy(url, authorization, child, timeoutMs) {
222
+ const deadline = Date.now() + timeoutMs;
223
+ while (Date.now() < deadline) {
224
+ if (child.exitCode !== null)
225
+ throw new Error('OPENCODE_SERVER_EXITED');
226
+ try {
227
+ const response = await fetch(url, {
228
+ headers: { authorization },
229
+ signal: AbortSignal.timeout(Math.min(1_000, Math.max(1, deadline - Date.now())))
230
+ });
231
+ if (response.ok)
232
+ return;
233
+ }
234
+ catch {
235
+ // The child may still be binding its loopback socket.
236
+ }
237
+ await new Promise((resolve) => setTimeout(resolve, 50));
238
+ }
239
+ throw new Error('OPENCODE_SERVER_START_TIMEOUT');
240
+ }
241
+ function unwrap(result) {
242
+ if (typeof result !== 'object' || result === null || !('data' in result))
243
+ return result;
244
+ const wrapped = result;
245
+ if (wrapped.error !== undefined)
246
+ throw new Error('OPENCODE_REQUEST_FAILED');
247
+ if (wrapped.data === undefined)
248
+ throw new Error('OPENCODE_RESPONSE_INVALID');
249
+ return wrapped.data;
250
+ }
@@ -101,12 +101,19 @@ export function platformCliInvocation(command, args, platform = process.platform
101
101
  const cliCommand = platformCliCommand(command, platform);
102
102
  if (platform !== 'win32' || !/\.(?:cmd|bat)$/i.test(cliCommand))
103
103
  return { command: cliCommand, args };
104
- if (![cliCommand, ...args].every((value) => /^[A-Za-z0-9_./:\\-]+$/.test(value)))
104
+ if (![cliCommand, ...args].every((value) => /^[A-Za-z0-9_@./:\\ -]+$/.test(value)))
105
105
  throw new Error('NODE_CLI_COMMAND_INVALID');
106
- return { command: comspec, args: ['/d', '/s', '/c', [cliCommand, ...args].join(' ')] };
106
+ return {
107
+ command: comspec,
108
+ args: [
109
+ '/d',
110
+ '/s',
111
+ '/c',
112
+ [cliCommand, ...args].map((value) => (value.includes(' ') ? `"${value}"` : value)).join(' ')
113
+ ]
114
+ };
107
115
  }
108
116
  export function nodeLog(event, fields = {}) {
109
117
  const safeFields = redactLogValue(fields);
110
118
  process.stdout.write(`${JSON.stringify({ timestamp: new Date().toISOString(), level: 'info', component: 'mar-node', event, ...safeFields })}\n`);
111
119
  }
112
- //# sourceMappingURL=operational.js.map
@@ -16,4 +16,3 @@ export async function terminateProcessTree(child, platform = process.platform, r
16
16
  child.kill('SIGTERM');
17
17
  }
18
18
  }
19
- //# sourceMappingURL=process-tree.js.map
@@ -0,0 +1,153 @@
1
+ import type { NodeCapabilities, RunnerDefaultConfiguration, RunnerCommandDescriptor, NodeEnvelope, RunnerName, RunnerProfile, RunnerCommandState, SessionActivityState } from '@myagentroam/protocol';
2
+ import type { NodeAgentSession, NodeConversationTurn, NodeDatabase, NodeWorkspace } from '../database.js';
3
+ import type { ChannelDelivery } from '../claude-channel.js';
4
+ import type { CodexSessionControl } from '../codex-app-server.js';
5
+ export type RegisteredRunnerName = RunnerName | 'fake';
6
+ /**
7
+ * Stable lifecycle boundary for every Node Runner implementation.
8
+ *
9
+ * Vendor clients and payload decoders stay behind subclasses. Domain services
10
+ * depend on this boundary (or a capability interface implemented by it), never
11
+ * on an SDK/App Server client directly.
12
+ */
13
+ export declare abstract class AbstractRunner<TName extends RegisteredRunnerName = RegisteredRunnerName> {
14
+ private control;
15
+ private commandControl;
16
+ abstract readonly name: TName;
17
+ /** Test-only runners are registered explicitly but never advertised. */
18
+ readonly advertised: boolean;
19
+ readonly interceptsRuns: boolean;
20
+ abstract readonly discoveryScope: 'ALL_WORKSPACES' | 'WORKSPACE_REQUIRED';
21
+ abstract profile(capabilities: NodeCapabilities): RunnerProfile | undefined;
22
+ refreshProfile(capabilities: NodeCapabilities, workspace?: NodeWorkspace, environment?: Readonly<Record<string, string>>): Promise<RunnerProfile | undefined>;
23
+ abstract available(capabilities: NodeCapabilities): boolean;
24
+ abstract defaultConfiguration(workspace?: NodeWorkspace): RunnerDefaultConfiguration;
25
+ supportsConfiguration(configuration: RunnerDefaultConfiguration, workspace?: NodeWorkspace): boolean;
26
+ protected abstract profileForValidation(): RunnerProfile;
27
+ abstract resumeExternal(session: NodeAgentSession, context: ExternalResumeContext, force?: boolean): Promise<ExternalResumeResult>;
28
+ abstract discoverSessions(context: RunnerDiscoveryContext, workspace?: NodeWorkspace): Promise<readonly NodeAgentSession[]>;
29
+ abstract readContextUsage(session: NodeAgentSession): Promise<RunnerContextUsage | undefined>;
30
+ presentSession(session: NodeAgentSession, capabilities: NodeCapabilities, context: RunnerSessionPresentationContext): RunnerSessionPresentation;
31
+ prepareMessageInput(input: string, collaborationMode: 'default' | 'plan'): string;
32
+ channelToken(sessionId: string): string | undefined;
33
+ deliverChannel(sessionId: string, messageId: string, senderId: string, token: string, content: string, notify: (runId: string, messageId: string, delivery: ChannelDelivery) => void): ChannelDelivery;
34
+ executeGlobalCommand(commandId: string, input: string): unknown;
35
+ serviceTier(): 'fast' | undefined;
36
+ usesRunnerTitleForRename(): boolean;
37
+ renameNative(session: NodeAgentSession, input: {
38
+ readonly title?: string | null;
39
+ readonly pinned?: boolean;
40
+ }, database: NodeDatabase): Promise<void>;
41
+ removeNative(session: NodeAgentSession, nativeSessionId: string): Promise<boolean>;
42
+ forkNative(session: NodeAgentSession, boundary: string | null, title: string): Promise<string | null>;
43
+ projectedRunnerTitle(metadata: ReturnType<NodeDatabase['getNativeSessionMetadata']>, input: RunnerProjectionTitleInput, database: NodeDatabase | undefined): string | null;
44
+ managedAliasId(nativeSessionId: string): string | undefined;
45
+ nativeAliasesFor(managedSessionId: string): readonly string[];
46
+ forgetNativeAlias(nativeSessionId: string): void;
47
+ rememberManagedNative(session: NodeAgentSession, nativeSessionId: string): void;
48
+ readManagedRunnerTitle(session: NodeAgentSession, nativeSessionId: string): Promise<string | undefined>;
49
+ readOfficialConversation(session: NodeAgentSession, input: {
50
+ readonly cursor?: string;
51
+ readonly limit?: number;
52
+ }, managedTurn: (nativeTurnId: string) => NodeConversationTurn | undefined): Promise<RunnerOfficialConversationPage | undefined>;
53
+ readExternalActivity(session: NodeAgentSession): Promise<SessionActivityState | undefined>;
54
+ acceptsTruncatedNativeHistory(): boolean;
55
+ nativeImageRoot(session: NodeAgentSession): string | undefined;
56
+ managedRunForNativeTurn(nativeTurnId: string): string | undefined;
57
+ abstract stop(): void | Promise<void>;
58
+ bindControl(control: RunnerControl): void;
59
+ acceptsRun(runner: unknown): boolean;
60
+ startManagedRun(envelope: NodeEnvelope): void;
61
+ cancelManagedRun(runId: string, intent: RunnerCancellationIntent): boolean;
62
+ decideApproval(input: RunnerApprovalDecision): boolean;
63
+ respondUserInput(runId: string, requestId: string, answers: Record<string, unknown>): boolean;
64
+ bindCommandControl(control: RunnerCommandControl): void;
65
+ executeCommand(session: NodeAgentSession, command: RunnerCommandDescriptor, input: string, context: {
66
+ readonly secretEnvironment: Readonly<Record<string, string>>;
67
+ }): Promise<unknown>;
68
+ }
69
+ export type RunnerCancellationIntent = 'CANCEL' | 'INTERRUPT';
70
+ export interface RunnerControl {
71
+ start(envelope: NodeEnvelope): void;
72
+ cancel(runId: string, intent: RunnerCancellationIntent): boolean;
73
+ decideApproval?(input: RunnerApprovalDecision): boolean;
74
+ respondUserInput?(runId: string, requestId: string, answers: Record<string, unknown>): boolean;
75
+ }
76
+ export interface RunnerCommandControl {
77
+ execute(session: NodeAgentSession, command: RunnerCommandDescriptor, input: string, context: {
78
+ readonly secretEnvironment: Readonly<Record<string, string>>;
79
+ }): Promise<unknown>;
80
+ }
81
+ export interface RunnerApprovalDecision {
82
+ readonly runId: string;
83
+ readonly approvalId: string;
84
+ readonly decision: 'APPROVED' | 'REJECTED';
85
+ }
86
+ export interface RunnerDiscoveryContext {
87
+ readonly workspaces: readonly NodeWorkspace[];
88
+ createProjection(input: {
89
+ readonly workspace: NodeWorkspace;
90
+ readonly runner: RunnerName;
91
+ readonly externalSessionId: string;
92
+ readonly cwd: string;
93
+ readonly title?: string;
94
+ readonly titleOrigin?: 'OFFICIAL' | 'NATIVE';
95
+ readonly lastActivityAt?: number;
96
+ }): NodeAgentSession;
97
+ }
98
+ export interface ExternalResumeContext {
99
+ readonly available: boolean;
100
+ projectionFor(session: NodeAgentSession): NodeAgentSession | undefined;
101
+ managedAliasFor(session: NodeAgentSession): NodeAgentSession | undefined;
102
+ currentResumed(sessionId: string): NodeAgentSession | undefined;
103
+ promote(sessionId: string): NodeAgentSession | undefined;
104
+ ensureConfiguration(session: NodeAgentSession): NodeAgentSession;
105
+ create(session: NodeAgentSession): NodeAgentSession;
106
+ workspacePath(workspaceId: string): string | undefined;
107
+ rememberManagedNative(nativeSessionId: string, sessionId: string): void;
108
+ }
109
+ export interface ExternalResumeResult {
110
+ readonly session?: NodeAgentSession;
111
+ readonly activity?: 'IDLE' | 'EXTERNAL_ACTIVE' | 'UNAVAILABLE';
112
+ readonly failureCode?: string;
113
+ readonly failureDetail?: string;
114
+ readonly messageFailureCode?: string;
115
+ }
116
+ export interface RunnerContextUsage {
117
+ readonly model?: string | null;
118
+ readonly usedTokens: number;
119
+ readonly maxTokens: number;
120
+ }
121
+ export interface RunnerProjectionTitleInput {
122
+ readonly externalSessionId: string;
123
+ readonly workspaceId: string;
124
+ readonly title?: string;
125
+ readonly titleOrigin?: 'OFFICIAL' | 'NATIVE';
126
+ }
127
+ export interface RunnerOfficialConversationPage {
128
+ readonly turns: readonly NodeConversationTurn[];
129
+ readonly nextCursor: string | null;
130
+ }
131
+ export interface RunnerSessionPresentationContext {
132
+ readonly managedActive: boolean;
133
+ readonly externalActivity: SessionActivityState | undefined;
134
+ readonly commandStates: readonly RunnerCommandState[];
135
+ }
136
+ export interface RunnerSessionPresentation {
137
+ readonly activity: SessionActivityState;
138
+ readonly control: {
139
+ readonly level: CodexSessionControl['level'];
140
+ readonly capabilities: CodexSessionControl['capabilities'];
141
+ };
142
+ readonly runtime: {
143
+ readonly level: CodexSessionControl['level'];
144
+ readonly capabilities: readonly string[];
145
+ readonly reasonCode: string | null;
146
+ };
147
+ readonly commandStates: readonly RunnerCommandState[];
148
+ }
149
+ export declare class RunnerCapabilityError extends Error {
150
+ readonly runner: RegisteredRunnerName;
151
+ readonly capability: string;
152
+ constructor(runner: RegisteredRunnerName, capability: string);
153
+ }