opencode-supertask 0.1.31 → 0.1.32-rc.1

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.
@@ -4,12 +4,18 @@ import { spawn } from "child_process";
4
4
  // src/core/launch-protocol.ts
5
5
  var LAUNCH_IDENTITY_ARGUMENT = "--supertask-launch-identity";
6
6
  var DRAIN_PROOF_MESSAGE_TYPE = "supertask-drained";
7
+ var DRAIN_PROOF_ACK_MESSAGE_TYPE = "supertask-drained-ack";
7
8
  function isLaunchIdentity(value) {
8
9
  return value != null && /^gateway-[1-9]\d*:launch:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
9
10
  }
10
11
  function drainProofForIdentity(launchIdentity) {
11
12
  return { type: DRAIN_PROOF_MESSAGE_TYPE, identity: launchIdentity };
12
13
  }
14
+ function isMatchingDrainProofAck(message, launchIdentity) {
15
+ if (typeof message !== "object" || message == null) return false;
16
+ const candidate = message;
17
+ return candidate.type === DRAIN_PROOF_ACK_MESSAGE_TYPE && candidate.identity === launchIdentity;
18
+ }
13
19
 
14
20
  // src/worker/launcher.ts
15
21
  var RELEASE_MESSAGE = "START";
@@ -17,6 +23,7 @@ var INITIAL_GROUP_PROBE_DELAY_MS = 1e3;
17
23
  var MAX_GROUP_PROBE_DELAY_MS = 5e3;
18
24
  var GROUP_PROBE_TIMEOUT_MS = 2e3;
19
25
  var MAX_PS_OUTPUT_CHARS = 4 * 1024 * 1024;
26
+ var DRAIN_PROOF_ACK_TIMEOUT_MS = 1e4;
20
27
  function waitForRelease() {
21
28
  return new Promise((resolve) => {
22
29
  let settled = false;
@@ -92,6 +99,37 @@ async function waitForProcessGroupDrain(options = {}) {
92
99
  probeDelayMs = Math.min(maxDelayMs, probeDelayMs * 2);
93
100
  }
94
101
  }
102
+ async function sendDrainProof(launchIdentity) {
103
+ if (!process.send) throw new Error("supertask launcher: drain proof IPC unavailable");
104
+ await new Promise((resolve, reject) => {
105
+ let settled = false;
106
+ const finish = (error) => {
107
+ if (settled) return;
108
+ settled = true;
109
+ clearTimeout(timeout);
110
+ process.off("message", onMessage);
111
+ process.off("disconnect", onDisconnect);
112
+ if (error) reject(error);
113
+ else resolve();
114
+ };
115
+ const onMessage = (message) => {
116
+ if (isMatchingDrainProofAck(message, launchIdentity)) finish();
117
+ };
118
+ const onDisconnect = () => finish(
119
+ new Error("supertask launcher: drain proof IPC disconnected before acknowledgment")
120
+ );
121
+ const timeout = setTimeout(() => finish(
122
+ new Error("supertask launcher: drain proof acknowledgment timed out")
123
+ ), DRAIN_PROOF_ACK_TIMEOUT_MS);
124
+ process.on("message", onMessage);
125
+ process.once("disconnect", onDisconnect);
126
+ try {
127
+ process.send(drainProofForIdentity(launchIdentity));
128
+ } catch (error) {
129
+ finish(error instanceof Error ? error : new Error(String(error)));
130
+ }
131
+ });
132
+ }
95
133
  async function main() {
96
134
  const launcherArgs = Bun.argv.slice(2);
97
135
  let launchIdentity = null;
@@ -134,13 +172,7 @@ async function main() {
134
172
  });
135
173
  await waitForProcessGroupDrain();
136
174
  if (launchIdentity) {
137
- if (!process.send) throw new Error("supertask launcher: drain proof IPC unavailable");
138
- await new Promise((resolve, reject) => {
139
- process.send(drainProofForIdentity(launchIdentity), (error) => {
140
- if (error) reject(error);
141
- else resolve();
142
- });
143
- });
175
+ await sendDrainProof(launchIdentity);
144
176
  }
145
177
  } finally {
146
178
  process.off("SIGTERM", preserveGroupLeader);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/worker/launcher.ts","../../src/core/launch-protocol.ts"],"sourcesContent":["import { spawn } from 'child_process';\nimport {\n drainProofForIdentity,\n isLaunchIdentity,\n LAUNCH_IDENTITY_ARGUMENT,\n} from '@core/launch-protocol';\n\nconst RELEASE_MESSAGE = 'START';\nconst INITIAL_GROUP_PROBE_DELAY_MS = 1_000;\nconst MAX_GROUP_PROBE_DELAY_MS = 5_000;\nconst GROUP_PROBE_TIMEOUT_MS = 2_000;\nconst MAX_PS_OUTPUT_CHARS = 4 * 1024 * 1024;\n\nfunction waitForRelease(): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n let input = '';\n const finish = (released: boolean) => {\n if (settled) return;\n settled = true;\n process.stdin.removeAllListeners();\n resolve(released);\n };\n\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => {\n input += chunk;\n const newline = input.indexOf('\\n');\n if (newline >= 0) finish(input.slice(0, newline).trim() === RELEASE_MESSAGE);\n });\n process.stdin.once('end', () => finish(false));\n process.stdin.once('error', () => finish(false));\n process.stdin.resume();\n });\n}\n\nasync function hasOtherProcessGroupMembers(): Promise<boolean | null> {\n return new Promise((resolve) => {\n const inspection = spawn('ps', ['-axo', 'pid=,pgid='], {\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n let output = '';\n let settled = false;\n const finish = (result: boolean | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve(result);\n };\n const timeout = setTimeout(() => {\n inspection.kill('SIGKILL');\n finish(null);\n }, GROUP_PROBE_TIMEOUT_MS);\n\n inspection.stdout.setEncoding('utf8');\n inspection.stdout.on('data', (chunk: string) => {\n output += chunk;\n if (output.length <= MAX_PS_OUTPUT_CHARS) return;\n inspection.kill('SIGKILL');\n finish(null);\n });\n inspection.once('error', () => finish(null));\n inspection.once('close', (code) => {\n if (code !== 0) {\n finish(null);\n return;\n }\n const hasMembers = output.split('\\n').some((line) => {\n const match = line.trim().match(/^(\\d+)\\s+(\\d+)$/);\n if (!match) return false;\n const pid = Number(match[1]);\n const processGroupId = Number(match[2]);\n return pid !== process.pid\n && pid !== inspection.pid\n && processGroupId === process.pid;\n });\n finish(hasMembers);\n });\n });\n}\n\nexport interface ProcessGroupDrainOptions {\n probe?: () => Promise<boolean | null>;\n delay?: (milliseconds: number) => Promise<void>;\n initialDelayMs?: number;\n maxDelayMs?: number;\n}\n\nexport async function waitForProcessGroupDrain(\n options: ProcessGroupDrainOptions = {},\n): Promise<void> {\n if (process.platform === 'win32') return;\n\n const probe = options.probe ?? hasOtherProcessGroupMembers;\n const delay = options.delay ?? Bun.sleep;\n const maxDelayMs = options.maxDelayMs ?? MAX_GROUP_PROBE_DELAY_MS;\n let probeDelayMs = options.initialDelayMs ?? INITIAL_GROUP_PROBE_DELAY_MS;\n while (true) {\n const hasMembers = await probe();\n if (hasMembers === false) return;\n await delay(probeDelayMs);\n probeDelayMs = Math.min(maxDelayMs, probeDelayMs * 2);\n }\n}\n\nasync function main(): Promise<void> {\n const launcherArgs = Bun.argv.slice(2);\n let launchIdentity: string | null = null;\n if (launcherArgs[0] === LAUNCH_IDENTITY_ARGUMENT) {\n if (!isLaunchIdentity(launcherArgs[1])) {\n console.error('supertask launcher: missing launch identity');\n process.exitCode = 127;\n return;\n }\n launchIdentity = launcherArgs[1];\n launcherArgs.splice(0, 2);\n }\n const [executable, ...args] = launcherArgs;\n if (!executable) {\n console.error('supertask launcher: missing OpenCode executable');\n process.exitCode = 127;\n return;\n }\n\n if (!await waitForRelease()) {\n process.exitCode = 125;\n return;\n }\n\n const preserveGroupLeader = () => {};\n process.on('SIGTERM', preserveGroupLeader);\n process.on('SIGINT', preserveGroupLeader);\n let result: { code: number | null; signal: NodeJS.Signals | null };\n try {\n const child = spawn(executable, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n child.stdout?.pipe(process.stdout, { end: false });\n child.stderr?.pipe(process.stderr, { end: false });\n result = await new Promise((resolve) => {\n child.once('error', (error) => {\n console.error(`supertask launcher: ${error.message}`);\n resolve({ code: 127, signal: null });\n });\n // 使用独立 pipe 转发输出;exit 只等 OpenCode 主进程结束,\n // 后续的进程组排空才是 guardian 退出的最终条件。\n child.once('exit', (code, signal) => resolve({ code, signal }));\n });\n\n // 保持进程组长存活,直到 OpenCode 派生的整棵进程树都退出。\n // Watchdog 因而始终可以通过 launcher 命令验证进程组归属,避免 PID/PGID 复用误杀。\n await waitForProcessGroupDrain();\n if (launchIdentity) {\n // IPC 不传递给 OpenCode;只有持有该次随机身份的 guardian\n // 可在整个进程组排空后向 Worker 发出绑定证明。\n if (!process.send) throw new Error('supertask launcher: drain proof IPC unavailable');\n await new Promise<void>((resolve, reject) => {\n process.send!(drainProofForIdentity(launchIdentity), (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n }\n } finally {\n process.off('SIGTERM', preserveGroupLeader);\n process.off('SIGINT', preserveGroupLeader);\n }\n\n if (result.signal) {\n try {\n process.kill(process.pid, result.signal);\n return;\n } catch {\n process.exitCode = 128;\n return;\n }\n }\n process.exitCode = result.code ?? 1;\n}\n\nif (import.meta.main) await main();\n","export const LEGACY_GUARDIAN_LAUNCH_PROTOCOL = 'gated-v2-guardian';\nexport const TOKEN_GUARDIAN_LAUNCH_PROTOCOL = 'gated-v3-token-guardian';\nexport const LAUNCH_IDENTITY_ARGUMENT = '--supertask-launch-identity';\nexport const DRAIN_PROOF_MESSAGE_TYPE = 'supertask-drained';\nexport const MANAGED_RUN_ENV = 'SUPERTASK_MANAGED_RUN';\nexport const MANAGED_RUN_ENV_VALUE = '1';\n\nexport function isLaunchIdentity(value: string | null | undefined): value is string {\n return value != null\n && /^gateway-[1-9]\\d*:launch:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);\n}\n\nexport function drainProofForIdentity(launchIdentity: string) {\n return { type: DRAIN_PROOF_MESSAGE_TYPE, identity: launchIdentity } as const;\n}\n\nexport function isMatchingDrainProof(message: unknown, launchIdentity: string): boolean {\n if (typeof message !== 'object' || message == null) return false;\n const candidate = message as Record<string, unknown>;\n return candidate.type === DRAIN_PROOF_MESSAGE_TYPE\n && candidate.identity === launchIdentity;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/worker/launcher.ts","../../src/core/launch-protocol.ts"],"sourcesContent":["import { spawn } from 'child_process';\nimport {\n drainProofForIdentity,\n isLaunchIdentity,\n isMatchingDrainProofAck,\n LAUNCH_IDENTITY_ARGUMENT,\n} from '@core/launch-protocol';\n\nconst RELEASE_MESSAGE = 'START';\nconst INITIAL_GROUP_PROBE_DELAY_MS = 1_000;\nconst MAX_GROUP_PROBE_DELAY_MS = 5_000;\nconst GROUP_PROBE_TIMEOUT_MS = 2_000;\nconst MAX_PS_OUTPUT_CHARS = 4 * 1024 * 1024;\nconst DRAIN_PROOF_ACK_TIMEOUT_MS = 10_000;\n\nfunction waitForRelease(): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n let input = '';\n const finish = (released: boolean) => {\n if (settled) return;\n settled = true;\n process.stdin.removeAllListeners();\n resolve(released);\n };\n\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => {\n input += chunk;\n const newline = input.indexOf('\\n');\n if (newline >= 0) finish(input.slice(0, newline).trim() === RELEASE_MESSAGE);\n });\n process.stdin.once('end', () => finish(false));\n process.stdin.once('error', () => finish(false));\n process.stdin.resume();\n });\n}\n\nasync function hasOtherProcessGroupMembers(): Promise<boolean | null> {\n return new Promise((resolve) => {\n const inspection = spawn('ps', ['-axo', 'pid=,pgid='], {\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n let output = '';\n let settled = false;\n const finish = (result: boolean | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve(result);\n };\n const timeout = setTimeout(() => {\n inspection.kill('SIGKILL');\n finish(null);\n }, GROUP_PROBE_TIMEOUT_MS);\n\n inspection.stdout.setEncoding('utf8');\n inspection.stdout.on('data', (chunk: string) => {\n output += chunk;\n if (output.length <= MAX_PS_OUTPUT_CHARS) return;\n inspection.kill('SIGKILL');\n finish(null);\n });\n inspection.once('error', () => finish(null));\n inspection.once('close', (code) => {\n if (code !== 0) {\n finish(null);\n return;\n }\n const hasMembers = output.split('\\n').some((line) => {\n const match = line.trim().match(/^(\\d+)\\s+(\\d+)$/);\n if (!match) return false;\n const pid = Number(match[1]);\n const processGroupId = Number(match[2]);\n return pid !== process.pid\n && pid !== inspection.pid\n && processGroupId === process.pid;\n });\n finish(hasMembers);\n });\n });\n}\n\nexport interface ProcessGroupDrainOptions {\n probe?: () => Promise<boolean | null>;\n delay?: (milliseconds: number) => Promise<void>;\n initialDelayMs?: number;\n maxDelayMs?: number;\n}\n\nexport async function waitForProcessGroupDrain(\n options: ProcessGroupDrainOptions = {},\n): Promise<void> {\n if (process.platform === 'win32') return;\n\n const probe = options.probe ?? hasOtherProcessGroupMembers;\n const delay = options.delay ?? Bun.sleep;\n const maxDelayMs = options.maxDelayMs ?? MAX_GROUP_PROBE_DELAY_MS;\n let probeDelayMs = options.initialDelayMs ?? INITIAL_GROUP_PROBE_DELAY_MS;\n while (true) {\n const hasMembers = await probe();\n if (hasMembers === false) return;\n await delay(probeDelayMs);\n probeDelayMs = Math.min(maxDelayMs, probeDelayMs * 2);\n }\n}\n\nasync function sendDrainProof(launchIdentity: string): Promise<void> {\n if (!process.send) throw new Error('supertask launcher: drain proof IPC unavailable');\n\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n process.off('message', onMessage);\n process.off('disconnect', onDisconnect);\n if (error) reject(error);\n else resolve();\n };\n const onMessage = (message: unknown) => {\n if (isMatchingDrainProofAck(message, launchIdentity)) finish();\n };\n const onDisconnect = () => finish(\n new Error('supertask launcher: drain proof IPC disconnected before acknowledgment'),\n );\n const timeout = setTimeout(() => finish(\n new Error('supertask launcher: drain proof acknowledgment timed out'),\n ), DRAIN_PROOF_ACK_TIMEOUT_MS);\n\n process.on('message', onMessage);\n process.once('disconnect', onDisconnect);\n try {\n // Bun 1.1 can deliver IPC messages but does not reliably invoke\n // the Node-compatible process.send callback. A bound acknowledgment\n // proves delivery without depending on that callback.\n process.send!(drainProofForIdentity(launchIdentity));\n } catch (error) {\n finish(error instanceof Error ? error : new Error(String(error)));\n }\n });\n}\n\nasync function main(): Promise<void> {\n const launcherArgs = Bun.argv.slice(2);\n let launchIdentity: string | null = null;\n if (launcherArgs[0] === LAUNCH_IDENTITY_ARGUMENT) {\n if (!isLaunchIdentity(launcherArgs[1])) {\n console.error('supertask launcher: missing launch identity');\n process.exitCode = 127;\n return;\n }\n launchIdentity = launcherArgs[1];\n launcherArgs.splice(0, 2);\n }\n const [executable, ...args] = launcherArgs;\n if (!executable) {\n console.error('supertask launcher: missing OpenCode executable');\n process.exitCode = 127;\n return;\n }\n\n if (!await waitForRelease()) {\n process.exitCode = 125;\n return;\n }\n\n const preserveGroupLeader = () => {};\n process.on('SIGTERM', preserveGroupLeader);\n process.on('SIGINT', preserveGroupLeader);\n let result: { code: number | null; signal: NodeJS.Signals | null };\n try {\n const child = spawn(executable, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n child.stdout?.pipe(process.stdout, { end: false });\n child.stderr?.pipe(process.stderr, { end: false });\n result = await new Promise((resolve) => {\n child.once('error', (error) => {\n console.error(`supertask launcher: ${error.message}`);\n resolve({ code: 127, signal: null });\n });\n // 使用独立 pipe 转发输出;exit 只等 OpenCode 主进程结束,\n // 后续的进程组排空才是 guardian 退出的最终条件。\n child.once('exit', (code, signal) => resolve({ code, signal }));\n });\n\n // 保持进程组长存活,直到 OpenCode 派生的整棵进程树都退出。\n // Watchdog 因而始终可以通过 launcher 命令验证进程组归属,避免 PID/PGID 复用误杀。\n await waitForProcessGroupDrain();\n if (launchIdentity) {\n // IPC 不传递给 OpenCode;只有持有该次随机身份的 guardian\n // 可在整个进程组排空后向 Worker 发出绑定证明。\n await sendDrainProof(launchIdentity);\n }\n } finally {\n process.off('SIGTERM', preserveGroupLeader);\n process.off('SIGINT', preserveGroupLeader);\n }\n\n if (result.signal) {\n try {\n process.kill(process.pid, result.signal);\n return;\n } catch {\n process.exitCode = 128;\n return;\n }\n }\n process.exitCode = result.code ?? 1;\n}\n\nif (import.meta.main) await main();\n","export const LEGACY_GUARDIAN_LAUNCH_PROTOCOL = 'gated-v2-guardian';\nexport const TOKEN_GUARDIAN_LAUNCH_PROTOCOL = 'gated-v3-token-guardian';\nexport const LAUNCH_IDENTITY_ARGUMENT = '--supertask-launch-identity';\nexport const DRAIN_PROOF_MESSAGE_TYPE = 'supertask-drained';\nexport const DRAIN_PROOF_ACK_MESSAGE_TYPE = 'supertask-drained-ack';\nexport const MANAGED_RUN_ENV = 'SUPERTASK_MANAGED_RUN';\nexport const MANAGED_RUN_ENV_VALUE = '1';\n\nexport function isLaunchIdentity(value: string | null | undefined): value is string {\n return value != null\n && /^gateway-[1-9]\\d*:launch:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);\n}\n\nexport function drainProofForIdentity(launchIdentity: string) {\n return { type: DRAIN_PROOF_MESSAGE_TYPE, identity: launchIdentity } as const;\n}\n\nexport function drainProofAckForIdentity(launchIdentity: string) {\n return { type: DRAIN_PROOF_ACK_MESSAGE_TYPE, identity: launchIdentity } as const;\n}\n\nexport function isMatchingDrainProof(message: unknown, launchIdentity: string): boolean {\n if (typeof message !== 'object' || message == null) return false;\n const candidate = message as Record<string, unknown>;\n return candidate.type === DRAIN_PROOF_MESSAGE_TYPE\n && candidate.identity === launchIdentity;\n}\n\nexport function isMatchingDrainProofAck(message: unknown, launchIdentity: string): boolean {\n if (typeof message !== 'object' || message == null) return false;\n const candidate = message as Record<string, unknown>;\n return candidate.type === DRAIN_PROOF_ACK_MESSAGE_TYPE\n && candidate.identity === launchIdentity;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-supertask",
3
- "version": "0.1.31",
3
+ "version": "0.1.32-rc.1",
4
4
  "description": "AI Agent 任务调度系统 — OpenCode 插件 + CLI + Gateway 常驻进程",
5
5
  "type": "module",
6
6
  "main": "dist/plugin/supertask.js",
@@ -77,6 +77,6 @@
77
77
  }
78
78
  },
79
79
  "engines": {
80
- "bun": ">=1.0.0"
80
+ "bun": ">=1.1.45"
81
81
  }
82
82
  }