acpx 0.1.10 → 0.1.11
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.
- package/dist/cli.js +1712 -1300
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
4
|
+
import { Command, CommanderError, InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
5
5
|
import { realpathSync } from "fs";
|
|
6
6
|
import fs6 from "fs/promises";
|
|
7
7
|
import path7 from "path";
|
|
@@ -13,7 +13,7 @@ var AGENT_REGISTRY = {
|
|
|
13
13
|
codex: "npx @zed-industries/codex-acp",
|
|
14
14
|
claude: "npx -y @zed-industries/claude-agent-acp",
|
|
15
15
|
gemini: "gemini",
|
|
16
|
-
opencode: "npx opencode-ai",
|
|
16
|
+
opencode: "npx -y opencode-ai acp",
|
|
17
17
|
pi: "npx pi-acp"
|
|
18
18
|
};
|
|
19
19
|
var DEFAULT_AGENT_NAME = "codex";
|
|
@@ -43,6 +43,223 @@ function listBuiltInAgents(overrides) {
|
|
|
43
43
|
return Object.keys(mergeAgentRegistry(overrides));
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// src/cli-internal-owner.ts
|
|
47
|
+
import { InvalidArgumentError } from "commander";
|
|
48
|
+
|
|
49
|
+
// src/types.ts
|
|
50
|
+
var EXIT_CODES = {
|
|
51
|
+
SUCCESS: 0,
|
|
52
|
+
ERROR: 1,
|
|
53
|
+
USAGE: 2,
|
|
54
|
+
TIMEOUT: 3,
|
|
55
|
+
NO_SESSION: 4,
|
|
56
|
+
PERMISSION_DENIED: 5,
|
|
57
|
+
INTERRUPTED: 130
|
|
58
|
+
};
|
|
59
|
+
var OUTPUT_FORMATS = ["text", "json", "quiet"];
|
|
60
|
+
var PERMISSION_MODES = ["approve-all", "approve-reads", "deny-all"];
|
|
61
|
+
var AUTH_POLICIES = ["skip", "fail"];
|
|
62
|
+
var NON_INTERACTIVE_PERMISSION_POLICIES = ["deny", "fail"];
|
|
63
|
+
var OUTPUT_ERROR_CODES = [
|
|
64
|
+
"NO_SESSION",
|
|
65
|
+
"TIMEOUT",
|
|
66
|
+
"PERMISSION_DENIED",
|
|
67
|
+
"PERMISSION_PROMPT_UNAVAILABLE",
|
|
68
|
+
"RUNTIME",
|
|
69
|
+
"USAGE"
|
|
70
|
+
];
|
|
71
|
+
var OUTPUT_ERROR_ORIGINS = ["cli", "runtime", "queue", "acp"];
|
|
72
|
+
|
|
73
|
+
// src/cli-internal-owner.ts
|
|
74
|
+
function parseNonEmptyValue(label, value) {
|
|
75
|
+
const trimmed = value.trim();
|
|
76
|
+
if (trimmed.length === 0) {
|
|
77
|
+
throw new InvalidArgumentError(`${label} must not be empty`);
|
|
78
|
+
}
|
|
79
|
+
return trimmed;
|
|
80
|
+
}
|
|
81
|
+
function parseNonNegativeMilliseconds(value) {
|
|
82
|
+
const parsed = Number(value);
|
|
83
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
84
|
+
throw new InvalidArgumentError("TTL must be a non-negative number of milliseconds");
|
|
85
|
+
}
|
|
86
|
+
return Math.round(parsed);
|
|
87
|
+
}
|
|
88
|
+
function parseTimeoutMilliseconds(value) {
|
|
89
|
+
const parsed = Number(value);
|
|
90
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
91
|
+
throw new InvalidArgumentError("Timeout must be a positive number of milliseconds");
|
|
92
|
+
}
|
|
93
|
+
return Math.round(parsed);
|
|
94
|
+
}
|
|
95
|
+
function parsePermissionMode(value) {
|
|
96
|
+
if (!PERMISSION_MODES.includes(value)) {
|
|
97
|
+
throw new InvalidArgumentError(
|
|
98
|
+
`Invalid permission mode "${value}". Expected one of: ${PERMISSION_MODES.join(", ")}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
function parseAuthPolicy(value) {
|
|
104
|
+
if (!AUTH_POLICIES.includes(value)) {
|
|
105
|
+
throw new InvalidArgumentError(
|
|
106
|
+
`Invalid auth policy "${value}". Expected one of: ${AUTH_POLICIES.join(", ")}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
function parseNonInteractivePermissionPolicy(value) {
|
|
112
|
+
if (!NON_INTERACTIVE_PERMISSION_POLICIES.includes(
|
|
113
|
+
value
|
|
114
|
+
)) {
|
|
115
|
+
throw new InvalidArgumentError(
|
|
116
|
+
`Invalid non-interactive permission policy "${value}". Expected one of: ${NON_INTERACTIVE_PERMISSION_POLICIES.join(", ")}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
function parseQueueOwnerFlags(argv, defaultTtlMs) {
|
|
122
|
+
if (argv[0] !== "__queue-owner") {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
const flags = {
|
|
126
|
+
ttlMs: defaultTtlMs
|
|
127
|
+
};
|
|
128
|
+
const consumeValue = (index, token) => {
|
|
129
|
+
if (token.includes("=")) {
|
|
130
|
+
return {
|
|
131
|
+
value: token.slice(token.indexOf("=") + 1),
|
|
132
|
+
next: index
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const value = argv[index + 1];
|
|
136
|
+
if (!value || value.startsWith("-")) {
|
|
137
|
+
throw new InvalidArgumentError(`${token} requires a value`);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
value,
|
|
141
|
+
next: index + 1
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
145
|
+
const token = argv[index];
|
|
146
|
+
if (token === "--session-id" || token.startsWith("--session-id=")) {
|
|
147
|
+
const consumed = consumeValue(index, token);
|
|
148
|
+
flags.sessionId = parseNonEmptyValue("Session id", consumed.value);
|
|
149
|
+
index = consumed.next;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (token === "--ttl-ms" || token.startsWith("--ttl-ms=")) {
|
|
153
|
+
const consumed = consumeValue(index, token);
|
|
154
|
+
flags.ttlMs = parseNonNegativeMilliseconds(consumed.value);
|
|
155
|
+
index = consumed.next;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (token === "--permission-mode" || token.startsWith("--permission-mode=")) {
|
|
159
|
+
const consumed = consumeValue(index, token);
|
|
160
|
+
flags.permissionMode = parsePermissionMode(consumed.value);
|
|
161
|
+
index = consumed.next;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (token === "--non-interactive-permissions" || token.startsWith("--non-interactive-permissions=")) {
|
|
165
|
+
const consumed = consumeValue(index, token);
|
|
166
|
+
flags.nonInteractivePermissions = parseNonInteractivePermissionPolicy(
|
|
167
|
+
consumed.value
|
|
168
|
+
);
|
|
169
|
+
index = consumed.next;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (token === "--auth-policy" || token.startsWith("--auth-policy=")) {
|
|
173
|
+
const consumed = consumeValue(index, token);
|
|
174
|
+
flags.authPolicy = parseAuthPolicy(consumed.value);
|
|
175
|
+
index = consumed.next;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (token === "--timeout-ms" || token.startsWith("--timeout-ms=")) {
|
|
179
|
+
const consumed = consumeValue(index, token);
|
|
180
|
+
flags.timeoutMs = parseTimeoutMilliseconds(consumed.value);
|
|
181
|
+
index = consumed.next;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (token === "--verbose") {
|
|
185
|
+
flags.verbose = true;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (token === "--suppress-sdk-console-errors") {
|
|
189
|
+
flags.suppressSdkConsoleErrors = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
throw new InvalidArgumentError(`Unknown __queue-owner option: ${token}`);
|
|
193
|
+
}
|
|
194
|
+
if (!flags.sessionId) {
|
|
195
|
+
throw new InvalidArgumentError("__queue-owner requires --session-id");
|
|
196
|
+
}
|
|
197
|
+
if (!flags.permissionMode) {
|
|
198
|
+
throw new InvalidArgumentError("__queue-owner requires --permission-mode");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
sessionId: flags.sessionId,
|
|
202
|
+
ttlMs: flags.ttlMs ?? defaultTtlMs,
|
|
203
|
+
permissionMode: flags.permissionMode,
|
|
204
|
+
nonInteractivePermissions: flags.nonInteractivePermissions,
|
|
205
|
+
authPolicy: flags.authPolicy,
|
|
206
|
+
timeoutMs: flags.timeoutMs,
|
|
207
|
+
verbose: flags.verbose,
|
|
208
|
+
suppressSdkConsoleErrors: flags.suppressSdkConsoleErrors
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/cli-public.ts
|
|
213
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
214
|
+
function configurePublicCli(options) {
|
|
215
|
+
const builtInAgents = options.listBuiltInAgents(options.config.agents);
|
|
216
|
+
for (const agentName of builtInAgents) {
|
|
217
|
+
options.registerAgentCommand(options.program, agentName, options.config);
|
|
218
|
+
}
|
|
219
|
+
options.registerDefaultCommands(options.program, options.config);
|
|
220
|
+
const scan = options.detectAgentToken(options.argv);
|
|
221
|
+
if (!scan.hasAgentOverride && scan.token && !options.topLevelVerbs.has(scan.token) && !builtInAgents.includes(scan.token)) {
|
|
222
|
+
options.registerAgentCommand(options.program, scan.token, options.config);
|
|
223
|
+
}
|
|
224
|
+
options.program.argument("[prompt...]", "Prompt text").action(async function(promptParts) {
|
|
225
|
+
if (promptParts.length === 0 && process.stdin.isTTY) {
|
|
226
|
+
if (options.requestedJsonStrict) {
|
|
227
|
+
throw new InvalidArgumentError2(
|
|
228
|
+
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
this.outputHelp();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
await options.handlePromptAction(this, promptParts);
|
|
235
|
+
});
|
|
236
|
+
options.program.addHelpText(
|
|
237
|
+
"after",
|
|
238
|
+
`
|
|
239
|
+
Examples:
|
|
240
|
+
acpx codex sessions new
|
|
241
|
+
acpx codex "fix the tests"
|
|
242
|
+
acpx codex prompt "fix the tests"
|
|
243
|
+
acpx codex --no-wait "queue follow-up task"
|
|
244
|
+
acpx codex exec "what does this repo do"
|
|
245
|
+
acpx codex cancel
|
|
246
|
+
acpx codex set-mode plan
|
|
247
|
+
acpx codex set approval_policy conservative
|
|
248
|
+
acpx codex -s backend "fix the API"
|
|
249
|
+
acpx codex sessions
|
|
250
|
+
acpx codex sessions new --name backend
|
|
251
|
+
acpx codex sessions ensure --name backend
|
|
252
|
+
acpx codex sessions close backend
|
|
253
|
+
acpx codex status
|
|
254
|
+
acpx config show
|
|
255
|
+
acpx config init
|
|
256
|
+
acpx --ttl 30 codex "investigate flaky tests"
|
|
257
|
+
acpx claude "refactor auth"
|
|
258
|
+
acpx gemini "add logging"
|
|
259
|
+
acpx --agent ./my-custom-server "do something"`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
46
263
|
// src/config.ts
|
|
47
264
|
import fs from "fs/promises";
|
|
48
265
|
import os from "os";
|
|
@@ -92,7 +309,7 @@ function parseTimeoutMs(value, sourcePath) {
|
|
|
92
309
|
}
|
|
93
310
|
return Math.round(value * 1e3);
|
|
94
311
|
}
|
|
95
|
-
function
|
|
312
|
+
function parsePermissionMode2(value, sourcePath) {
|
|
96
313
|
if (value == null) {
|
|
97
314
|
return void 0;
|
|
98
315
|
}
|
|
@@ -103,7 +320,7 @@ function parsePermissionMode(value, sourcePath) {
|
|
|
103
320
|
}
|
|
104
321
|
return value;
|
|
105
322
|
}
|
|
106
|
-
function
|
|
323
|
+
function parseNonInteractivePermissionPolicy2(value, sourcePath) {
|
|
107
324
|
if (value == null) {
|
|
108
325
|
return void 0;
|
|
109
326
|
}
|
|
@@ -116,7 +333,7 @@ function parseNonInteractivePermissionPolicy(value, sourcePath) {
|
|
|
116
333
|
}
|
|
117
334
|
return value;
|
|
118
335
|
}
|
|
119
|
-
function
|
|
336
|
+
function parseAuthPolicy2(value, sourcePath) {
|
|
120
337
|
if (value == null) {
|
|
121
338
|
return void 0;
|
|
122
339
|
}
|
|
@@ -239,15 +456,15 @@ async function loadResolvedConfig(cwd) {
|
|
|
239
456
|
const globalConfig = globalResult.config;
|
|
240
457
|
const projectConfig = projectResult.config;
|
|
241
458
|
const defaultAgent = parseDefaultAgent(projectConfig?.defaultAgent, projectPath) ?? parseDefaultAgent(globalConfig?.defaultAgent, globalPath) ?? DEFAULT_AGENT_NAME;
|
|
242
|
-
const defaultPermissions =
|
|
243
|
-
const nonInteractivePermissions =
|
|
459
|
+
const defaultPermissions = parsePermissionMode2(projectConfig?.defaultPermissions, projectPath) ?? parsePermissionMode2(globalConfig?.defaultPermissions, globalPath) ?? DEFAULT_PERMISSION_MODE;
|
|
460
|
+
const nonInteractivePermissions = parseNonInteractivePermissionPolicy2(
|
|
244
461
|
projectConfig?.nonInteractivePermissions,
|
|
245
462
|
projectPath
|
|
246
|
-
) ??
|
|
463
|
+
) ?? parseNonInteractivePermissionPolicy2(
|
|
247
464
|
globalConfig?.nonInteractivePermissions,
|
|
248
465
|
globalPath
|
|
249
466
|
) ?? DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY;
|
|
250
|
-
const authPolicy =
|
|
467
|
+
const authPolicy = parseAuthPolicy2(projectConfig?.authPolicy, projectPath) ?? parseAuthPolicy2(globalConfig?.authPolicy, globalPath) ?? DEFAULT_AUTH_POLICY;
|
|
251
468
|
const ttlMs = parseTtlMs(projectConfig?.ttl, projectPath) ?? parseTtlMs(globalConfig?.ttl, globalPath) ?? DEFAULT_TTL_MS;
|
|
252
469
|
const timeoutConfiguredInProject = projectConfig != null && Object.prototype.hasOwnProperty.call(projectConfig, "timeout");
|
|
253
470
|
const timeoutConfiguredInGlobal = globalConfig != null && Object.prototype.hasOwnProperty.call(globalConfig, "timeout");
|
|
@@ -499,29 +716,6 @@ function isAcpResourceNotFoundError(error) {
|
|
|
499
716
|
return isSessionNotFoundText(formatUnknownErrorMessage(error));
|
|
500
717
|
}
|
|
501
718
|
|
|
502
|
-
// src/types.ts
|
|
503
|
-
var EXIT_CODES = {
|
|
504
|
-
SUCCESS: 0,
|
|
505
|
-
ERROR: 1,
|
|
506
|
-
USAGE: 2,
|
|
507
|
-
TIMEOUT: 3,
|
|
508
|
-
NO_SESSION: 4,
|
|
509
|
-
PERMISSION_DENIED: 5,
|
|
510
|
-
INTERRUPTED: 130
|
|
511
|
-
};
|
|
512
|
-
var OUTPUT_FORMATS = ["text", "json", "quiet"];
|
|
513
|
-
var AUTH_POLICIES = ["skip", "fail"];
|
|
514
|
-
var NON_INTERACTIVE_PERMISSION_POLICIES = ["deny", "fail"];
|
|
515
|
-
var OUTPUT_ERROR_CODES = [
|
|
516
|
-
"NO_SESSION",
|
|
517
|
-
"TIMEOUT",
|
|
518
|
-
"PERMISSION_DENIED",
|
|
519
|
-
"PERMISSION_PROMPT_UNAVAILABLE",
|
|
520
|
-
"RUNTIME",
|
|
521
|
-
"USAGE"
|
|
522
|
-
];
|
|
523
|
-
var OUTPUT_ERROR_ORIGINS = ["cli", "runtime", "queue", "acp"];
|
|
524
|
-
|
|
525
719
|
// src/error-normalization.ts
|
|
526
720
|
var AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
527
721
|
function asRecord2(value) {
|
|
@@ -2914,115 +3108,270 @@ var AcpClient = class {
|
|
|
2914
3108
|
}
|
|
2915
3109
|
};
|
|
2916
3110
|
|
|
2917
|
-
// src/queue-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
3111
|
+
// src/queue-lease-store.ts
|
|
3112
|
+
import { createHash } from "crypto";
|
|
3113
|
+
import fs3 from "fs/promises";
|
|
3114
|
+
import os2 from "os";
|
|
3115
|
+
import path4 from "path";
|
|
3116
|
+
var PROCESS_EXIT_GRACE_MS = 1500;
|
|
3117
|
+
var PROCESS_POLL_MS = 50;
|
|
3118
|
+
var QUEUE_OWNER_STALE_HEARTBEAT_MS = 15e3;
|
|
3119
|
+
function queueBaseDir() {
|
|
3120
|
+
return path4.join(os2.homedir(), ".acpx", "queues");
|
|
3121
|
+
}
|
|
3122
|
+
function queueKeyForSession(sessionId) {
|
|
3123
|
+
return createHash("sha256").update(sessionId).digest("hex").slice(0, 24);
|
|
3124
|
+
}
|
|
3125
|
+
function queueLockFilePath(sessionId) {
|
|
3126
|
+
return path4.join(queueBaseDir(), `${queueKeyForSession(sessionId)}.lock`);
|
|
3127
|
+
}
|
|
3128
|
+
function queueSocketPath(sessionId) {
|
|
3129
|
+
const key = queueKeyForSession(sessionId);
|
|
3130
|
+
if (process.platform === "win32") {
|
|
3131
|
+
return `\\\\.\\pipe\\acpx-${key}`;
|
|
2935
3132
|
}
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3133
|
+
return path4.join(queueBaseDir(), `${key}.sock`);
|
|
3134
|
+
}
|
|
3135
|
+
function parseQueueOwnerRecord(raw) {
|
|
3136
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3137
|
+
return null;
|
|
2940
3138
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
3139
|
+
const record = raw;
|
|
3140
|
+
if (!Number.isInteger(record.pid) || record.pid <= 0 || typeof record.sessionId !== "string" || typeof record.socketPath !== "string" || typeof record.createdAt !== "string" || typeof record.heartbeatAt !== "string" || !Number.isInteger(record.ownerGeneration) || record.ownerGeneration <= 0 || !Number.isInteger(record.queueDepth) || record.queueDepth < 0) {
|
|
3141
|
+
return null;
|
|
2944
3142
|
}
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3143
|
+
return {
|
|
3144
|
+
pid: record.pid,
|
|
3145
|
+
sessionId: record.sessionId,
|
|
3146
|
+
socketPath: record.socketPath,
|
|
3147
|
+
createdAt: record.createdAt,
|
|
3148
|
+
heartbeatAt: record.heartbeatAt,
|
|
3149
|
+
ownerGeneration: record.ownerGeneration,
|
|
3150
|
+
queueDepth: record.queueDepth
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
function createOwnerGeneration() {
|
|
3154
|
+
return Date.now() * 1e3 + Math.floor(Math.random() * 1e3);
|
|
3155
|
+
}
|
|
3156
|
+
function nowIso4() {
|
|
3157
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
3158
|
+
}
|
|
3159
|
+
function isQueueOwnerHeartbeatStale(owner) {
|
|
3160
|
+
const heartbeatMs = Date.parse(owner.heartbeatAt);
|
|
3161
|
+
if (!Number.isFinite(heartbeatMs)) {
|
|
3162
|
+
return true;
|
|
2949
3163
|
}
|
|
2950
|
-
|
|
2951
|
-
|
|
3164
|
+
return Date.now() - heartbeatMs > QUEUE_OWNER_STALE_HEARTBEAT_MS;
|
|
3165
|
+
}
|
|
3166
|
+
async function ensureQueueDir() {
|
|
3167
|
+
await fs3.mkdir(queueBaseDir(), { recursive: true });
|
|
3168
|
+
}
|
|
3169
|
+
async function removeSocketFile(socketPath) {
|
|
3170
|
+
if (process.platform === "win32") {
|
|
3171
|
+
return;
|
|
2952
3172
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
3173
|
+
try {
|
|
3174
|
+
await fs3.unlink(socketPath);
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
if (error.code !== "ENOENT") {
|
|
3177
|
+
throw error;
|
|
3178
|
+
}
|
|
2955
3179
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
});
|
|
3180
|
+
}
|
|
3181
|
+
async function waitForProcessExit(pid, timeoutMs) {
|
|
3182
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
3183
|
+
while (Date.now() <= deadline) {
|
|
3184
|
+
if (!isProcessAlive(pid)) {
|
|
3185
|
+
return true;
|
|
2963
3186
|
}
|
|
3187
|
+
await waitMs2(PROCESS_POLL_MS);
|
|
2964
3188
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
this.pendingCancel = true;
|
|
2976
|
-
return true;
|
|
3189
|
+
return !isProcessAlive(pid);
|
|
3190
|
+
}
|
|
3191
|
+
async function cleanupStaleQueueOwner(sessionId, owner) {
|
|
3192
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3193
|
+
const socketPath = owner?.socketPath ?? queueSocketPath(sessionId);
|
|
3194
|
+
await removeSocketFile(socketPath).catch(() => {
|
|
3195
|
+
});
|
|
3196
|
+
await fs3.unlink(lockPath).catch((error) => {
|
|
3197
|
+
if (error.code !== "ENOENT") {
|
|
3198
|
+
throw error;
|
|
2977
3199
|
}
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
async function readQueueOwnerRecord(sessionId) {
|
|
3203
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3204
|
+
try {
|
|
3205
|
+
const payload = await fs3.readFile(lockPath, "utf8");
|
|
3206
|
+
const parsed = parseQueueOwnerRecord(JSON.parse(payload));
|
|
3207
|
+
return parsed ?? void 0;
|
|
3208
|
+
} catch {
|
|
3209
|
+
return void 0;
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
function isProcessAlive(pid) {
|
|
3213
|
+
if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) {
|
|
2978
3214
|
return false;
|
|
2979
3215
|
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3216
|
+
try {
|
|
3217
|
+
process.kill(pid, 0);
|
|
3218
|
+
return true;
|
|
3219
|
+
} catch {
|
|
3220
|
+
return false;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
async function terminateProcess(pid) {
|
|
3224
|
+
if (!isProcessAlive(pid)) {
|
|
3225
|
+
return false;
|
|
3226
|
+
}
|
|
3227
|
+
try {
|
|
3228
|
+
process.kill(pid, "SIGTERM");
|
|
3229
|
+
} catch {
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
if (await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS)) {
|
|
3233
|
+
return true;
|
|
3234
|
+
}
|
|
3235
|
+
try {
|
|
3236
|
+
process.kill(pid, "SIGKILL");
|
|
3237
|
+
} catch {
|
|
3238
|
+
return false;
|
|
3239
|
+
}
|
|
3240
|
+
await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS);
|
|
3241
|
+
return true;
|
|
3242
|
+
}
|
|
3243
|
+
async function ensureOwnerIsUsable(sessionId, owner) {
|
|
3244
|
+
const alive = isProcessAlive(owner.pid);
|
|
3245
|
+
const stale = isQueueOwnerHeartbeatStale(owner);
|
|
3246
|
+
if (alive && !stale) {
|
|
3247
|
+
return true;
|
|
3248
|
+
}
|
|
3249
|
+
if (alive) {
|
|
3250
|
+
await terminateProcess(owner.pid).catch(() => {
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3254
|
+
return false;
|
|
3255
|
+
}
|
|
3256
|
+
async function readQueueOwnerStatus(sessionId) {
|
|
3257
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3258
|
+
if (!owner) {
|
|
3259
|
+
return void 0;
|
|
3260
|
+
}
|
|
3261
|
+
const alive = await ensureOwnerIsUsable(sessionId, owner);
|
|
3262
|
+
if (!alive) {
|
|
3263
|
+
return void 0;
|
|
3264
|
+
}
|
|
3265
|
+
return {
|
|
3266
|
+
pid: owner.pid,
|
|
3267
|
+
socketPath: owner.socketPath,
|
|
3268
|
+
heartbeatAt: owner.heartbeatAt,
|
|
3269
|
+
ownerGeneration: owner.ownerGeneration,
|
|
3270
|
+
queueDepth: owner.queueDepth,
|
|
3271
|
+
alive,
|
|
3272
|
+
stale: isQueueOwnerHeartbeatStale(owner)
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
async function tryAcquireQueueOwnerLease(sessionId, nowIsoFactory = nowIso4) {
|
|
3276
|
+
await ensureQueueDir();
|
|
3277
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3278
|
+
const socketPath = queueSocketPath(sessionId);
|
|
3279
|
+
const createdAt = nowIsoFactory();
|
|
3280
|
+
const ownerGeneration = createOwnerGeneration();
|
|
3281
|
+
const payload = JSON.stringify(
|
|
3282
|
+
{
|
|
3283
|
+
pid: process.pid,
|
|
3284
|
+
sessionId,
|
|
3285
|
+
socketPath,
|
|
3286
|
+
createdAt,
|
|
3287
|
+
heartbeatAt: createdAt,
|
|
3288
|
+
ownerGeneration,
|
|
3289
|
+
queueDepth: 0
|
|
3290
|
+
},
|
|
3291
|
+
null,
|
|
3292
|
+
2
|
|
3293
|
+
);
|
|
3294
|
+
try {
|
|
3295
|
+
await fs3.writeFile(lockPath, `${payload}
|
|
3296
|
+
`, {
|
|
3297
|
+
encoding: "utf8",
|
|
3298
|
+
flag: "wx"
|
|
3299
|
+
});
|
|
3300
|
+
await removeSocketFile(socketPath).catch(() => {
|
|
3301
|
+
});
|
|
3302
|
+
return {
|
|
3303
|
+
sessionId,
|
|
3304
|
+
lockPath,
|
|
3305
|
+
socketPath,
|
|
3306
|
+
createdAt,
|
|
3307
|
+
ownerGeneration
|
|
3308
|
+
};
|
|
3309
|
+
} catch (error) {
|
|
3310
|
+
if (error.code !== "EEXIST") {
|
|
3311
|
+
throw error;
|
|
2984
3312
|
}
|
|
2985
|
-
const
|
|
2986
|
-
if (
|
|
2987
|
-
|
|
3313
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3314
|
+
if (!owner) {
|
|
3315
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3316
|
+
return void 0;
|
|
2988
3317
|
}
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
await this.options.withTimeout(
|
|
2996
|
-
async () => await activeController.setSessionMode(modeId),
|
|
2997
|
-
timeoutMs
|
|
2998
|
-
);
|
|
2999
|
-
return;
|
|
3318
|
+
if (!isProcessAlive(owner.pid) || isQueueOwnerHeartbeatStale(owner)) {
|
|
3319
|
+
if (isProcessAlive(owner.pid)) {
|
|
3320
|
+
await terminateProcess(owner.pid).catch(() => {
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3000
3324
|
}
|
|
3001
|
-
|
|
3325
|
+
return void 0;
|
|
3002
3326
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3327
|
+
}
|
|
3328
|
+
async function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso4) {
|
|
3329
|
+
const payload = JSON.stringify(
|
|
3330
|
+
{
|
|
3331
|
+
pid: process.pid,
|
|
3332
|
+
sessionId: lease.sessionId,
|
|
3333
|
+
socketPath: lease.socketPath,
|
|
3334
|
+
createdAt: lease.createdAt,
|
|
3335
|
+
heartbeatAt: nowIsoFactory(),
|
|
3336
|
+
ownerGeneration: lease.ownerGeneration,
|
|
3337
|
+
queueDepth: Math.max(0, Math.round(options.queueDepth))
|
|
3338
|
+
},
|
|
3339
|
+
null,
|
|
3340
|
+
2
|
|
3341
|
+
);
|
|
3342
|
+
await fs3.writeFile(lease.lockPath, `${payload}
|
|
3343
|
+
`, {
|
|
3344
|
+
encoding: "utf8"
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
async function releaseQueueOwnerLease(lease) {
|
|
3348
|
+
await removeSocketFile(lease.socketPath).catch(() => {
|
|
3349
|
+
});
|
|
3350
|
+
await fs3.unlink(lease.lockPath).catch((error) => {
|
|
3351
|
+
if (error.code !== "ENOENT") {
|
|
3352
|
+
throw error;
|
|
3011
3353
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
async function terminateQueueOwnerForSession(sessionId) {
|
|
3357
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3358
|
+
if (!owner) {
|
|
3359
|
+
return;
|
|
3017
3360
|
}
|
|
3018
|
-
|
|
3361
|
+
if (isProcessAlive(owner.pid)) {
|
|
3362
|
+
await terminateProcess(owner.pid);
|
|
3363
|
+
}
|
|
3364
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3365
|
+
}
|
|
3366
|
+
async function waitMs2(ms) {
|
|
3367
|
+
await new Promise((resolve) => {
|
|
3368
|
+
setTimeout(resolve, ms);
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3019
3371
|
|
|
3020
|
-
// src/queue-ipc.ts
|
|
3021
|
-
import {
|
|
3022
|
-
import fs3 from "fs/promises";
|
|
3372
|
+
// src/queue-ipc-client.ts
|
|
3373
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3023
3374
|
import net from "net";
|
|
3024
|
-
import os2 from "os";
|
|
3025
|
-
import path4 from "path";
|
|
3026
3375
|
|
|
3027
3376
|
// src/queue-messages.ts
|
|
3028
3377
|
function asRecord4(value) {
|
|
@@ -3255,236 +3604,629 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3255
3604
|
return null;
|
|
3256
3605
|
}
|
|
3257
3606
|
|
|
3258
|
-
// src/queue-ipc.ts
|
|
3259
|
-
var PROCESS_EXIT_GRACE_MS = 1500;
|
|
3260
|
-
var PROCESS_POLL_MS = 50;
|
|
3607
|
+
// src/queue-ipc-client.ts
|
|
3261
3608
|
var QUEUE_CONNECT_ATTEMPTS = 40;
|
|
3262
3609
|
var QUEUE_CONNECT_RETRY_MS = 50;
|
|
3263
|
-
function
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
3267
|
-
return {
|
|
3268
|
-
type: "error",
|
|
3269
|
-
requestId,
|
|
3270
|
-
code: "RUNTIME",
|
|
3271
|
-
detailCode,
|
|
3272
|
-
origin: "queue",
|
|
3273
|
-
retryable: options.retryable,
|
|
3274
|
-
message
|
|
3275
|
-
};
|
|
3610
|
+
function shouldRetryQueueConnect(error) {
|
|
3611
|
+
const code = error.code;
|
|
3612
|
+
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
3276
3613
|
}
|
|
3277
|
-
function
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3614
|
+
async function connectToSocket(socketPath) {
|
|
3615
|
+
return await new Promise((resolve, reject) => {
|
|
3616
|
+
const socket = net.createConnection(socketPath);
|
|
3617
|
+
const onConnect = () => {
|
|
3618
|
+
socket.off("error", onError);
|
|
3619
|
+
resolve(socket);
|
|
3620
|
+
};
|
|
3621
|
+
const onError = (error) => {
|
|
3622
|
+
socket.off("connect", onConnect);
|
|
3623
|
+
reject(error);
|
|
3624
|
+
};
|
|
3625
|
+
socket.once("connect", onConnect);
|
|
3626
|
+
socket.once("error", onError);
|
|
3283
3627
|
});
|
|
3284
|
-
return {
|
|
3285
|
-
type: "error",
|
|
3286
|
-
requestId,
|
|
3287
|
-
code: normalized.code,
|
|
3288
|
-
detailCode: normalized.detailCode,
|
|
3289
|
-
origin: normalized.origin,
|
|
3290
|
-
message: normalized.message,
|
|
3291
|
-
retryable: normalized.retryable,
|
|
3292
|
-
acp: normalized.acp
|
|
3293
|
-
};
|
|
3294
3628
|
}
|
|
3295
|
-
function
|
|
3296
|
-
|
|
3297
|
-
|
|
3629
|
+
async function connectToQueueOwner(owner) {
|
|
3630
|
+
let lastError;
|
|
3631
|
+
for (let attempt = 0; attempt < QUEUE_CONNECT_ATTEMPTS; attempt += 1) {
|
|
3632
|
+
try {
|
|
3633
|
+
return await connectToSocket(owner.socketPath);
|
|
3634
|
+
} catch (error) {
|
|
3635
|
+
lastError = error;
|
|
3636
|
+
if (!shouldRetryQueueConnect(error)) {
|
|
3637
|
+
throw error;
|
|
3638
|
+
}
|
|
3639
|
+
if (!isProcessAlive(owner.pid)) {
|
|
3640
|
+
return void 0;
|
|
3641
|
+
}
|
|
3642
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
3643
|
+
}
|
|
3298
3644
|
}
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
return true;
|
|
3302
|
-
} catch {
|
|
3303
|
-
return false;
|
|
3645
|
+
if (lastError && !shouldRetryQueueConnect(lastError)) {
|
|
3646
|
+
throw lastError;
|
|
3304
3647
|
}
|
|
3648
|
+
return void 0;
|
|
3305
3649
|
}
|
|
3306
|
-
async function
|
|
3307
|
-
const
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3650
|
+
async function submitToQueueOwner(owner, options) {
|
|
3651
|
+
const socket = await connectToQueueOwner(owner);
|
|
3652
|
+
if (!socket) {
|
|
3653
|
+
return void 0;
|
|
3654
|
+
}
|
|
3655
|
+
socket.setEncoding("utf8");
|
|
3656
|
+
const requestId = randomUUID2();
|
|
3657
|
+
const request = {
|
|
3658
|
+
type: "submit_prompt",
|
|
3659
|
+
requestId,
|
|
3660
|
+
message: options.message,
|
|
3661
|
+
permissionMode: options.permissionMode,
|
|
3662
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
3663
|
+
timeoutMs: options.timeoutMs,
|
|
3664
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
3665
|
+
waitForCompletion: options.waitForCompletion
|
|
3666
|
+
};
|
|
3667
|
+
options.outputFormatter.setContext({
|
|
3668
|
+
sessionId: options.sessionId,
|
|
3669
|
+
requestId,
|
|
3670
|
+
stream: "prompt"
|
|
3671
|
+
});
|
|
3672
|
+
return await new Promise((resolve, reject) => {
|
|
3673
|
+
let settled = false;
|
|
3674
|
+
let acknowledged = false;
|
|
3675
|
+
let buffer = "";
|
|
3676
|
+
let sawDone = false;
|
|
3677
|
+
const finishResolve = (result) => {
|
|
3678
|
+
if (settled) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
settled = true;
|
|
3682
|
+
socket.removeAllListeners();
|
|
3683
|
+
if (!socket.destroyed) {
|
|
3684
|
+
socket.end();
|
|
3685
|
+
}
|
|
3686
|
+
resolve(result);
|
|
3687
|
+
};
|
|
3688
|
+
const finishReject = (error) => {
|
|
3689
|
+
if (settled) {
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
settled = true;
|
|
3693
|
+
socket.removeAllListeners();
|
|
3694
|
+
if (!socket.destroyed) {
|
|
3695
|
+
socket.destroy();
|
|
3696
|
+
}
|
|
3697
|
+
reject(error);
|
|
3698
|
+
};
|
|
3699
|
+
const processLine = (line) => {
|
|
3700
|
+
let parsed;
|
|
3701
|
+
try {
|
|
3702
|
+
parsed = JSON.parse(line);
|
|
3703
|
+
} catch {
|
|
3704
|
+
finishReject(
|
|
3705
|
+
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3706
|
+
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3707
|
+
origin: "queue",
|
|
3708
|
+
retryable: true
|
|
3709
|
+
})
|
|
3710
|
+
);
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
const message = parseQueueOwnerMessage(parsed);
|
|
3714
|
+
if (!message || message.requestId !== requestId) {
|
|
3715
|
+
finishReject(
|
|
3716
|
+
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3717
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3718
|
+
origin: "queue",
|
|
3719
|
+
retryable: true
|
|
3720
|
+
})
|
|
3721
|
+
);
|
|
3722
|
+
return;
|
|
3723
|
+
}
|
|
3724
|
+
if (message.type === "accepted") {
|
|
3725
|
+
acknowledged = true;
|
|
3726
|
+
options.outputFormatter.setContext({
|
|
3727
|
+
sessionId: options.sessionId,
|
|
3728
|
+
requestId: message.requestId,
|
|
3729
|
+
stream: "prompt"
|
|
3730
|
+
});
|
|
3731
|
+
if (!options.waitForCompletion) {
|
|
3732
|
+
const queued = {
|
|
3733
|
+
queued: true,
|
|
3734
|
+
sessionId: options.sessionId,
|
|
3735
|
+
requestId
|
|
3736
|
+
};
|
|
3737
|
+
finishResolve(queued);
|
|
3738
|
+
}
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
if (message.type === "error") {
|
|
3742
|
+
options.outputFormatter.setContext({
|
|
3743
|
+
sessionId: options.sessionId,
|
|
3744
|
+
requestId: message.requestId,
|
|
3745
|
+
stream: "prompt"
|
|
3746
|
+
});
|
|
3747
|
+
options.outputFormatter.onError({
|
|
3748
|
+
code: message.code ?? "RUNTIME",
|
|
3749
|
+
detailCode: message.detailCode,
|
|
3750
|
+
origin: message.origin ?? "queue",
|
|
3751
|
+
message: message.message,
|
|
3752
|
+
retryable: message.retryable,
|
|
3753
|
+
acp: message.acp
|
|
3754
|
+
});
|
|
3755
|
+
options.outputFormatter.flush();
|
|
3756
|
+
const queueErrorAlreadyEmitted = options.errorEmissionPolicy?.queueErrorAlreadyEmitted ?? true;
|
|
3757
|
+
finishReject(
|
|
3758
|
+
new QueueConnectionError(message.message, {
|
|
3759
|
+
outputCode: message.code,
|
|
3760
|
+
detailCode: message.detailCode,
|
|
3761
|
+
origin: message.origin ?? "queue",
|
|
3762
|
+
retryable: message.retryable,
|
|
3763
|
+
acp: message.acp,
|
|
3764
|
+
...queueErrorAlreadyEmitted ? { outputAlreadyEmitted: true } : {}
|
|
3765
|
+
})
|
|
3766
|
+
);
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
if (!acknowledged) {
|
|
3770
|
+
finishReject(
|
|
3771
|
+
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3772
|
+
detailCode: "QUEUE_ACK_MISSING",
|
|
3773
|
+
origin: "queue",
|
|
3774
|
+
retryable: true
|
|
3775
|
+
})
|
|
3776
|
+
);
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
if (message.type === "session_update") {
|
|
3780
|
+
options.outputFormatter.onSessionUpdate(message.notification);
|
|
3781
|
+
return;
|
|
3782
|
+
}
|
|
3783
|
+
if (message.type === "client_operation") {
|
|
3784
|
+
options.outputFormatter.onClientOperation(message.operation);
|
|
3785
|
+
return;
|
|
3786
|
+
}
|
|
3787
|
+
if (message.type === "done") {
|
|
3788
|
+
options.outputFormatter.onDone(message.stopReason);
|
|
3789
|
+
sawDone = true;
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
if (message.type === "result") {
|
|
3793
|
+
if (!sawDone) {
|
|
3794
|
+
options.outputFormatter.onDone(message.result.stopReason);
|
|
3795
|
+
}
|
|
3796
|
+
options.outputFormatter.flush();
|
|
3797
|
+
finishResolve(message.result);
|
|
3798
|
+
return;
|
|
3799
|
+
}
|
|
3800
|
+
finishReject(
|
|
3801
|
+
new QueueProtocolError("Queue owner returned unexpected response", {
|
|
3802
|
+
detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
|
|
3803
|
+
origin: "queue",
|
|
3804
|
+
retryable: true
|
|
3805
|
+
})
|
|
3806
|
+
);
|
|
3807
|
+
};
|
|
3808
|
+
socket.on("data", (chunk) => {
|
|
3809
|
+
buffer += chunk;
|
|
3810
|
+
let index = buffer.indexOf("\n");
|
|
3811
|
+
while (index >= 0) {
|
|
3812
|
+
const line = buffer.slice(0, index).trim();
|
|
3813
|
+
buffer = buffer.slice(index + 1);
|
|
3814
|
+
if (line.length > 0) {
|
|
3815
|
+
processLine(line);
|
|
3816
|
+
}
|
|
3817
|
+
index = buffer.indexOf("\n");
|
|
3818
|
+
}
|
|
3314
3819
|
});
|
|
3820
|
+
socket.once("error", (error) => {
|
|
3821
|
+
finishReject(error);
|
|
3822
|
+
});
|
|
3823
|
+
socket.once("close", () => {
|
|
3824
|
+
if (settled) {
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
if (!acknowledged) {
|
|
3828
|
+
finishReject(
|
|
3829
|
+
new QueueConnectionError(
|
|
3830
|
+
"Queue owner disconnected before acknowledging request",
|
|
3831
|
+
{
|
|
3832
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
3833
|
+
origin: "queue",
|
|
3834
|
+
retryable: true
|
|
3835
|
+
}
|
|
3836
|
+
)
|
|
3837
|
+
);
|
|
3838
|
+
return;
|
|
3839
|
+
}
|
|
3840
|
+
if (!options.waitForCompletion) {
|
|
3841
|
+
const queued = {
|
|
3842
|
+
queued: true,
|
|
3843
|
+
sessionId: options.sessionId,
|
|
3844
|
+
requestId
|
|
3845
|
+
};
|
|
3846
|
+
finishResolve(queued);
|
|
3847
|
+
return;
|
|
3848
|
+
}
|
|
3849
|
+
finishReject(
|
|
3850
|
+
new QueueConnectionError("Queue owner disconnected before prompt completion", {
|
|
3851
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
3852
|
+
origin: "queue",
|
|
3853
|
+
retryable: true
|
|
3854
|
+
})
|
|
3855
|
+
);
|
|
3856
|
+
});
|
|
3857
|
+
socket.write(`${JSON.stringify(request)}
|
|
3858
|
+
`);
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
3862
|
+
const socket = await connectToQueueOwner(owner);
|
|
3863
|
+
if (!socket) {
|
|
3864
|
+
return void 0;
|
|
3315
3865
|
}
|
|
3316
|
-
|
|
3866
|
+
socket.setEncoding("utf8");
|
|
3867
|
+
return await new Promise((resolve, reject) => {
|
|
3868
|
+
let settled = false;
|
|
3869
|
+
let acknowledged = false;
|
|
3870
|
+
let buffer = "";
|
|
3871
|
+
const finishResolve = (result) => {
|
|
3872
|
+
if (settled) {
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
settled = true;
|
|
3876
|
+
socket.removeAllListeners();
|
|
3877
|
+
if (!socket.destroyed) {
|
|
3878
|
+
socket.end();
|
|
3879
|
+
}
|
|
3880
|
+
resolve(result);
|
|
3881
|
+
};
|
|
3882
|
+
const finishReject = (error) => {
|
|
3883
|
+
if (settled) {
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
settled = true;
|
|
3887
|
+
socket.removeAllListeners();
|
|
3888
|
+
if (!socket.destroyed) {
|
|
3889
|
+
socket.destroy();
|
|
3890
|
+
}
|
|
3891
|
+
reject(error);
|
|
3892
|
+
};
|
|
3893
|
+
const processLine = (line) => {
|
|
3894
|
+
let parsed;
|
|
3895
|
+
try {
|
|
3896
|
+
parsed = JSON.parse(line);
|
|
3897
|
+
} catch {
|
|
3898
|
+
finishReject(
|
|
3899
|
+
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3900
|
+
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3901
|
+
origin: "queue",
|
|
3902
|
+
retryable: true
|
|
3903
|
+
})
|
|
3904
|
+
);
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3907
|
+
const message = parseQueueOwnerMessage(parsed);
|
|
3908
|
+
if (!message || message.requestId !== request.requestId) {
|
|
3909
|
+
finishReject(
|
|
3910
|
+
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3911
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3912
|
+
origin: "queue",
|
|
3913
|
+
retryable: true
|
|
3914
|
+
})
|
|
3915
|
+
);
|
|
3916
|
+
return;
|
|
3917
|
+
}
|
|
3918
|
+
if (message.type === "accepted") {
|
|
3919
|
+
acknowledged = true;
|
|
3920
|
+
return;
|
|
3921
|
+
}
|
|
3922
|
+
if (message.type === "error") {
|
|
3923
|
+
finishReject(
|
|
3924
|
+
new QueueConnectionError(message.message, {
|
|
3925
|
+
outputCode: message.code,
|
|
3926
|
+
detailCode: message.detailCode,
|
|
3927
|
+
origin: message.origin ?? "queue",
|
|
3928
|
+
retryable: message.retryable,
|
|
3929
|
+
acp: message.acp
|
|
3930
|
+
})
|
|
3931
|
+
);
|
|
3932
|
+
return;
|
|
3933
|
+
}
|
|
3934
|
+
if (!acknowledged) {
|
|
3935
|
+
finishReject(
|
|
3936
|
+
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3937
|
+
detailCode: "QUEUE_ACK_MISSING",
|
|
3938
|
+
origin: "queue",
|
|
3939
|
+
retryable: true
|
|
3940
|
+
})
|
|
3941
|
+
);
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
if (!isExpectedResponse(message)) {
|
|
3945
|
+
finishReject(
|
|
3946
|
+
new QueueProtocolError("Queue owner returned unexpected response", {
|
|
3947
|
+
detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
|
|
3948
|
+
origin: "queue",
|
|
3949
|
+
retryable: true
|
|
3950
|
+
})
|
|
3951
|
+
);
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
finishResolve(message);
|
|
3955
|
+
};
|
|
3956
|
+
socket.on("data", (chunk) => {
|
|
3957
|
+
buffer += chunk;
|
|
3958
|
+
let index = buffer.indexOf("\n");
|
|
3959
|
+
while (index >= 0) {
|
|
3960
|
+
const line = buffer.slice(0, index).trim();
|
|
3961
|
+
buffer = buffer.slice(index + 1);
|
|
3962
|
+
if (line.length > 0) {
|
|
3963
|
+
processLine(line);
|
|
3964
|
+
}
|
|
3965
|
+
index = buffer.indexOf("\n");
|
|
3966
|
+
}
|
|
3967
|
+
});
|
|
3968
|
+
socket.once("error", (error) => {
|
|
3969
|
+
finishReject(error);
|
|
3970
|
+
});
|
|
3971
|
+
socket.once("close", () => {
|
|
3972
|
+
if (settled) {
|
|
3973
|
+
return;
|
|
3974
|
+
}
|
|
3975
|
+
if (!acknowledged) {
|
|
3976
|
+
finishReject(
|
|
3977
|
+
new QueueConnectionError(
|
|
3978
|
+
"Queue owner disconnected before acknowledging request",
|
|
3979
|
+
{
|
|
3980
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
3981
|
+
origin: "queue",
|
|
3982
|
+
retryable: true
|
|
3983
|
+
}
|
|
3984
|
+
)
|
|
3985
|
+
);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3988
|
+
finishReject(
|
|
3989
|
+
new QueueConnectionError("Queue owner disconnected before responding", {
|
|
3990
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
3991
|
+
origin: "queue",
|
|
3992
|
+
retryable: true
|
|
3993
|
+
})
|
|
3994
|
+
);
|
|
3995
|
+
});
|
|
3996
|
+
socket.write(`${JSON.stringify(request)}
|
|
3997
|
+
`);
|
|
3998
|
+
});
|
|
3317
3999
|
}
|
|
3318
|
-
async function
|
|
3319
|
-
|
|
3320
|
-
|
|
4000
|
+
async function submitCancelToQueueOwner(owner) {
|
|
4001
|
+
const request = {
|
|
4002
|
+
type: "cancel_prompt",
|
|
4003
|
+
requestId: randomUUID2()
|
|
4004
|
+
};
|
|
4005
|
+
const response = await submitControlToQueueOwner(
|
|
4006
|
+
owner,
|
|
4007
|
+
request,
|
|
4008
|
+
(message) => message.type === "cancel_result"
|
|
4009
|
+
);
|
|
4010
|
+
if (!response) {
|
|
4011
|
+
return void 0;
|
|
4012
|
+
}
|
|
4013
|
+
if (response.requestId !== request.requestId) {
|
|
4014
|
+
throw new QueueProtocolError("Queue owner returned mismatched cancel response", {
|
|
4015
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4016
|
+
origin: "queue",
|
|
4017
|
+
retryable: true
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
return response.cancelled;
|
|
4021
|
+
}
|
|
4022
|
+
async function submitSetModeToQueueOwner(owner, modeId, timeoutMs) {
|
|
4023
|
+
const request = {
|
|
4024
|
+
type: "set_mode",
|
|
4025
|
+
requestId: randomUUID2(),
|
|
4026
|
+
modeId,
|
|
4027
|
+
timeoutMs
|
|
4028
|
+
};
|
|
4029
|
+
const response = await submitControlToQueueOwner(
|
|
4030
|
+
owner,
|
|
4031
|
+
request,
|
|
4032
|
+
(message) => message.type === "set_mode_result"
|
|
4033
|
+
);
|
|
4034
|
+
if (!response) {
|
|
4035
|
+
return void 0;
|
|
4036
|
+
}
|
|
4037
|
+
if (response.requestId !== request.requestId) {
|
|
4038
|
+
throw new QueueProtocolError("Queue owner returned mismatched set_mode response", {
|
|
4039
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4040
|
+
origin: "queue",
|
|
4041
|
+
retryable: true
|
|
4042
|
+
});
|
|
4043
|
+
}
|
|
4044
|
+
return true;
|
|
4045
|
+
}
|
|
4046
|
+
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
|
|
4047
|
+
const request = {
|
|
4048
|
+
type: "set_config_option",
|
|
4049
|
+
requestId: randomUUID2(),
|
|
4050
|
+
configId,
|
|
4051
|
+
value,
|
|
4052
|
+
timeoutMs
|
|
4053
|
+
};
|
|
4054
|
+
const response = await submitControlToQueueOwner(
|
|
4055
|
+
owner,
|
|
4056
|
+
request,
|
|
4057
|
+
(message) => message.type === "set_config_option_result"
|
|
4058
|
+
);
|
|
4059
|
+
if (!response) {
|
|
4060
|
+
return void 0;
|
|
4061
|
+
}
|
|
4062
|
+
if (response.requestId !== request.requestId) {
|
|
4063
|
+
throw new QueueProtocolError(
|
|
4064
|
+
"Queue owner returned mismatched set_config_option response",
|
|
4065
|
+
{
|
|
4066
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4067
|
+
origin: "queue",
|
|
4068
|
+
retryable: true
|
|
4069
|
+
}
|
|
4070
|
+
);
|
|
4071
|
+
}
|
|
4072
|
+
return response.response;
|
|
4073
|
+
}
|
|
4074
|
+
async function trySubmitToRunningOwner(options) {
|
|
4075
|
+
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4076
|
+
if (!owner) {
|
|
4077
|
+
return void 0;
|
|
3321
4078
|
}
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
} catch {
|
|
3325
|
-
return false;
|
|
4079
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4080
|
+
return void 0;
|
|
3326
4081
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
4082
|
+
const submitted = await submitToQueueOwner(owner, options);
|
|
4083
|
+
if (submitted) {
|
|
4084
|
+
if (options.verbose) {
|
|
4085
|
+
process.stderr.write(
|
|
4086
|
+
`[acpx] queued prompt on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4087
|
+
`
|
|
4088
|
+
);
|
|
4089
|
+
}
|
|
4090
|
+
return submitted;
|
|
3329
4091
|
}
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
} catch {
|
|
3333
|
-
return false;
|
|
4092
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4093
|
+
return void 0;
|
|
3334
4094
|
}
|
|
3335
|
-
|
|
3336
|
-
|
|
4095
|
+
throw new QueueConnectionError(
|
|
4096
|
+
"Session queue owner is running but not accepting queue requests",
|
|
4097
|
+
{
|
|
4098
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4099
|
+
origin: "queue",
|
|
4100
|
+
retryable: true
|
|
4101
|
+
}
|
|
4102
|
+
);
|
|
3337
4103
|
}
|
|
3338
|
-
function
|
|
3339
|
-
|
|
3340
|
-
|
|
4104
|
+
async function tryCancelOnRunningOwner(options) {
|
|
4105
|
+
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4106
|
+
if (!owner) {
|
|
4107
|
+
return void 0;
|
|
3341
4108
|
}
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
return null;
|
|
4109
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4110
|
+
return void 0;
|
|
3345
4111
|
}
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
function queueLockFilePath(sessionId) {
|
|
3356
|
-
return path4.join(queueBaseDir(), `${queueKeyForSession(sessionId)}.lock`);
|
|
3357
|
-
}
|
|
3358
|
-
function queueSocketPath(sessionId) {
|
|
3359
|
-
const key = queueKeyForSession(sessionId);
|
|
3360
|
-
if (process.platform === "win32") {
|
|
3361
|
-
return `\\\\.\\pipe\\acpx-${key}`;
|
|
4112
|
+
const cancelled2 = await submitCancelToQueueOwner(owner);
|
|
4113
|
+
if (cancelled2 !== void 0) {
|
|
4114
|
+
if (options.verbose) {
|
|
4115
|
+
process.stderr.write(
|
|
4116
|
+
`[acpx] requested cancel on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4117
|
+
`
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
return cancelled2;
|
|
3362
4121
|
}
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
4122
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4123
|
+
return void 0;
|
|
4124
|
+
}
|
|
4125
|
+
throw new QueueConnectionError(
|
|
4126
|
+
"Session queue owner is running but not accepting cancel requests",
|
|
4127
|
+
{
|
|
4128
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4129
|
+
origin: "queue",
|
|
4130
|
+
retryable: true
|
|
4131
|
+
}
|
|
4132
|
+
);
|
|
3367
4133
|
}
|
|
3368
|
-
async function
|
|
3369
|
-
|
|
3370
|
-
|
|
4134
|
+
async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
|
|
4135
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
4136
|
+
if (!owner) {
|
|
4137
|
+
return void 0;
|
|
3371
4138
|
}
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
}
|
|
3375
|
-
|
|
3376
|
-
|
|
4139
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4140
|
+
return void 0;
|
|
4141
|
+
}
|
|
4142
|
+
const submitted = await submitSetModeToQueueOwner(owner, modeId, timeoutMs);
|
|
4143
|
+
if (submitted) {
|
|
4144
|
+
if (verbose) {
|
|
4145
|
+
process.stderr.write(
|
|
4146
|
+
`[acpx] requested session/set_mode on owner pid ${owner.pid} for session ${sessionId}
|
|
4147
|
+
`
|
|
4148
|
+
);
|
|
3377
4149
|
}
|
|
4150
|
+
return true;
|
|
3378
4151
|
}
|
|
3379
|
-
|
|
3380
|
-
async function readQueueOwnerRecord(sessionId) {
|
|
3381
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
3382
|
-
try {
|
|
3383
|
-
const payload = await fs3.readFile(lockPath, "utf8");
|
|
3384
|
-
const parsed = parseQueueOwnerRecord(JSON.parse(payload));
|
|
3385
|
-
return parsed ?? void 0;
|
|
3386
|
-
} catch {
|
|
4152
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
3387
4153
|
return void 0;
|
|
3388
4154
|
}
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
await fs3.unlink(lockPath).catch((error) => {
|
|
3396
|
-
if (error.code !== "ENOENT") {
|
|
3397
|
-
throw error;
|
|
4155
|
+
throw new QueueConnectionError(
|
|
4156
|
+
"Session queue owner is running but not accepting set_mode requests",
|
|
4157
|
+
{
|
|
4158
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4159
|
+
origin: "queue",
|
|
4160
|
+
retryable: true
|
|
3398
4161
|
}
|
|
3399
|
-
|
|
4162
|
+
);
|
|
3400
4163
|
}
|
|
3401
|
-
async function
|
|
3402
|
-
await
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
4164
|
+
async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
|
|
4165
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
4166
|
+
if (!owner) {
|
|
4167
|
+
return void 0;
|
|
4168
|
+
}
|
|
4169
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4170
|
+
return void 0;
|
|
4171
|
+
}
|
|
4172
|
+
const response = await submitSetConfigOptionToQueueOwner(
|
|
4173
|
+
owner,
|
|
4174
|
+
configId,
|
|
4175
|
+
value,
|
|
4176
|
+
timeoutMs
|
|
3414
4177
|
);
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
await removeSocketFile(socketPath).catch(() => {
|
|
3422
|
-
});
|
|
3423
|
-
return { lockPath, socketPath };
|
|
3424
|
-
} catch (error) {
|
|
3425
|
-
if (error.code !== "EEXIST") {
|
|
3426
|
-
throw error;
|
|
3427
|
-
}
|
|
3428
|
-
const owner = await readQueueOwnerRecord(sessionId);
|
|
3429
|
-
if (!owner || !isProcessAlive(owner.pid)) {
|
|
3430
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4178
|
+
if (response) {
|
|
4179
|
+
if (verbose) {
|
|
4180
|
+
process.stderr.write(
|
|
4181
|
+
`[acpx] requested session/set_config_option on owner pid ${owner.pid} for session ${sessionId}
|
|
4182
|
+
`
|
|
4183
|
+
);
|
|
3431
4184
|
}
|
|
4185
|
+
return response;
|
|
4186
|
+
}
|
|
4187
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
3432
4188
|
return void 0;
|
|
3433
4189
|
}
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
throw error;
|
|
4190
|
+
throw new QueueConnectionError(
|
|
4191
|
+
"Session queue owner is running but not accepting set_config_option requests",
|
|
4192
|
+
{
|
|
4193
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4194
|
+
origin: "queue",
|
|
4195
|
+
retryable: true
|
|
3441
4196
|
}
|
|
3442
|
-
|
|
3443
|
-
}
|
|
3444
|
-
function shouldRetryQueueConnect(error) {
|
|
3445
|
-
const code = error.code;
|
|
3446
|
-
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
4197
|
+
);
|
|
3447
4198
|
}
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
4199
|
+
|
|
4200
|
+
// src/queue-ipc-server.ts
|
|
4201
|
+
import net2 from "net";
|
|
4202
|
+
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
4203
|
+
return {
|
|
4204
|
+
type: "error",
|
|
4205
|
+
requestId,
|
|
4206
|
+
code: "RUNTIME",
|
|
4207
|
+
detailCode,
|
|
4208
|
+
origin: "queue",
|
|
4209
|
+
retryable: options.retryable,
|
|
4210
|
+
message
|
|
4211
|
+
};
|
|
3452
4212
|
}
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
};
|
|
3460
|
-
const onError = (error) => {
|
|
3461
|
-
socket.off("connect", onConnect);
|
|
3462
|
-
reject(error);
|
|
3463
|
-
};
|
|
3464
|
-
socket.once("connect", onConnect);
|
|
3465
|
-
socket.once("error", onError);
|
|
4213
|
+
function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
|
|
4214
|
+
const normalized = normalizeOutputError(error, {
|
|
4215
|
+
defaultCode: "RUNTIME",
|
|
4216
|
+
origin: "queue",
|
|
4217
|
+
detailCode,
|
|
4218
|
+
retryable: options.retryable
|
|
3466
4219
|
});
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
}
|
|
3478
|
-
if (!isProcessAlive(owner.pid)) {
|
|
3479
|
-
return void 0;
|
|
3480
|
-
}
|
|
3481
|
-
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
3482
|
-
}
|
|
3483
|
-
}
|
|
3484
|
-
if (lastError && !shouldRetryQueueConnect(lastError)) {
|
|
3485
|
-
throw lastError;
|
|
3486
|
-
}
|
|
3487
|
-
return void 0;
|
|
4220
|
+
return {
|
|
4221
|
+
type: "error",
|
|
4222
|
+
requestId,
|
|
4223
|
+
code: normalized.code,
|
|
4224
|
+
detailCode: normalized.detailCode,
|
|
4225
|
+
origin: normalized.origin,
|
|
4226
|
+
message: normalized.message,
|
|
4227
|
+
retryable: normalized.retryable,
|
|
4228
|
+
acp: normalized.acp
|
|
4229
|
+
};
|
|
3488
4230
|
}
|
|
3489
4231
|
function writeQueueMessage(socket, message) {
|
|
3490
4232
|
if (socket.destroyed || !socket.writable) {
|
|
@@ -3505,7 +4247,7 @@ var SessionQueueOwner = class _SessionQueueOwner {
|
|
|
3505
4247
|
}
|
|
3506
4248
|
static async start(lease, controlHandlers) {
|
|
3507
4249
|
const ownerRef = { current: void 0 };
|
|
3508
|
-
const server =
|
|
4250
|
+
const server = net2.createServer((socket) => {
|
|
3509
4251
|
ownerRef.current?.handleConnection(socket);
|
|
3510
4252
|
});
|
|
3511
4253
|
ownerRef.current = new _SessionQueueOwner(server, controlHandlers);
|
|
@@ -3524,557 +4266,238 @@ var SessionQueueOwner = class _SessionQueueOwner {
|
|
|
3524
4266
|
});
|
|
3525
4267
|
return ownerRef.current;
|
|
3526
4268
|
}
|
|
3527
|
-
async close() {
|
|
3528
|
-
if (this.closed) {
|
|
3529
|
-
return;
|
|
3530
|
-
}
|
|
3531
|
-
this.closed = true;
|
|
3532
|
-
for (const waiter of this.waiters.splice(0)) {
|
|
3533
|
-
waiter(void 0);
|
|
3534
|
-
}
|
|
3535
|
-
for (const task of this.pending.splice(0)) {
|
|
3536
|
-
if (task.waitForCompletion) {
|
|
3537
|
-
task.send(
|
|
3538
|
-
makeQueueOwnerError(
|
|
3539
|
-
task.requestId,
|
|
3540
|
-
"Queue owner shutting down before prompt execution",
|
|
3541
|
-
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
3542
|
-
{
|
|
3543
|
-
retryable: true
|
|
3544
|
-
}
|
|
3545
|
-
)
|
|
3546
|
-
);
|
|
3547
|
-
}
|
|
3548
|
-
task.close();
|
|
3549
|
-
}
|
|
3550
|
-
await new Promise((resolve) => {
|
|
3551
|
-
this.server.close(() => resolve());
|
|
3552
|
-
});
|
|
3553
|
-
}
|
|
3554
|
-
async nextTask(timeoutMs) {
|
|
3555
|
-
if (this.pending.length > 0) {
|
|
3556
|
-
return this.pending.shift();
|
|
3557
|
-
}
|
|
3558
|
-
if (this.closed) {
|
|
3559
|
-
return void 0;
|
|
3560
|
-
}
|
|
3561
|
-
return await new Promise((resolve) => {
|
|
3562
|
-
const shouldTimeout = timeoutMs != null;
|
|
3563
|
-
const timer = shouldTimeout && setTimeout(
|
|
3564
|
-
() => {
|
|
3565
|
-
const index = this.waiters.indexOf(waiter);
|
|
3566
|
-
if (index >= 0) {
|
|
3567
|
-
this.waiters.splice(index, 1);
|
|
3568
|
-
}
|
|
3569
|
-
resolve(void 0);
|
|
3570
|
-
},
|
|
3571
|
-
Math.max(0, timeoutMs)
|
|
3572
|
-
);
|
|
3573
|
-
const waiter = (task) => {
|
|
3574
|
-
if (timer) {
|
|
3575
|
-
clearTimeout(timer);
|
|
3576
|
-
}
|
|
3577
|
-
resolve(task);
|
|
3578
|
-
};
|
|
3579
|
-
this.waiters.push(waiter);
|
|
3580
|
-
});
|
|
3581
|
-
}
|
|
3582
|
-
enqueue(task) {
|
|
3583
|
-
if (this.closed) {
|
|
3584
|
-
if (task.waitForCompletion) {
|
|
3585
|
-
task.send(
|
|
3586
|
-
makeQueueOwnerError(
|
|
3587
|
-
task.requestId,
|
|
3588
|
-
"Queue owner is shutting down",
|
|
3589
|
-
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
3590
|
-
{
|
|
3591
|
-
retryable: true
|
|
3592
|
-
}
|
|
3593
|
-
)
|
|
3594
|
-
);
|
|
3595
|
-
}
|
|
3596
|
-
task.close();
|
|
3597
|
-
return;
|
|
3598
|
-
}
|
|
3599
|
-
const waiter = this.waiters.shift();
|
|
3600
|
-
if (waiter) {
|
|
3601
|
-
waiter(task);
|
|
3602
|
-
return;
|
|
3603
|
-
}
|
|
3604
|
-
this.pending.push(task);
|
|
3605
|
-
}
|
|
3606
|
-
handleConnection(socket) {
|
|
3607
|
-
socket.setEncoding("utf8");
|
|
3608
|
-
if (this.closed) {
|
|
3609
|
-
writeQueueMessage(
|
|
3610
|
-
socket,
|
|
3611
|
-
makeQueueOwnerError("unknown", "Queue owner is closed", "QUEUE_OWNER_CLOSED", {
|
|
3612
|
-
retryable: true
|
|
3613
|
-
})
|
|
3614
|
-
);
|
|
3615
|
-
socket.end();
|
|
3616
|
-
return;
|
|
3617
|
-
}
|
|
3618
|
-
let buffer = "";
|
|
3619
|
-
let handled = false;
|
|
3620
|
-
const fail = (requestId, message, detailCode) => {
|
|
3621
|
-
writeQueueMessage(
|
|
3622
|
-
socket,
|
|
3623
|
-
makeQueueOwnerError(requestId, message, detailCode, {
|
|
3624
|
-
retryable: false
|
|
3625
|
-
})
|
|
3626
|
-
);
|
|
3627
|
-
socket.end();
|
|
3628
|
-
};
|
|
3629
|
-
const processLine = (line) => {
|
|
3630
|
-
if (handled) {
|
|
3631
|
-
return;
|
|
3632
|
-
}
|
|
3633
|
-
handled = true;
|
|
3634
|
-
let parsed;
|
|
3635
|
-
try {
|
|
3636
|
-
parsed = JSON.parse(line);
|
|
3637
|
-
} catch {
|
|
3638
|
-
fail(
|
|
3639
|
-
"unknown",
|
|
3640
|
-
"Invalid queue request payload",
|
|
3641
|
-
"QUEUE_REQUEST_PAYLOAD_INVALID_JSON"
|
|
3642
|
-
);
|
|
3643
|
-
return;
|
|
3644
|
-
}
|
|
3645
|
-
const request = parseQueueRequest(parsed);
|
|
3646
|
-
if (!request) {
|
|
3647
|
-
fail("unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
3648
|
-
return;
|
|
3649
|
-
}
|
|
3650
|
-
if (request.type === "cancel_prompt") {
|
|
3651
|
-
writeQueueMessage(socket, {
|
|
3652
|
-
type: "accepted",
|
|
3653
|
-
requestId: request.requestId
|
|
3654
|
-
});
|
|
3655
|
-
void this.controlHandlers.cancelPrompt().then((cancelled2) => {
|
|
3656
|
-
writeQueueMessage(socket, {
|
|
3657
|
-
type: "cancel_result",
|
|
3658
|
-
requestId: request.requestId,
|
|
3659
|
-
cancelled: cancelled2
|
|
3660
|
-
});
|
|
3661
|
-
}).catch((error) => {
|
|
3662
|
-
writeQueueMessage(
|
|
3663
|
-
socket,
|
|
3664
|
-
makeQueueOwnerErrorFromUnknown(
|
|
3665
|
-
request.requestId,
|
|
3666
|
-
error,
|
|
3667
|
-
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
3668
|
-
)
|
|
3669
|
-
);
|
|
3670
|
-
}).finally(() => {
|
|
3671
|
-
if (!socket.destroyed) {
|
|
3672
|
-
socket.end();
|
|
3673
|
-
}
|
|
3674
|
-
});
|
|
3675
|
-
return;
|
|
3676
|
-
}
|
|
3677
|
-
if (request.type === "set_mode") {
|
|
3678
|
-
writeQueueMessage(socket, {
|
|
3679
|
-
type: "accepted",
|
|
3680
|
-
requestId: request.requestId
|
|
3681
|
-
});
|
|
3682
|
-
void this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs).then(() => {
|
|
3683
|
-
writeQueueMessage(socket, {
|
|
3684
|
-
type: "set_mode_result",
|
|
3685
|
-
requestId: request.requestId,
|
|
3686
|
-
modeId: request.modeId
|
|
3687
|
-
});
|
|
3688
|
-
}).catch((error) => {
|
|
3689
|
-
writeQueueMessage(
|
|
3690
|
-
socket,
|
|
3691
|
-
makeQueueOwnerErrorFromUnknown(
|
|
3692
|
-
request.requestId,
|
|
3693
|
-
error,
|
|
3694
|
-
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
3695
|
-
)
|
|
3696
|
-
);
|
|
3697
|
-
}).finally(() => {
|
|
3698
|
-
if (!socket.destroyed) {
|
|
3699
|
-
socket.end();
|
|
3700
|
-
}
|
|
3701
|
-
});
|
|
3702
|
-
return;
|
|
3703
|
-
}
|
|
3704
|
-
if (request.type === "set_config_option") {
|
|
3705
|
-
writeQueueMessage(socket, {
|
|
3706
|
-
type: "accepted",
|
|
3707
|
-
requestId: request.requestId
|
|
3708
|
-
});
|
|
3709
|
-
void this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs).then((response) => {
|
|
3710
|
-
writeQueueMessage(socket, {
|
|
3711
|
-
type: "set_config_option_result",
|
|
3712
|
-
requestId: request.requestId,
|
|
3713
|
-
response
|
|
3714
|
-
});
|
|
3715
|
-
}).catch((error) => {
|
|
3716
|
-
writeQueueMessage(
|
|
3717
|
-
socket,
|
|
3718
|
-
makeQueueOwnerErrorFromUnknown(
|
|
3719
|
-
request.requestId,
|
|
3720
|
-
error,
|
|
3721
|
-
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
3722
|
-
)
|
|
3723
|
-
);
|
|
3724
|
-
}).finally(() => {
|
|
3725
|
-
if (!socket.destroyed) {
|
|
3726
|
-
socket.end();
|
|
3727
|
-
}
|
|
3728
|
-
});
|
|
3729
|
-
return;
|
|
3730
|
-
}
|
|
3731
|
-
const task = {
|
|
3732
|
-
requestId: request.requestId,
|
|
3733
|
-
message: request.message,
|
|
3734
|
-
permissionMode: request.permissionMode,
|
|
3735
|
-
nonInteractivePermissions: request.nonInteractivePermissions,
|
|
3736
|
-
timeoutMs: request.timeoutMs,
|
|
3737
|
-
suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
|
|
3738
|
-
waitForCompletion: request.waitForCompletion,
|
|
3739
|
-
send: (message) => {
|
|
3740
|
-
writeQueueMessage(socket, message);
|
|
3741
|
-
},
|
|
3742
|
-
close: () => {
|
|
3743
|
-
if (!socket.destroyed) {
|
|
3744
|
-
socket.end();
|
|
3745
|
-
}
|
|
3746
|
-
}
|
|
3747
|
-
};
|
|
3748
|
-
writeQueueMessage(socket, {
|
|
3749
|
-
type: "accepted",
|
|
3750
|
-
requestId: request.requestId
|
|
3751
|
-
});
|
|
3752
|
-
if (!request.waitForCompletion) {
|
|
3753
|
-
task.close();
|
|
3754
|
-
}
|
|
3755
|
-
this.enqueue(task);
|
|
3756
|
-
};
|
|
3757
|
-
socket.on("data", (chunk) => {
|
|
3758
|
-
buffer += chunk;
|
|
3759
|
-
let index = buffer.indexOf("\n");
|
|
3760
|
-
while (index >= 0) {
|
|
3761
|
-
const line = buffer.slice(0, index).trim();
|
|
3762
|
-
buffer = buffer.slice(index + 1);
|
|
3763
|
-
if (line.length > 0) {
|
|
3764
|
-
processLine(line);
|
|
3765
|
-
}
|
|
3766
|
-
index = buffer.indexOf("\n");
|
|
3767
|
-
}
|
|
3768
|
-
});
|
|
3769
|
-
socket.on("error", () => {
|
|
3770
|
-
});
|
|
3771
|
-
}
|
|
3772
|
-
};
|
|
3773
|
-
async function submitToQueueOwner(owner, options) {
|
|
3774
|
-
const socket = await connectToQueueOwner(owner);
|
|
3775
|
-
if (!socket) {
|
|
3776
|
-
return void 0;
|
|
3777
|
-
}
|
|
3778
|
-
socket.setEncoding("utf8");
|
|
3779
|
-
const requestId = randomUUID2();
|
|
3780
|
-
const request = {
|
|
3781
|
-
type: "submit_prompt",
|
|
3782
|
-
requestId,
|
|
3783
|
-
message: options.message,
|
|
3784
|
-
permissionMode: options.permissionMode,
|
|
3785
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
3786
|
-
timeoutMs: options.timeoutMs,
|
|
3787
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
3788
|
-
waitForCompletion: options.waitForCompletion
|
|
3789
|
-
};
|
|
3790
|
-
options.outputFormatter.setContext({
|
|
3791
|
-
sessionId: options.sessionId,
|
|
3792
|
-
requestId,
|
|
3793
|
-
stream: "prompt"
|
|
3794
|
-
});
|
|
3795
|
-
return await new Promise((resolve, reject) => {
|
|
3796
|
-
let settled = false;
|
|
3797
|
-
let acknowledged = false;
|
|
3798
|
-
let buffer = "";
|
|
3799
|
-
let sawDone = false;
|
|
3800
|
-
const finishResolve = (result) => {
|
|
3801
|
-
if (settled) {
|
|
3802
|
-
return;
|
|
3803
|
-
}
|
|
3804
|
-
settled = true;
|
|
3805
|
-
socket.removeAllListeners();
|
|
3806
|
-
if (!socket.destroyed) {
|
|
3807
|
-
socket.end();
|
|
3808
|
-
}
|
|
3809
|
-
resolve(result);
|
|
3810
|
-
};
|
|
3811
|
-
const finishReject = (error) => {
|
|
3812
|
-
if (settled) {
|
|
3813
|
-
return;
|
|
3814
|
-
}
|
|
3815
|
-
settled = true;
|
|
3816
|
-
socket.removeAllListeners();
|
|
3817
|
-
if (!socket.destroyed) {
|
|
3818
|
-
socket.destroy();
|
|
3819
|
-
}
|
|
3820
|
-
reject(error);
|
|
3821
|
-
};
|
|
3822
|
-
const processLine = (line) => {
|
|
3823
|
-
let parsed;
|
|
3824
|
-
try {
|
|
3825
|
-
parsed = JSON.parse(line);
|
|
3826
|
-
} catch {
|
|
3827
|
-
finishReject(
|
|
3828
|
-
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3829
|
-
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3830
|
-
origin: "queue",
|
|
3831
|
-
retryable: true
|
|
3832
|
-
})
|
|
3833
|
-
);
|
|
3834
|
-
return;
|
|
3835
|
-
}
|
|
3836
|
-
const message = parseQueueOwnerMessage(parsed);
|
|
3837
|
-
if (!message || message.requestId !== requestId) {
|
|
3838
|
-
finishReject(
|
|
3839
|
-
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3840
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3841
|
-
origin: "queue",
|
|
3842
|
-
retryable: true
|
|
3843
|
-
})
|
|
3844
|
-
);
|
|
3845
|
-
return;
|
|
3846
|
-
}
|
|
3847
|
-
if (message.type === "accepted") {
|
|
3848
|
-
acknowledged = true;
|
|
3849
|
-
options.outputFormatter.setContext({
|
|
3850
|
-
sessionId: options.sessionId,
|
|
3851
|
-
requestId: message.requestId,
|
|
3852
|
-
stream: "prompt"
|
|
3853
|
-
});
|
|
3854
|
-
if (!options.waitForCompletion) {
|
|
3855
|
-
const queued = {
|
|
3856
|
-
queued: true,
|
|
3857
|
-
sessionId: options.sessionId,
|
|
3858
|
-
requestId
|
|
3859
|
-
};
|
|
3860
|
-
finishResolve(queued);
|
|
3861
|
-
}
|
|
3862
|
-
return;
|
|
3863
|
-
}
|
|
3864
|
-
if (message.type === "error") {
|
|
3865
|
-
options.outputFormatter.setContext({
|
|
3866
|
-
sessionId: options.sessionId,
|
|
3867
|
-
requestId: message.requestId,
|
|
3868
|
-
stream: "prompt"
|
|
3869
|
-
});
|
|
3870
|
-
options.outputFormatter.onError({
|
|
3871
|
-
code: message.code ?? "RUNTIME",
|
|
3872
|
-
detailCode: message.detailCode,
|
|
3873
|
-
origin: message.origin ?? "queue",
|
|
3874
|
-
message: message.message,
|
|
3875
|
-
retryable: message.retryable,
|
|
3876
|
-
acp: message.acp
|
|
3877
|
-
});
|
|
3878
|
-
options.outputFormatter.flush();
|
|
3879
|
-
const queueErrorAlreadyEmitted = options.errorEmissionPolicy?.queueErrorAlreadyEmitted ?? true;
|
|
3880
|
-
finishReject(
|
|
3881
|
-
new QueueConnectionError(message.message, {
|
|
3882
|
-
outputCode: message.code,
|
|
3883
|
-
detailCode: message.detailCode,
|
|
3884
|
-
origin: message.origin ?? "queue",
|
|
3885
|
-
retryable: message.retryable,
|
|
3886
|
-
acp: message.acp,
|
|
3887
|
-
...queueErrorAlreadyEmitted ? { outputAlreadyEmitted: true } : {}
|
|
3888
|
-
})
|
|
3889
|
-
);
|
|
3890
|
-
return;
|
|
3891
|
-
}
|
|
3892
|
-
if (!acknowledged) {
|
|
3893
|
-
finishReject(
|
|
3894
|
-
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3895
|
-
detailCode: "QUEUE_ACK_MISSING",
|
|
3896
|
-
origin: "queue",
|
|
3897
|
-
retryable: true
|
|
3898
|
-
})
|
|
4269
|
+
async close() {
|
|
4270
|
+
if (this.closed) {
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
this.closed = true;
|
|
4274
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
4275
|
+
waiter(void 0);
|
|
4276
|
+
}
|
|
4277
|
+
for (const task of this.pending.splice(0)) {
|
|
4278
|
+
if (task.waitForCompletion) {
|
|
4279
|
+
task.send(
|
|
4280
|
+
makeQueueOwnerError(
|
|
4281
|
+
task.requestId,
|
|
4282
|
+
"Queue owner shutting down before prompt execution",
|
|
4283
|
+
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
4284
|
+
{
|
|
4285
|
+
retryable: true
|
|
4286
|
+
}
|
|
4287
|
+
)
|
|
3899
4288
|
);
|
|
3900
|
-
return;
|
|
3901
|
-
}
|
|
3902
|
-
if (message.type === "session_update") {
|
|
3903
|
-
options.outputFormatter.onSessionUpdate(message.notification);
|
|
3904
|
-
return;
|
|
3905
|
-
}
|
|
3906
|
-
if (message.type === "client_operation") {
|
|
3907
|
-
options.outputFormatter.onClientOperation(message.operation);
|
|
3908
|
-
return;
|
|
3909
|
-
}
|
|
3910
|
-
if (message.type === "done") {
|
|
3911
|
-
options.outputFormatter.onDone(message.stopReason);
|
|
3912
|
-
sawDone = true;
|
|
3913
|
-
return;
|
|
3914
|
-
}
|
|
3915
|
-
if (message.type === "result") {
|
|
3916
|
-
if (!sawDone) {
|
|
3917
|
-
options.outputFormatter.onDone(message.result.stopReason);
|
|
3918
|
-
}
|
|
3919
|
-
options.outputFormatter.flush();
|
|
3920
|
-
finishResolve(message.result);
|
|
3921
|
-
return;
|
|
3922
4289
|
}
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
4290
|
+
task.close();
|
|
4291
|
+
}
|
|
4292
|
+
await new Promise((resolve) => {
|
|
4293
|
+
this.server.close(() => resolve());
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
async nextTask(timeoutMs) {
|
|
4297
|
+
if (this.pending.length > 0) {
|
|
4298
|
+
return this.pending.shift();
|
|
4299
|
+
}
|
|
4300
|
+
if (this.closed) {
|
|
4301
|
+
return void 0;
|
|
4302
|
+
}
|
|
4303
|
+
return await new Promise((resolve) => {
|
|
4304
|
+
const shouldTimeout = timeoutMs != null;
|
|
4305
|
+
const timer = shouldTimeout && setTimeout(
|
|
4306
|
+
() => {
|
|
4307
|
+
const index = this.waiters.indexOf(waiter);
|
|
4308
|
+
if (index >= 0) {
|
|
4309
|
+
this.waiters.splice(index, 1);
|
|
4310
|
+
}
|
|
4311
|
+
resolve(void 0);
|
|
4312
|
+
},
|
|
4313
|
+
Math.max(0, timeoutMs)
|
|
3929
4314
|
);
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
let index = buffer.indexOf("\n");
|
|
3934
|
-
while (index >= 0) {
|
|
3935
|
-
const line = buffer.slice(0, index).trim();
|
|
3936
|
-
buffer = buffer.slice(index + 1);
|
|
3937
|
-
if (line.length > 0) {
|
|
3938
|
-
processLine(line);
|
|
4315
|
+
const waiter = (task) => {
|
|
4316
|
+
if (timer) {
|
|
4317
|
+
clearTimeout(timer);
|
|
3939
4318
|
}
|
|
3940
|
-
|
|
3941
|
-
}
|
|
3942
|
-
|
|
3943
|
-
socket.once("error", (error) => {
|
|
3944
|
-
finishReject(error);
|
|
4319
|
+
resolve(task);
|
|
4320
|
+
};
|
|
4321
|
+
this.waiters.push(waiter);
|
|
3945
4322
|
});
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
4323
|
+
}
|
|
4324
|
+
queueDepth() {
|
|
4325
|
+
return this.pending.length;
|
|
4326
|
+
}
|
|
4327
|
+
enqueue(task) {
|
|
4328
|
+
if (this.closed) {
|
|
4329
|
+
if (task.waitForCompletion) {
|
|
4330
|
+
task.send(
|
|
4331
|
+
makeQueueOwnerError(
|
|
4332
|
+
task.requestId,
|
|
4333
|
+
"Queue owner is shutting down",
|
|
4334
|
+
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
3954
4335
|
{
|
|
3955
|
-
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
3956
|
-
origin: "queue",
|
|
3957
4336
|
retryable: true
|
|
3958
4337
|
}
|
|
3959
4338
|
)
|
|
3960
4339
|
);
|
|
3961
|
-
return;
|
|
3962
|
-
}
|
|
3963
|
-
if (!options.waitForCompletion) {
|
|
3964
|
-
const queued = {
|
|
3965
|
-
queued: true,
|
|
3966
|
-
sessionId: options.sessionId,
|
|
3967
|
-
requestId
|
|
3968
|
-
};
|
|
3969
|
-
finishResolve(queued);
|
|
3970
|
-
return;
|
|
3971
4340
|
}
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
4341
|
+
task.close();
|
|
4342
|
+
return;
|
|
4343
|
+
}
|
|
4344
|
+
const waiter = this.waiters.shift();
|
|
4345
|
+
if (waiter) {
|
|
4346
|
+
waiter(task);
|
|
4347
|
+
return;
|
|
4348
|
+
}
|
|
4349
|
+
this.pending.push(task);
|
|
4350
|
+
}
|
|
4351
|
+
handleConnection(socket) {
|
|
4352
|
+
socket.setEncoding("utf8");
|
|
4353
|
+
if (this.closed) {
|
|
4354
|
+
writeQueueMessage(
|
|
4355
|
+
socket,
|
|
4356
|
+
makeQueueOwnerError("unknown", "Queue owner is closed", "QUEUE_OWNER_CLOSED", {
|
|
3976
4357
|
retryable: true
|
|
3977
4358
|
})
|
|
3978
4359
|
);
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
});
|
|
3983
|
-
}
|
|
3984
|
-
async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
3985
|
-
const socket = await connectToQueueOwner(owner);
|
|
3986
|
-
if (!socket) {
|
|
3987
|
-
return void 0;
|
|
3988
|
-
}
|
|
3989
|
-
socket.setEncoding("utf8");
|
|
3990
|
-
return await new Promise((resolve, reject) => {
|
|
3991
|
-
let settled = false;
|
|
3992
|
-
let acknowledged = false;
|
|
4360
|
+
socket.end();
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
3993
4363
|
let buffer = "";
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
resolve(result);
|
|
4364
|
+
let handled = false;
|
|
4365
|
+
const fail = (requestId, message, detailCode) => {
|
|
4366
|
+
writeQueueMessage(
|
|
4367
|
+
socket,
|
|
4368
|
+
makeQueueOwnerError(requestId, message, detailCode, {
|
|
4369
|
+
retryable: false
|
|
4370
|
+
})
|
|
4371
|
+
);
|
|
4372
|
+
socket.end();
|
|
4004
4373
|
};
|
|
4005
|
-
const
|
|
4006
|
-
if (
|
|
4374
|
+
const processLine = (line) => {
|
|
4375
|
+
if (handled) {
|
|
4007
4376
|
return;
|
|
4008
4377
|
}
|
|
4009
|
-
|
|
4010
|
-
socket.removeAllListeners();
|
|
4011
|
-
if (!socket.destroyed) {
|
|
4012
|
-
socket.destroy();
|
|
4013
|
-
}
|
|
4014
|
-
reject(error);
|
|
4015
|
-
};
|
|
4016
|
-
const processLine = (line) => {
|
|
4378
|
+
handled = true;
|
|
4017
4379
|
let parsed;
|
|
4018
4380
|
try {
|
|
4019
4381
|
parsed = JSON.parse(line);
|
|
4020
4382
|
} catch {
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
retryable: true
|
|
4026
|
-
})
|
|
4383
|
+
fail(
|
|
4384
|
+
"unknown",
|
|
4385
|
+
"Invalid queue request payload",
|
|
4386
|
+
"QUEUE_REQUEST_PAYLOAD_INVALID_JSON"
|
|
4027
4387
|
);
|
|
4028
4388
|
return;
|
|
4029
4389
|
}
|
|
4030
|
-
const
|
|
4031
|
-
if (!
|
|
4032
|
-
|
|
4033
|
-
new QueueProtocolError("Queue owner sent malformed message", {
|
|
4034
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4035
|
-
origin: "queue",
|
|
4036
|
-
retryable: true
|
|
4037
|
-
})
|
|
4038
|
-
);
|
|
4390
|
+
const request = parseQueueRequest(parsed);
|
|
4391
|
+
if (!request) {
|
|
4392
|
+
fail("unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
4039
4393
|
return;
|
|
4040
4394
|
}
|
|
4041
|
-
if (
|
|
4042
|
-
|
|
4395
|
+
if (request.type === "cancel_prompt") {
|
|
4396
|
+
writeQueueMessage(socket, {
|
|
4397
|
+
type: "accepted",
|
|
4398
|
+
requestId: request.requestId
|
|
4399
|
+
});
|
|
4400
|
+
void this.controlHandlers.cancelPrompt().then((cancelled2) => {
|
|
4401
|
+
writeQueueMessage(socket, {
|
|
4402
|
+
type: "cancel_result",
|
|
4403
|
+
requestId: request.requestId,
|
|
4404
|
+
cancelled: cancelled2
|
|
4405
|
+
});
|
|
4406
|
+
}).catch((error) => {
|
|
4407
|
+
writeQueueMessage(
|
|
4408
|
+
socket,
|
|
4409
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4410
|
+
request.requestId,
|
|
4411
|
+
error,
|
|
4412
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4413
|
+
)
|
|
4414
|
+
);
|
|
4415
|
+
}).finally(() => {
|
|
4416
|
+
if (!socket.destroyed) {
|
|
4417
|
+
socket.end();
|
|
4418
|
+
}
|
|
4419
|
+
});
|
|
4043
4420
|
return;
|
|
4044
4421
|
}
|
|
4045
|
-
if (
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4422
|
+
if (request.type === "set_mode") {
|
|
4423
|
+
writeQueueMessage(socket, {
|
|
4424
|
+
type: "accepted",
|
|
4425
|
+
requestId: request.requestId
|
|
4426
|
+
});
|
|
4427
|
+
void this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs).then(() => {
|
|
4428
|
+
writeQueueMessage(socket, {
|
|
4429
|
+
type: "set_mode_result",
|
|
4430
|
+
requestId: request.requestId,
|
|
4431
|
+
modeId: request.modeId
|
|
4432
|
+
});
|
|
4433
|
+
}).catch((error) => {
|
|
4434
|
+
writeQueueMessage(
|
|
4435
|
+
socket,
|
|
4436
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4437
|
+
request.requestId,
|
|
4438
|
+
error,
|
|
4439
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4440
|
+
)
|
|
4441
|
+
);
|
|
4442
|
+
}).finally(() => {
|
|
4443
|
+
if (!socket.destroyed) {
|
|
4444
|
+
socket.end();
|
|
4445
|
+
}
|
|
4446
|
+
});
|
|
4055
4447
|
return;
|
|
4056
4448
|
}
|
|
4057
|
-
if (
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4449
|
+
if (request.type === "set_config_option") {
|
|
4450
|
+
writeQueueMessage(socket, {
|
|
4451
|
+
type: "accepted",
|
|
4452
|
+
requestId: request.requestId
|
|
4453
|
+
});
|
|
4454
|
+
void this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs).then((response) => {
|
|
4455
|
+
writeQueueMessage(socket, {
|
|
4456
|
+
type: "set_config_option_result",
|
|
4457
|
+
requestId: request.requestId,
|
|
4458
|
+
response
|
|
4459
|
+
});
|
|
4460
|
+
}).catch((error) => {
|
|
4461
|
+
writeQueueMessage(
|
|
4462
|
+
socket,
|
|
4463
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4464
|
+
request.requestId,
|
|
4465
|
+
error,
|
|
4466
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4467
|
+
)
|
|
4468
|
+
);
|
|
4469
|
+
}).finally(() => {
|
|
4470
|
+
if (!socket.destroyed) {
|
|
4471
|
+
socket.end();
|
|
4472
|
+
}
|
|
4473
|
+
});
|
|
4065
4474
|
return;
|
|
4066
4475
|
}
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4476
|
+
const task = {
|
|
4477
|
+
requestId: request.requestId,
|
|
4478
|
+
message: request.message,
|
|
4479
|
+
permissionMode: request.permissionMode,
|
|
4480
|
+
nonInteractivePermissions: request.nonInteractivePermissions,
|
|
4481
|
+
timeoutMs: request.timeoutMs,
|
|
4482
|
+
suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
|
|
4483
|
+
waitForCompletion: request.waitForCompletion,
|
|
4484
|
+
send: (message) => {
|
|
4485
|
+
writeQueueMessage(socket, message);
|
|
4486
|
+
},
|
|
4487
|
+
close: () => {
|
|
4488
|
+
if (!socket.destroyed) {
|
|
4489
|
+
socket.end();
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4493
|
+
writeQueueMessage(socket, {
|
|
4494
|
+
type: "accepted",
|
|
4495
|
+
requestId: request.requestId
|
|
4496
|
+
});
|
|
4497
|
+
if (!request.waitForCompletion) {
|
|
4498
|
+
task.close();
|
|
4076
4499
|
}
|
|
4077
|
-
|
|
4500
|
+
this.enqueue(task);
|
|
4078
4501
|
};
|
|
4079
4502
|
socket.on("data", (chunk) => {
|
|
4080
4503
|
buffer += chunk;
|
|
@@ -4088,256 +4511,264 @@ async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
|
4088
4511
|
index = buffer.indexOf("\n");
|
|
4089
4512
|
}
|
|
4090
4513
|
});
|
|
4091
|
-
socket.
|
|
4092
|
-
finishReject(error);
|
|
4093
|
-
});
|
|
4094
|
-
socket.once("close", () => {
|
|
4095
|
-
if (settled) {
|
|
4096
|
-
return;
|
|
4097
|
-
}
|
|
4098
|
-
if (!acknowledged) {
|
|
4099
|
-
finishReject(
|
|
4100
|
-
new QueueConnectionError(
|
|
4101
|
-
"Queue owner disconnected before acknowledging request",
|
|
4102
|
-
{
|
|
4103
|
-
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
4104
|
-
origin: "queue",
|
|
4105
|
-
retryable: true
|
|
4106
|
-
}
|
|
4107
|
-
)
|
|
4108
|
-
);
|
|
4109
|
-
return;
|
|
4110
|
-
}
|
|
4111
|
-
finishReject(
|
|
4112
|
-
new QueueConnectionError("Queue owner disconnected before responding", {
|
|
4113
|
-
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
4114
|
-
origin: "queue",
|
|
4115
|
-
retryable: true
|
|
4116
|
-
})
|
|
4117
|
-
);
|
|
4118
|
-
});
|
|
4119
|
-
socket.write(`${JSON.stringify(request)}
|
|
4120
|
-
`);
|
|
4121
|
-
});
|
|
4122
|
-
}
|
|
4123
|
-
async function submitCancelToQueueOwner(owner) {
|
|
4124
|
-
const request = {
|
|
4125
|
-
type: "cancel_prompt",
|
|
4126
|
-
requestId: randomUUID2()
|
|
4127
|
-
};
|
|
4128
|
-
const response = await submitControlToQueueOwner(
|
|
4129
|
-
owner,
|
|
4130
|
-
request,
|
|
4131
|
-
(message) => message.type === "cancel_result"
|
|
4132
|
-
);
|
|
4133
|
-
if (!response) {
|
|
4134
|
-
return void 0;
|
|
4135
|
-
}
|
|
4136
|
-
if (response.requestId !== request.requestId) {
|
|
4137
|
-
throw new QueueProtocolError("Queue owner returned mismatched cancel response", {
|
|
4138
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4139
|
-
origin: "queue",
|
|
4140
|
-
retryable: true
|
|
4514
|
+
socket.on("error", () => {
|
|
4141
4515
|
});
|
|
4142
4516
|
}
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
owner,
|
|
4154
|
-
request,
|
|
4155
|
-
(message) => message.type === "set_mode_result"
|
|
4156
|
-
);
|
|
4157
|
-
if (!response) {
|
|
4158
|
-
return void 0;
|
|
4517
|
+
};
|
|
4518
|
+
|
|
4519
|
+
// src/queue-owner-turn-controller.ts
|
|
4520
|
+
var QueueOwnerTurnController = class {
|
|
4521
|
+
options;
|
|
4522
|
+
state = "idle";
|
|
4523
|
+
pendingCancel = false;
|
|
4524
|
+
activeController;
|
|
4525
|
+
constructor(options) {
|
|
4526
|
+
this.options = options;
|
|
4159
4527
|
}
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4163
|
-
origin: "queue",
|
|
4164
|
-
retryable: true
|
|
4165
|
-
});
|
|
4528
|
+
get lifecycleState() {
|
|
4529
|
+
return this.state;
|
|
4166
4530
|
}
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
|
|
4170
|
-
const request = {
|
|
4171
|
-
type: "set_config_option",
|
|
4172
|
-
requestId: randomUUID2(),
|
|
4173
|
-
configId,
|
|
4174
|
-
value,
|
|
4175
|
-
timeoutMs
|
|
4176
|
-
};
|
|
4177
|
-
const response = await submitControlToQueueOwner(
|
|
4178
|
-
owner,
|
|
4179
|
-
request,
|
|
4180
|
-
(message) => message.type === "set_config_option_result"
|
|
4181
|
-
);
|
|
4182
|
-
if (!response) {
|
|
4183
|
-
return void 0;
|
|
4531
|
+
get hasPendingCancel() {
|
|
4532
|
+
return this.pendingCancel;
|
|
4184
4533
|
}
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
{
|
|
4189
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4190
|
-
origin: "queue",
|
|
4191
|
-
retryable: true
|
|
4192
|
-
}
|
|
4193
|
-
);
|
|
4534
|
+
beginTurn() {
|
|
4535
|
+
this.state = "starting";
|
|
4536
|
+
this.pendingCancel = false;
|
|
4194
4537
|
}
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
if (!owner) {
|
|
4200
|
-
return void 0;
|
|
4538
|
+
markPromptActive() {
|
|
4539
|
+
if (this.state === "starting" || this.state === "active") {
|
|
4540
|
+
this.state = "active";
|
|
4541
|
+
}
|
|
4201
4542
|
}
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4543
|
+
endTurn() {
|
|
4544
|
+
this.state = "idle";
|
|
4545
|
+
this.pendingCancel = false;
|
|
4205
4546
|
}
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
`[acpx] queued prompt on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4211
|
-
`
|
|
4212
|
-
);
|
|
4213
|
-
}
|
|
4214
|
-
return submitted;
|
|
4547
|
+
beginClosing() {
|
|
4548
|
+
this.state = "closing";
|
|
4549
|
+
this.pendingCancel = false;
|
|
4550
|
+
this.activeController = void 0;
|
|
4215
4551
|
}
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
return void 0;
|
|
4552
|
+
setActiveController(controller) {
|
|
4553
|
+
this.activeController = controller;
|
|
4219
4554
|
}
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4555
|
+
clearActiveController() {
|
|
4556
|
+
this.activeController = void 0;
|
|
4557
|
+
}
|
|
4558
|
+
assertCanHandleControlRequest() {
|
|
4559
|
+
if (this.state === "closing") {
|
|
4560
|
+
throw new QueueConnectionError("Queue owner is closing", {
|
|
4561
|
+
detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
|
|
4562
|
+
origin: "queue",
|
|
4563
|
+
retryable: true
|
|
4564
|
+
});
|
|
4226
4565
|
}
|
|
4227
|
-
);
|
|
4228
|
-
}
|
|
4229
|
-
async function tryCancelOnRunningOwner(options) {
|
|
4230
|
-
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4231
|
-
if (!owner) {
|
|
4232
|
-
return void 0;
|
|
4233
4566
|
}
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4567
|
+
async requestCancel() {
|
|
4568
|
+
const activeController = this.activeController;
|
|
4569
|
+
if (activeController?.hasActivePrompt()) {
|
|
4570
|
+
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
4571
|
+
if (cancelled2) {
|
|
4572
|
+
this.pendingCancel = false;
|
|
4573
|
+
}
|
|
4574
|
+
return cancelled2;
|
|
4575
|
+
}
|
|
4576
|
+
if (this.state === "starting" || this.state === "active") {
|
|
4577
|
+
this.pendingCancel = true;
|
|
4578
|
+
return true;
|
|
4579
|
+
}
|
|
4580
|
+
return false;
|
|
4237
4581
|
}
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
if (
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4582
|
+
async applyPendingCancel() {
|
|
4583
|
+
const activeController = this.activeController;
|
|
4584
|
+
if (!this.pendingCancel || !activeController || !activeController.hasActivePrompt()) {
|
|
4585
|
+
return false;
|
|
4586
|
+
}
|
|
4587
|
+
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
4588
|
+
if (cancelled2) {
|
|
4589
|
+
this.pendingCancel = false;
|
|
4245
4590
|
}
|
|
4246
4591
|
return cancelled2;
|
|
4247
4592
|
}
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
retryable: true
|
|
4593
|
+
async setSessionMode(modeId, timeoutMs) {
|
|
4594
|
+
this.assertCanHandleControlRequest();
|
|
4595
|
+
const activeController = this.activeController;
|
|
4596
|
+
if (activeController) {
|
|
4597
|
+
await this.options.withTimeout(
|
|
4598
|
+
async () => await activeController.setSessionMode(modeId),
|
|
4599
|
+
timeoutMs
|
|
4600
|
+
);
|
|
4601
|
+
return;
|
|
4258
4602
|
}
|
|
4259
|
-
|
|
4260
|
-
}
|
|
4261
|
-
async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
|
|
4262
|
-
const owner = await readQueueOwnerRecord(sessionId);
|
|
4263
|
-
if (!owner) {
|
|
4264
|
-
return void 0;
|
|
4265
|
-
}
|
|
4266
|
-
if (!isProcessAlive(owner.pid)) {
|
|
4267
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4268
|
-
return void 0;
|
|
4603
|
+
await this.options.setSessionModeFallback(modeId, timeoutMs);
|
|
4269
4604
|
}
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4605
|
+
async setSessionConfigOption(configId, value, timeoutMs) {
|
|
4606
|
+
this.assertCanHandleControlRequest();
|
|
4607
|
+
const activeController = this.activeController;
|
|
4608
|
+
if (activeController) {
|
|
4609
|
+
return await this.options.withTimeout(
|
|
4610
|
+
async () => await activeController.setSessionConfigOption(configId, value),
|
|
4611
|
+
timeoutMs
|
|
4276
4612
|
);
|
|
4277
4613
|
}
|
|
4278
|
-
return
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4614
|
+
return await this.options.setSessionConfigOptionFallback(
|
|
4615
|
+
configId,
|
|
4616
|
+
value,
|
|
4617
|
+
timeoutMs
|
|
4618
|
+
);
|
|
4283
4619
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
}
|
|
4293
|
-
async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
|
|
4294
|
-
const owner = await readQueueOwnerRecord(sessionId);
|
|
4295
|
-
if (!owner) {
|
|
4296
|
-
return void 0;
|
|
4620
|
+
};
|
|
4621
|
+
|
|
4622
|
+
// src/session-owner-runtime.ts
|
|
4623
|
+
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4624
|
+
var QUEUE_OWNER_HEARTBEAT_INTERVAL_MS = 2e3;
|
|
4625
|
+
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
4626
|
+
if (ttlMs == null) {
|
|
4627
|
+
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4297
4628
|
}
|
|
4298
|
-
if (!
|
|
4299
|
-
|
|
4300
|
-
return void 0;
|
|
4629
|
+
if (!Number.isFinite(ttlMs) || ttlMs < 0) {
|
|
4630
|
+
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4301
4631
|
}
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
timeoutMs
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4632
|
+
return Math.round(ttlMs);
|
|
4633
|
+
}
|
|
4634
|
+
function createQueueOwnerTurnRuntime(options, deps) {
|
|
4635
|
+
const turnController = new QueueOwnerTurnController({
|
|
4636
|
+
withTimeout: async (run, timeoutMs) => await deps.withTimeout(run, timeoutMs),
|
|
4637
|
+
setSessionModeFallback: deps.setSessionModeFallback,
|
|
4638
|
+
setSessionConfigOptionFallback: deps.setSessionConfigOptionFallback
|
|
4639
|
+
});
|
|
4640
|
+
const applyPendingCancel = async () => {
|
|
4641
|
+
return await turnController.applyPendingCancel();
|
|
4642
|
+
};
|
|
4643
|
+
const scheduleApplyPendingCancel = () => {
|
|
4644
|
+
void applyPendingCancel().catch((error) => {
|
|
4645
|
+
if (options.verbose) {
|
|
4646
|
+
process.stderr.write(
|
|
4647
|
+
`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}
|
|
4648
|
+
`
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
4651
|
+
});
|
|
4652
|
+
};
|
|
4653
|
+
return {
|
|
4654
|
+
beginClosing: () => {
|
|
4655
|
+
turnController.beginClosing();
|
|
4656
|
+
},
|
|
4657
|
+
onClientAvailable: (controller) => {
|
|
4658
|
+
turnController.setActiveController(controller);
|
|
4659
|
+
scheduleApplyPendingCancel();
|
|
4660
|
+
},
|
|
4661
|
+
onClientClosed: () => {
|
|
4662
|
+
turnController.clearActiveController();
|
|
4663
|
+
},
|
|
4664
|
+
onPromptActive: async () => {
|
|
4665
|
+
turnController.markPromptActive();
|
|
4666
|
+
await applyPendingCancel();
|
|
4667
|
+
},
|
|
4668
|
+
runPromptTurn: async (run) => {
|
|
4669
|
+
turnController.beginTurn();
|
|
4670
|
+
try {
|
|
4671
|
+
return await run();
|
|
4672
|
+
} finally {
|
|
4673
|
+
turnController.endTurn();
|
|
4674
|
+
}
|
|
4675
|
+
},
|
|
4676
|
+
controlHandlers: {
|
|
4677
|
+
cancelPrompt: async () => {
|
|
4678
|
+
const accepted = await turnController.requestCancel();
|
|
4679
|
+
if (!accepted) {
|
|
4680
|
+
return false;
|
|
4681
|
+
}
|
|
4682
|
+
await applyPendingCancel();
|
|
4683
|
+
return true;
|
|
4684
|
+
},
|
|
4685
|
+
setSessionMode: async (modeId, timeoutMs) => {
|
|
4686
|
+
await turnController.setSessionMode(modeId, timeoutMs);
|
|
4687
|
+
},
|
|
4688
|
+
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
4689
|
+
return await turnController.setSessionConfigOption(configId, value, timeoutMs);
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
async function runQueueOwnerProcess(options, deps) {
|
|
4695
|
+
const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
|
|
4696
|
+
const lease = await tryAcquireQueueOwnerLease(options.sessionId);
|
|
4697
|
+
if (!lease) {
|
|
4698
|
+
if (options.verbose) {
|
|
4310
4699
|
process.stderr.write(
|
|
4311
|
-
`[acpx]
|
|
4700
|
+
`[acpx] queue owner already active for session ${options.sessionId}; skipping spawn
|
|
4312
4701
|
`
|
|
4313
4702
|
);
|
|
4314
4703
|
}
|
|
4315
|
-
return response;
|
|
4316
|
-
}
|
|
4317
|
-
if (!isProcessAlive(owner.pid)) {
|
|
4318
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4319
|
-
return void 0;
|
|
4320
|
-
}
|
|
4321
|
-
throw new QueueConnectionError(
|
|
4322
|
-
"Session queue owner is running but not accepting set_config_option requests",
|
|
4323
|
-
{
|
|
4324
|
-
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4325
|
-
origin: "queue",
|
|
4326
|
-
retryable: true
|
|
4327
|
-
}
|
|
4328
|
-
);
|
|
4329
|
-
}
|
|
4330
|
-
async function terminateQueueOwnerForSession(sessionId) {
|
|
4331
|
-
const owner = await readQueueOwnerRecord(sessionId);
|
|
4332
|
-
if (!owner) {
|
|
4333
4704
|
return;
|
|
4334
4705
|
}
|
|
4335
|
-
|
|
4336
|
-
|
|
4706
|
+
const runtime = createQueueOwnerTurnRuntime(options, deps);
|
|
4707
|
+
let owner;
|
|
4708
|
+
let heartbeatTimer;
|
|
4709
|
+
const refreshHeartbeat = async () => {
|
|
4710
|
+
if (!owner) {
|
|
4711
|
+
return;
|
|
4712
|
+
}
|
|
4713
|
+
await refreshQueueOwnerLease(lease, {
|
|
4714
|
+
queueDepth: owner.queueDepth()
|
|
4715
|
+
}).catch((error) => {
|
|
4716
|
+
if (options.verbose) {
|
|
4717
|
+
process.stderr.write(
|
|
4718
|
+
`[acpx] queue owner heartbeat update failed: ${formatErrorMessage(error)}
|
|
4719
|
+
`
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4722
|
+
});
|
|
4723
|
+
};
|
|
4724
|
+
try {
|
|
4725
|
+
owner = await SessionQueueOwner.start(lease, runtime.controlHandlers);
|
|
4726
|
+
await refreshHeartbeat();
|
|
4727
|
+
heartbeatTimer = setInterval(() => {
|
|
4728
|
+
void refreshHeartbeat();
|
|
4729
|
+
}, QUEUE_OWNER_HEARTBEAT_INTERVAL_MS);
|
|
4730
|
+
heartbeatTimer.unref();
|
|
4731
|
+
const idleWaitMs = queueOwnerTtlMs === 0 ? void 0 : Math.max(0, queueOwnerTtlMs);
|
|
4732
|
+
while (true) {
|
|
4733
|
+
const task = await owner.nextTask(idleWaitMs);
|
|
4734
|
+
if (!task) {
|
|
4735
|
+
if (queueOwnerTtlMs > 0 && options.verbose) {
|
|
4736
|
+
process.stderr.write(
|
|
4737
|
+
`[acpx] queue owner TTL expired after ${Math.round(queueOwnerTtlMs / 1e3)}s for session ${options.sessionId}; shutting down
|
|
4738
|
+
`
|
|
4739
|
+
);
|
|
4740
|
+
}
|
|
4741
|
+
break;
|
|
4742
|
+
}
|
|
4743
|
+
await runtime.runPromptTurn(async () => {
|
|
4744
|
+
await deps.runQueuedTask(options.sessionId, task, {
|
|
4745
|
+
verbose: options.verbose,
|
|
4746
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
4747
|
+
authCredentials: options.authCredentials,
|
|
4748
|
+
authPolicy: options.authPolicy,
|
|
4749
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
4750
|
+
onClientAvailable: runtime.onClientAvailable,
|
|
4751
|
+
onClientClosed: runtime.onClientClosed,
|
|
4752
|
+
onPromptActive: runtime.onPromptActive
|
|
4753
|
+
});
|
|
4754
|
+
});
|
|
4755
|
+
await refreshHeartbeat();
|
|
4756
|
+
}
|
|
4757
|
+
} finally {
|
|
4758
|
+
if (heartbeatTimer) {
|
|
4759
|
+
clearInterval(heartbeatTimer);
|
|
4760
|
+
}
|
|
4761
|
+
runtime.beginClosing();
|
|
4762
|
+
if (owner) {
|
|
4763
|
+
await owner.close();
|
|
4764
|
+
}
|
|
4765
|
+
await releaseQueueOwnerLease(lease);
|
|
4337
4766
|
}
|
|
4338
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4339
4767
|
}
|
|
4340
4768
|
|
|
4769
|
+
// src/session-owner-spawn.ts
|
|
4770
|
+
import { spawn as spawn3 } from "child_process";
|
|
4771
|
+
|
|
4341
4772
|
// src/session-persistence.ts
|
|
4342
4773
|
import { statSync } from "fs";
|
|
4343
4774
|
import fs4 from "fs/promises";
|
|
@@ -4586,6 +5017,124 @@ async function findSessionByDirectoryWalk(options) {
|
|
|
4586
5017
|
}
|
|
4587
5018
|
}
|
|
4588
5019
|
|
|
5020
|
+
// src/session-owner-spawn.ts
|
|
5021
|
+
var QUEUE_OWNER_STARTUP_TIMEOUT_MS = 1e4;
|
|
5022
|
+
var QUEUE_OWNER_RESPAWN_BACKOFF_MS = 250;
|
|
5023
|
+
function isQueueNotAcceptingError(error) {
|
|
5024
|
+
return error instanceof QueueConnectionError && error.detailCode === "QUEUE_NOT_ACCEPTING_REQUESTS";
|
|
5025
|
+
}
|
|
5026
|
+
function spawnDetachedQueueOwner(ownerSpawn) {
|
|
5027
|
+
const child = spawn3(ownerSpawn.command, ownerSpawn.args, {
|
|
5028
|
+
cwd: ownerSpawn.cwd,
|
|
5029
|
+
env: ownerSpawn.env,
|
|
5030
|
+
stdio: "ignore",
|
|
5031
|
+
detached: true
|
|
5032
|
+
});
|
|
5033
|
+
child.unref();
|
|
5034
|
+
}
|
|
5035
|
+
async function buildDefaultQueueOwnerSpawn(options, queueOwnerTtlMs) {
|
|
5036
|
+
const entrypoint = process.argv[1];
|
|
5037
|
+
if (!entrypoint) {
|
|
5038
|
+
throw new Error("Cannot spawn queue owner process: CLI entrypoint is missing");
|
|
5039
|
+
}
|
|
5040
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5041
|
+
const args = [
|
|
5042
|
+
entrypoint,
|
|
5043
|
+
"__queue-owner",
|
|
5044
|
+
"--session-id",
|
|
5045
|
+
options.sessionId,
|
|
5046
|
+
"--ttl-ms",
|
|
5047
|
+
String(queueOwnerTtlMs),
|
|
5048
|
+
"--permission-mode",
|
|
5049
|
+
options.permissionMode
|
|
5050
|
+
];
|
|
5051
|
+
if (options.nonInteractivePermissions) {
|
|
5052
|
+
args.push("--non-interactive-permissions", options.nonInteractivePermissions);
|
|
5053
|
+
}
|
|
5054
|
+
if (options.authPolicy) {
|
|
5055
|
+
args.push("--auth-policy", options.authPolicy);
|
|
5056
|
+
}
|
|
5057
|
+
if (options.timeoutMs != null && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
5058
|
+
args.push("--timeout-ms", String(Math.round(options.timeoutMs)));
|
|
5059
|
+
}
|
|
5060
|
+
if (options.verbose) {
|
|
5061
|
+
args.push("--verbose");
|
|
5062
|
+
}
|
|
5063
|
+
if (options.suppressSdkConsoleErrors) {
|
|
5064
|
+
args.push("--suppress-sdk-console-errors");
|
|
5065
|
+
}
|
|
5066
|
+
return {
|
|
5067
|
+
command: process.execPath,
|
|
5068
|
+
args,
|
|
5069
|
+
cwd: absolutePath(record.cwd)
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
async function sendViaDetachedQueueOwner(options) {
|
|
5073
|
+
const waitForCompletion = options.waitForCompletion !== false;
|
|
5074
|
+
const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
|
|
5075
|
+
const ownerSpawn = options.queueOwnerSpawn ?? await buildDefaultQueueOwnerSpawn(options, queueOwnerTtlMs);
|
|
5076
|
+
const startupDeadline = Date.now() + QUEUE_OWNER_STARTUP_TIMEOUT_MS;
|
|
5077
|
+
let lastSpawnAttemptAt = 0;
|
|
5078
|
+
for (; ; ) {
|
|
5079
|
+
try {
|
|
5080
|
+
const queuedToOwner = await trySubmitToRunningOwner({
|
|
5081
|
+
sessionId: options.sessionId,
|
|
5082
|
+
message: options.message,
|
|
5083
|
+
permissionMode: options.permissionMode,
|
|
5084
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5085
|
+
outputFormatter: options.outputFormatter,
|
|
5086
|
+
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
5087
|
+
timeoutMs: options.timeoutMs,
|
|
5088
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5089
|
+
waitForCompletion,
|
|
5090
|
+
verbose: options.verbose
|
|
5091
|
+
});
|
|
5092
|
+
if (queuedToOwner) {
|
|
5093
|
+
return queuedToOwner;
|
|
5094
|
+
}
|
|
5095
|
+
} catch (error) {
|
|
5096
|
+
if (!isQueueNotAcceptingError(error)) {
|
|
5097
|
+
throw error;
|
|
5098
|
+
}
|
|
5099
|
+
if (Date.now() >= startupDeadline) {
|
|
5100
|
+
throw new QueueConnectionError(
|
|
5101
|
+
"Timed out waiting for detached queue owner to accept prompt requests",
|
|
5102
|
+
{
|
|
5103
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
5104
|
+
origin: "queue",
|
|
5105
|
+
retryable: true,
|
|
5106
|
+
cause: error instanceof Error ? error : void 0
|
|
5107
|
+
}
|
|
5108
|
+
);
|
|
5109
|
+
}
|
|
5110
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
5111
|
+
continue;
|
|
5112
|
+
}
|
|
5113
|
+
const now = Date.now();
|
|
5114
|
+
if (now >= startupDeadline) {
|
|
5115
|
+
throw new QueueConnectionError(
|
|
5116
|
+
"Timed out waiting for detached queue owner to start",
|
|
5117
|
+
{
|
|
5118
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
5119
|
+
origin: "queue",
|
|
5120
|
+
retryable: true
|
|
5121
|
+
}
|
|
5122
|
+
);
|
|
5123
|
+
}
|
|
5124
|
+
if (now - lastSpawnAttemptAt >= QUEUE_OWNER_RESPAWN_BACKOFF_MS) {
|
|
5125
|
+
spawnDetachedQueueOwner(ownerSpawn);
|
|
5126
|
+
lastSpawnAttemptAt = now;
|
|
5127
|
+
if (options.verbose) {
|
|
5128
|
+
process.stderr.write(
|
|
5129
|
+
`[acpx] starting detached queue owner for session ${options.sessionId}
|
|
5130
|
+
`
|
|
5131
|
+
);
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5134
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
|
|
4589
5138
|
// src/session-runtime-history.ts
|
|
4590
5139
|
var SESSION_HISTORY_MAX_ENTRIES = 500;
|
|
4591
5140
|
var SESSION_HISTORY_PREVIEW_CHARS = 220;
|
|
@@ -4774,7 +5323,6 @@ async function connectAndLoadSession(options) {
|
|
|
4774
5323
|
}
|
|
4775
5324
|
|
|
4776
5325
|
// src/session-runtime.ts
|
|
4777
|
-
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4778
5326
|
var INTERRUPT_CANCEL_WAIT_MS = 2500;
|
|
4779
5327
|
var TimeoutError = class extends Error {
|
|
4780
5328
|
constructor(timeoutMs) {
|
|
@@ -4902,15 +5450,6 @@ var DISCARD_OUTPUT_FORMATTER = {
|
|
|
4902
5450
|
flush() {
|
|
4903
5451
|
}
|
|
4904
5452
|
};
|
|
4905
|
-
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
4906
|
-
if (ttlMs == null) {
|
|
4907
|
-
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4908
|
-
}
|
|
4909
|
-
if (!Number.isFinite(ttlMs) || ttlMs < 0) {
|
|
4910
|
-
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4911
|
-
}
|
|
4912
|
-
return Math.round(ttlMs);
|
|
4913
|
-
}
|
|
4914
5453
|
function shouldFallbackToNewSession(error) {
|
|
4915
5454
|
if (error instanceof TimeoutError || error instanceof InterruptedError) {
|
|
4916
5455
|
return false;
|
|
@@ -5376,179 +5915,41 @@ async function ensureSession(options) {
|
|
|
5376
5915
|
created: true
|
|
5377
5916
|
};
|
|
5378
5917
|
}
|
|
5379
|
-
async function
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
outputFormatter: options.outputFormatter,
|
|
5388
|
-
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
5389
|
-
timeoutMs: options.timeoutMs,
|
|
5390
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5391
|
-
waitForCompletion,
|
|
5392
|
-
verbose: options.verbose
|
|
5393
|
-
});
|
|
5394
|
-
if (queuedToOwner) {
|
|
5395
|
-
return queuedToOwner;
|
|
5396
|
-
}
|
|
5397
|
-
for (; ; ) {
|
|
5398
|
-
const lease = await tryAcquireQueueOwnerLease(options.sessionId);
|
|
5399
|
-
if (!lease) {
|
|
5400
|
-
const retryQueued = await trySubmitToRunningOwner({
|
|
5401
|
-
sessionId: options.sessionId,
|
|
5402
|
-
message: options.message,
|
|
5403
|
-
permissionMode: options.permissionMode,
|
|
5918
|
+
async function runQueueOwnerProcess2(options) {
|
|
5919
|
+
await runQueueOwnerProcess(options, {
|
|
5920
|
+
runQueuedTask,
|
|
5921
|
+
withTimeout: async (run, timeoutMs) => await withTimeout(run(), timeoutMs),
|
|
5922
|
+
setSessionModeFallback: async (modeId, timeoutMs) => {
|
|
5923
|
+
await runSessionSetModeDirect({
|
|
5924
|
+
sessionRecordId: options.sessionId,
|
|
5925
|
+
modeId,
|
|
5404
5926
|
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
timeoutMs
|
|
5408
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5409
|
-
waitForCompletion,
|
|
5927
|
+
authCredentials: options.authCredentials,
|
|
5928
|
+
authPolicy: options.authPolicy,
|
|
5929
|
+
timeoutMs,
|
|
5410
5930
|
verbose: options.verbose
|
|
5411
5931
|
});
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
sessionRecordId: options.sessionId,
|
|
5424
|
-
modeId,
|
|
5425
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5426
|
-
authCredentials: options.authCredentials,
|
|
5427
|
-
authPolicy: options.authPolicy,
|
|
5428
|
-
timeoutMs,
|
|
5429
|
-
verbose: options.verbose
|
|
5430
|
-
});
|
|
5431
|
-
},
|
|
5432
|
-
setSessionConfigOptionFallback: async (configId, value, timeoutMs) => {
|
|
5433
|
-
const result = await runSessionSetConfigOptionDirect({
|
|
5434
|
-
sessionRecordId: options.sessionId,
|
|
5435
|
-
configId,
|
|
5436
|
-
value,
|
|
5437
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5438
|
-
authCredentials: options.authCredentials,
|
|
5439
|
-
authPolicy: options.authPolicy,
|
|
5440
|
-
timeoutMs,
|
|
5441
|
-
verbose: options.verbose
|
|
5442
|
-
});
|
|
5443
|
-
return result.response;
|
|
5444
|
-
}
|
|
5445
|
-
});
|
|
5446
|
-
const applyPendingCancel = async () => {
|
|
5447
|
-
return await turnController.applyPendingCancel();
|
|
5448
|
-
};
|
|
5449
|
-
const scheduleApplyPendingCancel = () => {
|
|
5450
|
-
void applyPendingCancel().catch((error) => {
|
|
5451
|
-
if (options.verbose) {
|
|
5452
|
-
process.stderr.write(
|
|
5453
|
-
`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}
|
|
5454
|
-
`
|
|
5455
|
-
);
|
|
5456
|
-
}
|
|
5457
|
-
});
|
|
5458
|
-
};
|
|
5459
|
-
const setActiveController = (controller) => {
|
|
5460
|
-
turnController.setActiveController(controller);
|
|
5461
|
-
scheduleApplyPendingCancel();
|
|
5462
|
-
};
|
|
5463
|
-
const clearActiveController = () => {
|
|
5464
|
-
turnController.clearActiveController();
|
|
5465
|
-
};
|
|
5466
|
-
const runPromptTurn = async (run) => {
|
|
5467
|
-
turnController.beginTurn();
|
|
5468
|
-
try {
|
|
5469
|
-
return await run();
|
|
5470
|
-
} finally {
|
|
5471
|
-
turnController.endTurn();
|
|
5472
|
-
}
|
|
5473
|
-
};
|
|
5474
|
-
try {
|
|
5475
|
-
owner = await SessionQueueOwner.start(lease, {
|
|
5476
|
-
cancelPrompt: async () => {
|
|
5477
|
-
const accepted = await turnController.requestCancel();
|
|
5478
|
-
if (!accepted) {
|
|
5479
|
-
return false;
|
|
5480
|
-
}
|
|
5481
|
-
await applyPendingCancel();
|
|
5482
|
-
return true;
|
|
5483
|
-
},
|
|
5484
|
-
setSessionMode: async (modeId, timeoutMs) => {
|
|
5485
|
-
await turnController.setSessionMode(modeId, timeoutMs);
|
|
5486
|
-
},
|
|
5487
|
-
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
5488
|
-
return await turnController.setSessionConfigOption(
|
|
5489
|
-
configId,
|
|
5490
|
-
value,
|
|
5491
|
-
timeoutMs
|
|
5492
|
-
);
|
|
5493
|
-
}
|
|
5494
|
-
});
|
|
5495
|
-
const localResult = await runPromptTurn(async () => {
|
|
5496
|
-
return await runSessionPrompt({
|
|
5497
|
-
sessionRecordId: options.sessionId,
|
|
5498
|
-
message: options.message,
|
|
5499
|
-
permissionMode: options.permissionMode,
|
|
5500
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5501
|
-
authCredentials: options.authCredentials,
|
|
5502
|
-
authPolicy: options.authPolicy,
|
|
5503
|
-
outputFormatter: options.outputFormatter,
|
|
5504
|
-
timeoutMs: options.timeoutMs,
|
|
5505
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5506
|
-
verbose: options.verbose,
|
|
5507
|
-
onClientAvailable: setActiveController,
|
|
5508
|
-
onClientClosed: clearActiveController,
|
|
5509
|
-
onPromptActive: async () => {
|
|
5510
|
-
turnController.markPromptActive();
|
|
5511
|
-
await applyPendingCancel();
|
|
5512
|
-
}
|
|
5513
|
-
});
|
|
5932
|
+
},
|
|
5933
|
+
setSessionConfigOptionFallback: async (configId, value, timeoutMs) => {
|
|
5934
|
+
const result = await runSessionSetConfigOptionDirect({
|
|
5935
|
+
sessionRecordId: options.sessionId,
|
|
5936
|
+
configId,
|
|
5937
|
+
value,
|
|
5938
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5939
|
+
authCredentials: options.authCredentials,
|
|
5940
|
+
authPolicy: options.authPolicy,
|
|
5941
|
+
timeoutMs,
|
|
5942
|
+
verbose: options.verbose
|
|
5514
5943
|
});
|
|
5515
|
-
|
|
5516
|
-
while (true) {
|
|
5517
|
-
const task = await owner.nextTask(idleWaitMs);
|
|
5518
|
-
if (!task) {
|
|
5519
|
-
if (queueOwnerTtlMs > 0 && options.verbose) {
|
|
5520
|
-
process.stderr.write(
|
|
5521
|
-
`[acpx] queue owner TTL expired after ${Math.round(queueOwnerTtlMs / 1e3)}s for session ${options.sessionId}; shutting down
|
|
5522
|
-
`
|
|
5523
|
-
);
|
|
5524
|
-
}
|
|
5525
|
-
break;
|
|
5526
|
-
}
|
|
5527
|
-
await runPromptTurn(async () => {
|
|
5528
|
-
await runQueuedTask(options.sessionId, task, {
|
|
5529
|
-
verbose: options.verbose,
|
|
5530
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5531
|
-
authCredentials: options.authCredentials,
|
|
5532
|
-
authPolicy: options.authPolicy,
|
|
5533
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5534
|
-
onClientAvailable: setActiveController,
|
|
5535
|
-
onClientClosed: clearActiveController,
|
|
5536
|
-
onPromptActive: async () => {
|
|
5537
|
-
turnController.markPromptActive();
|
|
5538
|
-
await applyPendingCancel();
|
|
5539
|
-
}
|
|
5540
|
-
});
|
|
5541
|
-
});
|
|
5542
|
-
}
|
|
5543
|
-
return localResult;
|
|
5544
|
-
} finally {
|
|
5545
|
-
turnController.beginClosing();
|
|
5546
|
-
if (owner) {
|
|
5547
|
-
await owner.close();
|
|
5548
|
-
}
|
|
5549
|
-
await releaseQueueOwnerLease(lease);
|
|
5944
|
+
return result.response;
|
|
5550
5945
|
}
|
|
5551
|
-
}
|
|
5946
|
+
});
|
|
5947
|
+
}
|
|
5948
|
+
async function sendSession(options) {
|
|
5949
|
+
return await sendViaDetachedQueueOwner(options);
|
|
5950
|
+
}
|
|
5951
|
+
async function readSessionQueueOwnerStatus(sessionId) {
|
|
5952
|
+
return await readQueueOwnerStatus(sessionId);
|
|
5552
5953
|
}
|
|
5553
5954
|
async function cancelSessionPrompt(options) {
|
|
5554
5955
|
const cancelled2 = await tryCancelOnRunningOwner(options);
|
|
@@ -5654,6 +6055,7 @@ var NoSessionError = class extends Error {
|
|
|
5654
6055
|
}
|
|
5655
6056
|
};
|
|
5656
6057
|
var TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
|
|
6058
|
+
"__queue-owner",
|
|
5657
6059
|
"prompt",
|
|
5658
6060
|
"exec",
|
|
5659
6061
|
"cancel",
|
|
@@ -5666,25 +6068,25 @@ var TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
|
|
|
5666
6068
|
]);
|
|
5667
6069
|
function parseOutputFormat2(value) {
|
|
5668
6070
|
if (!OUTPUT_FORMATS.includes(value)) {
|
|
5669
|
-
throw new
|
|
6071
|
+
throw new InvalidArgumentError3(
|
|
5670
6072
|
`Invalid format "${value}". Expected one of: ${OUTPUT_FORMATS.join(", ")}`
|
|
5671
6073
|
);
|
|
5672
6074
|
}
|
|
5673
6075
|
return value;
|
|
5674
6076
|
}
|
|
5675
|
-
function
|
|
6077
|
+
function parseAuthPolicy3(value) {
|
|
5676
6078
|
if (!AUTH_POLICIES.includes(value)) {
|
|
5677
|
-
throw new
|
|
6079
|
+
throw new InvalidArgumentError3(
|
|
5678
6080
|
`Invalid auth policy "${value}". Expected one of: ${AUTH_POLICIES.join(", ")}`
|
|
5679
6081
|
);
|
|
5680
6082
|
}
|
|
5681
6083
|
return value;
|
|
5682
6084
|
}
|
|
5683
|
-
function
|
|
6085
|
+
function parseNonInteractivePermissionPolicy3(value) {
|
|
5684
6086
|
if (!NON_INTERACTIVE_PERMISSION_POLICIES.includes(
|
|
5685
6087
|
value
|
|
5686
6088
|
)) {
|
|
5687
|
-
throw new
|
|
6089
|
+
throw new InvalidArgumentError3(
|
|
5688
6090
|
`Invalid non-interactive permission policy "${value}". Expected one of: ${NON_INTERACTIVE_PERMISSION_POLICIES.join(", ")}`
|
|
5689
6091
|
);
|
|
5690
6092
|
}
|
|
@@ -5693,35 +6095,35 @@ function parseNonInteractivePermissionPolicy2(value) {
|
|
|
5693
6095
|
function parseTimeoutSeconds(value) {
|
|
5694
6096
|
const parsed = Number(value);
|
|
5695
6097
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
5696
|
-
throw new
|
|
6098
|
+
throw new InvalidArgumentError3("Timeout must be a positive number of seconds");
|
|
5697
6099
|
}
|
|
5698
6100
|
return Math.round(parsed * 1e3);
|
|
5699
6101
|
}
|
|
5700
6102
|
function parseTtlSeconds(value) {
|
|
5701
6103
|
const parsed = Number(value);
|
|
5702
6104
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
5703
|
-
throw new
|
|
6105
|
+
throw new InvalidArgumentError3("TTL must be a non-negative number of seconds");
|
|
5704
6106
|
}
|
|
5705
6107
|
return Math.round(parsed * 1e3);
|
|
5706
6108
|
}
|
|
5707
6109
|
function parseSessionName(value) {
|
|
5708
6110
|
const trimmed = value.trim();
|
|
5709
6111
|
if (trimmed.length === 0) {
|
|
5710
|
-
throw new
|
|
6112
|
+
throw new InvalidArgumentError3("Session name must not be empty");
|
|
5711
6113
|
}
|
|
5712
6114
|
return trimmed;
|
|
5713
6115
|
}
|
|
5714
|
-
function
|
|
6116
|
+
function parseNonEmptyValue2(label, value) {
|
|
5715
6117
|
const trimmed = value.trim();
|
|
5716
6118
|
if (trimmed.length === 0) {
|
|
5717
|
-
throw new
|
|
6119
|
+
throw new InvalidArgumentError3(`${label} must not be empty`);
|
|
5718
6120
|
}
|
|
5719
6121
|
return trimmed;
|
|
5720
6122
|
}
|
|
5721
6123
|
function parseHistoryLimit(value) {
|
|
5722
6124
|
const parsed = Number(value);
|
|
5723
6125
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
5724
|
-
throw new
|
|
6126
|
+
throw new InvalidArgumentError3("Limit must be a positive integer");
|
|
5725
6127
|
}
|
|
5726
6128
|
return parsed;
|
|
5727
6129
|
}
|
|
@@ -5730,7 +6132,7 @@ function resolvePermissionMode(flags, defaultMode) {
|
|
|
5730
6132
|
Boolean
|
|
5731
6133
|
).length;
|
|
5732
6134
|
if (selected2 > 1) {
|
|
5733
|
-
throw new
|
|
6135
|
+
throw new InvalidArgumentError3(
|
|
5734
6136
|
"Use only one permission mode: --approve-all, --approve-reads, or --deny-all"
|
|
5735
6137
|
);
|
|
5736
6138
|
}
|
|
@@ -5757,7 +6159,7 @@ async function readPrompt(promptParts, filePath, cwd) {
|
|
|
5757
6159
|
);
|
|
5758
6160
|
const prompt2 = pieces.join("\n\n").trim();
|
|
5759
6161
|
if (!prompt2) {
|
|
5760
|
-
throw new
|
|
6162
|
+
throw new InvalidArgumentError3("Prompt from --file is empty");
|
|
5761
6163
|
}
|
|
5762
6164
|
return prompt2;
|
|
5763
6165
|
}
|
|
@@ -5766,13 +6168,13 @@ async function readPrompt(promptParts, filePath, cwd) {
|
|
|
5766
6168
|
return joined;
|
|
5767
6169
|
}
|
|
5768
6170
|
if (process.stdin.isTTY) {
|
|
5769
|
-
throw new
|
|
6171
|
+
throw new InvalidArgumentError3(
|
|
5770
6172
|
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
5771
6173
|
);
|
|
5772
6174
|
}
|
|
5773
6175
|
const prompt = (await readPromptInputFromStdin()).trim();
|
|
5774
6176
|
if (!prompt) {
|
|
5775
|
-
throw new
|
|
6177
|
+
throw new InvalidArgumentError3("Prompt from stdin is empty");
|
|
5776
6178
|
}
|
|
5777
6179
|
return prompt;
|
|
5778
6180
|
}
|
|
@@ -5787,14 +6189,14 @@ function addGlobalFlags(command) {
|
|
|
5787
6189
|
return command.option("--agent <command>", "Raw ACP agent command (escape hatch)").option("--cwd <dir>", "Working directory", process.cwd()).option(
|
|
5788
6190
|
"--auth-policy <policy>",
|
|
5789
6191
|
"Authentication policy: skip or fail when auth is required",
|
|
5790
|
-
|
|
6192
|
+
parseAuthPolicy3
|
|
5791
6193
|
).option("--approve-all", "Auto-approve all permission requests").option(
|
|
5792
6194
|
"--approve-reads",
|
|
5793
6195
|
"Auto-approve read/search requests and prompt for writes"
|
|
5794
6196
|
).option("--deny-all", "Deny all permission requests").option(
|
|
5795
6197
|
"--non-interactive-permissions <policy>",
|
|
5796
6198
|
"When prompting is unavailable: deny or fail",
|
|
5797
|
-
|
|
6199
|
+
parseNonInteractivePermissionPolicy3
|
|
5798
6200
|
).option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat2).option(
|
|
5799
6201
|
"--json-strict",
|
|
5800
6202
|
"Strict JSON mode: requires --format json and suppresses non-JSON stderr output"
|
|
@@ -5851,10 +6253,10 @@ function resolveGlobalFlags(command, config) {
|
|
|
5851
6253
|
const jsonStrict = opts.jsonStrict === true;
|
|
5852
6254
|
const verbose = opts.verbose === true;
|
|
5853
6255
|
if (jsonStrict && format !== "json") {
|
|
5854
|
-
throw new
|
|
6256
|
+
throw new InvalidArgumentError3("--json-strict requires --format json");
|
|
5855
6257
|
}
|
|
5856
6258
|
if (jsonStrict && verbose) {
|
|
5857
|
-
throw new
|
|
6259
|
+
throw new InvalidArgumentError3("--json-strict cannot be combined with --verbose");
|
|
5858
6260
|
}
|
|
5859
6261
|
return {
|
|
5860
6262
|
agent: opts.agent,
|
|
@@ -5883,7 +6285,7 @@ function resolveOutputPolicy(format, jsonStrict) {
|
|
|
5883
6285
|
function resolveAgentInvocation(explicitAgentName, globalFlags, config) {
|
|
5884
6286
|
const override = globalFlags.agent?.trim();
|
|
5885
6287
|
if (override && explicitAgentName) {
|
|
5886
|
-
throw new
|
|
6288
|
+
throw new InvalidArgumentError3(
|
|
5887
6289
|
"Do not combine positional agent with --agent override"
|
|
5888
6290
|
);
|
|
5889
6291
|
}
|
|
@@ -6561,7 +6963,12 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6561
6963
|
uptime: null,
|
|
6562
6964
|
lastPromptTime: null,
|
|
6563
6965
|
exitCode: null,
|
|
6564
|
-
signal: null
|
|
6966
|
+
signal: null,
|
|
6967
|
+
ownerPid: null,
|
|
6968
|
+
ownerStatus: null,
|
|
6969
|
+
ownerGeneration: null,
|
|
6970
|
+
ownerHeartbeatAt: null,
|
|
6971
|
+
ownerQueueDepth: null
|
|
6565
6972
|
};
|
|
6566
6973
|
if (globalFlags.format === "json") {
|
|
6567
6974
|
process.stdout.write(`${JSON.stringify(payload2)}
|
|
@@ -6583,10 +6990,14 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6583
6990
|
process.stdout.write(`uptime: -
|
|
6584
6991
|
`);
|
|
6585
6992
|
process.stdout.write(`lastPromptTime: -
|
|
6993
|
+
`);
|
|
6994
|
+
process.stdout.write(`ownerStatus: -
|
|
6586
6995
|
`);
|
|
6587
6996
|
return;
|
|
6588
6997
|
}
|
|
6589
6998
|
const running = isProcessAlive(record.pid);
|
|
6999
|
+
const owner = await readSessionQueueOwnerStatus(record.id);
|
|
7000
|
+
const ownerStatus = owner ? owner.stale ? "stale" : "active" : "inactive";
|
|
6590
7001
|
const payload = {
|
|
6591
7002
|
...canonicalSessionIdentity(record),
|
|
6592
7003
|
agentCommand: record.agentCommand,
|
|
@@ -6595,7 +7006,12 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6595
7006
|
uptime: running ? formatUptime(record.agentStartedAt) ?? null : null,
|
|
6596
7007
|
lastPromptTime: record.lastPromptAt ?? null,
|
|
6597
7008
|
exitCode: running ? null : record.lastAgentExitCode ?? null,
|
|
6598
|
-
signal: running ? null : record.lastAgentExitSignal ?? null
|
|
7009
|
+
signal: running ? null : record.lastAgentExitSignal ?? null,
|
|
7010
|
+
ownerPid: owner?.pid ?? null,
|
|
7011
|
+
ownerStatus,
|
|
7012
|
+
ownerGeneration: owner?.ownerGeneration ?? null,
|
|
7013
|
+
ownerHeartbeatAt: owner?.heartbeatAt ?? null,
|
|
7014
|
+
ownerQueueDepth: owner?.queueDepth ?? null
|
|
6599
7015
|
};
|
|
6600
7016
|
if (globalFlags.format === "json") {
|
|
6601
7017
|
process.stdout.write(`${JSON.stringify(payload)}
|
|
@@ -6622,6 +7038,16 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6622
7038
|
process.stdout.write(`uptime: ${payload.uptime ?? "-"}
|
|
6623
7039
|
`);
|
|
6624
7040
|
process.stdout.write(`lastPromptTime: ${payload.lastPromptTime ?? "-"}
|
|
7041
|
+
`);
|
|
7042
|
+
process.stdout.write(`ownerStatus: ${payload.ownerStatus}
|
|
7043
|
+
`);
|
|
7044
|
+
process.stdout.write(`ownerPid: ${payload.ownerPid ?? "-"}
|
|
7045
|
+
`);
|
|
7046
|
+
process.stdout.write(`ownerGeneration: ${payload.ownerGeneration ?? "-"}
|
|
7047
|
+
`);
|
|
7048
|
+
process.stdout.write(`ownerHeartbeatAt: ${payload.ownerHeartbeatAt ?? "-"}
|
|
7049
|
+
`);
|
|
7050
|
+
process.stdout.write(`ownerQueueDepth: ${payload.ownerQueueDepth ?? "-"}
|
|
6625
7051
|
`);
|
|
6626
7052
|
if (payload.status === "dead") {
|
|
6627
7053
|
process.stdout.write(`exitCode: ${payload.exitCode ?? "-"}
|
|
@@ -6726,7 +7152,7 @@ function registerSharedAgentSubcommands(parent, explicitAgentName, config, descr
|
|
|
6726
7152
|
const setModeCommand = parent.command("set-mode").description(descriptions.setMode).argument(
|
|
6727
7153
|
"<mode>",
|
|
6728
7154
|
"Mode id",
|
|
6729
|
-
(value) =>
|
|
7155
|
+
(value) => parseNonEmptyValue2("Mode", value)
|
|
6730
7156
|
);
|
|
6731
7157
|
addSessionNameOption(setModeCommand);
|
|
6732
7158
|
setModeCommand.action(async function(modeId, flags) {
|
|
@@ -6735,11 +7161,11 @@ function registerSharedAgentSubcommands(parent, explicitAgentName, config, descr
|
|
|
6735
7161
|
const setConfigCommand = parent.command("set").description(descriptions.setConfig).argument(
|
|
6736
7162
|
"<key>",
|
|
6737
7163
|
"Config option id",
|
|
6738
|
-
(value) =>
|
|
7164
|
+
(value) => parseNonEmptyValue2("Config option key", value)
|
|
6739
7165
|
).argument(
|
|
6740
7166
|
"<value>",
|
|
6741
7167
|
"Config option value",
|
|
6742
|
-
(value) =>
|
|
7168
|
+
(value) => parseNonEmptyValue2("Config option value", value)
|
|
6743
7169
|
);
|
|
6744
7170
|
addSessionNameOption(setConfigCommand);
|
|
6745
7171
|
setConfigCommand.action(async function(key, value, flags) {
|
|
@@ -6934,7 +7360,10 @@ async function main(argv = process.argv) {
|
|
|
6934
7360
|
requestedOutputFormat,
|
|
6935
7361
|
requestedJsonStrict
|
|
6936
7362
|
);
|
|
6937
|
-
const
|
|
7363
|
+
const internalQueueOwnerFlags = parseQueueOwnerFlags(
|
|
7364
|
+
argv.slice(2),
|
|
7365
|
+
DEFAULT_QUEUE_OWNER_TTL_MS
|
|
7366
|
+
);
|
|
6938
7367
|
const program = new Command();
|
|
6939
7368
|
program.name("acpx").description("Headless CLI client for the Agent Client Protocol").enablePositionalOptions().showHelpAfterError();
|
|
6940
7369
|
if (requestedJsonStrict) {
|
|
@@ -6946,56 +7375,39 @@ async function main(argv = process.argv) {
|
|
|
6946
7375
|
});
|
|
6947
7376
|
}
|
|
6948
7377
|
addGlobalFlags(program);
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
6962
|
-
);
|
|
6963
|
-
}
|
|
6964
|
-
this.outputHelp();
|
|
6965
|
-
return;
|
|
7378
|
+
configurePublicCli({
|
|
7379
|
+
program,
|
|
7380
|
+
argv: argv.slice(2),
|
|
7381
|
+
config,
|
|
7382
|
+
requestedJsonStrict,
|
|
7383
|
+
topLevelVerbs: TOP_LEVEL_VERBS,
|
|
7384
|
+
listBuiltInAgents,
|
|
7385
|
+
detectAgentToken,
|
|
7386
|
+
registerAgentCommand,
|
|
7387
|
+
registerDefaultCommands,
|
|
7388
|
+
handlePromptAction: async (command, promptParts) => {
|
|
7389
|
+
await handlePrompt(void 0, promptParts, {}, command, config);
|
|
6966
7390
|
}
|
|
6967
|
-
await handlePrompt(void 0, promptParts, {}, this, config);
|
|
6968
7391
|
});
|
|
6969
|
-
program.addHelpText(
|
|
6970
|
-
"after",
|
|
6971
|
-
`
|
|
6972
|
-
Examples:
|
|
6973
|
-
acpx codex sessions new
|
|
6974
|
-
acpx codex "fix the tests"
|
|
6975
|
-
acpx codex prompt "fix the tests"
|
|
6976
|
-
acpx codex --no-wait "queue follow-up task"
|
|
6977
|
-
acpx codex exec "what does this repo do"
|
|
6978
|
-
acpx codex cancel
|
|
6979
|
-
acpx codex set-mode plan
|
|
6980
|
-
acpx codex set approval_policy conservative
|
|
6981
|
-
acpx codex -s backend "fix the API"
|
|
6982
|
-
acpx codex sessions
|
|
6983
|
-
acpx codex sessions new --name backend
|
|
6984
|
-
acpx codex sessions ensure --name backend
|
|
6985
|
-
acpx codex sessions close backend
|
|
6986
|
-
acpx codex status
|
|
6987
|
-
acpx config show
|
|
6988
|
-
acpx config init
|
|
6989
|
-
acpx --ttl 30 codex "investigate flaky tests"
|
|
6990
|
-
acpx claude "refactor auth"
|
|
6991
|
-
acpx gemini "add logging"
|
|
6992
|
-
acpx --agent ./my-custom-server "do something"`
|
|
6993
|
-
);
|
|
6994
7392
|
program.exitOverride((error) => {
|
|
6995
7393
|
throw error;
|
|
6996
7394
|
});
|
|
6997
7395
|
await runWithOutputPolicy(requestedOutputPolicy, async () => {
|
|
6998
7396
|
try {
|
|
7397
|
+
if (internalQueueOwnerFlags) {
|
|
7398
|
+
await runQueueOwnerProcess2({
|
|
7399
|
+
sessionId: internalQueueOwnerFlags.sessionId,
|
|
7400
|
+
ttlMs: internalQueueOwnerFlags.ttlMs,
|
|
7401
|
+
permissionMode: internalQueueOwnerFlags.permissionMode,
|
|
7402
|
+
nonInteractivePermissions: internalQueueOwnerFlags.nonInteractivePermissions,
|
|
7403
|
+
authCredentials: config.auth,
|
|
7404
|
+
authPolicy: internalQueueOwnerFlags.authPolicy,
|
|
7405
|
+
timeoutMs: internalQueueOwnerFlags.timeoutMs,
|
|
7406
|
+
suppressSdkConsoleErrors: internalQueueOwnerFlags.suppressSdkConsoleErrors,
|
|
7407
|
+
verbose: internalQueueOwnerFlags.verbose
|
|
7408
|
+
});
|
|
7409
|
+
return;
|
|
7410
|
+
}
|
|
6999
7411
|
await program.parseAsync(argv);
|
|
7000
7412
|
} catch (error) {
|
|
7001
7413
|
if (error instanceof CommanderError) {
|