acpx 0.1.10 → 0.1.12
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 +1682 -1216
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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
|
-
import
|
|
7
|
+
import path8 from "path";
|
|
8
8
|
import { pathToFileURL } from "url";
|
|
9
9
|
import { findSkillsRoot, maybeHandleSkillflag } from "skillflag";
|
|
10
10
|
|
|
@@ -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) {
|
|
@@ -859,12 +1053,12 @@ function formatLocations(locations) {
|
|
|
859
1053
|
}
|
|
860
1054
|
const unique = /* @__PURE__ */ new Set();
|
|
861
1055
|
for (const location of locations) {
|
|
862
|
-
const
|
|
863
|
-
if (!
|
|
1056
|
+
const path9 = location.path?.trim();
|
|
1057
|
+
if (!path9) {
|
|
864
1058
|
continue;
|
|
865
1059
|
}
|
|
866
1060
|
const line = typeof location.line === "number" && Number.isFinite(location.line) ? `:${Math.max(1, Math.trunc(location.line))}` : "";
|
|
867
|
-
unique.add(`${
|
|
1061
|
+
unique.add(`${path9}${line}`);
|
|
868
1062
|
}
|
|
869
1063
|
const items = [...unique];
|
|
870
1064
|
if (items.length === 0) {
|
|
@@ -877,15 +1071,15 @@ function formatLocations(locations) {
|
|
|
877
1071
|
}
|
|
878
1072
|
return `${visible.join(", ")}, +${hidden} more`;
|
|
879
1073
|
}
|
|
880
|
-
function summarizeDiff(
|
|
1074
|
+
function summarizeDiff(path9, oldText, newText) {
|
|
881
1075
|
const oldLines = oldText ? oldText.split("\n").length : 0;
|
|
882
1076
|
const newLines = newText.split("\n").length;
|
|
883
1077
|
const delta = newLines - oldLines;
|
|
884
1078
|
if (delta === 0) {
|
|
885
|
-
return `diff ${
|
|
1079
|
+
return `diff ${path9} (line count unchanged)`;
|
|
886
1080
|
}
|
|
887
1081
|
const signedDelta = `${delta > 0 ? "+" : ""}${delta}`;
|
|
888
|
-
return `diff ${
|
|
1082
|
+
return `diff ${path9} (${signedDelta} lines)`;
|
|
889
1083
|
}
|
|
890
1084
|
function textFromContentBlock(content) {
|
|
891
1085
|
switch (content.type) {
|
|
@@ -1445,7 +1639,7 @@ function createOutputFormatter(format, options = {}) {
|
|
|
1445
1639
|
|
|
1446
1640
|
// src/session-runtime.ts
|
|
1447
1641
|
import fs5 from "fs/promises";
|
|
1448
|
-
import
|
|
1642
|
+
import path7 from "path";
|
|
1449
1643
|
|
|
1450
1644
|
// src/client.ts
|
|
1451
1645
|
import {
|
|
@@ -1454,7 +1648,7 @@ import {
|
|
|
1454
1648
|
ndJsonStream
|
|
1455
1649
|
} from "@agentclientprotocol/sdk";
|
|
1456
1650
|
import { spawn as spawn2 } from "child_process";
|
|
1457
|
-
import
|
|
1651
|
+
import path4 from "path";
|
|
1458
1652
|
import { Readable, Writable } from "stream";
|
|
1459
1653
|
|
|
1460
1654
|
// src/filesystem.ts
|
|
@@ -1817,6 +2011,60 @@ function extractAgentSessionId(meta) {
|
|
|
1817
2011
|
return void 0;
|
|
1818
2012
|
}
|
|
1819
2013
|
|
|
2014
|
+
// src/version.ts
|
|
2015
|
+
import { readFileSync } from "fs";
|
|
2016
|
+
import path3 from "path";
|
|
2017
|
+
import { fileURLToPath } from "url";
|
|
2018
|
+
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
2019
|
+
var MODULE_DIR = path3.dirname(fileURLToPath(import.meta.url));
|
|
2020
|
+
var cachedVersion = null;
|
|
2021
|
+
function parseVersion(value) {
|
|
2022
|
+
if (typeof value !== "string") {
|
|
2023
|
+
return null;
|
|
2024
|
+
}
|
|
2025
|
+
const trimmed = value.trim();
|
|
2026
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2027
|
+
}
|
|
2028
|
+
function readPackageVersion(packageJsonPath) {
|
|
2029
|
+
try {
|
|
2030
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
2031
|
+
return parseVersion(parsed.version);
|
|
2032
|
+
} catch {
|
|
2033
|
+
return null;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
function resolveVersionFromAncestors(startDir) {
|
|
2037
|
+
let current = startDir;
|
|
2038
|
+
while (true) {
|
|
2039
|
+
const packageVersion = readPackageVersion(path3.join(current, "package.json"));
|
|
2040
|
+
if (packageVersion) {
|
|
2041
|
+
return packageVersion;
|
|
2042
|
+
}
|
|
2043
|
+
const parent = path3.dirname(current);
|
|
2044
|
+
if (parent === current) {
|
|
2045
|
+
return null;
|
|
2046
|
+
}
|
|
2047
|
+
current = parent;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
function resolveAcpxVersion(params) {
|
|
2051
|
+
const envVersion = parseVersion((params?.env ?? process.env).npm_package_version);
|
|
2052
|
+
if (envVersion) {
|
|
2053
|
+
return envVersion;
|
|
2054
|
+
}
|
|
2055
|
+
if (params?.packageJsonPath) {
|
|
2056
|
+
return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
|
|
2057
|
+
}
|
|
2058
|
+
return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
|
|
2059
|
+
}
|
|
2060
|
+
function getAcpxVersion() {
|
|
2061
|
+
if (cachedVersion) {
|
|
2062
|
+
return cachedVersion;
|
|
2063
|
+
}
|
|
2064
|
+
cachedVersion = resolveAcpxVersion();
|
|
2065
|
+
return cachedVersion;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
1820
2068
|
// src/terminal.ts
|
|
1821
2069
|
import { spawn } from "child_process";
|
|
1822
2070
|
import { randomUUID } from "crypto";
|
|
@@ -2283,7 +2531,7 @@ function splitCommandLine(value) {
|
|
|
2283
2531
|
};
|
|
2284
2532
|
}
|
|
2285
2533
|
function asAbsoluteCwd(cwd) {
|
|
2286
|
-
return
|
|
2534
|
+
return path4.resolve(cwd);
|
|
2287
2535
|
}
|
|
2288
2536
|
function toEnvToken(value) {
|
|
2289
2537
|
return value.trim().replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
@@ -2491,7 +2739,7 @@ var AcpClient = class {
|
|
|
2491
2739
|
},
|
|
2492
2740
|
clientInfo: {
|
|
2493
2741
|
name: "acpx",
|
|
2494
|
-
version:
|
|
2742
|
+
version: getAcpxVersion()
|
|
2495
2743
|
}
|
|
2496
2744
|
});
|
|
2497
2745
|
await this.authenticateIfRequired(connection, initResult.authMethods ?? []);
|
|
@@ -2914,115 +3162,270 @@ var AcpClient = class {
|
|
|
2914
3162
|
}
|
|
2915
3163
|
};
|
|
2916
3164
|
|
|
2917
|
-
// src/queue-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
3165
|
+
// src/queue-lease-store.ts
|
|
3166
|
+
import { createHash } from "crypto";
|
|
3167
|
+
import fs3 from "fs/promises";
|
|
3168
|
+
import os2 from "os";
|
|
3169
|
+
import path5 from "path";
|
|
3170
|
+
var PROCESS_EXIT_GRACE_MS = 1500;
|
|
3171
|
+
var PROCESS_POLL_MS = 50;
|
|
3172
|
+
var QUEUE_OWNER_STALE_HEARTBEAT_MS = 15e3;
|
|
3173
|
+
function queueBaseDir() {
|
|
3174
|
+
return path5.join(os2.homedir(), ".acpx", "queues");
|
|
3175
|
+
}
|
|
3176
|
+
function queueKeyForSession(sessionId) {
|
|
3177
|
+
return createHash("sha256").update(sessionId).digest("hex").slice(0, 24);
|
|
3178
|
+
}
|
|
3179
|
+
function queueLockFilePath(sessionId) {
|
|
3180
|
+
return path5.join(queueBaseDir(), `${queueKeyForSession(sessionId)}.lock`);
|
|
3181
|
+
}
|
|
3182
|
+
function queueSocketPath(sessionId) {
|
|
3183
|
+
const key = queueKeyForSession(sessionId);
|
|
3184
|
+
if (process.platform === "win32") {
|
|
3185
|
+
return `\\\\.\\pipe\\acpx-${key}`;
|
|
2925
3186
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
3187
|
+
return path5.join(queueBaseDir(), `${key}.sock`);
|
|
3188
|
+
}
|
|
3189
|
+
function parseQueueOwnerRecord(raw) {
|
|
3190
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3191
|
+
return null;
|
|
2928
3192
|
}
|
|
2929
|
-
|
|
2930
|
-
|
|
3193
|
+
const record = raw;
|
|
3194
|
+
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) {
|
|
3195
|
+
return null;
|
|
2931
3196
|
}
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
3197
|
+
return {
|
|
3198
|
+
pid: record.pid,
|
|
3199
|
+
sessionId: record.sessionId,
|
|
3200
|
+
socketPath: record.socketPath,
|
|
3201
|
+
createdAt: record.createdAt,
|
|
3202
|
+
heartbeatAt: record.heartbeatAt,
|
|
3203
|
+
ownerGeneration: record.ownerGeneration,
|
|
3204
|
+
queueDepth: record.queueDepth
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
function createOwnerGeneration() {
|
|
3208
|
+
return Date.now() * 1e3 + Math.floor(Math.random() * 1e3);
|
|
3209
|
+
}
|
|
3210
|
+
function nowIso4() {
|
|
3211
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
3212
|
+
}
|
|
3213
|
+
function isQueueOwnerHeartbeatStale(owner) {
|
|
3214
|
+
const heartbeatMs = Date.parse(owner.heartbeatAt);
|
|
3215
|
+
if (!Number.isFinite(heartbeatMs)) {
|
|
3216
|
+
return true;
|
|
2935
3217
|
}
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3218
|
+
return Date.now() - heartbeatMs > QUEUE_OWNER_STALE_HEARTBEAT_MS;
|
|
3219
|
+
}
|
|
3220
|
+
async function ensureQueueDir() {
|
|
3221
|
+
await fs3.mkdir(queueBaseDir(), { recursive: true });
|
|
3222
|
+
}
|
|
3223
|
+
async function removeSocketFile(socketPath) {
|
|
3224
|
+
if (process.platform === "win32") {
|
|
3225
|
+
return;
|
|
2940
3226
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
3227
|
+
try {
|
|
3228
|
+
await fs3.unlink(socketPath);
|
|
3229
|
+
} catch (error) {
|
|
3230
|
+
if (error.code !== "ENOENT") {
|
|
3231
|
+
throw error;
|
|
3232
|
+
}
|
|
2944
3233
|
}
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3234
|
+
}
|
|
3235
|
+
async function waitForProcessExit(pid, timeoutMs) {
|
|
3236
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
3237
|
+
while (Date.now() <= deadline) {
|
|
3238
|
+
if (!isProcessAlive(pid)) {
|
|
3239
|
+
return true;
|
|
3240
|
+
}
|
|
3241
|
+
await waitMs2(PROCESS_POLL_MS);
|
|
2949
3242
|
}
|
|
2950
|
-
|
|
2951
|
-
|
|
3243
|
+
return !isProcessAlive(pid);
|
|
3244
|
+
}
|
|
3245
|
+
async function cleanupStaleQueueOwner(sessionId, owner) {
|
|
3246
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3247
|
+
const socketPath = owner?.socketPath ?? queueSocketPath(sessionId);
|
|
3248
|
+
await removeSocketFile(socketPath).catch(() => {
|
|
3249
|
+
});
|
|
3250
|
+
await fs3.unlink(lockPath).catch((error) => {
|
|
3251
|
+
if (error.code !== "ENOENT") {
|
|
3252
|
+
throw error;
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
async function readQueueOwnerRecord(sessionId) {
|
|
3257
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3258
|
+
try {
|
|
3259
|
+
const payload = await fs3.readFile(lockPath, "utf8");
|
|
3260
|
+
const parsed = parseQueueOwnerRecord(JSON.parse(payload));
|
|
3261
|
+
return parsed ?? void 0;
|
|
3262
|
+
} catch {
|
|
3263
|
+
return void 0;
|
|
2952
3264
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
3265
|
+
}
|
|
3266
|
+
function isProcessAlive(pid) {
|
|
3267
|
+
if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) {
|
|
3268
|
+
return false;
|
|
2955
3269
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
retryable: true
|
|
2962
|
-
});
|
|
2963
|
-
}
|
|
3270
|
+
try {
|
|
3271
|
+
process.kill(pid, 0);
|
|
3272
|
+
return true;
|
|
3273
|
+
} catch {
|
|
3274
|
+
return false;
|
|
2964
3275
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
2969
|
-
if (cancelled2) {
|
|
2970
|
-
this.pendingCancel = false;
|
|
2971
|
-
}
|
|
2972
|
-
return cancelled2;
|
|
2973
|
-
}
|
|
2974
|
-
if (this.state === "starting" || this.state === "active") {
|
|
2975
|
-
this.pendingCancel = true;
|
|
2976
|
-
return true;
|
|
2977
|
-
}
|
|
3276
|
+
}
|
|
3277
|
+
async function terminateProcess(pid) {
|
|
3278
|
+
if (!isProcessAlive(pid)) {
|
|
2978
3279
|
return false;
|
|
2979
3280
|
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3281
|
+
try {
|
|
3282
|
+
process.kill(pid, "SIGTERM");
|
|
3283
|
+
} catch {
|
|
3284
|
+
return false;
|
|
3285
|
+
}
|
|
3286
|
+
if (await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS)) {
|
|
3287
|
+
return true;
|
|
3288
|
+
}
|
|
3289
|
+
try {
|
|
3290
|
+
process.kill(pid, "SIGKILL");
|
|
3291
|
+
} catch {
|
|
3292
|
+
return false;
|
|
3293
|
+
}
|
|
3294
|
+
await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS);
|
|
3295
|
+
return true;
|
|
3296
|
+
}
|
|
3297
|
+
async function ensureOwnerIsUsable(sessionId, owner) {
|
|
3298
|
+
const alive = isProcessAlive(owner.pid);
|
|
3299
|
+
const stale = isQueueOwnerHeartbeatStale(owner);
|
|
3300
|
+
if (alive && !stale) {
|
|
3301
|
+
return true;
|
|
3302
|
+
}
|
|
3303
|
+
if (alive) {
|
|
3304
|
+
await terminateProcess(owner.pid).catch(() => {
|
|
3305
|
+
});
|
|
3306
|
+
}
|
|
3307
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3308
|
+
return false;
|
|
3309
|
+
}
|
|
3310
|
+
async function readQueueOwnerStatus(sessionId) {
|
|
3311
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3312
|
+
if (!owner) {
|
|
3313
|
+
return void 0;
|
|
3314
|
+
}
|
|
3315
|
+
const alive = await ensureOwnerIsUsable(sessionId, owner);
|
|
3316
|
+
if (!alive) {
|
|
3317
|
+
return void 0;
|
|
3318
|
+
}
|
|
3319
|
+
return {
|
|
3320
|
+
pid: owner.pid,
|
|
3321
|
+
socketPath: owner.socketPath,
|
|
3322
|
+
heartbeatAt: owner.heartbeatAt,
|
|
3323
|
+
ownerGeneration: owner.ownerGeneration,
|
|
3324
|
+
queueDepth: owner.queueDepth,
|
|
3325
|
+
alive,
|
|
3326
|
+
stale: isQueueOwnerHeartbeatStale(owner)
|
|
3327
|
+
};
|
|
3328
|
+
}
|
|
3329
|
+
async function tryAcquireQueueOwnerLease(sessionId, nowIsoFactory = nowIso4) {
|
|
3330
|
+
await ensureQueueDir();
|
|
3331
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3332
|
+
const socketPath = queueSocketPath(sessionId);
|
|
3333
|
+
const createdAt = nowIsoFactory();
|
|
3334
|
+
const ownerGeneration = createOwnerGeneration();
|
|
3335
|
+
const payload = JSON.stringify(
|
|
3336
|
+
{
|
|
3337
|
+
pid: process.pid,
|
|
3338
|
+
sessionId,
|
|
3339
|
+
socketPath,
|
|
3340
|
+
createdAt,
|
|
3341
|
+
heartbeatAt: createdAt,
|
|
3342
|
+
ownerGeneration,
|
|
3343
|
+
queueDepth: 0
|
|
3344
|
+
},
|
|
3345
|
+
null,
|
|
3346
|
+
2
|
|
3347
|
+
);
|
|
3348
|
+
try {
|
|
3349
|
+
await fs3.writeFile(lockPath, `${payload}
|
|
3350
|
+
`, {
|
|
3351
|
+
encoding: "utf8",
|
|
3352
|
+
flag: "wx"
|
|
3353
|
+
});
|
|
3354
|
+
await removeSocketFile(socketPath).catch(() => {
|
|
3355
|
+
});
|
|
3356
|
+
return {
|
|
3357
|
+
sessionId,
|
|
3358
|
+
lockPath,
|
|
3359
|
+
socketPath,
|
|
3360
|
+
createdAt,
|
|
3361
|
+
ownerGeneration
|
|
3362
|
+
};
|
|
3363
|
+
} catch (error) {
|
|
3364
|
+
if (error.code !== "EEXIST") {
|
|
3365
|
+
throw error;
|
|
2984
3366
|
}
|
|
2985
|
-
const
|
|
2986
|
-
if (
|
|
2987
|
-
|
|
3367
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3368
|
+
if (!owner) {
|
|
3369
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3370
|
+
return void 0;
|
|
2988
3371
|
}
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
await this.options.withTimeout(
|
|
2996
|
-
async () => await activeController.setSessionMode(modeId),
|
|
2997
|
-
timeoutMs
|
|
2998
|
-
);
|
|
2999
|
-
return;
|
|
3372
|
+
if (!isProcessAlive(owner.pid) || isQueueOwnerHeartbeatStale(owner)) {
|
|
3373
|
+
if (isProcessAlive(owner.pid)) {
|
|
3374
|
+
await terminateProcess(owner.pid).catch(() => {
|
|
3375
|
+
});
|
|
3376
|
+
}
|
|
3377
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3000
3378
|
}
|
|
3001
|
-
|
|
3379
|
+
return void 0;
|
|
3002
3380
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3381
|
+
}
|
|
3382
|
+
async function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso4) {
|
|
3383
|
+
const payload = JSON.stringify(
|
|
3384
|
+
{
|
|
3385
|
+
pid: process.pid,
|
|
3386
|
+
sessionId: lease.sessionId,
|
|
3387
|
+
socketPath: lease.socketPath,
|
|
3388
|
+
createdAt: lease.createdAt,
|
|
3389
|
+
heartbeatAt: nowIsoFactory(),
|
|
3390
|
+
ownerGeneration: lease.ownerGeneration,
|
|
3391
|
+
queueDepth: Math.max(0, Math.round(options.queueDepth))
|
|
3392
|
+
},
|
|
3393
|
+
null,
|
|
3394
|
+
2
|
|
3395
|
+
);
|
|
3396
|
+
await fs3.writeFile(lease.lockPath, `${payload}
|
|
3397
|
+
`, {
|
|
3398
|
+
encoding: "utf8"
|
|
3399
|
+
});
|
|
3400
|
+
}
|
|
3401
|
+
async function releaseQueueOwnerLease(lease) {
|
|
3402
|
+
await removeSocketFile(lease.socketPath).catch(() => {
|
|
3403
|
+
});
|
|
3404
|
+
await fs3.unlink(lease.lockPath).catch((error) => {
|
|
3405
|
+
if (error.code !== "ENOENT") {
|
|
3406
|
+
throw error;
|
|
3011
3407
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3410
|
+
async function terminateQueueOwnerForSession(sessionId) {
|
|
3411
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3412
|
+
if (!owner) {
|
|
3413
|
+
return;
|
|
3017
3414
|
}
|
|
3018
|
-
|
|
3415
|
+
if (isProcessAlive(owner.pid)) {
|
|
3416
|
+
await terminateProcess(owner.pid);
|
|
3417
|
+
}
|
|
3418
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3419
|
+
}
|
|
3420
|
+
async function waitMs2(ms) {
|
|
3421
|
+
await new Promise((resolve) => {
|
|
3422
|
+
setTimeout(resolve, ms);
|
|
3423
|
+
});
|
|
3424
|
+
}
|
|
3019
3425
|
|
|
3020
|
-
// src/queue-ipc.ts
|
|
3021
|
-
import {
|
|
3022
|
-
import fs3 from "fs/promises";
|
|
3426
|
+
// src/queue-ipc-client.ts
|
|
3427
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3023
3428
|
import net from "net";
|
|
3024
|
-
import os2 from "os";
|
|
3025
|
-
import path4 from "path";
|
|
3026
3429
|
|
|
3027
3430
|
// src/queue-messages.ts
|
|
3028
3431
|
function asRecord4(value) {
|
|
@@ -3255,214 +3658,26 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3255
3658
|
return null;
|
|
3256
3659
|
}
|
|
3257
3660
|
|
|
3258
|
-
// src/queue-ipc.ts
|
|
3259
|
-
var PROCESS_EXIT_GRACE_MS = 1500;
|
|
3260
|
-
var PROCESS_POLL_MS = 50;
|
|
3661
|
+
// src/queue-ipc-client.ts
|
|
3261
3662
|
var QUEUE_CONNECT_ATTEMPTS = 40;
|
|
3262
3663
|
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
|
-
};
|
|
3664
|
+
function shouldRetryQueueConnect(error) {
|
|
3665
|
+
const code = error.code;
|
|
3666
|
+
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
3276
3667
|
}
|
|
3277
|
-
function
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
message: normalized.message,
|
|
3291
|
-
retryable: normalized.retryable,
|
|
3292
|
-
acp: normalized.acp
|
|
3293
|
-
};
|
|
3294
|
-
}
|
|
3295
|
-
function isProcessAlive(pid) {
|
|
3296
|
-
if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) {
|
|
3297
|
-
return false;
|
|
3298
|
-
}
|
|
3299
|
-
try {
|
|
3300
|
-
process.kill(pid, 0);
|
|
3301
|
-
return true;
|
|
3302
|
-
} catch {
|
|
3303
|
-
return false;
|
|
3304
|
-
}
|
|
3305
|
-
}
|
|
3306
|
-
async function waitForProcessExit(pid, timeoutMs) {
|
|
3307
|
-
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
3308
|
-
while (Date.now() <= deadline) {
|
|
3309
|
-
if (!isProcessAlive(pid)) {
|
|
3310
|
-
return true;
|
|
3311
|
-
}
|
|
3312
|
-
await new Promise((resolve) => {
|
|
3313
|
-
setTimeout(resolve, PROCESS_POLL_MS);
|
|
3314
|
-
});
|
|
3315
|
-
}
|
|
3316
|
-
return !isProcessAlive(pid);
|
|
3317
|
-
}
|
|
3318
|
-
async function terminateProcess(pid) {
|
|
3319
|
-
if (!isProcessAlive(pid)) {
|
|
3320
|
-
return false;
|
|
3321
|
-
}
|
|
3322
|
-
try {
|
|
3323
|
-
process.kill(pid, "SIGTERM");
|
|
3324
|
-
} catch {
|
|
3325
|
-
return false;
|
|
3326
|
-
}
|
|
3327
|
-
if (await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS)) {
|
|
3328
|
-
return true;
|
|
3329
|
-
}
|
|
3330
|
-
try {
|
|
3331
|
-
process.kill(pid, "SIGKILL");
|
|
3332
|
-
} catch {
|
|
3333
|
-
return false;
|
|
3334
|
-
}
|
|
3335
|
-
await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS);
|
|
3336
|
-
return true;
|
|
3337
|
-
}
|
|
3338
|
-
function parseQueueOwnerRecord(raw) {
|
|
3339
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3340
|
-
return null;
|
|
3341
|
-
}
|
|
3342
|
-
const record = raw;
|
|
3343
|
-
if (!Number.isInteger(record.pid) || record.pid <= 0 || typeof record.sessionId !== "string" || typeof record.socketPath !== "string") {
|
|
3344
|
-
return null;
|
|
3345
|
-
}
|
|
3346
|
-
return {
|
|
3347
|
-
pid: record.pid,
|
|
3348
|
-
sessionId: record.sessionId,
|
|
3349
|
-
socketPath: record.socketPath
|
|
3350
|
-
};
|
|
3351
|
-
}
|
|
3352
|
-
function queueKeyForSession(sessionId) {
|
|
3353
|
-
return createHash("sha256").update(sessionId).digest("hex").slice(0, 24);
|
|
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}`;
|
|
3362
|
-
}
|
|
3363
|
-
return path4.join(queueBaseDir(), `${key}.sock`);
|
|
3364
|
-
}
|
|
3365
|
-
async function ensureQueueDir() {
|
|
3366
|
-
await fs3.mkdir(queueBaseDir(), { recursive: true });
|
|
3367
|
-
}
|
|
3368
|
-
async function removeSocketFile(socketPath) {
|
|
3369
|
-
if (process.platform === "win32") {
|
|
3370
|
-
return;
|
|
3371
|
-
}
|
|
3372
|
-
try {
|
|
3373
|
-
await fs3.unlink(socketPath);
|
|
3374
|
-
} catch (error) {
|
|
3375
|
-
if (error.code !== "ENOENT") {
|
|
3376
|
-
throw error;
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
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 {
|
|
3387
|
-
return void 0;
|
|
3388
|
-
}
|
|
3389
|
-
}
|
|
3390
|
-
async function cleanupStaleQueueOwner(sessionId, owner) {
|
|
3391
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
3392
|
-
const socketPath = owner?.socketPath ?? queueSocketPath(sessionId);
|
|
3393
|
-
await removeSocketFile(socketPath).catch(() => {
|
|
3394
|
-
});
|
|
3395
|
-
await fs3.unlink(lockPath).catch((error) => {
|
|
3396
|
-
if (error.code !== "ENOENT") {
|
|
3397
|
-
throw error;
|
|
3398
|
-
}
|
|
3399
|
-
});
|
|
3400
|
-
}
|
|
3401
|
-
async function tryAcquireQueueOwnerLease(sessionId, nowIso4 = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3402
|
-
await ensureQueueDir();
|
|
3403
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
3404
|
-
const socketPath = queueSocketPath(sessionId);
|
|
3405
|
-
const payload = JSON.stringify(
|
|
3406
|
-
{
|
|
3407
|
-
pid: process.pid,
|
|
3408
|
-
sessionId,
|
|
3409
|
-
socketPath,
|
|
3410
|
-
createdAt: nowIso4()
|
|
3411
|
-
},
|
|
3412
|
-
null,
|
|
3413
|
-
2
|
|
3414
|
-
);
|
|
3415
|
-
try {
|
|
3416
|
-
await fs3.writeFile(lockPath, `${payload}
|
|
3417
|
-
`, {
|
|
3418
|
-
encoding: "utf8",
|
|
3419
|
-
flag: "wx"
|
|
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);
|
|
3431
|
-
}
|
|
3432
|
-
return void 0;
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
async function releaseQueueOwnerLease(lease) {
|
|
3436
|
-
await removeSocketFile(lease.socketPath).catch(() => {
|
|
3437
|
-
});
|
|
3438
|
-
await fs3.unlink(lease.lockPath).catch((error) => {
|
|
3439
|
-
if (error.code !== "ENOENT") {
|
|
3440
|
-
throw error;
|
|
3441
|
-
}
|
|
3442
|
-
});
|
|
3443
|
-
}
|
|
3444
|
-
function shouldRetryQueueConnect(error) {
|
|
3445
|
-
const code = error.code;
|
|
3446
|
-
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
3447
|
-
}
|
|
3448
|
-
async function waitMs2(ms) {
|
|
3449
|
-
await new Promise((resolve) => {
|
|
3450
|
-
setTimeout(resolve, ms);
|
|
3451
|
-
});
|
|
3452
|
-
}
|
|
3453
|
-
async function connectToSocket(socketPath) {
|
|
3454
|
-
return await new Promise((resolve, reject) => {
|
|
3455
|
-
const socket = net.createConnection(socketPath);
|
|
3456
|
-
const onConnect = () => {
|
|
3457
|
-
socket.off("error", onError);
|
|
3458
|
-
resolve(socket);
|
|
3459
|
-
};
|
|
3460
|
-
const onError = (error) => {
|
|
3461
|
-
socket.off("connect", onConnect);
|
|
3462
|
-
reject(error);
|
|
3463
|
-
};
|
|
3464
|
-
socket.once("connect", onConnect);
|
|
3465
|
-
socket.once("error", onError);
|
|
3668
|
+
async function connectToSocket(socketPath) {
|
|
3669
|
+
return await new Promise((resolve, reject) => {
|
|
3670
|
+
const socket = net.createConnection(socketPath);
|
|
3671
|
+
const onConnect = () => {
|
|
3672
|
+
socket.off("error", onError);
|
|
3673
|
+
resolve(socket);
|
|
3674
|
+
};
|
|
3675
|
+
const onError = (error) => {
|
|
3676
|
+
socket.off("connect", onConnect);
|
|
3677
|
+
reject(error);
|
|
3678
|
+
};
|
|
3679
|
+
socket.once("connect", onConnect);
|
|
3680
|
+
socket.once("error", onError);
|
|
3466
3681
|
});
|
|
3467
3682
|
}
|
|
3468
3683
|
async function connectToQueueOwner(owner) {
|
|
@@ -3486,320 +3701,36 @@ async function connectToQueueOwner(owner) {
|
|
|
3486
3701
|
}
|
|
3487
3702
|
return void 0;
|
|
3488
3703
|
}
|
|
3489
|
-
function
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
socket.write(`${JSON.stringify(message)}
|
|
3494
|
-
`);
|
|
3495
|
-
}
|
|
3496
|
-
var SessionQueueOwner = class _SessionQueueOwner {
|
|
3497
|
-
server;
|
|
3498
|
-
controlHandlers;
|
|
3499
|
-
pending = [];
|
|
3500
|
-
waiters = [];
|
|
3501
|
-
closed = false;
|
|
3502
|
-
constructor(server, controlHandlers) {
|
|
3503
|
-
this.server = server;
|
|
3504
|
-
this.controlHandlers = controlHandlers;
|
|
3505
|
-
}
|
|
3506
|
-
static async start(lease, controlHandlers) {
|
|
3507
|
-
const ownerRef = { current: void 0 };
|
|
3508
|
-
const server = net.createServer((socket) => {
|
|
3509
|
-
ownerRef.current?.handleConnection(socket);
|
|
3510
|
-
});
|
|
3511
|
-
ownerRef.current = new _SessionQueueOwner(server, controlHandlers);
|
|
3512
|
-
await new Promise((resolve, reject) => {
|
|
3513
|
-
const onListening = () => {
|
|
3514
|
-
server.off("error", onError);
|
|
3515
|
-
resolve();
|
|
3516
|
-
};
|
|
3517
|
-
const onError = (error) => {
|
|
3518
|
-
server.off("listening", onListening);
|
|
3519
|
-
reject(error);
|
|
3520
|
-
};
|
|
3521
|
-
server.once("listening", onListening);
|
|
3522
|
-
server.once("error", onError);
|
|
3523
|
-
server.listen(lease.socketPath);
|
|
3524
|
-
});
|
|
3525
|
-
return ownerRef.current;
|
|
3704
|
+
async function submitToQueueOwner(owner, options) {
|
|
3705
|
+
const socket = await connectToQueueOwner(owner);
|
|
3706
|
+
if (!socket) {
|
|
3707
|
+
return void 0;
|
|
3526
3708
|
}
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
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;
|
|
3709
|
+
socket.setEncoding("utf8");
|
|
3710
|
+
const requestId = randomUUID2();
|
|
3711
|
+
const request = {
|
|
3712
|
+
type: "submit_prompt",
|
|
3713
|
+
requestId,
|
|
3714
|
+
message: options.message,
|
|
3715
|
+
permissionMode: options.permissionMode,
|
|
3716
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
3717
|
+
timeoutMs: options.timeoutMs,
|
|
3718
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
3719
|
+
waitForCompletion: options.waitForCompletion
|
|
3720
|
+
};
|
|
3721
|
+
options.outputFormatter.setContext({
|
|
3722
|
+
sessionId: options.sessionId,
|
|
3723
|
+
requestId,
|
|
3724
|
+
stream: "prompt"
|
|
3725
|
+
});
|
|
3726
|
+
return await new Promise((resolve, reject) => {
|
|
3727
|
+
let settled = false;
|
|
3728
|
+
let acknowledged = false;
|
|
3729
|
+
let buffer = "";
|
|
3730
|
+
let sawDone = false;
|
|
3731
|
+
const finishResolve = (result) => {
|
|
3732
|
+
if (settled) {
|
|
3733
|
+
return;
|
|
3803
3734
|
}
|
|
3804
3735
|
settled = true;
|
|
3805
3736
|
socket.removeAllListeners();
|
|
@@ -3976,105 +3907,651 @@ async function submitToQueueOwner(owner, options) {
|
|
|
3976
3907
|
retryable: true
|
|
3977
3908
|
})
|
|
3978
3909
|
);
|
|
3979
|
-
});
|
|
3980
|
-
socket.write(`${JSON.stringify(request)}
|
|
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;
|
|
3910
|
+
});
|
|
3911
|
+
socket.write(`${JSON.stringify(request)}
|
|
3912
|
+
`);
|
|
3913
|
+
});
|
|
3914
|
+
}
|
|
3915
|
+
async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
3916
|
+
const socket = await connectToQueueOwner(owner);
|
|
3917
|
+
if (!socket) {
|
|
3918
|
+
return void 0;
|
|
3919
|
+
}
|
|
3920
|
+
socket.setEncoding("utf8");
|
|
3921
|
+
return await new Promise((resolve, reject) => {
|
|
3922
|
+
let settled = false;
|
|
3923
|
+
let acknowledged = false;
|
|
3924
|
+
let buffer = "";
|
|
3925
|
+
const finishResolve = (result) => {
|
|
3926
|
+
if (settled) {
|
|
3927
|
+
return;
|
|
3928
|
+
}
|
|
3929
|
+
settled = true;
|
|
3930
|
+
socket.removeAllListeners();
|
|
3931
|
+
if (!socket.destroyed) {
|
|
3932
|
+
socket.end();
|
|
3933
|
+
}
|
|
3934
|
+
resolve(result);
|
|
3935
|
+
};
|
|
3936
|
+
const finishReject = (error) => {
|
|
3937
|
+
if (settled) {
|
|
3938
|
+
return;
|
|
3939
|
+
}
|
|
3940
|
+
settled = true;
|
|
3941
|
+
socket.removeAllListeners();
|
|
3942
|
+
if (!socket.destroyed) {
|
|
3943
|
+
socket.destroy();
|
|
3944
|
+
}
|
|
3945
|
+
reject(error);
|
|
3946
|
+
};
|
|
3947
|
+
const processLine = (line) => {
|
|
3948
|
+
let parsed;
|
|
3949
|
+
try {
|
|
3950
|
+
parsed = JSON.parse(line);
|
|
3951
|
+
} catch {
|
|
3952
|
+
finishReject(
|
|
3953
|
+
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3954
|
+
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3955
|
+
origin: "queue",
|
|
3956
|
+
retryable: true
|
|
3957
|
+
})
|
|
3958
|
+
);
|
|
3959
|
+
return;
|
|
3960
|
+
}
|
|
3961
|
+
const message = parseQueueOwnerMessage(parsed);
|
|
3962
|
+
if (!message || message.requestId !== request.requestId) {
|
|
3963
|
+
finishReject(
|
|
3964
|
+
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3965
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3966
|
+
origin: "queue",
|
|
3967
|
+
retryable: true
|
|
3968
|
+
})
|
|
3969
|
+
);
|
|
3970
|
+
return;
|
|
3971
|
+
}
|
|
3972
|
+
if (message.type === "accepted") {
|
|
3973
|
+
acknowledged = true;
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
if (message.type === "error") {
|
|
3977
|
+
finishReject(
|
|
3978
|
+
new QueueConnectionError(message.message, {
|
|
3979
|
+
outputCode: message.code,
|
|
3980
|
+
detailCode: message.detailCode,
|
|
3981
|
+
origin: message.origin ?? "queue",
|
|
3982
|
+
retryable: message.retryable,
|
|
3983
|
+
acp: message.acp
|
|
3984
|
+
})
|
|
3985
|
+
);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3988
|
+
if (!acknowledged) {
|
|
3989
|
+
finishReject(
|
|
3990
|
+
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3991
|
+
detailCode: "QUEUE_ACK_MISSING",
|
|
3992
|
+
origin: "queue",
|
|
3993
|
+
retryable: true
|
|
3994
|
+
})
|
|
3995
|
+
);
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
3998
|
+
if (!isExpectedResponse(message)) {
|
|
3999
|
+
finishReject(
|
|
4000
|
+
new QueueProtocolError("Queue owner returned unexpected response", {
|
|
4001
|
+
detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
|
|
4002
|
+
origin: "queue",
|
|
4003
|
+
retryable: true
|
|
4004
|
+
})
|
|
4005
|
+
);
|
|
4006
|
+
return;
|
|
4007
|
+
}
|
|
4008
|
+
finishResolve(message);
|
|
4009
|
+
};
|
|
4010
|
+
socket.on("data", (chunk) => {
|
|
4011
|
+
buffer += chunk;
|
|
4012
|
+
let index = buffer.indexOf("\n");
|
|
4013
|
+
while (index >= 0) {
|
|
4014
|
+
const line = buffer.slice(0, index).trim();
|
|
4015
|
+
buffer = buffer.slice(index + 1);
|
|
4016
|
+
if (line.length > 0) {
|
|
4017
|
+
processLine(line);
|
|
4018
|
+
}
|
|
4019
|
+
index = buffer.indexOf("\n");
|
|
4020
|
+
}
|
|
4021
|
+
});
|
|
4022
|
+
socket.once("error", (error) => {
|
|
4023
|
+
finishReject(error);
|
|
4024
|
+
});
|
|
4025
|
+
socket.once("close", () => {
|
|
4026
|
+
if (settled) {
|
|
4027
|
+
return;
|
|
4028
|
+
}
|
|
4029
|
+
if (!acknowledged) {
|
|
4030
|
+
finishReject(
|
|
4031
|
+
new QueueConnectionError(
|
|
4032
|
+
"Queue owner disconnected before acknowledging request",
|
|
4033
|
+
{
|
|
4034
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
4035
|
+
origin: "queue",
|
|
4036
|
+
retryable: true
|
|
4037
|
+
}
|
|
4038
|
+
)
|
|
4039
|
+
);
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
finishReject(
|
|
4043
|
+
new QueueConnectionError("Queue owner disconnected before responding", {
|
|
4044
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
4045
|
+
origin: "queue",
|
|
4046
|
+
retryable: true
|
|
4047
|
+
})
|
|
4048
|
+
);
|
|
4049
|
+
});
|
|
4050
|
+
socket.write(`${JSON.stringify(request)}
|
|
4051
|
+
`);
|
|
4052
|
+
});
|
|
4053
|
+
}
|
|
4054
|
+
async function submitCancelToQueueOwner(owner) {
|
|
4055
|
+
const request = {
|
|
4056
|
+
type: "cancel_prompt",
|
|
4057
|
+
requestId: randomUUID2()
|
|
4058
|
+
};
|
|
4059
|
+
const response = await submitControlToQueueOwner(
|
|
4060
|
+
owner,
|
|
4061
|
+
request,
|
|
4062
|
+
(message) => message.type === "cancel_result"
|
|
4063
|
+
);
|
|
4064
|
+
if (!response) {
|
|
4065
|
+
return void 0;
|
|
4066
|
+
}
|
|
4067
|
+
if (response.requestId !== request.requestId) {
|
|
4068
|
+
throw new QueueProtocolError("Queue owner returned mismatched cancel response", {
|
|
4069
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4070
|
+
origin: "queue",
|
|
4071
|
+
retryable: true
|
|
4072
|
+
});
|
|
4073
|
+
}
|
|
4074
|
+
return response.cancelled;
|
|
4075
|
+
}
|
|
4076
|
+
async function submitSetModeToQueueOwner(owner, modeId, timeoutMs) {
|
|
4077
|
+
const request = {
|
|
4078
|
+
type: "set_mode",
|
|
4079
|
+
requestId: randomUUID2(),
|
|
4080
|
+
modeId,
|
|
4081
|
+
timeoutMs
|
|
4082
|
+
};
|
|
4083
|
+
const response = await submitControlToQueueOwner(
|
|
4084
|
+
owner,
|
|
4085
|
+
request,
|
|
4086
|
+
(message) => message.type === "set_mode_result"
|
|
4087
|
+
);
|
|
4088
|
+
if (!response) {
|
|
4089
|
+
return void 0;
|
|
4090
|
+
}
|
|
4091
|
+
if (response.requestId !== request.requestId) {
|
|
4092
|
+
throw new QueueProtocolError("Queue owner returned mismatched set_mode response", {
|
|
4093
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4094
|
+
origin: "queue",
|
|
4095
|
+
retryable: true
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
return true;
|
|
4099
|
+
}
|
|
4100
|
+
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
|
|
4101
|
+
const request = {
|
|
4102
|
+
type: "set_config_option",
|
|
4103
|
+
requestId: randomUUID2(),
|
|
4104
|
+
configId,
|
|
4105
|
+
value,
|
|
4106
|
+
timeoutMs
|
|
4107
|
+
};
|
|
4108
|
+
const response = await submitControlToQueueOwner(
|
|
4109
|
+
owner,
|
|
4110
|
+
request,
|
|
4111
|
+
(message) => message.type === "set_config_option_result"
|
|
4112
|
+
);
|
|
4113
|
+
if (!response) {
|
|
4114
|
+
return void 0;
|
|
4115
|
+
}
|
|
4116
|
+
if (response.requestId !== request.requestId) {
|
|
4117
|
+
throw new QueueProtocolError(
|
|
4118
|
+
"Queue owner returned mismatched set_config_option response",
|
|
4119
|
+
{
|
|
4120
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4121
|
+
origin: "queue",
|
|
4122
|
+
retryable: true
|
|
4123
|
+
}
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
return response.response;
|
|
4127
|
+
}
|
|
4128
|
+
async function trySubmitToRunningOwner(options) {
|
|
4129
|
+
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4130
|
+
if (!owner) {
|
|
4131
|
+
return void 0;
|
|
4132
|
+
}
|
|
4133
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4134
|
+
return void 0;
|
|
4135
|
+
}
|
|
4136
|
+
const submitted = await submitToQueueOwner(owner, options);
|
|
4137
|
+
if (submitted) {
|
|
4138
|
+
if (options.verbose) {
|
|
4139
|
+
process.stderr.write(
|
|
4140
|
+
`[acpx] queued prompt on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4141
|
+
`
|
|
4142
|
+
);
|
|
4143
|
+
}
|
|
4144
|
+
return submitted;
|
|
4145
|
+
}
|
|
4146
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4147
|
+
return void 0;
|
|
4148
|
+
}
|
|
4149
|
+
throw new QueueConnectionError(
|
|
4150
|
+
"Session queue owner is running but not accepting queue requests",
|
|
4151
|
+
{
|
|
4152
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4153
|
+
origin: "queue",
|
|
4154
|
+
retryable: true
|
|
4155
|
+
}
|
|
4156
|
+
);
|
|
4157
|
+
}
|
|
4158
|
+
async function tryCancelOnRunningOwner(options) {
|
|
4159
|
+
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4160
|
+
if (!owner) {
|
|
4161
|
+
return void 0;
|
|
4162
|
+
}
|
|
4163
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4164
|
+
return void 0;
|
|
4165
|
+
}
|
|
4166
|
+
const cancelled2 = await submitCancelToQueueOwner(owner);
|
|
4167
|
+
if (cancelled2 !== void 0) {
|
|
4168
|
+
if (options.verbose) {
|
|
4169
|
+
process.stderr.write(
|
|
4170
|
+
`[acpx] requested cancel on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4171
|
+
`
|
|
4172
|
+
);
|
|
4173
|
+
}
|
|
4174
|
+
return cancelled2;
|
|
4175
|
+
}
|
|
4176
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4177
|
+
return void 0;
|
|
4178
|
+
}
|
|
4179
|
+
throw new QueueConnectionError(
|
|
4180
|
+
"Session queue owner is running but not accepting cancel requests",
|
|
4181
|
+
{
|
|
4182
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4183
|
+
origin: "queue",
|
|
4184
|
+
retryable: true
|
|
4185
|
+
}
|
|
4186
|
+
);
|
|
4187
|
+
}
|
|
4188
|
+
async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
|
|
4189
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
4190
|
+
if (!owner) {
|
|
4191
|
+
return void 0;
|
|
4192
|
+
}
|
|
4193
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4194
|
+
return void 0;
|
|
4195
|
+
}
|
|
4196
|
+
const submitted = await submitSetModeToQueueOwner(owner, modeId, timeoutMs);
|
|
4197
|
+
if (submitted) {
|
|
4198
|
+
if (verbose) {
|
|
4199
|
+
process.stderr.write(
|
|
4200
|
+
`[acpx] requested session/set_mode on owner pid ${owner.pid} for session ${sessionId}
|
|
4201
|
+
`
|
|
4202
|
+
);
|
|
4203
|
+
}
|
|
4204
|
+
return true;
|
|
4205
|
+
}
|
|
4206
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4207
|
+
return void 0;
|
|
4208
|
+
}
|
|
4209
|
+
throw new QueueConnectionError(
|
|
4210
|
+
"Session queue owner is running but not accepting set_mode requests",
|
|
4211
|
+
{
|
|
4212
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4213
|
+
origin: "queue",
|
|
4214
|
+
retryable: true
|
|
4215
|
+
}
|
|
4216
|
+
);
|
|
4217
|
+
}
|
|
4218
|
+
async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
|
|
4219
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
4220
|
+
if (!owner) {
|
|
4221
|
+
return void 0;
|
|
4222
|
+
}
|
|
4223
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4224
|
+
return void 0;
|
|
4225
|
+
}
|
|
4226
|
+
const response = await submitSetConfigOptionToQueueOwner(
|
|
4227
|
+
owner,
|
|
4228
|
+
configId,
|
|
4229
|
+
value,
|
|
4230
|
+
timeoutMs
|
|
4231
|
+
);
|
|
4232
|
+
if (response) {
|
|
4233
|
+
if (verbose) {
|
|
4234
|
+
process.stderr.write(
|
|
4235
|
+
`[acpx] requested session/set_config_option on owner pid ${owner.pid} for session ${sessionId}
|
|
4236
|
+
`
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
4239
|
+
return response;
|
|
4240
|
+
}
|
|
4241
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4242
|
+
return void 0;
|
|
4243
|
+
}
|
|
4244
|
+
throw new QueueConnectionError(
|
|
4245
|
+
"Session queue owner is running but not accepting set_config_option requests",
|
|
4246
|
+
{
|
|
4247
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4248
|
+
origin: "queue",
|
|
4249
|
+
retryable: true
|
|
4250
|
+
}
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
|
|
4254
|
+
// src/queue-ipc-server.ts
|
|
4255
|
+
import net2 from "net";
|
|
4256
|
+
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
4257
|
+
return {
|
|
4258
|
+
type: "error",
|
|
4259
|
+
requestId,
|
|
4260
|
+
code: "RUNTIME",
|
|
4261
|
+
detailCode,
|
|
4262
|
+
origin: "queue",
|
|
4263
|
+
retryable: options.retryable,
|
|
4264
|
+
message
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
4267
|
+
function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
|
|
4268
|
+
const normalized = normalizeOutputError(error, {
|
|
4269
|
+
defaultCode: "RUNTIME",
|
|
4270
|
+
origin: "queue",
|
|
4271
|
+
detailCode,
|
|
4272
|
+
retryable: options.retryable
|
|
4273
|
+
});
|
|
4274
|
+
return {
|
|
4275
|
+
type: "error",
|
|
4276
|
+
requestId,
|
|
4277
|
+
code: normalized.code,
|
|
4278
|
+
detailCode: normalized.detailCode,
|
|
4279
|
+
origin: normalized.origin,
|
|
4280
|
+
message: normalized.message,
|
|
4281
|
+
retryable: normalized.retryable,
|
|
4282
|
+
acp: normalized.acp
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
function writeQueueMessage(socket, message) {
|
|
4286
|
+
if (socket.destroyed || !socket.writable) {
|
|
4287
|
+
return;
|
|
4288
|
+
}
|
|
4289
|
+
socket.write(`${JSON.stringify(message)}
|
|
4290
|
+
`);
|
|
4291
|
+
}
|
|
4292
|
+
var SessionQueueOwner = class _SessionQueueOwner {
|
|
4293
|
+
server;
|
|
4294
|
+
controlHandlers;
|
|
4295
|
+
pending = [];
|
|
4296
|
+
waiters = [];
|
|
4297
|
+
closed = false;
|
|
4298
|
+
constructor(server, controlHandlers) {
|
|
4299
|
+
this.server = server;
|
|
4300
|
+
this.controlHandlers = controlHandlers;
|
|
4301
|
+
}
|
|
4302
|
+
static async start(lease, controlHandlers) {
|
|
4303
|
+
const ownerRef = { current: void 0 };
|
|
4304
|
+
const server = net2.createServer((socket) => {
|
|
4305
|
+
ownerRef.current?.handleConnection(socket);
|
|
4306
|
+
});
|
|
4307
|
+
ownerRef.current = new _SessionQueueOwner(server, controlHandlers);
|
|
4308
|
+
await new Promise((resolve, reject) => {
|
|
4309
|
+
const onListening = () => {
|
|
4310
|
+
server.off("error", onError);
|
|
4311
|
+
resolve();
|
|
4312
|
+
};
|
|
4313
|
+
const onError = (error) => {
|
|
4314
|
+
server.off("listening", onListening);
|
|
4315
|
+
reject(error);
|
|
4316
|
+
};
|
|
4317
|
+
server.once("listening", onListening);
|
|
4318
|
+
server.once("error", onError);
|
|
4319
|
+
server.listen(lease.socketPath);
|
|
4320
|
+
});
|
|
4321
|
+
return ownerRef.current;
|
|
4322
|
+
}
|
|
4323
|
+
async close() {
|
|
4324
|
+
if (this.closed) {
|
|
4325
|
+
return;
|
|
4326
|
+
}
|
|
4327
|
+
this.closed = true;
|
|
4328
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
4329
|
+
waiter(void 0);
|
|
4330
|
+
}
|
|
4331
|
+
for (const task of this.pending.splice(0)) {
|
|
4332
|
+
if (task.waitForCompletion) {
|
|
4333
|
+
task.send(
|
|
4334
|
+
makeQueueOwnerError(
|
|
4335
|
+
task.requestId,
|
|
4336
|
+
"Queue owner shutting down before prompt execution",
|
|
4337
|
+
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
4338
|
+
{
|
|
4339
|
+
retryable: true
|
|
4340
|
+
}
|
|
4341
|
+
)
|
|
4342
|
+
);
|
|
4343
|
+
}
|
|
4344
|
+
task.close();
|
|
4345
|
+
}
|
|
4346
|
+
await new Promise((resolve) => {
|
|
4347
|
+
this.server.close(() => resolve());
|
|
4348
|
+
});
|
|
4349
|
+
}
|
|
4350
|
+
async nextTask(timeoutMs) {
|
|
4351
|
+
if (this.pending.length > 0) {
|
|
4352
|
+
return this.pending.shift();
|
|
4353
|
+
}
|
|
4354
|
+
if (this.closed) {
|
|
4355
|
+
return void 0;
|
|
4356
|
+
}
|
|
4357
|
+
return await new Promise((resolve) => {
|
|
4358
|
+
const shouldTimeout = timeoutMs != null;
|
|
4359
|
+
const timer = shouldTimeout && setTimeout(
|
|
4360
|
+
() => {
|
|
4361
|
+
const index = this.waiters.indexOf(waiter);
|
|
4362
|
+
if (index >= 0) {
|
|
4363
|
+
this.waiters.splice(index, 1);
|
|
4364
|
+
}
|
|
4365
|
+
resolve(void 0);
|
|
4366
|
+
},
|
|
4367
|
+
Math.max(0, timeoutMs)
|
|
4368
|
+
);
|
|
4369
|
+
const waiter = (task) => {
|
|
4370
|
+
if (timer) {
|
|
4371
|
+
clearTimeout(timer);
|
|
4372
|
+
}
|
|
4373
|
+
resolve(task);
|
|
4374
|
+
};
|
|
4375
|
+
this.waiters.push(waiter);
|
|
4376
|
+
});
|
|
4377
|
+
}
|
|
4378
|
+
queueDepth() {
|
|
4379
|
+
return this.pending.length;
|
|
4380
|
+
}
|
|
4381
|
+
enqueue(task) {
|
|
4382
|
+
if (this.closed) {
|
|
4383
|
+
if (task.waitForCompletion) {
|
|
4384
|
+
task.send(
|
|
4385
|
+
makeQueueOwnerError(
|
|
4386
|
+
task.requestId,
|
|
4387
|
+
"Queue owner is shutting down",
|
|
4388
|
+
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
4389
|
+
{
|
|
4390
|
+
retryable: true
|
|
4391
|
+
}
|
|
4392
|
+
)
|
|
4393
|
+
);
|
|
4394
|
+
}
|
|
4395
|
+
task.close();
|
|
4396
|
+
return;
|
|
4397
|
+
}
|
|
4398
|
+
const waiter = this.waiters.shift();
|
|
4399
|
+
if (waiter) {
|
|
4400
|
+
waiter(task);
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4403
|
+
this.pending.push(task);
|
|
4404
|
+
}
|
|
4405
|
+
handleConnection(socket) {
|
|
4406
|
+
socket.setEncoding("utf8");
|
|
4407
|
+
if (this.closed) {
|
|
4408
|
+
writeQueueMessage(
|
|
4409
|
+
socket,
|
|
4410
|
+
makeQueueOwnerError("unknown", "Queue owner is closed", "QUEUE_OWNER_CLOSED", {
|
|
4411
|
+
retryable: true
|
|
4412
|
+
})
|
|
4413
|
+
);
|
|
4414
|
+
socket.end();
|
|
4415
|
+
return;
|
|
4416
|
+
}
|
|
3993
4417
|
let buffer = "";
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
resolve(result);
|
|
4418
|
+
let handled = false;
|
|
4419
|
+
const fail = (requestId, message, detailCode) => {
|
|
4420
|
+
writeQueueMessage(
|
|
4421
|
+
socket,
|
|
4422
|
+
makeQueueOwnerError(requestId, message, detailCode, {
|
|
4423
|
+
retryable: false
|
|
4424
|
+
})
|
|
4425
|
+
);
|
|
4426
|
+
socket.end();
|
|
4004
4427
|
};
|
|
4005
|
-
const
|
|
4006
|
-
if (
|
|
4428
|
+
const processLine = (line) => {
|
|
4429
|
+
if (handled) {
|
|
4007
4430
|
return;
|
|
4008
4431
|
}
|
|
4009
|
-
|
|
4010
|
-
socket.removeAllListeners();
|
|
4011
|
-
if (!socket.destroyed) {
|
|
4012
|
-
socket.destroy();
|
|
4013
|
-
}
|
|
4014
|
-
reject(error);
|
|
4015
|
-
};
|
|
4016
|
-
const processLine = (line) => {
|
|
4432
|
+
handled = true;
|
|
4017
4433
|
let parsed;
|
|
4018
4434
|
try {
|
|
4019
4435
|
parsed = JSON.parse(line);
|
|
4020
4436
|
} catch {
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
retryable: true
|
|
4026
|
-
})
|
|
4437
|
+
fail(
|
|
4438
|
+
"unknown",
|
|
4439
|
+
"Invalid queue request payload",
|
|
4440
|
+
"QUEUE_REQUEST_PAYLOAD_INVALID_JSON"
|
|
4027
4441
|
);
|
|
4028
4442
|
return;
|
|
4029
4443
|
}
|
|
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
|
-
);
|
|
4444
|
+
const request = parseQueueRequest(parsed);
|
|
4445
|
+
if (!request) {
|
|
4446
|
+
fail("unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
4039
4447
|
return;
|
|
4040
4448
|
}
|
|
4041
|
-
if (
|
|
4042
|
-
|
|
4449
|
+
if (request.type === "cancel_prompt") {
|
|
4450
|
+
writeQueueMessage(socket, {
|
|
4451
|
+
type: "accepted",
|
|
4452
|
+
requestId: request.requestId
|
|
4453
|
+
});
|
|
4454
|
+
void this.controlHandlers.cancelPrompt().then((cancelled2) => {
|
|
4455
|
+
writeQueueMessage(socket, {
|
|
4456
|
+
type: "cancel_result",
|
|
4457
|
+
requestId: request.requestId,
|
|
4458
|
+
cancelled: cancelled2
|
|
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
|
+
});
|
|
4043
4474
|
return;
|
|
4044
4475
|
}
|
|
4045
|
-
if (
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4476
|
+
if (request.type === "set_mode") {
|
|
4477
|
+
writeQueueMessage(socket, {
|
|
4478
|
+
type: "accepted",
|
|
4479
|
+
requestId: request.requestId
|
|
4480
|
+
});
|
|
4481
|
+
void this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs).then(() => {
|
|
4482
|
+
writeQueueMessage(socket, {
|
|
4483
|
+
type: "set_mode_result",
|
|
4484
|
+
requestId: request.requestId,
|
|
4485
|
+
modeId: request.modeId
|
|
4486
|
+
});
|
|
4487
|
+
}).catch((error) => {
|
|
4488
|
+
writeQueueMessage(
|
|
4489
|
+
socket,
|
|
4490
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4491
|
+
request.requestId,
|
|
4492
|
+
error,
|
|
4493
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4494
|
+
)
|
|
4495
|
+
);
|
|
4496
|
+
}).finally(() => {
|
|
4497
|
+
if (!socket.destroyed) {
|
|
4498
|
+
socket.end();
|
|
4499
|
+
}
|
|
4500
|
+
});
|
|
4055
4501
|
return;
|
|
4056
4502
|
}
|
|
4057
|
-
if (
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4503
|
+
if (request.type === "set_config_option") {
|
|
4504
|
+
writeQueueMessage(socket, {
|
|
4505
|
+
type: "accepted",
|
|
4506
|
+
requestId: request.requestId
|
|
4507
|
+
});
|
|
4508
|
+
void this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs).then((response) => {
|
|
4509
|
+
writeQueueMessage(socket, {
|
|
4510
|
+
type: "set_config_option_result",
|
|
4511
|
+
requestId: request.requestId,
|
|
4512
|
+
response
|
|
4513
|
+
});
|
|
4514
|
+
}).catch((error) => {
|
|
4515
|
+
writeQueueMessage(
|
|
4516
|
+
socket,
|
|
4517
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4518
|
+
request.requestId,
|
|
4519
|
+
error,
|
|
4520
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4521
|
+
)
|
|
4522
|
+
);
|
|
4523
|
+
}).finally(() => {
|
|
4524
|
+
if (!socket.destroyed) {
|
|
4525
|
+
socket.end();
|
|
4526
|
+
}
|
|
4527
|
+
});
|
|
4065
4528
|
return;
|
|
4066
4529
|
}
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4530
|
+
const task = {
|
|
4531
|
+
requestId: request.requestId,
|
|
4532
|
+
message: request.message,
|
|
4533
|
+
permissionMode: request.permissionMode,
|
|
4534
|
+
nonInteractivePermissions: request.nonInteractivePermissions,
|
|
4535
|
+
timeoutMs: request.timeoutMs,
|
|
4536
|
+
suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
|
|
4537
|
+
waitForCompletion: request.waitForCompletion,
|
|
4538
|
+
send: (message) => {
|
|
4539
|
+
writeQueueMessage(socket, message);
|
|
4540
|
+
},
|
|
4541
|
+
close: () => {
|
|
4542
|
+
if (!socket.destroyed) {
|
|
4543
|
+
socket.end();
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
};
|
|
4547
|
+
writeQueueMessage(socket, {
|
|
4548
|
+
type: "accepted",
|
|
4549
|
+
requestId: request.requestId
|
|
4550
|
+
});
|
|
4551
|
+
if (!request.waitForCompletion) {
|
|
4552
|
+
task.close();
|
|
4076
4553
|
}
|
|
4077
|
-
|
|
4554
|
+
this.enqueue(task);
|
|
4078
4555
|
};
|
|
4079
4556
|
socket.on("data", (chunk) => {
|
|
4080
4557
|
buffer += chunk;
|
|
@@ -4088,268 +4565,276 @@ async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
|
4088
4565
|
index = buffer.indexOf("\n");
|
|
4089
4566
|
}
|
|
4090
4567
|
});
|
|
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
|
|
4568
|
+
socket.on("error", () => {
|
|
4141
4569
|
});
|
|
4142
4570
|
}
|
|
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;
|
|
4571
|
+
};
|
|
4572
|
+
|
|
4573
|
+
// src/queue-owner-turn-controller.ts
|
|
4574
|
+
var QueueOwnerTurnController = class {
|
|
4575
|
+
options;
|
|
4576
|
+
state = "idle";
|
|
4577
|
+
pendingCancel = false;
|
|
4578
|
+
activeController;
|
|
4579
|
+
constructor(options) {
|
|
4580
|
+
this.options = options;
|
|
4159
4581
|
}
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4163
|
-
origin: "queue",
|
|
4164
|
-
retryable: true
|
|
4165
|
-
});
|
|
4582
|
+
get lifecycleState() {
|
|
4583
|
+
return this.state;
|
|
4166
4584
|
}
|
|
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;
|
|
4585
|
+
get hasPendingCancel() {
|
|
4586
|
+
return this.pendingCancel;
|
|
4184
4587
|
}
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
{
|
|
4189
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4190
|
-
origin: "queue",
|
|
4191
|
-
retryable: true
|
|
4192
|
-
}
|
|
4193
|
-
);
|
|
4588
|
+
beginTurn() {
|
|
4589
|
+
this.state = "starting";
|
|
4590
|
+
this.pendingCancel = false;
|
|
4194
4591
|
}
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
if (!owner) {
|
|
4200
|
-
return void 0;
|
|
4592
|
+
markPromptActive() {
|
|
4593
|
+
if (this.state === "starting" || this.state === "active") {
|
|
4594
|
+
this.state = "active";
|
|
4595
|
+
}
|
|
4201
4596
|
}
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4597
|
+
endTurn() {
|
|
4598
|
+
this.state = "idle";
|
|
4599
|
+
this.pendingCancel = false;
|
|
4205
4600
|
}
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
`[acpx] queued prompt on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4211
|
-
`
|
|
4212
|
-
);
|
|
4213
|
-
}
|
|
4214
|
-
return submitted;
|
|
4601
|
+
beginClosing() {
|
|
4602
|
+
this.state = "closing";
|
|
4603
|
+
this.pendingCancel = false;
|
|
4604
|
+
this.activeController = void 0;
|
|
4215
4605
|
}
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
return void 0;
|
|
4606
|
+
setActiveController(controller) {
|
|
4607
|
+
this.activeController = controller;
|
|
4219
4608
|
}
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4609
|
+
clearActiveController() {
|
|
4610
|
+
this.activeController = void 0;
|
|
4611
|
+
}
|
|
4612
|
+
assertCanHandleControlRequest() {
|
|
4613
|
+
if (this.state === "closing") {
|
|
4614
|
+
throw new QueueConnectionError("Queue owner is closing", {
|
|
4615
|
+
detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
|
|
4616
|
+
origin: "queue",
|
|
4617
|
+
retryable: true
|
|
4618
|
+
});
|
|
4226
4619
|
}
|
|
4227
|
-
);
|
|
4228
|
-
}
|
|
4229
|
-
async function tryCancelOnRunningOwner(options) {
|
|
4230
|
-
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4231
|
-
if (!owner) {
|
|
4232
|
-
return void 0;
|
|
4233
4620
|
}
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4621
|
+
async requestCancel() {
|
|
4622
|
+
const activeController = this.activeController;
|
|
4623
|
+
if (activeController?.hasActivePrompt()) {
|
|
4624
|
+
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
4625
|
+
if (cancelled2) {
|
|
4626
|
+
this.pendingCancel = false;
|
|
4627
|
+
}
|
|
4628
|
+
return cancelled2;
|
|
4629
|
+
}
|
|
4630
|
+
if (this.state === "starting" || this.state === "active") {
|
|
4631
|
+
this.pendingCancel = true;
|
|
4632
|
+
return true;
|
|
4633
|
+
}
|
|
4634
|
+
return false;
|
|
4237
4635
|
}
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
if (
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4636
|
+
async applyPendingCancel() {
|
|
4637
|
+
const activeController = this.activeController;
|
|
4638
|
+
if (!this.pendingCancel || !activeController || !activeController.hasActivePrompt()) {
|
|
4639
|
+
return false;
|
|
4640
|
+
}
|
|
4641
|
+
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
4642
|
+
if (cancelled2) {
|
|
4643
|
+
this.pendingCancel = false;
|
|
4245
4644
|
}
|
|
4246
4645
|
return cancelled2;
|
|
4247
4646
|
}
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
retryable: true
|
|
4647
|
+
async setSessionMode(modeId, timeoutMs) {
|
|
4648
|
+
this.assertCanHandleControlRequest();
|
|
4649
|
+
const activeController = this.activeController;
|
|
4650
|
+
if (activeController) {
|
|
4651
|
+
await this.options.withTimeout(
|
|
4652
|
+
async () => await activeController.setSessionMode(modeId),
|
|
4653
|
+
timeoutMs
|
|
4654
|
+
);
|
|
4655
|
+
return;
|
|
4258
4656
|
}
|
|
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;
|
|
4657
|
+
await this.options.setSessionModeFallback(modeId, timeoutMs);
|
|
4269
4658
|
}
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4659
|
+
async setSessionConfigOption(configId, value, timeoutMs) {
|
|
4660
|
+
this.assertCanHandleControlRequest();
|
|
4661
|
+
const activeController = this.activeController;
|
|
4662
|
+
if (activeController) {
|
|
4663
|
+
return await this.options.withTimeout(
|
|
4664
|
+
async () => await activeController.setSessionConfigOption(configId, value),
|
|
4665
|
+
timeoutMs
|
|
4276
4666
|
);
|
|
4277
4667
|
}
|
|
4278
|
-
return
|
|
4668
|
+
return await this.options.setSessionConfigOptionFallback(
|
|
4669
|
+
configId,
|
|
4670
|
+
value,
|
|
4671
|
+
timeoutMs
|
|
4672
|
+
);
|
|
4279
4673
|
}
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4674
|
+
};
|
|
4675
|
+
|
|
4676
|
+
// src/session-owner-runtime.ts
|
|
4677
|
+
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4678
|
+
var QUEUE_OWNER_HEARTBEAT_INTERVAL_MS = 2e3;
|
|
4679
|
+
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
4680
|
+
if (ttlMs == null) {
|
|
4681
|
+
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4283
4682
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4683
|
+
if (!Number.isFinite(ttlMs) || ttlMs < 0) {
|
|
4684
|
+
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4685
|
+
}
|
|
4686
|
+
return Math.round(ttlMs);
|
|
4687
|
+
}
|
|
4688
|
+
function createQueueOwnerTurnRuntime(options, deps) {
|
|
4689
|
+
const turnController = new QueueOwnerTurnController({
|
|
4690
|
+
withTimeout: async (run, timeoutMs) => await deps.withTimeout(run, timeoutMs),
|
|
4691
|
+
setSessionModeFallback: deps.setSessionModeFallback,
|
|
4692
|
+
setSessionConfigOptionFallback: deps.setSessionConfigOptionFallback
|
|
4693
|
+
});
|
|
4694
|
+
const applyPendingCancel = async () => {
|
|
4695
|
+
return await turnController.applyPendingCancel();
|
|
4696
|
+
};
|
|
4697
|
+
const scheduleApplyPendingCancel = () => {
|
|
4698
|
+
void applyPendingCancel().catch((error) => {
|
|
4699
|
+
if (options.verbose) {
|
|
4700
|
+
process.stderr.write(
|
|
4701
|
+
`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}
|
|
4702
|
+
`
|
|
4703
|
+
);
|
|
4704
|
+
}
|
|
4705
|
+
});
|
|
4706
|
+
};
|
|
4707
|
+
return {
|
|
4708
|
+
beginClosing: () => {
|
|
4709
|
+
turnController.beginClosing();
|
|
4710
|
+
},
|
|
4711
|
+
onClientAvailable: (controller) => {
|
|
4712
|
+
turnController.setActiveController(controller);
|
|
4713
|
+
scheduleApplyPendingCancel();
|
|
4714
|
+
},
|
|
4715
|
+
onClientClosed: () => {
|
|
4716
|
+
turnController.clearActiveController();
|
|
4717
|
+
},
|
|
4718
|
+
onPromptActive: async () => {
|
|
4719
|
+
turnController.markPromptActive();
|
|
4720
|
+
await applyPendingCancel();
|
|
4721
|
+
},
|
|
4722
|
+
runPromptTurn: async (run) => {
|
|
4723
|
+
turnController.beginTurn();
|
|
4724
|
+
try {
|
|
4725
|
+
return await run();
|
|
4726
|
+
} finally {
|
|
4727
|
+
turnController.endTurn();
|
|
4728
|
+
}
|
|
4729
|
+
},
|
|
4730
|
+
controlHandlers: {
|
|
4731
|
+
cancelPrompt: async () => {
|
|
4732
|
+
const accepted = await turnController.requestCancel();
|
|
4733
|
+
if (!accepted) {
|
|
4734
|
+
return false;
|
|
4735
|
+
}
|
|
4736
|
+
await applyPendingCancel();
|
|
4737
|
+
return true;
|
|
4738
|
+
},
|
|
4739
|
+
setSessionMode: async (modeId, timeoutMs) => {
|
|
4740
|
+
await turnController.setSessionMode(modeId, timeoutMs);
|
|
4741
|
+
},
|
|
4742
|
+
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
4743
|
+
return await turnController.setSessionConfigOption(configId, value, timeoutMs);
|
|
4744
|
+
}
|
|
4290
4745
|
}
|
|
4291
|
-
|
|
4746
|
+
};
|
|
4292
4747
|
}
|
|
4293
|
-
async function
|
|
4294
|
-
const
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
if (!isProcessAlive(owner.pid)) {
|
|
4299
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4300
|
-
return void 0;
|
|
4301
|
-
}
|
|
4302
|
-
const response = await submitSetConfigOptionToQueueOwner(
|
|
4303
|
-
owner,
|
|
4304
|
-
configId,
|
|
4305
|
-
value,
|
|
4306
|
-
timeoutMs
|
|
4307
|
-
);
|
|
4308
|
-
if (response) {
|
|
4309
|
-
if (verbose) {
|
|
4748
|
+
async function runQueueOwnerProcess(options, deps) {
|
|
4749
|
+
const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
|
|
4750
|
+
const lease = await tryAcquireQueueOwnerLease(options.sessionId);
|
|
4751
|
+
if (!lease) {
|
|
4752
|
+
if (options.verbose) {
|
|
4310
4753
|
process.stderr.write(
|
|
4311
|
-
`[acpx]
|
|
4754
|
+
`[acpx] queue owner already active for session ${options.sessionId}; skipping spawn
|
|
4312
4755
|
`
|
|
4313
4756
|
);
|
|
4314
4757
|
}
|
|
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
4758
|
return;
|
|
4334
4759
|
}
|
|
4335
|
-
|
|
4336
|
-
|
|
4760
|
+
const runtime = createQueueOwnerTurnRuntime(options, deps);
|
|
4761
|
+
let owner;
|
|
4762
|
+
let heartbeatTimer;
|
|
4763
|
+
const refreshHeartbeat = async () => {
|
|
4764
|
+
if (!owner) {
|
|
4765
|
+
return;
|
|
4766
|
+
}
|
|
4767
|
+
await refreshQueueOwnerLease(lease, {
|
|
4768
|
+
queueDepth: owner.queueDepth()
|
|
4769
|
+
}).catch((error) => {
|
|
4770
|
+
if (options.verbose) {
|
|
4771
|
+
process.stderr.write(
|
|
4772
|
+
`[acpx] queue owner heartbeat update failed: ${formatErrorMessage(error)}
|
|
4773
|
+
`
|
|
4774
|
+
);
|
|
4775
|
+
}
|
|
4776
|
+
});
|
|
4777
|
+
};
|
|
4778
|
+
try {
|
|
4779
|
+
owner = await SessionQueueOwner.start(lease, runtime.controlHandlers);
|
|
4780
|
+
await refreshHeartbeat();
|
|
4781
|
+
heartbeatTimer = setInterval(() => {
|
|
4782
|
+
void refreshHeartbeat();
|
|
4783
|
+
}, QUEUE_OWNER_HEARTBEAT_INTERVAL_MS);
|
|
4784
|
+
heartbeatTimer.unref();
|
|
4785
|
+
const idleWaitMs = queueOwnerTtlMs === 0 ? void 0 : Math.max(0, queueOwnerTtlMs);
|
|
4786
|
+
while (true) {
|
|
4787
|
+
const task = await owner.nextTask(idleWaitMs);
|
|
4788
|
+
if (!task) {
|
|
4789
|
+
if (queueOwnerTtlMs > 0 && options.verbose) {
|
|
4790
|
+
process.stderr.write(
|
|
4791
|
+
`[acpx] queue owner TTL expired after ${Math.round(queueOwnerTtlMs / 1e3)}s for session ${options.sessionId}; shutting down
|
|
4792
|
+
`
|
|
4793
|
+
);
|
|
4794
|
+
}
|
|
4795
|
+
break;
|
|
4796
|
+
}
|
|
4797
|
+
await runtime.runPromptTurn(async () => {
|
|
4798
|
+
await deps.runQueuedTask(options.sessionId, task, {
|
|
4799
|
+
verbose: options.verbose,
|
|
4800
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
4801
|
+
authCredentials: options.authCredentials,
|
|
4802
|
+
authPolicy: options.authPolicy,
|
|
4803
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
4804
|
+
onClientAvailable: runtime.onClientAvailable,
|
|
4805
|
+
onClientClosed: runtime.onClientClosed,
|
|
4806
|
+
onPromptActive: runtime.onPromptActive
|
|
4807
|
+
});
|
|
4808
|
+
});
|
|
4809
|
+
await refreshHeartbeat();
|
|
4810
|
+
}
|
|
4811
|
+
} finally {
|
|
4812
|
+
if (heartbeatTimer) {
|
|
4813
|
+
clearInterval(heartbeatTimer);
|
|
4814
|
+
}
|
|
4815
|
+
runtime.beginClosing();
|
|
4816
|
+
if (owner) {
|
|
4817
|
+
await owner.close();
|
|
4818
|
+
}
|
|
4819
|
+
await releaseQueueOwnerLease(lease);
|
|
4337
4820
|
}
|
|
4338
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4339
4821
|
}
|
|
4340
4822
|
|
|
4823
|
+
// src/session-owner-spawn.ts
|
|
4824
|
+
import { spawn as spawn3 } from "child_process";
|
|
4825
|
+
|
|
4341
4826
|
// src/session-persistence.ts
|
|
4342
4827
|
import { statSync } from "fs";
|
|
4343
4828
|
import fs4 from "fs/promises";
|
|
4344
4829
|
import os3 from "os";
|
|
4345
|
-
import
|
|
4830
|
+
import path6 from "path";
|
|
4346
4831
|
var DEFAULT_HISTORY_LIMIT = 20;
|
|
4347
4832
|
function sessionFilePath(id) {
|
|
4348
4833
|
const safeId = encodeURIComponent(id);
|
|
4349
|
-
return
|
|
4834
|
+
return path6.join(sessionBaseDir(), `${safeId}.json`);
|
|
4350
4835
|
}
|
|
4351
4836
|
function sessionBaseDir() {
|
|
4352
|
-
return
|
|
4837
|
+
return path6.join(os3.homedir(), ".acpx", "sessions");
|
|
4353
4838
|
}
|
|
4354
4839
|
async function ensureSessionDir() {
|
|
4355
4840
|
await fs4.mkdir(sessionBaseDir(), { recursive: true });
|
|
@@ -4468,7 +4953,7 @@ async function resolveSessionRecord(sessionId) {
|
|
|
4468
4953
|
throw new SessionNotFoundError(sessionId);
|
|
4469
4954
|
}
|
|
4470
4955
|
function hasGitDirectory(dir) {
|
|
4471
|
-
const gitPath =
|
|
4956
|
+
const gitPath = path6.join(dir, ".git");
|
|
4472
4957
|
try {
|
|
4473
4958
|
return statSync(gitPath).isDirectory();
|
|
4474
4959
|
} catch {
|
|
@@ -4476,15 +4961,15 @@ function hasGitDirectory(dir) {
|
|
|
4476
4961
|
}
|
|
4477
4962
|
}
|
|
4478
4963
|
function isWithinBoundary(boundary, target) {
|
|
4479
|
-
const relative =
|
|
4480
|
-
return relative.length === 0 || !relative.startsWith("..") && !
|
|
4964
|
+
const relative = path6.relative(boundary, target);
|
|
4965
|
+
return relative.length === 0 || !relative.startsWith("..") && !path6.isAbsolute(relative);
|
|
4481
4966
|
}
|
|
4482
4967
|
function absolutePath(value) {
|
|
4483
|
-
return
|
|
4968
|
+
return path6.resolve(value);
|
|
4484
4969
|
}
|
|
4485
4970
|
function findGitRepositoryRoot(startDir) {
|
|
4486
4971
|
let current = absolutePath(startDir);
|
|
4487
|
-
const root =
|
|
4972
|
+
const root = path6.parse(current).root;
|
|
4488
4973
|
for (; ; ) {
|
|
4489
4974
|
if (hasGitDirectory(current)) {
|
|
4490
4975
|
return current;
|
|
@@ -4492,7 +4977,7 @@ function findGitRepositoryRoot(startDir) {
|
|
|
4492
4977
|
if (current === root) {
|
|
4493
4978
|
return void 0;
|
|
4494
4979
|
}
|
|
4495
|
-
const parent =
|
|
4980
|
+
const parent = path6.dirname(current);
|
|
4496
4981
|
if (parent === current) {
|
|
4497
4982
|
return void 0;
|
|
4498
4983
|
}
|
|
@@ -4517,7 +5002,7 @@ async function listSessions() {
|
|
|
4517
5002
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
4518
5003
|
continue;
|
|
4519
5004
|
}
|
|
4520
|
-
const fullPath =
|
|
5005
|
+
const fullPath = path6.join(sessionBaseDir(), entry.name);
|
|
4521
5006
|
try {
|
|
4522
5007
|
const payload = await fs4.readFile(fullPath, "utf8");
|
|
4523
5008
|
const parsed = parseSessionRecord(JSON.parse(payload));
|
|
@@ -4578,7 +5063,7 @@ async function findSessionByDirectoryWalk(options) {
|
|
|
4578
5063
|
if (dir === walkBoundary) {
|
|
4579
5064
|
return void 0;
|
|
4580
5065
|
}
|
|
4581
|
-
const parent =
|
|
5066
|
+
const parent = path6.dirname(dir);
|
|
4582
5067
|
if (parent === dir) {
|
|
4583
5068
|
return void 0;
|
|
4584
5069
|
}
|
|
@@ -4586,6 +5071,124 @@ async function findSessionByDirectoryWalk(options) {
|
|
|
4586
5071
|
}
|
|
4587
5072
|
}
|
|
4588
5073
|
|
|
5074
|
+
// src/session-owner-spawn.ts
|
|
5075
|
+
var QUEUE_OWNER_STARTUP_TIMEOUT_MS = 1e4;
|
|
5076
|
+
var QUEUE_OWNER_RESPAWN_BACKOFF_MS = 250;
|
|
5077
|
+
function isQueueNotAcceptingError(error) {
|
|
5078
|
+
return error instanceof QueueConnectionError && error.detailCode === "QUEUE_NOT_ACCEPTING_REQUESTS";
|
|
5079
|
+
}
|
|
5080
|
+
function spawnDetachedQueueOwner(ownerSpawn) {
|
|
5081
|
+
const child = spawn3(ownerSpawn.command, ownerSpawn.args, {
|
|
5082
|
+
cwd: ownerSpawn.cwd,
|
|
5083
|
+
env: ownerSpawn.env,
|
|
5084
|
+
stdio: "ignore",
|
|
5085
|
+
detached: true
|
|
5086
|
+
});
|
|
5087
|
+
child.unref();
|
|
5088
|
+
}
|
|
5089
|
+
async function buildDefaultQueueOwnerSpawn(options, queueOwnerTtlMs) {
|
|
5090
|
+
const entrypoint = process.argv[1];
|
|
5091
|
+
if (!entrypoint) {
|
|
5092
|
+
throw new Error("Cannot spawn queue owner process: CLI entrypoint is missing");
|
|
5093
|
+
}
|
|
5094
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5095
|
+
const args = [
|
|
5096
|
+
entrypoint,
|
|
5097
|
+
"__queue-owner",
|
|
5098
|
+
"--session-id",
|
|
5099
|
+
options.sessionId,
|
|
5100
|
+
"--ttl-ms",
|
|
5101
|
+
String(queueOwnerTtlMs),
|
|
5102
|
+
"--permission-mode",
|
|
5103
|
+
options.permissionMode
|
|
5104
|
+
];
|
|
5105
|
+
if (options.nonInteractivePermissions) {
|
|
5106
|
+
args.push("--non-interactive-permissions", options.nonInteractivePermissions);
|
|
5107
|
+
}
|
|
5108
|
+
if (options.authPolicy) {
|
|
5109
|
+
args.push("--auth-policy", options.authPolicy);
|
|
5110
|
+
}
|
|
5111
|
+
if (options.timeoutMs != null && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
5112
|
+
args.push("--timeout-ms", String(Math.round(options.timeoutMs)));
|
|
5113
|
+
}
|
|
5114
|
+
if (options.verbose) {
|
|
5115
|
+
args.push("--verbose");
|
|
5116
|
+
}
|
|
5117
|
+
if (options.suppressSdkConsoleErrors) {
|
|
5118
|
+
args.push("--suppress-sdk-console-errors");
|
|
5119
|
+
}
|
|
5120
|
+
return {
|
|
5121
|
+
command: process.execPath,
|
|
5122
|
+
args,
|
|
5123
|
+
cwd: absolutePath(record.cwd)
|
|
5124
|
+
};
|
|
5125
|
+
}
|
|
5126
|
+
async function sendViaDetachedQueueOwner(options) {
|
|
5127
|
+
const waitForCompletion = options.waitForCompletion !== false;
|
|
5128
|
+
const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
|
|
5129
|
+
const ownerSpawn = options.queueOwnerSpawn ?? await buildDefaultQueueOwnerSpawn(options, queueOwnerTtlMs);
|
|
5130
|
+
const startupDeadline = Date.now() + QUEUE_OWNER_STARTUP_TIMEOUT_MS;
|
|
5131
|
+
let lastSpawnAttemptAt = 0;
|
|
5132
|
+
for (; ; ) {
|
|
5133
|
+
try {
|
|
5134
|
+
const queuedToOwner = await trySubmitToRunningOwner({
|
|
5135
|
+
sessionId: options.sessionId,
|
|
5136
|
+
message: options.message,
|
|
5137
|
+
permissionMode: options.permissionMode,
|
|
5138
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5139
|
+
outputFormatter: options.outputFormatter,
|
|
5140
|
+
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
5141
|
+
timeoutMs: options.timeoutMs,
|
|
5142
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5143
|
+
waitForCompletion,
|
|
5144
|
+
verbose: options.verbose
|
|
5145
|
+
});
|
|
5146
|
+
if (queuedToOwner) {
|
|
5147
|
+
return queuedToOwner;
|
|
5148
|
+
}
|
|
5149
|
+
} catch (error) {
|
|
5150
|
+
if (!isQueueNotAcceptingError(error)) {
|
|
5151
|
+
throw error;
|
|
5152
|
+
}
|
|
5153
|
+
if (Date.now() >= startupDeadline) {
|
|
5154
|
+
throw new QueueConnectionError(
|
|
5155
|
+
"Timed out waiting for detached queue owner to accept prompt requests",
|
|
5156
|
+
{
|
|
5157
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
5158
|
+
origin: "queue",
|
|
5159
|
+
retryable: true,
|
|
5160
|
+
cause: error instanceof Error ? error : void 0
|
|
5161
|
+
}
|
|
5162
|
+
);
|
|
5163
|
+
}
|
|
5164
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
5165
|
+
continue;
|
|
5166
|
+
}
|
|
5167
|
+
const now = Date.now();
|
|
5168
|
+
if (now >= startupDeadline) {
|
|
5169
|
+
throw new QueueConnectionError(
|
|
5170
|
+
"Timed out waiting for detached queue owner to start",
|
|
5171
|
+
{
|
|
5172
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
5173
|
+
origin: "queue",
|
|
5174
|
+
retryable: true
|
|
5175
|
+
}
|
|
5176
|
+
);
|
|
5177
|
+
}
|
|
5178
|
+
if (now - lastSpawnAttemptAt >= QUEUE_OWNER_RESPAWN_BACKOFF_MS) {
|
|
5179
|
+
spawnDetachedQueueOwner(ownerSpawn);
|
|
5180
|
+
lastSpawnAttemptAt = now;
|
|
5181
|
+
if (options.verbose) {
|
|
5182
|
+
process.stderr.write(
|
|
5183
|
+
`[acpx] starting detached queue owner for session ${options.sessionId}
|
|
5184
|
+
`
|
|
5185
|
+
);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
|
|
4589
5192
|
// src/session-runtime-history.ts
|
|
4590
5193
|
var SESSION_HISTORY_MAX_ENTRIES = 500;
|
|
4591
5194
|
var SESSION_HISTORY_PREVIEW_CHARS = 220;
|
|
@@ -4774,7 +5377,6 @@ async function connectAndLoadSession(options) {
|
|
|
4774
5377
|
}
|
|
4775
5378
|
|
|
4776
5379
|
// src/session-runtime.ts
|
|
4777
|
-
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4778
5380
|
var INTERRUPT_CANCEL_WAIT_MS = 2500;
|
|
4779
5381
|
var TimeoutError = class extends Error {
|
|
4780
5382
|
constructor(timeoutMs) {
|
|
@@ -4902,15 +5504,6 @@ var DISCARD_OUTPUT_FORMATTER = {
|
|
|
4902
5504
|
flush() {
|
|
4903
5505
|
}
|
|
4904
5506
|
};
|
|
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
5507
|
function shouldFallbackToNewSession(error) {
|
|
4915
5508
|
if (error instanceof TimeoutError || error instanceof InterruptedError) {
|
|
4916
5509
|
return false;
|
|
@@ -5376,179 +5969,41 @@ async function ensureSession(options) {
|
|
|
5376
5969
|
created: true
|
|
5377
5970
|
};
|
|
5378
5971
|
}
|
|
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,
|
|
5972
|
+
async function runQueueOwnerProcess2(options) {
|
|
5973
|
+
await runQueueOwnerProcess(options, {
|
|
5974
|
+
runQueuedTask,
|
|
5975
|
+
withTimeout: async (run, timeoutMs) => await withTimeout(run(), timeoutMs),
|
|
5976
|
+
setSessionModeFallback: async (modeId, timeoutMs) => {
|
|
5977
|
+
await runSessionSetModeDirect({
|
|
5978
|
+
sessionRecordId: options.sessionId,
|
|
5979
|
+
modeId,
|
|
5404
5980
|
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
timeoutMs
|
|
5408
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5409
|
-
waitForCompletion,
|
|
5981
|
+
authCredentials: options.authCredentials,
|
|
5982
|
+
authPolicy: options.authPolicy,
|
|
5983
|
+
timeoutMs,
|
|
5410
5984
|
verbose: options.verbose
|
|
5411
5985
|
});
|
|
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
|
-
});
|
|
5986
|
+
},
|
|
5987
|
+
setSessionConfigOptionFallback: async (configId, value, timeoutMs) => {
|
|
5988
|
+
const result = await runSessionSetConfigOptionDirect({
|
|
5989
|
+
sessionRecordId: options.sessionId,
|
|
5990
|
+
configId,
|
|
5991
|
+
value,
|
|
5992
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5993
|
+
authCredentials: options.authCredentials,
|
|
5994
|
+
authPolicy: options.authPolicy,
|
|
5995
|
+
timeoutMs,
|
|
5996
|
+
verbose: options.verbose
|
|
5514
5997
|
});
|
|
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);
|
|
5998
|
+
return result.response;
|
|
5550
5999
|
}
|
|
5551
|
-
}
|
|
6000
|
+
});
|
|
6001
|
+
}
|
|
6002
|
+
async function sendSession(options) {
|
|
6003
|
+
return await sendViaDetachedQueueOwner(options);
|
|
6004
|
+
}
|
|
6005
|
+
async function readSessionQueueOwnerStatus(sessionId) {
|
|
6006
|
+
return await readQueueOwnerStatus(sessionId);
|
|
5552
6007
|
}
|
|
5553
6008
|
async function cancelSessionPrompt(options) {
|
|
5554
6009
|
const cancelled2 = await tryCancelOnRunningOwner(options);
|
|
@@ -5626,9 +6081,9 @@ async function isLikelyMatchingProcess(pid, agentCommand) {
|
|
|
5626
6081
|
if (argv.length === 0) {
|
|
5627
6082
|
return false;
|
|
5628
6083
|
}
|
|
5629
|
-
const executableBase =
|
|
5630
|
-
const expectedBase =
|
|
5631
|
-
return executableBase === expectedBase || argv.some((entry) =>
|
|
6084
|
+
const executableBase = path7.basename(argv[0]);
|
|
6085
|
+
const expectedBase = path7.basename(expectedToken);
|
|
6086
|
+
return executableBase === expectedBase || argv.some((entry) => path7.basename(entry) === expectedBase);
|
|
5632
6087
|
} catch {
|
|
5633
6088
|
return true;
|
|
5634
6089
|
}
|
|
@@ -5654,6 +6109,7 @@ var NoSessionError = class extends Error {
|
|
|
5654
6109
|
}
|
|
5655
6110
|
};
|
|
5656
6111
|
var TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
|
|
6112
|
+
"__queue-owner",
|
|
5657
6113
|
"prompt",
|
|
5658
6114
|
"exec",
|
|
5659
6115
|
"cancel",
|
|
@@ -5666,25 +6122,25 @@ var TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
|
|
|
5666
6122
|
]);
|
|
5667
6123
|
function parseOutputFormat2(value) {
|
|
5668
6124
|
if (!OUTPUT_FORMATS.includes(value)) {
|
|
5669
|
-
throw new
|
|
6125
|
+
throw new InvalidArgumentError3(
|
|
5670
6126
|
`Invalid format "${value}". Expected one of: ${OUTPUT_FORMATS.join(", ")}`
|
|
5671
6127
|
);
|
|
5672
6128
|
}
|
|
5673
6129
|
return value;
|
|
5674
6130
|
}
|
|
5675
|
-
function
|
|
6131
|
+
function parseAuthPolicy3(value) {
|
|
5676
6132
|
if (!AUTH_POLICIES.includes(value)) {
|
|
5677
|
-
throw new
|
|
6133
|
+
throw new InvalidArgumentError3(
|
|
5678
6134
|
`Invalid auth policy "${value}". Expected one of: ${AUTH_POLICIES.join(", ")}`
|
|
5679
6135
|
);
|
|
5680
6136
|
}
|
|
5681
6137
|
return value;
|
|
5682
6138
|
}
|
|
5683
|
-
function
|
|
6139
|
+
function parseNonInteractivePermissionPolicy3(value) {
|
|
5684
6140
|
if (!NON_INTERACTIVE_PERMISSION_POLICIES.includes(
|
|
5685
6141
|
value
|
|
5686
6142
|
)) {
|
|
5687
|
-
throw new
|
|
6143
|
+
throw new InvalidArgumentError3(
|
|
5688
6144
|
`Invalid non-interactive permission policy "${value}". Expected one of: ${NON_INTERACTIVE_PERMISSION_POLICIES.join(", ")}`
|
|
5689
6145
|
);
|
|
5690
6146
|
}
|
|
@@ -5693,35 +6149,35 @@ function parseNonInteractivePermissionPolicy2(value) {
|
|
|
5693
6149
|
function parseTimeoutSeconds(value) {
|
|
5694
6150
|
const parsed = Number(value);
|
|
5695
6151
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
5696
|
-
throw new
|
|
6152
|
+
throw new InvalidArgumentError3("Timeout must be a positive number of seconds");
|
|
5697
6153
|
}
|
|
5698
6154
|
return Math.round(parsed * 1e3);
|
|
5699
6155
|
}
|
|
5700
6156
|
function parseTtlSeconds(value) {
|
|
5701
6157
|
const parsed = Number(value);
|
|
5702
6158
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
5703
|
-
throw new
|
|
6159
|
+
throw new InvalidArgumentError3("TTL must be a non-negative number of seconds");
|
|
5704
6160
|
}
|
|
5705
6161
|
return Math.round(parsed * 1e3);
|
|
5706
6162
|
}
|
|
5707
6163
|
function parseSessionName(value) {
|
|
5708
6164
|
const trimmed = value.trim();
|
|
5709
6165
|
if (trimmed.length === 0) {
|
|
5710
|
-
throw new
|
|
6166
|
+
throw new InvalidArgumentError3("Session name must not be empty");
|
|
5711
6167
|
}
|
|
5712
6168
|
return trimmed;
|
|
5713
6169
|
}
|
|
5714
|
-
function
|
|
6170
|
+
function parseNonEmptyValue2(label, value) {
|
|
5715
6171
|
const trimmed = value.trim();
|
|
5716
6172
|
if (trimmed.length === 0) {
|
|
5717
|
-
throw new
|
|
6173
|
+
throw new InvalidArgumentError3(`${label} must not be empty`);
|
|
5718
6174
|
}
|
|
5719
6175
|
return trimmed;
|
|
5720
6176
|
}
|
|
5721
6177
|
function parseHistoryLimit(value) {
|
|
5722
6178
|
const parsed = Number(value);
|
|
5723
6179
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
5724
|
-
throw new
|
|
6180
|
+
throw new InvalidArgumentError3("Limit must be a positive integer");
|
|
5725
6181
|
}
|
|
5726
6182
|
return parsed;
|
|
5727
6183
|
}
|
|
@@ -5730,7 +6186,7 @@ function resolvePermissionMode(flags, defaultMode) {
|
|
|
5730
6186
|
Boolean
|
|
5731
6187
|
).length;
|
|
5732
6188
|
if (selected2 > 1) {
|
|
5733
|
-
throw new
|
|
6189
|
+
throw new InvalidArgumentError3(
|
|
5734
6190
|
"Use only one permission mode: --approve-all, --approve-reads, or --deny-all"
|
|
5735
6191
|
);
|
|
5736
6192
|
}
|
|
@@ -5751,13 +6207,13 @@ async function readPromptInputFromStdin() {
|
|
|
5751
6207
|
}
|
|
5752
6208
|
async function readPrompt(promptParts, filePath, cwd) {
|
|
5753
6209
|
if (filePath) {
|
|
5754
|
-
const source = filePath === "-" ? await readPromptInputFromStdin() : await fs6.readFile(
|
|
6210
|
+
const source = filePath === "-" ? await readPromptInputFromStdin() : await fs6.readFile(path8.resolve(cwd, filePath), "utf8");
|
|
5755
6211
|
const pieces = [source.trim(), promptParts.join(" ").trim()].filter(
|
|
5756
6212
|
(value) => value.length > 0
|
|
5757
6213
|
);
|
|
5758
6214
|
const prompt2 = pieces.join("\n\n").trim();
|
|
5759
6215
|
if (!prompt2) {
|
|
5760
|
-
throw new
|
|
6216
|
+
throw new InvalidArgumentError3("Prompt from --file is empty");
|
|
5761
6217
|
}
|
|
5762
6218
|
return prompt2;
|
|
5763
6219
|
}
|
|
@@ -5766,13 +6222,13 @@ async function readPrompt(promptParts, filePath, cwd) {
|
|
|
5766
6222
|
return joined;
|
|
5767
6223
|
}
|
|
5768
6224
|
if (process.stdin.isTTY) {
|
|
5769
|
-
throw new
|
|
6225
|
+
throw new InvalidArgumentError3(
|
|
5770
6226
|
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
5771
6227
|
);
|
|
5772
6228
|
}
|
|
5773
6229
|
const prompt = (await readPromptInputFromStdin()).trim();
|
|
5774
6230
|
if (!prompt) {
|
|
5775
|
-
throw new
|
|
6231
|
+
throw new InvalidArgumentError3("Prompt from stdin is empty");
|
|
5776
6232
|
}
|
|
5777
6233
|
return prompt;
|
|
5778
6234
|
}
|
|
@@ -5787,14 +6243,14 @@ function addGlobalFlags(command) {
|
|
|
5787
6243
|
return command.option("--agent <command>", "Raw ACP agent command (escape hatch)").option("--cwd <dir>", "Working directory", process.cwd()).option(
|
|
5788
6244
|
"--auth-policy <policy>",
|
|
5789
6245
|
"Authentication policy: skip or fail when auth is required",
|
|
5790
|
-
|
|
6246
|
+
parseAuthPolicy3
|
|
5791
6247
|
).option("--approve-all", "Auto-approve all permission requests").option(
|
|
5792
6248
|
"--approve-reads",
|
|
5793
6249
|
"Auto-approve read/search requests and prompt for writes"
|
|
5794
6250
|
).option("--deny-all", "Deny all permission requests").option(
|
|
5795
6251
|
"--non-interactive-permissions <policy>",
|
|
5796
6252
|
"When prompting is unavailable: deny or fail",
|
|
5797
|
-
|
|
6253
|
+
parseNonInteractivePermissionPolicy3
|
|
5798
6254
|
).option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat2).option(
|
|
5799
6255
|
"--json-strict",
|
|
5800
6256
|
"Strict JSON mode: requires --format json and suppresses non-JSON stderr output"
|
|
@@ -5851,10 +6307,10 @@ function resolveGlobalFlags(command, config) {
|
|
|
5851
6307
|
const jsonStrict = opts.jsonStrict === true;
|
|
5852
6308
|
const verbose = opts.verbose === true;
|
|
5853
6309
|
if (jsonStrict && format !== "json") {
|
|
5854
|
-
throw new
|
|
6310
|
+
throw new InvalidArgumentError3("--json-strict requires --format json");
|
|
5855
6311
|
}
|
|
5856
6312
|
if (jsonStrict && verbose) {
|
|
5857
|
-
throw new
|
|
6313
|
+
throw new InvalidArgumentError3("--json-strict cannot be combined with --verbose");
|
|
5858
6314
|
}
|
|
5859
6315
|
return {
|
|
5860
6316
|
agent: opts.agent,
|
|
@@ -5883,7 +6339,7 @@ function resolveOutputPolicy(format, jsonStrict) {
|
|
|
5883
6339
|
function resolveAgentInvocation(explicitAgentName, globalFlags, config) {
|
|
5884
6340
|
const override = globalFlags.agent?.trim();
|
|
5885
6341
|
if (override && explicitAgentName) {
|
|
5886
|
-
throw new
|
|
6342
|
+
throw new InvalidArgumentError3(
|
|
5887
6343
|
"Do not combine positional agent with --agent override"
|
|
5888
6344
|
);
|
|
5889
6345
|
}
|
|
@@ -5892,7 +6348,7 @@ function resolveAgentInvocation(explicitAgentName, globalFlags, config) {
|
|
|
5892
6348
|
return {
|
|
5893
6349
|
agentName,
|
|
5894
6350
|
agentCommand,
|
|
5895
|
-
cwd:
|
|
6351
|
+
cwd: path8.resolve(globalFlags.cwd)
|
|
5896
6352
|
};
|
|
5897
6353
|
}
|
|
5898
6354
|
function printSessionsByFormat(sessions, format) {
|
|
@@ -6031,19 +6487,19 @@ function formatSessionLabel(record) {
|
|
|
6031
6487
|
return record.name ?? "cwd";
|
|
6032
6488
|
}
|
|
6033
6489
|
function formatRoutedFrom(sessionCwd, currentCwd) {
|
|
6034
|
-
const relative =
|
|
6490
|
+
const relative = path8.relative(sessionCwd, currentCwd);
|
|
6035
6491
|
if (!relative || relative === ".") {
|
|
6036
6492
|
return void 0;
|
|
6037
6493
|
}
|
|
6038
|
-
return relative.startsWith(".") ? relative : `.${
|
|
6494
|
+
return relative.startsWith(".") ? relative : `.${path8.sep}${relative}`;
|
|
6039
6495
|
}
|
|
6040
6496
|
function sessionConnectionStatus(record) {
|
|
6041
6497
|
return isProcessAlive(record.pid) ? "connected" : "needs reconnect";
|
|
6042
6498
|
}
|
|
6043
6499
|
function formatPromptSessionBannerLine(record, currentCwd) {
|
|
6044
6500
|
const label = formatSessionLabel(record);
|
|
6045
|
-
const normalizedSessionCwd =
|
|
6046
|
-
const normalizedCurrentCwd =
|
|
6501
|
+
const normalizedSessionCwd = path8.resolve(record.cwd);
|
|
6502
|
+
const normalizedCurrentCwd = path8.resolve(currentCwd);
|
|
6047
6503
|
const routedFrom = normalizedSessionCwd === normalizedCurrentCwd ? void 0 : formatRoutedFrom(normalizedSessionCwd, normalizedCurrentCwd);
|
|
6048
6504
|
const status = sessionConnectionStatus(record);
|
|
6049
6505
|
if (routedFrom) {
|
|
@@ -6561,7 +7017,12 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6561
7017
|
uptime: null,
|
|
6562
7018
|
lastPromptTime: null,
|
|
6563
7019
|
exitCode: null,
|
|
6564
|
-
signal: null
|
|
7020
|
+
signal: null,
|
|
7021
|
+
ownerPid: null,
|
|
7022
|
+
ownerStatus: null,
|
|
7023
|
+
ownerGeneration: null,
|
|
7024
|
+
ownerHeartbeatAt: null,
|
|
7025
|
+
ownerQueueDepth: null
|
|
6565
7026
|
};
|
|
6566
7027
|
if (globalFlags.format === "json") {
|
|
6567
7028
|
process.stdout.write(`${JSON.stringify(payload2)}
|
|
@@ -6583,10 +7044,14 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6583
7044
|
process.stdout.write(`uptime: -
|
|
6584
7045
|
`);
|
|
6585
7046
|
process.stdout.write(`lastPromptTime: -
|
|
7047
|
+
`);
|
|
7048
|
+
process.stdout.write(`ownerStatus: -
|
|
6586
7049
|
`);
|
|
6587
7050
|
return;
|
|
6588
7051
|
}
|
|
6589
7052
|
const running = isProcessAlive(record.pid);
|
|
7053
|
+
const owner = await readSessionQueueOwnerStatus(record.id);
|
|
7054
|
+
const ownerStatus = owner ? owner.stale ? "stale" : "active" : "inactive";
|
|
6590
7055
|
const payload = {
|
|
6591
7056
|
...canonicalSessionIdentity(record),
|
|
6592
7057
|
agentCommand: record.agentCommand,
|
|
@@ -6595,7 +7060,12 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6595
7060
|
uptime: running ? formatUptime(record.agentStartedAt) ?? null : null,
|
|
6596
7061
|
lastPromptTime: record.lastPromptAt ?? null,
|
|
6597
7062
|
exitCode: running ? null : record.lastAgentExitCode ?? null,
|
|
6598
|
-
signal: running ? null : record.lastAgentExitSignal ?? null
|
|
7063
|
+
signal: running ? null : record.lastAgentExitSignal ?? null,
|
|
7064
|
+
ownerPid: owner?.pid ?? null,
|
|
7065
|
+
ownerStatus,
|
|
7066
|
+
ownerGeneration: owner?.ownerGeneration ?? null,
|
|
7067
|
+
ownerHeartbeatAt: owner?.heartbeatAt ?? null,
|
|
7068
|
+
ownerQueueDepth: owner?.queueDepth ?? null
|
|
6599
7069
|
};
|
|
6600
7070
|
if (globalFlags.format === "json") {
|
|
6601
7071
|
process.stdout.write(`${JSON.stringify(payload)}
|
|
@@ -6622,6 +7092,16 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6622
7092
|
process.stdout.write(`uptime: ${payload.uptime ?? "-"}
|
|
6623
7093
|
`);
|
|
6624
7094
|
process.stdout.write(`lastPromptTime: ${payload.lastPromptTime ?? "-"}
|
|
7095
|
+
`);
|
|
7096
|
+
process.stdout.write(`ownerStatus: ${payload.ownerStatus}
|
|
7097
|
+
`);
|
|
7098
|
+
process.stdout.write(`ownerPid: ${payload.ownerPid ?? "-"}
|
|
7099
|
+
`);
|
|
7100
|
+
process.stdout.write(`ownerGeneration: ${payload.ownerGeneration ?? "-"}
|
|
7101
|
+
`);
|
|
7102
|
+
process.stdout.write(`ownerHeartbeatAt: ${payload.ownerHeartbeatAt ?? "-"}
|
|
7103
|
+
`);
|
|
7104
|
+
process.stdout.write(`ownerQueueDepth: ${payload.ownerQueueDepth ?? "-"}
|
|
6625
7105
|
`);
|
|
6626
7106
|
if (payload.status === "dead") {
|
|
6627
7107
|
process.stdout.write(`exitCode: ${payload.exitCode ?? "-"}
|
|
@@ -6726,7 +7206,7 @@ function registerSharedAgentSubcommands(parent, explicitAgentName, config, descr
|
|
|
6726
7206
|
const setModeCommand = parent.command("set-mode").description(descriptions.setMode).argument(
|
|
6727
7207
|
"<mode>",
|
|
6728
7208
|
"Mode id",
|
|
6729
|
-
(value) =>
|
|
7209
|
+
(value) => parseNonEmptyValue2("Mode", value)
|
|
6730
7210
|
);
|
|
6731
7211
|
addSessionNameOption(setModeCommand);
|
|
6732
7212
|
setModeCommand.action(async function(modeId, flags) {
|
|
@@ -6735,11 +7215,11 @@ function registerSharedAgentSubcommands(parent, explicitAgentName, config, descr
|
|
|
6735
7215
|
const setConfigCommand = parent.command("set").description(descriptions.setConfig).argument(
|
|
6736
7216
|
"<key>",
|
|
6737
7217
|
"Config option id",
|
|
6738
|
-
(value) =>
|
|
7218
|
+
(value) => parseNonEmptyValue2("Config option key", value)
|
|
6739
7219
|
).argument(
|
|
6740
7220
|
"<value>",
|
|
6741
7221
|
"Config option value",
|
|
6742
|
-
(value) =>
|
|
7222
|
+
(value) => parseNonEmptyValue2("Config option value", value)
|
|
6743
7223
|
);
|
|
6744
7224
|
addSessionNameOption(setConfigCommand);
|
|
6745
7225
|
setConfigCommand.action(async function(key, value, flags) {
|
|
@@ -6831,14 +7311,14 @@ function detectInitialCwd(argv) {
|
|
|
6831
7311
|
if (token === "--cwd") {
|
|
6832
7312
|
const next = argv[index + 1];
|
|
6833
7313
|
if (next && next !== "--") {
|
|
6834
|
-
return
|
|
7314
|
+
return path8.resolve(next);
|
|
6835
7315
|
}
|
|
6836
7316
|
break;
|
|
6837
7317
|
}
|
|
6838
7318
|
if (token.startsWith("--cwd=")) {
|
|
6839
7319
|
const value = token.slice("--cwd=".length).trim();
|
|
6840
7320
|
if (value.length > 0) {
|
|
6841
|
-
return
|
|
7321
|
+
return path8.resolve(value);
|
|
6842
7322
|
}
|
|
6843
7323
|
break;
|
|
6844
7324
|
}
|
|
@@ -6934,9 +7414,12 @@ async function main(argv = process.argv) {
|
|
|
6934
7414
|
requestedOutputFormat,
|
|
6935
7415
|
requestedJsonStrict
|
|
6936
7416
|
);
|
|
6937
|
-
const
|
|
7417
|
+
const internalQueueOwnerFlags = parseQueueOwnerFlags(
|
|
7418
|
+
argv.slice(2),
|
|
7419
|
+
DEFAULT_QUEUE_OWNER_TTL_MS
|
|
7420
|
+
);
|
|
6938
7421
|
const program = new Command();
|
|
6939
|
-
program.name("acpx").description("Headless CLI client for the Agent Client Protocol").enablePositionalOptions().showHelpAfterError();
|
|
7422
|
+
program.name("acpx").description("Headless CLI client for the Agent Client Protocol").version(getAcpxVersion()).enablePositionalOptions().showHelpAfterError();
|
|
6940
7423
|
if (requestedJsonStrict) {
|
|
6941
7424
|
program.configureOutput({
|
|
6942
7425
|
writeOut: () => {
|
|
@@ -6946,56 +7429,39 @@ async function main(argv = process.argv) {
|
|
|
6946
7429
|
});
|
|
6947
7430
|
}
|
|
6948
7431
|
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;
|
|
7432
|
+
configurePublicCli({
|
|
7433
|
+
program,
|
|
7434
|
+
argv: argv.slice(2),
|
|
7435
|
+
config,
|
|
7436
|
+
requestedJsonStrict,
|
|
7437
|
+
topLevelVerbs: TOP_LEVEL_VERBS,
|
|
7438
|
+
listBuiltInAgents,
|
|
7439
|
+
detectAgentToken,
|
|
7440
|
+
registerAgentCommand,
|
|
7441
|
+
registerDefaultCommands,
|
|
7442
|
+
handlePromptAction: async (command, promptParts) => {
|
|
7443
|
+
await handlePrompt(void 0, promptParts, {}, command, config);
|
|
6966
7444
|
}
|
|
6967
|
-
await handlePrompt(void 0, promptParts, {}, this, config);
|
|
6968
7445
|
});
|
|
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
7446
|
program.exitOverride((error) => {
|
|
6995
7447
|
throw error;
|
|
6996
7448
|
});
|
|
6997
7449
|
await runWithOutputPolicy(requestedOutputPolicy, async () => {
|
|
6998
7450
|
try {
|
|
7451
|
+
if (internalQueueOwnerFlags) {
|
|
7452
|
+
await runQueueOwnerProcess2({
|
|
7453
|
+
sessionId: internalQueueOwnerFlags.sessionId,
|
|
7454
|
+
ttlMs: internalQueueOwnerFlags.ttlMs,
|
|
7455
|
+
permissionMode: internalQueueOwnerFlags.permissionMode,
|
|
7456
|
+
nonInteractivePermissions: internalQueueOwnerFlags.nonInteractivePermissions,
|
|
7457
|
+
authCredentials: config.auth,
|
|
7458
|
+
authPolicy: internalQueueOwnerFlags.authPolicy,
|
|
7459
|
+
timeoutMs: internalQueueOwnerFlags.timeoutMs,
|
|
7460
|
+
suppressSdkConsoleErrors: internalQueueOwnerFlags.suppressSdkConsoleErrors,
|
|
7461
|
+
verbose: internalQueueOwnerFlags.verbose
|
|
7462
|
+
});
|
|
7463
|
+
return;
|
|
7464
|
+
}
|
|
6999
7465
|
await program.parseAsync(argv);
|
|
7000
7466
|
} catch (error) {
|
|
7001
7467
|
if (error instanceof CommanderError) {
|