commonswarm 0.1.62 → 0.1.63
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/cswarm.cjs +2993 -243
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -4381,8 +4381,8 @@ var require_RealtimeChannel = __commonJS({
|
|
|
4381
4381
|
}
|
|
4382
4382
|
/** @internal */
|
|
4383
4383
|
_notThisChannelEvent(event, ref) {
|
|
4384
|
-
const { close, error, leave, join:
|
|
4385
|
-
const events = [close, error, leave,
|
|
4384
|
+
const { close, error, leave, join: join16 } = constants_1.CHANNEL_EVENTS;
|
|
4385
|
+
const events = [close, error, leave, join16];
|
|
4386
4386
|
return ref && events.includes(event) && ref !== this.joinPush.ref;
|
|
4387
4387
|
}
|
|
4388
4388
|
/** @internal */
|
|
@@ -13499,6 +13499,2885 @@ var require_main3 = __commonJS({
|
|
|
13499
13499
|
}
|
|
13500
13500
|
});
|
|
13501
13501
|
|
|
13502
|
+
// src/host/types.ts
|
|
13503
|
+
var TRANSIENT_ACP_CODES, AcpHostError, AcpProtocolError, AcpTimeoutError, AcpChildExitError, AcpTransportError, AcpVersionError, AcpVersionParseError, AcpVersionBelowFloorError, AcpPermissionCanaryError, AcpPromptsBlockedError;
|
|
13504
|
+
var init_types = __esm({
|
|
13505
|
+
"src/host/types.ts"() {
|
|
13506
|
+
"use strict";
|
|
13507
|
+
TRANSIENT_ACP_CODES = /* @__PURE__ */ new Set([
|
|
13508
|
+
"timeout",
|
|
13509
|
+
"child_exit",
|
|
13510
|
+
"transport"
|
|
13511
|
+
]);
|
|
13512
|
+
AcpHostError = class extends Error {
|
|
13513
|
+
code;
|
|
13514
|
+
constructor(code, message) {
|
|
13515
|
+
super(message);
|
|
13516
|
+
this.name = "AcpHostError";
|
|
13517
|
+
this.code = code;
|
|
13518
|
+
}
|
|
13519
|
+
};
|
|
13520
|
+
AcpProtocolError = class extends AcpHostError {
|
|
13521
|
+
constructor(message, code = "protocol_error", peerError = null) {
|
|
13522
|
+
super(code, message);
|
|
13523
|
+
this.peerError = peerError;
|
|
13524
|
+
this.name = "AcpProtocolError";
|
|
13525
|
+
}
|
|
13526
|
+
peerError;
|
|
13527
|
+
};
|
|
13528
|
+
AcpTimeoutError = class extends AcpHostError {
|
|
13529
|
+
constructor(message) {
|
|
13530
|
+
super("timeout", message);
|
|
13531
|
+
this.name = "AcpTimeoutError";
|
|
13532
|
+
}
|
|
13533
|
+
};
|
|
13534
|
+
AcpChildExitError = class extends AcpHostError {
|
|
13535
|
+
exitCode;
|
|
13536
|
+
signal;
|
|
13537
|
+
constructor(exitCode, signal) {
|
|
13538
|
+
super(
|
|
13539
|
+
"child_exit",
|
|
13540
|
+
`ACP child exited (code=${exitCode ?? "null"}, signal=${signal ?? "null"})`
|
|
13541
|
+
);
|
|
13542
|
+
this.name = "AcpChildExitError";
|
|
13543
|
+
this.exitCode = exitCode;
|
|
13544
|
+
this.signal = signal;
|
|
13545
|
+
}
|
|
13546
|
+
};
|
|
13547
|
+
AcpTransportError = class extends AcpHostError {
|
|
13548
|
+
constructor(cause) {
|
|
13549
|
+
super("transport", `ACP transport failed: ${cause.message}`);
|
|
13550
|
+
this.cause = cause;
|
|
13551
|
+
this.name = "AcpTransportError";
|
|
13552
|
+
}
|
|
13553
|
+
cause;
|
|
13554
|
+
};
|
|
13555
|
+
AcpVersionError = class extends AcpHostError {
|
|
13556
|
+
constructor(message, code = "version_refused") {
|
|
13557
|
+
super(code, message);
|
|
13558
|
+
this.name = "AcpVersionError";
|
|
13559
|
+
}
|
|
13560
|
+
};
|
|
13561
|
+
AcpVersionParseError = class extends AcpVersionError {
|
|
13562
|
+
constructor(message) {
|
|
13563
|
+
super(message, "version_unparseable");
|
|
13564
|
+
this.name = "AcpVersionParseError";
|
|
13565
|
+
}
|
|
13566
|
+
};
|
|
13567
|
+
AcpVersionBelowFloorError = class extends AcpVersionError {
|
|
13568
|
+
constructor(provider, minimum, actual) {
|
|
13569
|
+
super(
|
|
13570
|
+
`refusing ${provider} ${actual}; CommonSwarm requires ${minimum} or newer`,
|
|
13571
|
+
"version_below_floor"
|
|
13572
|
+
);
|
|
13573
|
+
this.provider = provider;
|
|
13574
|
+
this.minimum = minimum;
|
|
13575
|
+
this.actual = actual;
|
|
13576
|
+
this.name = "AcpVersionBelowFloorError";
|
|
13577
|
+
}
|
|
13578
|
+
provider;
|
|
13579
|
+
minimum;
|
|
13580
|
+
actual;
|
|
13581
|
+
};
|
|
13582
|
+
AcpPermissionCanaryError = class extends AcpHostError {
|
|
13583
|
+
constructor(message, reasonCode = null, minimumRequiredVersion = null, peerError = null) {
|
|
13584
|
+
super("permission_canary_failed", message);
|
|
13585
|
+
this.reasonCode = reasonCode;
|
|
13586
|
+
this.minimumRequiredVersion = minimumRequiredVersion;
|
|
13587
|
+
this.peerError = peerError;
|
|
13588
|
+
this.name = "AcpPermissionCanaryError";
|
|
13589
|
+
}
|
|
13590
|
+
reasonCode;
|
|
13591
|
+
minimumRequiredVersion;
|
|
13592
|
+
peerError;
|
|
13593
|
+
};
|
|
13594
|
+
AcpPromptsBlockedError = class extends AcpHostError {
|
|
13595
|
+
constructor() {
|
|
13596
|
+
super(
|
|
13597
|
+
"prompts_blocked",
|
|
13598
|
+
"Real prompts are blocked until the permission-boundary canary passes"
|
|
13599
|
+
);
|
|
13600
|
+
this.name = "AcpPromptsBlockedError";
|
|
13601
|
+
}
|
|
13602
|
+
};
|
|
13603
|
+
}
|
|
13604
|
+
});
|
|
13605
|
+
|
|
13606
|
+
// src/host/version.ts
|
|
13607
|
+
function parseSemVer(value) {
|
|
13608
|
+
if (!SEMVER_RE.test(value)) return null;
|
|
13609
|
+
const withoutBuild = value.split("+", 1)[0];
|
|
13610
|
+
const dash = withoutBuild.indexOf("-");
|
|
13611
|
+
const coreText = dash === -1 ? withoutBuild : withoutBuild.slice(0, dash);
|
|
13612
|
+
const prereleaseText = dash === -1 ? null : withoutBuild.slice(dash + 1);
|
|
13613
|
+
const coreParts = coreText.split(".");
|
|
13614
|
+
if (coreParts.length !== 3) return null;
|
|
13615
|
+
return {
|
|
13616
|
+
core: [BigInt(coreParts[0]), BigInt(coreParts[1]), BigInt(coreParts[2])],
|
|
13617
|
+
prerelease: prereleaseText === null ? null : prereleaseText.split(".")
|
|
13618
|
+
};
|
|
13619
|
+
}
|
|
13620
|
+
function compareSemVer(left, right) {
|
|
13621
|
+
const a = parseSemVer(left);
|
|
13622
|
+
const b2 = parseSemVer(right);
|
|
13623
|
+
if (!a || !b2) {
|
|
13624
|
+
throw new AcpVersionParseError(
|
|
13625
|
+
`cannot compare invalid semantic versions: ${JSON.stringify(left)} and ${JSON.stringify(right)}`
|
|
13626
|
+
);
|
|
13627
|
+
}
|
|
13628
|
+
for (let index = 0; index < 3; index += 1) {
|
|
13629
|
+
if (a.core[index] < b2.core[index]) return -1;
|
|
13630
|
+
if (a.core[index] > b2.core[index]) return 1;
|
|
13631
|
+
}
|
|
13632
|
+
if (a.prerelease === null && b2.prerelease === null) return 0;
|
|
13633
|
+
if (a.prerelease === null) return 1;
|
|
13634
|
+
if (b2.prerelease === null) return -1;
|
|
13635
|
+
const length = Math.max(a.prerelease.length, b2.prerelease.length);
|
|
13636
|
+
for (let index = 0; index < length; index += 1) {
|
|
13637
|
+
const leftPart = a.prerelease[index];
|
|
13638
|
+
const rightPart = b2.prerelease[index];
|
|
13639
|
+
if (leftPart === void 0) return -1;
|
|
13640
|
+
if (rightPart === void 0) return 1;
|
|
13641
|
+
if (leftPart === rightPart) continue;
|
|
13642
|
+
const leftNumeric = /^\d+$/.test(leftPart);
|
|
13643
|
+
const rightNumeric = /^\d+$/.test(rightPart);
|
|
13644
|
+
if (leftNumeric && rightNumeric) {
|
|
13645
|
+
return BigInt(leftPart) < BigInt(rightPart) ? -1 : 1;
|
|
13646
|
+
}
|
|
13647
|
+
if (leftNumeric) return -1;
|
|
13648
|
+
if (rightNumeric) return 1;
|
|
13649
|
+
return leftPart < rightPart ? -1 : 1;
|
|
13650
|
+
}
|
|
13651
|
+
return 0;
|
|
13652
|
+
}
|
|
13653
|
+
function parseProviderVersionOutput(stdout, productPattern, allowBare = true) {
|
|
13654
|
+
const lines = stdout.split(/\r?\n/);
|
|
13655
|
+
for (const line of lines) {
|
|
13656
|
+
const pattern = new RegExp(productPattern.source, productPattern.flags.replace("g", ""));
|
|
13657
|
+
const product = pattern.exec(line);
|
|
13658
|
+
if (!product) continue;
|
|
13659
|
+
const after = line.slice(product.index + product[0].length);
|
|
13660
|
+
const afterMatch = new RegExp(
|
|
13661
|
+
`^\\s+(${SEMVER_SOURCE})(?=$|\\s|\\()`
|
|
13662
|
+
).exec(after);
|
|
13663
|
+
if (afterMatch?.[1]) return afterMatch[1];
|
|
13664
|
+
const before = line.slice(0, product.index);
|
|
13665
|
+
const beforeMatch = new RegExp(`(${SEMVER_SOURCE})\\s*\\($`).exec(before);
|
|
13666
|
+
if (beforeMatch?.[1]) return beforeMatch[1];
|
|
13667
|
+
}
|
|
13668
|
+
if (!allowBare) return null;
|
|
13669
|
+
for (const line of lines) {
|
|
13670
|
+
const trimmed = line.trim();
|
|
13671
|
+
const match = new RegExp(`^(${SEMVER_SOURCE})$`).exec(trimmed);
|
|
13672
|
+
if (match?.[1]) return match[1];
|
|
13673
|
+
}
|
|
13674
|
+
return null;
|
|
13675
|
+
}
|
|
13676
|
+
function assertProviderVersionFloor(options) {
|
|
13677
|
+
if (compareSemVer(options.version, options.minimumVersion) < 0) {
|
|
13678
|
+
throw new AcpVersionBelowFloorError(
|
|
13679
|
+
options.provider,
|
|
13680
|
+
options.minimumVersion,
|
|
13681
|
+
options.version
|
|
13682
|
+
);
|
|
13683
|
+
}
|
|
13684
|
+
if (compareSemVer(options.version, options.lastMeasuredVersion) > 0) {
|
|
13685
|
+
options.onNewerVersion?.({
|
|
13686
|
+
provider: options.provider,
|
|
13687
|
+
runningVersion: options.version,
|
|
13688
|
+
lastMeasuredVersion: options.lastMeasuredVersion
|
|
13689
|
+
});
|
|
13690
|
+
}
|
|
13691
|
+
}
|
|
13692
|
+
var CORE_IDENTIFIER, PRERELEASE_IDENTIFIER, BUILD_IDENTIFIER, SEMVER_SOURCE, SEMVER_RE;
|
|
13693
|
+
var init_version = __esm({
|
|
13694
|
+
"src/host/version.ts"() {
|
|
13695
|
+
"use strict";
|
|
13696
|
+
init_types();
|
|
13697
|
+
CORE_IDENTIFIER = "(?:0|[1-9]\\d*)";
|
|
13698
|
+
PRERELEASE_IDENTIFIER = "(?:0|[1-9]\\d*|[A-Za-z-][0-9A-Za-z-]*)";
|
|
13699
|
+
BUILD_IDENTIFIER = "[0-9A-Za-z-]+";
|
|
13700
|
+
SEMVER_SOURCE = `${CORE_IDENTIFIER}\\.${CORE_IDENTIFIER}\\.${CORE_IDENTIFIER}(?:-${PRERELEASE_IDENTIFIER}(?:\\.${PRERELEASE_IDENTIFIER})*)?(?:\\+${BUILD_IDENTIFIER}(?:\\.${BUILD_IDENTIFIER})*)?`;
|
|
13701
|
+
SEMVER_RE = new RegExp(`^${SEMVER_SOURCE}$`);
|
|
13702
|
+
}
|
|
13703
|
+
});
|
|
13704
|
+
|
|
13705
|
+
// src/host/bounds.ts
|
|
13706
|
+
var ACP_MAX_LINE_BYTES, ACP_MAX_FRAME_BYTES, ACP_MAX_PENDING_REQUESTS, ACP_MAX_ACCUMULATED_TEXT_CHARS, ACP_DEFAULT_REQUEST_TIMEOUT_MS, ACP_VERSION_CHECK_TIMEOUT_MS, OPENCODE_MIN_VERSION, OPENCODE_LAST_MEASURED_VERSION, CLAUDE_ACP_MIN_VERSION, CLAUDE_ACP_LAST_MEASURED_VERSION, CLAUDE_PERMISSION_MODE_ID, CODEX_ACP_MIN_VERSION, CODEX_ACP_LAST_MEASURED_VERSION, CODEX_PERMISSION_MODE_ID, ACP_PROTOCOL_VERSION, OPENCODE_FORCED_PERMISSION_TOOLS;
|
|
13707
|
+
var init_bounds = __esm({
|
|
13708
|
+
"src/host/bounds.ts"() {
|
|
13709
|
+
"use strict";
|
|
13710
|
+
ACP_MAX_LINE_BYTES = 1048576;
|
|
13711
|
+
ACP_MAX_FRAME_BYTES = ACP_MAX_LINE_BYTES;
|
|
13712
|
+
ACP_MAX_PENDING_REQUESTS = 32;
|
|
13713
|
+
ACP_MAX_ACCUMULATED_TEXT_CHARS = 4194304;
|
|
13714
|
+
ACP_DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
|
|
13715
|
+
ACP_VERSION_CHECK_TIMEOUT_MS = 5e3;
|
|
13716
|
+
OPENCODE_MIN_VERSION = "1.18.10";
|
|
13717
|
+
OPENCODE_LAST_MEASURED_VERSION = "1.18.10";
|
|
13718
|
+
CLAUDE_ACP_MIN_VERSION = "0.64.2";
|
|
13719
|
+
CLAUDE_ACP_LAST_MEASURED_VERSION = "0.64.2";
|
|
13720
|
+
CLAUDE_PERMISSION_MODE_ID = "default";
|
|
13721
|
+
CODEX_ACP_MIN_VERSION = "1.1.9";
|
|
13722
|
+
CODEX_ACP_LAST_MEASURED_VERSION = "1.8.0";
|
|
13723
|
+
CODEX_PERMISSION_MODE_ID = "read-only";
|
|
13724
|
+
ACP_PROTOCOL_VERSION = 1;
|
|
13725
|
+
OPENCODE_FORCED_PERMISSION_TOOLS = [
|
|
13726
|
+
"bash",
|
|
13727
|
+
"glob",
|
|
13728
|
+
"read",
|
|
13729
|
+
"grep",
|
|
13730
|
+
"webfetch",
|
|
13731
|
+
"websearch",
|
|
13732
|
+
"write",
|
|
13733
|
+
"edit",
|
|
13734
|
+
"task",
|
|
13735
|
+
"apply_patch",
|
|
13736
|
+
"todowrite",
|
|
13737
|
+
"question",
|
|
13738
|
+
"skill",
|
|
13739
|
+
"execute",
|
|
13740
|
+
"external_directory",
|
|
13741
|
+
"*"
|
|
13742
|
+
];
|
|
13743
|
+
}
|
|
13744
|
+
});
|
|
13745
|
+
|
|
13746
|
+
// src/host/credential-redaction.ts
|
|
13747
|
+
function redactCredentialText(value) {
|
|
13748
|
+
return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(SECRET_SHAPE_GLOBAL_RE, "[redacted-credential]");
|
|
13749
|
+
}
|
|
13750
|
+
var EXOTIC_SEPARATORS, SEPARATOR_CLASS_SOURCE, ANSI_ESCAPE_GLOBAL_RE2, CONTROL_AND_SEPARATOR_STRIP_RE, SECRET_SHAPE_RE, SECRET_SHAPE_GLOBAL_RE;
|
|
13751
|
+
var init_credential_redaction = __esm({
|
|
13752
|
+
"src/host/credential-redaction.ts"() {
|
|
13753
|
+
"use strict";
|
|
13754
|
+
EXOTIC_SEPARATORS = "\\u00a0\\u1680\\u2000-\\u200d\\u2028\\u2029\\u202a-\\u202e\\u2060\\u2066-\\u2069\\u202f\\u205f\\u3000\\ufeff";
|
|
13755
|
+
SEPARATOR_CLASS_SOURCE = "\\t\\n\\x0b\\f\\r " + EXOTIC_SEPARATORS;
|
|
13756
|
+
ANSI_ESCAPE_GLOBAL_RE2 = new RegExp("\\u001b\\[[0-?]*[ -\\/]*[@-~]", "g");
|
|
13757
|
+
CONTROL_AND_SEPARATOR_STRIP_RE = new RegExp(
|
|
13758
|
+
"[\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f" + EXOTIC_SEPARATORS + "]",
|
|
13759
|
+
"g"
|
|
13760
|
+
);
|
|
13761
|
+
SECRET_SHAPE_RE = new RegExp(
|
|
13762
|
+
`swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*|cswarm-wake:[A-Za-z0-9_-]{43}`,
|
|
13763
|
+
"i"
|
|
13764
|
+
);
|
|
13765
|
+
SECRET_SHAPE_GLOBAL_RE = new RegExp(SECRET_SHAPE_RE.source, "gi");
|
|
13766
|
+
}
|
|
13767
|
+
});
|
|
13768
|
+
|
|
13769
|
+
// src/host/env.ts
|
|
13770
|
+
function sanitizeChildEnv(parent = process.env) {
|
|
13771
|
+
const out = {};
|
|
13772
|
+
for (const [key2, value] of Object.entries(parent)) {
|
|
13773
|
+
if (value === void 0) continue;
|
|
13774
|
+
if (!ALLOWED_EXACT.has(key2)) continue;
|
|
13775
|
+
if (DENY_NAME_RE.test(key2)) continue;
|
|
13776
|
+
if (key2.startsWith("SWARM_")) continue;
|
|
13777
|
+
out[key2] = value;
|
|
13778
|
+
}
|
|
13779
|
+
return out;
|
|
13780
|
+
}
|
|
13781
|
+
var ALLOWED_EXACT, DENY_NAME_RE;
|
|
13782
|
+
var init_env = __esm({
|
|
13783
|
+
"src/host/env.ts"() {
|
|
13784
|
+
"use strict";
|
|
13785
|
+
ALLOWED_EXACT = /* @__PURE__ */ new Set([
|
|
13786
|
+
"PATH",
|
|
13787
|
+
"HOME",
|
|
13788
|
+
"USER",
|
|
13789
|
+
"LOGNAME",
|
|
13790
|
+
"SHELL",
|
|
13791
|
+
"TMPDIR",
|
|
13792
|
+
"TMP",
|
|
13793
|
+
"TEMP",
|
|
13794
|
+
"LANG",
|
|
13795
|
+
"LC_ALL",
|
|
13796
|
+
"LC_CTYPE",
|
|
13797
|
+
"LC_MESSAGES",
|
|
13798
|
+
"LC_COLLATE",
|
|
13799
|
+
"LC_TIME",
|
|
13800
|
+
"TERM",
|
|
13801
|
+
"COLORTERM",
|
|
13802
|
+
"NO_COLOR",
|
|
13803
|
+
"FORCE_COLOR",
|
|
13804
|
+
"XDG_CONFIG_HOME",
|
|
13805
|
+
"XDG_DATA_HOME",
|
|
13806
|
+
"XDG_CACHE_HOME",
|
|
13807
|
+
"XDG_RUNTIME_DIR",
|
|
13808
|
+
"XDG_STATE_HOME",
|
|
13809
|
+
"GROK_HOME"
|
|
13810
|
+
]);
|
|
13811
|
+
DENY_NAME_RE = /(?:^|_)(?:SWARM|SECRET|TOKEN|PASSWORD|PASSWD|CREDENTIAL|PRIVATE_KEY|API_KEY|AUTH|COOKIE)(?:_|$)/i;
|
|
13812
|
+
}
|
|
13813
|
+
});
|
|
13814
|
+
|
|
13815
|
+
// src/host/sanitize.ts
|
|
13816
|
+
function redactString(value) {
|
|
13817
|
+
return redactCredentialText(value).replace(SECRET_VALUE_RE, (_m, q) => `redacted=${q}***${q}`).replace(JWT_RE, "[redacted-jwt]");
|
|
13818
|
+
}
|
|
13819
|
+
function redactUnknown(value, depth = 0) {
|
|
13820
|
+
if (depth > 6) return "[truncated]";
|
|
13821
|
+
if (typeof value === "string") {
|
|
13822
|
+
if (value.length > 4096) {
|
|
13823
|
+
return redactString(value.slice(0, 4096)) + "\u2026";
|
|
13824
|
+
}
|
|
13825
|
+
return redactString(value);
|
|
13826
|
+
}
|
|
13827
|
+
if (Array.isArray(value)) {
|
|
13828
|
+
return value.slice(0, 32).map((item) => redactUnknown(item, depth + 1));
|
|
13829
|
+
}
|
|
13830
|
+
if (value && typeof value === "object") {
|
|
13831
|
+
const out = {};
|
|
13832
|
+
for (const [k, v] of Object.entries(value)) {
|
|
13833
|
+
if (/secret|token|password|authorization|api[_-]?key|credential/i.test(k)) {
|
|
13834
|
+
out[k] = "[redacted]";
|
|
13835
|
+
continue;
|
|
13836
|
+
}
|
|
13837
|
+
if (k === "rawInput" || k === "rawOutput" || k === "env") {
|
|
13838
|
+
out[k] = "[redacted]";
|
|
13839
|
+
continue;
|
|
13840
|
+
}
|
|
13841
|
+
out[k] = redactUnknown(v, depth + 1);
|
|
13842
|
+
}
|
|
13843
|
+
return out;
|
|
13844
|
+
}
|
|
13845
|
+
return value;
|
|
13846
|
+
}
|
|
13847
|
+
function sanitizeUpdateDetail(detail) {
|
|
13848
|
+
if (!detail) return void 0;
|
|
13849
|
+
return redactUnknown(detail);
|
|
13850
|
+
}
|
|
13851
|
+
function sanitizeText(text) {
|
|
13852
|
+
return redactString(text);
|
|
13853
|
+
}
|
|
13854
|
+
var SECRET_VALUE_RE, JWT_RE;
|
|
13855
|
+
var init_sanitize = __esm({
|
|
13856
|
+
"src/host/sanitize.ts"() {
|
|
13857
|
+
"use strict";
|
|
13858
|
+
init_credential_redaction();
|
|
13859
|
+
SECRET_VALUE_RE = /(?:(?:api[_-]?key|token|secret|password|authorization|bearer)\s*[:=]\s*)(["']?)([^\s"'\\]{8,})\1/gi;
|
|
13860
|
+
JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
|
|
13861
|
+
}
|
|
13862
|
+
});
|
|
13863
|
+
|
|
13864
|
+
// src/host/stderr-tail.ts
|
|
13865
|
+
function sanitizeStderrTail(raw) {
|
|
13866
|
+
return redactCredentialText(raw).slice(-TAIL_MAX_CHARS).trim();
|
|
13867
|
+
}
|
|
13868
|
+
function attachStderrTailRing(stderr) {
|
|
13869
|
+
const chunks = [];
|
|
13870
|
+
let total = 0;
|
|
13871
|
+
let evicted = false;
|
|
13872
|
+
stderr.on("data", (chunk) => {
|
|
13873
|
+
const buffer2 = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
13874
|
+
chunks.push(buffer2);
|
|
13875
|
+
total += buffer2.length;
|
|
13876
|
+
while (total > RING_CAPACITY_BYTES && chunks.length > 0) {
|
|
13877
|
+
evicted = true;
|
|
13878
|
+
const head2 = chunks[0];
|
|
13879
|
+
const excess = total - RING_CAPACITY_BYTES;
|
|
13880
|
+
if (head2.length <= excess) {
|
|
13881
|
+
chunks.shift();
|
|
13882
|
+
total -= head2.length;
|
|
13883
|
+
} else {
|
|
13884
|
+
chunks[0] = head2.subarray(excess);
|
|
13885
|
+
total -= excess;
|
|
13886
|
+
}
|
|
13887
|
+
}
|
|
13888
|
+
});
|
|
13889
|
+
stderr.resume();
|
|
13890
|
+
return {
|
|
13891
|
+
read() {
|
|
13892
|
+
let text = Buffer.concat(chunks).toString("utf8");
|
|
13893
|
+
if (evicted) {
|
|
13894
|
+
const newline = text.indexOf("\n");
|
|
13895
|
+
text = newline === -1 ? "" : text.slice(newline + 1);
|
|
13896
|
+
}
|
|
13897
|
+
return sanitizeStderrTail(text);
|
|
13898
|
+
}
|
|
13899
|
+
};
|
|
13900
|
+
}
|
|
13901
|
+
function attachStderrTailExitObserver(child, onStderrTail) {
|
|
13902
|
+
const stderrTail = attachStderrTailRing(child.stderr);
|
|
13903
|
+
return (handler) => {
|
|
13904
|
+
const observeExit = (code, signal) => {
|
|
13905
|
+
let completed = false;
|
|
13906
|
+
let timer2 = null;
|
|
13907
|
+
const complete = () => {
|
|
13908
|
+
if (completed) return;
|
|
13909
|
+
completed = true;
|
|
13910
|
+
if (timer2) clearTimeout(timer2);
|
|
13911
|
+
child.removeListener("close", complete);
|
|
13912
|
+
try {
|
|
13913
|
+
onStderrTail?.(stderrTail.read());
|
|
13914
|
+
} finally {
|
|
13915
|
+
handler(code, signal);
|
|
13916
|
+
}
|
|
13917
|
+
};
|
|
13918
|
+
child.once("close", complete);
|
|
13919
|
+
timer2 = setTimeout(complete, STDERR_EXIT_GRACE_MS);
|
|
13920
|
+
timer2.unref();
|
|
13921
|
+
};
|
|
13922
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
13923
|
+
observeExit(child.exitCode, child.signalCode);
|
|
13924
|
+
} else {
|
|
13925
|
+
child.once("exit", observeExit);
|
|
13926
|
+
}
|
|
13927
|
+
};
|
|
13928
|
+
}
|
|
13929
|
+
var RING_CAPACITY_BYTES, TAIL_MAX_CHARS, STDERR_EXIT_GRACE_MS, STDERR_READABLE_END_GRACE_MS;
|
|
13930
|
+
var init_stderr_tail = __esm({
|
|
13931
|
+
"src/host/stderr-tail.ts"() {
|
|
13932
|
+
"use strict";
|
|
13933
|
+
init_credential_redaction();
|
|
13934
|
+
RING_CAPACITY_BYTES = 4096;
|
|
13935
|
+
TAIL_MAX_CHARS = 2048;
|
|
13936
|
+
STDERR_EXIT_GRACE_MS = 100;
|
|
13937
|
+
STDERR_READABLE_END_GRACE_MS = STDERR_EXIT_GRACE_MS + 50;
|
|
13938
|
+
}
|
|
13939
|
+
});
|
|
13940
|
+
|
|
13941
|
+
// src/host/permission.ts
|
|
13942
|
+
function defaultPermissionCallback(request) {
|
|
13943
|
+
const rejectOnce = request.options.find((opt) => opt.kind === "reject_once");
|
|
13944
|
+
if (rejectOnce) {
|
|
13945
|
+
return { outcome: "selected", optionId: rejectOnce.optionId };
|
|
13946
|
+
}
|
|
13947
|
+
const rejectAlways = request.options.find((opt) => opt.kind === "reject_always");
|
|
13948
|
+
if (rejectAlways) {
|
|
13949
|
+
return { outcome: "selected", optionId: rejectAlways.optionId };
|
|
13950
|
+
}
|
|
13951
|
+
return { outcome: "cancelled" };
|
|
13952
|
+
}
|
|
13953
|
+
function resolvePermissionCallback(callback) {
|
|
13954
|
+
return callback ?? defaultPermissionCallback;
|
|
13955
|
+
}
|
|
13956
|
+
function parsePermissionOptions(raw) {
|
|
13957
|
+
if (!Array.isArray(raw)) return [];
|
|
13958
|
+
const seen = /* @__PURE__ */ new Map();
|
|
13959
|
+
for (const item of raw) {
|
|
13960
|
+
if (!item || typeof item !== "object") continue;
|
|
13961
|
+
const id = item.optionId;
|
|
13962
|
+
if (typeof id === "string" && id) seen.set(id, (seen.get(id) ?? 0) + 1);
|
|
13963
|
+
}
|
|
13964
|
+
const options = [];
|
|
13965
|
+
for (const item of raw) {
|
|
13966
|
+
if (!item || typeof item !== "object") continue;
|
|
13967
|
+
const rec = item;
|
|
13968
|
+
const optionId = rec.optionId;
|
|
13969
|
+
const name = rec.name;
|
|
13970
|
+
const kind = rec.kind;
|
|
13971
|
+
if (typeof optionId !== "string" || !optionId) continue;
|
|
13972
|
+
if ((seen.get(optionId) ?? 0) > 1) continue;
|
|
13973
|
+
if (typeof name !== "string") continue;
|
|
13974
|
+
if (kind !== "allow_once" && kind !== "allow_always" && kind !== "reject_once" && kind !== "reject_always") {
|
|
13975
|
+
continue;
|
|
13976
|
+
}
|
|
13977
|
+
options.push({ optionId, name, kind });
|
|
13978
|
+
}
|
|
13979
|
+
return options;
|
|
13980
|
+
}
|
|
13981
|
+
function permissionDecisionToResult(decision) {
|
|
13982
|
+
if (decision.outcome === "cancelled") {
|
|
13983
|
+
return { outcome: { outcome: "cancelled" } };
|
|
13984
|
+
}
|
|
13985
|
+
return {
|
|
13986
|
+
outcome: {
|
|
13987
|
+
outcome: "selected",
|
|
13988
|
+
optionId: decision.optionId
|
|
13989
|
+
}
|
|
13990
|
+
};
|
|
13991
|
+
}
|
|
13992
|
+
var init_permission = __esm({
|
|
13993
|
+
"src/host/permission.ts"() {
|
|
13994
|
+
"use strict";
|
|
13995
|
+
}
|
|
13996
|
+
});
|
|
13997
|
+
|
|
13998
|
+
// src/host/transport.ts
|
|
13999
|
+
function asAcpHostError(error) {
|
|
14000
|
+
if (error instanceof AcpHostError) return error;
|
|
14001
|
+
return new AcpTransportError(
|
|
14002
|
+
error instanceof Error ? error : new Error(String(error))
|
|
14003
|
+
);
|
|
14004
|
+
}
|
|
14005
|
+
var import_node_events, AcpTransport;
|
|
14006
|
+
var init_transport = __esm({
|
|
14007
|
+
"src/host/transport.ts"() {
|
|
14008
|
+
"use strict";
|
|
14009
|
+
import_node_events = require("node:events");
|
|
14010
|
+
init_bounds();
|
|
14011
|
+
init_types();
|
|
14012
|
+
AcpTransport = class extends import_node_events.EventEmitter {
|
|
14013
|
+
writable;
|
|
14014
|
+
handlers;
|
|
14015
|
+
requestTimeoutMs;
|
|
14016
|
+
pending = /* @__PURE__ */ new Map();
|
|
14017
|
+
nextId = 1;
|
|
14018
|
+
closed = false;
|
|
14019
|
+
buffer = Buffer.alloc(0);
|
|
14020
|
+
childExit = null;
|
|
14021
|
+
constructor(options) {
|
|
14022
|
+
super();
|
|
14023
|
+
this.writable = options.writable;
|
|
14024
|
+
this.handlers = options.handlers ?? {};
|
|
14025
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS;
|
|
14026
|
+
const readableEndGraceMs = Math.max(0, options.readableEndGraceMs ?? 0);
|
|
14027
|
+
let readableEndTimer = null;
|
|
14028
|
+
options.readable.on("data", (chunk) => {
|
|
14029
|
+
this.onData(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
14030
|
+
});
|
|
14031
|
+
options.readable.on("end", () => {
|
|
14032
|
+
const fail = () => {
|
|
14033
|
+
readableEndTimer = null;
|
|
14034
|
+
this.failAll(
|
|
14035
|
+
new AcpChildExitError(
|
|
14036
|
+
this.childExit?.code ?? null,
|
|
14037
|
+
this.childExit?.signal ?? null
|
|
14038
|
+
)
|
|
14039
|
+
);
|
|
14040
|
+
};
|
|
14041
|
+
if (readableEndGraceMs > 0 && options.onChildExit) {
|
|
14042
|
+
readableEndTimer = setTimeout(fail, readableEndGraceMs);
|
|
14043
|
+
} else {
|
|
14044
|
+
fail();
|
|
14045
|
+
}
|
|
14046
|
+
});
|
|
14047
|
+
options.readable.on("error", (err) => {
|
|
14048
|
+
this.failAll(asAcpHostError(err));
|
|
14049
|
+
});
|
|
14050
|
+
options.writable.on("error", (err) => {
|
|
14051
|
+
this.failAll(asAcpHostError(err));
|
|
14052
|
+
});
|
|
14053
|
+
options.onChildExit?.((code, signal) => {
|
|
14054
|
+
if (readableEndTimer) {
|
|
14055
|
+
clearTimeout(readableEndTimer);
|
|
14056
|
+
readableEndTimer = null;
|
|
14057
|
+
}
|
|
14058
|
+
this.childExit = { code, signal };
|
|
14059
|
+
this.failAll(new AcpChildExitError(code, signal));
|
|
14060
|
+
});
|
|
14061
|
+
}
|
|
14062
|
+
get pendingCount() {
|
|
14063
|
+
return this.pending.size;
|
|
14064
|
+
}
|
|
14065
|
+
get isClosed() {
|
|
14066
|
+
return this.closed;
|
|
14067
|
+
}
|
|
14068
|
+
request(method, params, timeoutMs) {
|
|
14069
|
+
if (this.closed) {
|
|
14070
|
+
return Promise.reject(new AcpProtocolError("transport closed", "closed"));
|
|
14071
|
+
}
|
|
14072
|
+
if (this.childExit) {
|
|
14073
|
+
return Promise.reject(
|
|
14074
|
+
new AcpChildExitError(this.childExit.code, this.childExit.signal)
|
|
14075
|
+
);
|
|
14076
|
+
}
|
|
14077
|
+
if (this.pending.size >= ACP_MAX_PENDING_REQUESTS) {
|
|
14078
|
+
return Promise.reject(
|
|
14079
|
+
new AcpProtocolError(
|
|
14080
|
+
`pending request limit ${ACP_MAX_PENDING_REQUESTS} exceeded`,
|
|
14081
|
+
"pending_limit"
|
|
14082
|
+
)
|
|
14083
|
+
);
|
|
14084
|
+
}
|
|
14085
|
+
const id = this.nextId++;
|
|
14086
|
+
const key2 = String(id);
|
|
14087
|
+
const frame = {
|
|
14088
|
+
jsonrpc: "2.0",
|
|
14089
|
+
id,
|
|
14090
|
+
method,
|
|
14091
|
+
...params !== void 0 ? { params } : {}
|
|
14092
|
+
};
|
|
14093
|
+
return new Promise((resolve4, reject) => {
|
|
14094
|
+
const timer2 = setTimeout(() => {
|
|
14095
|
+
this.pending.delete(key2);
|
|
14096
|
+
reject(new AcpTimeoutError(`ACP request timed out: ${method}`));
|
|
14097
|
+
}, timeoutMs ?? this.requestTimeoutMs);
|
|
14098
|
+
this.pending.set(key2, { resolve: resolve4, reject, timer: timer2, method });
|
|
14099
|
+
try {
|
|
14100
|
+
this.writeFrame(frame);
|
|
14101
|
+
} catch (err) {
|
|
14102
|
+
clearTimeout(timer2);
|
|
14103
|
+
this.pending.delete(key2);
|
|
14104
|
+
reject(asAcpHostError(err));
|
|
14105
|
+
}
|
|
14106
|
+
});
|
|
14107
|
+
}
|
|
14108
|
+
/** Notification — no id field (ACP session/cancel). */
|
|
14109
|
+
notify(method, params) {
|
|
14110
|
+
if (this.closed) {
|
|
14111
|
+
throw new AcpProtocolError("transport closed", "closed");
|
|
14112
|
+
}
|
|
14113
|
+
const frame = {
|
|
14114
|
+
jsonrpc: "2.0",
|
|
14115
|
+
method
|
|
14116
|
+
};
|
|
14117
|
+
if (params !== void 0) frame.params = params;
|
|
14118
|
+
this.writeFrame(frame);
|
|
14119
|
+
}
|
|
14120
|
+
respond(id, result) {
|
|
14121
|
+
this.writeFrame({ jsonrpc: "2.0", id, result });
|
|
14122
|
+
}
|
|
14123
|
+
respondError(id, code, message) {
|
|
14124
|
+
this.writeFrame({
|
|
14125
|
+
jsonrpc: "2.0",
|
|
14126
|
+
id,
|
|
14127
|
+
error: { code, message }
|
|
14128
|
+
});
|
|
14129
|
+
}
|
|
14130
|
+
close() {
|
|
14131
|
+
if (this.closed) return;
|
|
14132
|
+
this.closed = true;
|
|
14133
|
+
this.failAll(new AcpProtocolError("transport closed", "closed"));
|
|
14134
|
+
try {
|
|
14135
|
+
this.writable.end();
|
|
14136
|
+
} catch {
|
|
14137
|
+
}
|
|
14138
|
+
}
|
|
14139
|
+
writeFrame(frame) {
|
|
14140
|
+
const line = JSON.stringify(frame);
|
|
14141
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
14142
|
+
if (bytes > ACP_MAX_FRAME_BYTES) {
|
|
14143
|
+
throw new AcpProtocolError(
|
|
14144
|
+
`outbound frame exceeds ${ACP_MAX_FRAME_BYTES} bytes`,
|
|
14145
|
+
"frame_too_large"
|
|
14146
|
+
);
|
|
14147
|
+
}
|
|
14148
|
+
this.writable.write(line + "\n");
|
|
14149
|
+
}
|
|
14150
|
+
onData(chunk) {
|
|
14151
|
+
if (this.closed) return;
|
|
14152
|
+
if (this.buffer.length + chunk.length > ACP_MAX_LINE_BYTES * 2) {
|
|
14153
|
+
this.buffer = Buffer.alloc(0);
|
|
14154
|
+
const err = new AcpProtocolError(
|
|
14155
|
+
"inbound buffer exceeded safe limit",
|
|
14156
|
+
"buffer_overflow"
|
|
14157
|
+
);
|
|
14158
|
+
this.handlers.onProtocolError?.(err);
|
|
14159
|
+
this.emit("protocolError", err);
|
|
14160
|
+
return;
|
|
14161
|
+
}
|
|
14162
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
14163
|
+
while (true) {
|
|
14164
|
+
const nl = this.buffer.indexOf(10);
|
|
14165
|
+
if (nl === -1) {
|
|
14166
|
+
if (this.buffer.length > ACP_MAX_LINE_BYTES) {
|
|
14167
|
+
this.buffer = Buffer.alloc(0);
|
|
14168
|
+
const err = new AcpProtocolError(
|
|
14169
|
+
`inbound line exceeds ${ACP_MAX_LINE_BYTES} bytes`,
|
|
14170
|
+
"line_too_large"
|
|
14171
|
+
);
|
|
14172
|
+
this.handlers.onProtocolError?.(err);
|
|
14173
|
+
this.emit("protocolError", err);
|
|
14174
|
+
}
|
|
14175
|
+
break;
|
|
14176
|
+
}
|
|
14177
|
+
const lineBuf = this.buffer.subarray(0, nl);
|
|
14178
|
+
this.buffer = this.buffer.subarray(nl + 1);
|
|
14179
|
+
const end = lineBuf.length > 0 && lineBuf[lineBuf.length - 1] === 13 ? lineBuf.length - 1 : lineBuf.length;
|
|
14180
|
+
if (end === 0) continue;
|
|
14181
|
+
if (end > ACP_MAX_LINE_BYTES) {
|
|
14182
|
+
const err = new AcpProtocolError(
|
|
14183
|
+
`inbound line exceeds ${ACP_MAX_LINE_BYTES} bytes`,
|
|
14184
|
+
"line_too_large"
|
|
14185
|
+
);
|
|
14186
|
+
this.handlers.onProtocolError?.(err);
|
|
14187
|
+
this.emit("protocolError", err);
|
|
14188
|
+
continue;
|
|
14189
|
+
}
|
|
14190
|
+
const line = lineBuf.subarray(0, end).toString("utf8");
|
|
14191
|
+
this.handleLine(line);
|
|
14192
|
+
}
|
|
14193
|
+
}
|
|
14194
|
+
handleLine(line) {
|
|
14195
|
+
let msg;
|
|
14196
|
+
try {
|
|
14197
|
+
msg = JSON.parse(line);
|
|
14198
|
+
} catch {
|
|
14199
|
+
const err2 = new AcpProtocolError("malformed JSON line", "malformed_json");
|
|
14200
|
+
this.handlers.onProtocolError?.(err2);
|
|
14201
|
+
this.emit("protocolError", err2);
|
|
14202
|
+
return;
|
|
14203
|
+
}
|
|
14204
|
+
if (!msg || typeof msg !== "object") {
|
|
14205
|
+
const err2 = new AcpProtocolError("non-object JSON-RPC frame", "malformed_frame");
|
|
14206
|
+
this.handlers.onProtocolError?.(err2);
|
|
14207
|
+
this.emit("protocolError", err2);
|
|
14208
|
+
return;
|
|
14209
|
+
}
|
|
14210
|
+
const rec = msg;
|
|
14211
|
+
if (rec.jsonrpc !== "2.0") {
|
|
14212
|
+
const err2 = new AcpProtocolError("missing jsonrpc 2.0", "malformed_frame");
|
|
14213
|
+
this.handlers.onProtocolError?.(err2);
|
|
14214
|
+
this.emit("protocolError", err2);
|
|
14215
|
+
return;
|
|
14216
|
+
}
|
|
14217
|
+
if ("id" in rec && rec.id !== null && rec.id !== void 0 && !("method" in rec)) {
|
|
14218
|
+
const hasResult = "result" in rec;
|
|
14219
|
+
const hasError = "error" in rec;
|
|
14220
|
+
if (hasResult === hasError) {
|
|
14221
|
+
const err2 = new AcpProtocolError(
|
|
14222
|
+
"response must carry exactly one of result or error",
|
|
14223
|
+
"malformed_frame"
|
|
14224
|
+
);
|
|
14225
|
+
this.handlers.onProtocolError?.(err2);
|
|
14226
|
+
this.emit("protocolError", err2);
|
|
14227
|
+
return;
|
|
14228
|
+
}
|
|
14229
|
+
this.handleResponse(rec);
|
|
14230
|
+
return;
|
|
14231
|
+
}
|
|
14232
|
+
if (typeof rec.method === "string" && "id" in rec && rec.id !== null && rec.id !== void 0) {
|
|
14233
|
+
const id = rec.id;
|
|
14234
|
+
if (typeof id !== "string" && typeof id !== "number") {
|
|
14235
|
+
const err2 = new AcpProtocolError("invalid request id", "malformed_frame");
|
|
14236
|
+
this.handlers.onProtocolError?.(err2);
|
|
14237
|
+
return;
|
|
14238
|
+
}
|
|
14239
|
+
void Promise.resolve(this.handlers.onRequest?.(id, rec.method, rec.params)).catch(
|
|
14240
|
+
(err2) => {
|
|
14241
|
+
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
14242
|
+
try {
|
|
14243
|
+
this.respondError(id, -32e3, message);
|
|
14244
|
+
} catch {
|
|
14245
|
+
}
|
|
14246
|
+
}
|
|
14247
|
+
);
|
|
14248
|
+
return;
|
|
14249
|
+
}
|
|
14250
|
+
if (typeof rec.method === "string") {
|
|
14251
|
+
try {
|
|
14252
|
+
this.handlers.onNotification?.(rec.method, rec.params);
|
|
14253
|
+
} catch (err2) {
|
|
14254
|
+
this.emit("handlerError", err2);
|
|
14255
|
+
}
|
|
14256
|
+
return;
|
|
14257
|
+
}
|
|
14258
|
+
const err = new AcpProtocolError("unrecognized JSON-RPC frame", "malformed_frame");
|
|
14259
|
+
this.handlers.onProtocolError?.(err);
|
|
14260
|
+
this.emit("protocolError", err);
|
|
14261
|
+
}
|
|
14262
|
+
handleResponse(rec) {
|
|
14263
|
+
const key2 = String(rec.id);
|
|
14264
|
+
const pending = this.pending.get(key2);
|
|
14265
|
+
if (!pending) {
|
|
14266
|
+
return;
|
|
14267
|
+
}
|
|
14268
|
+
clearTimeout(pending.timer);
|
|
14269
|
+
this.pending.delete(key2);
|
|
14270
|
+
if ("error" in rec && rec.error !== void 0) {
|
|
14271
|
+
const errObj = rec.error;
|
|
14272
|
+
const message = errObj && typeof errObj.message === "string" ? errObj.message : `RPC error for ${pending.method}`;
|
|
14273
|
+
const peerError = errObj && typeof errObj.code === "number" && Number.isInteger(errObj.code) ? {
|
|
14274
|
+
code: errObj.code,
|
|
14275
|
+
...Object.prototype.hasOwnProperty.call(errObj, "data") ? { data: errObj.data } : {}
|
|
14276
|
+
} : null;
|
|
14277
|
+
pending.reject(new AcpProtocolError(message, "rpc_error", peerError));
|
|
14278
|
+
return;
|
|
14279
|
+
}
|
|
14280
|
+
pending.resolve(rec.result);
|
|
14281
|
+
}
|
|
14282
|
+
failAll(error) {
|
|
14283
|
+
if (this.closed && this.pending.size === 0) return;
|
|
14284
|
+
for (const [key2, pending] of this.pending) {
|
|
14285
|
+
clearTimeout(pending.timer);
|
|
14286
|
+
pending.reject(error);
|
|
14287
|
+
this.pending.delete(key2);
|
|
14288
|
+
}
|
|
14289
|
+
}
|
|
14290
|
+
};
|
|
14291
|
+
}
|
|
14292
|
+
});
|
|
14293
|
+
|
|
14294
|
+
// src/host/session.ts
|
|
14295
|
+
function assertAbsoluteExistingCwd(cwd) {
|
|
14296
|
+
if (!cwd || typeof cwd !== "string") {
|
|
14297
|
+
throw new AcpProtocolError("cwd is required", "invalid_cwd");
|
|
14298
|
+
}
|
|
14299
|
+
if (!(0, import_node_path13.isAbsolute)(cwd)) {
|
|
14300
|
+
throw new AcpProtocolError("cwd must be an absolute path", "invalid_cwd");
|
|
14301
|
+
}
|
|
14302
|
+
let st;
|
|
14303
|
+
try {
|
|
14304
|
+
st = (0, import_node_fs2.statSync)(cwd);
|
|
14305
|
+
} catch {
|
|
14306
|
+
throw new AcpProtocolError(`cwd does not exist: ${cwd}`, "invalid_cwd");
|
|
14307
|
+
}
|
|
14308
|
+
if (!st.isDirectory()) {
|
|
14309
|
+
throw new AcpProtocolError(`cwd is not a directory: ${cwd}`, "invalid_cwd");
|
|
14310
|
+
}
|
|
14311
|
+
return cwd;
|
|
14312
|
+
}
|
|
14313
|
+
function isRecord(value) {
|
|
14314
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
14315
|
+
}
|
|
14316
|
+
function asStopReason(value) {
|
|
14317
|
+
if (value === "end_turn" || value === "max_tokens" || value === "max_turn_requests" || value === "refusal" || value === "cancelled") {
|
|
14318
|
+
return value;
|
|
14319
|
+
}
|
|
14320
|
+
throw new AcpProtocolError(
|
|
14321
|
+
`invalid stopReason: ${String(value)}`,
|
|
14322
|
+
"invalid_response"
|
|
14323
|
+
);
|
|
14324
|
+
}
|
|
14325
|
+
function isHostRejectDecision(decision, options) {
|
|
14326
|
+
if (decision.outcome === "cancelled") return true;
|
|
14327
|
+
if (decision.outcome !== "selected") return false;
|
|
14328
|
+
const chosen = options.find((opt) => opt.optionId === decision.optionId);
|
|
14329
|
+
return chosen?.kind === "reject_once" || chosen?.kind === "reject_always";
|
|
14330
|
+
}
|
|
14331
|
+
function updateKind(raw) {
|
|
14332
|
+
switch (raw) {
|
|
14333
|
+
case "agent_message_chunk":
|
|
14334
|
+
case "agent_thought_chunk":
|
|
14335
|
+
case "tool_call":
|
|
14336
|
+
case "tool_call_update":
|
|
14337
|
+
case "plan":
|
|
14338
|
+
case "available_commands_update":
|
|
14339
|
+
return raw;
|
|
14340
|
+
default:
|
|
14341
|
+
return "unknown";
|
|
14342
|
+
}
|
|
14343
|
+
}
|
|
14344
|
+
function createBoundTransport(options) {
|
|
14345
|
+
return new AcpTransport({
|
|
14346
|
+
readable: options.readable,
|
|
14347
|
+
writable: options.writable,
|
|
14348
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
14349
|
+
onChildExit: options.onChildExit,
|
|
14350
|
+
readableEndGraceMs: options.readableEndGraceMs,
|
|
14351
|
+
handlers: {
|
|
14352
|
+
onNotification: (method, params) => {
|
|
14353
|
+
options.getSession()?.handleAgentNotification(method, params);
|
|
14354
|
+
},
|
|
14355
|
+
onRequest: async (id, method, params) => {
|
|
14356
|
+
const session = options.getSession();
|
|
14357
|
+
if (!session) {
|
|
14358
|
+
return;
|
|
14359
|
+
}
|
|
14360
|
+
await session.handleAgentRequest(id, method, params);
|
|
14361
|
+
}
|
|
14362
|
+
}
|
|
14363
|
+
});
|
|
14364
|
+
}
|
|
14365
|
+
var import_node_fs2, import_node_path13, CANARY_TERMINAL_DENY_STATUSES, AcpHostSession;
|
|
14366
|
+
var init_session = __esm({
|
|
14367
|
+
"src/host/session.ts"() {
|
|
14368
|
+
"use strict";
|
|
14369
|
+
import_node_fs2 = require("node:fs");
|
|
14370
|
+
import_node_path13 = require("node:path");
|
|
14371
|
+
init_bounds();
|
|
14372
|
+
init_permission();
|
|
14373
|
+
init_sanitize();
|
|
14374
|
+
init_transport();
|
|
14375
|
+
init_types();
|
|
14376
|
+
CANARY_TERMINAL_DENY_STATUSES = /* @__PURE__ */ new Set([
|
|
14377
|
+
"rejected",
|
|
14378
|
+
"denied",
|
|
14379
|
+
"cancelled",
|
|
14380
|
+
"canceled",
|
|
14381
|
+
"failed",
|
|
14382
|
+
"error"
|
|
14383
|
+
]);
|
|
14384
|
+
AcpHostSession = class _AcpHostSession {
|
|
14385
|
+
transport;
|
|
14386
|
+
cwd;
|
|
14387
|
+
permissionCallback;
|
|
14388
|
+
requiredModeId;
|
|
14389
|
+
events;
|
|
14390
|
+
requestTimeoutMs;
|
|
14391
|
+
sessionId = null;
|
|
14392
|
+
agentVersion;
|
|
14393
|
+
promptsEnabled;
|
|
14394
|
+
promptInFlight = false;
|
|
14395
|
+
closed = false;
|
|
14396
|
+
/**
|
|
14397
|
+
* Canary denial is host-authored only: we record toolCallIds we ourselves
|
|
14398
|
+
* rejected, then accept a bounded structured terminal status on that same id.
|
|
14399
|
+
* Provider free-text / error-body regex never unlocks prompts.
|
|
14400
|
+
*/
|
|
14401
|
+
canaryState = {
|
|
14402
|
+
sawPermissionRequest: false,
|
|
14403
|
+
sawDeniedToolResult: false,
|
|
14404
|
+
rejectedToolKeys: /* @__PURE__ */ new Set()
|
|
14405
|
+
};
|
|
14406
|
+
constructor(options) {
|
|
14407
|
+
this.transport = options.transport;
|
|
14408
|
+
this.cwd = assertAbsoluteExistingCwd(options.cwd);
|
|
14409
|
+
this.requiredModeId = options.requiredModeId;
|
|
14410
|
+
this.permissionCallback = resolvePermissionCallback(options.permissionCallback);
|
|
14411
|
+
this.events = options.events ?? {};
|
|
14412
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS;
|
|
14413
|
+
this.promptsEnabled = options.promptsEnabled === true;
|
|
14414
|
+
}
|
|
14415
|
+
/**
|
|
14416
|
+
* Wire an existing transport, run initialize + session/new, return a ready session.
|
|
14417
|
+
* Real prompts stay blocked until {@link enablePromptsAfterCanary} (or test opt-in).
|
|
14418
|
+
*/
|
|
14419
|
+
static async connect(options) {
|
|
14420
|
+
const session = new _AcpHostSession(options);
|
|
14421
|
+
session.attachHandlers();
|
|
14422
|
+
await session.initialize(options.clientName, options.clientVersion);
|
|
14423
|
+
await session.newSession();
|
|
14424
|
+
return session;
|
|
14425
|
+
}
|
|
14426
|
+
/**
|
|
14427
|
+
* Build a session around a transport that is already initialized (tests).
|
|
14428
|
+
*/
|
|
14429
|
+
static attachInitialized(options) {
|
|
14430
|
+
const session = new _AcpHostSession(options);
|
|
14431
|
+
session.attachHandlers();
|
|
14432
|
+
session.sessionId = options.sessionId;
|
|
14433
|
+
session.agentVersion = options.agentVersion;
|
|
14434
|
+
return session;
|
|
14435
|
+
}
|
|
14436
|
+
get info() {
|
|
14437
|
+
if (!this.sessionId) {
|
|
14438
|
+
throw new AcpProtocolError("session not opened", "no_session");
|
|
14439
|
+
}
|
|
14440
|
+
return {
|
|
14441
|
+
sessionId: this.sessionId,
|
|
14442
|
+
cwd: this.cwd,
|
|
14443
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
14444
|
+
agentVersion: this.agentVersion
|
|
14445
|
+
};
|
|
14446
|
+
}
|
|
14447
|
+
get arePromptsEnabled() {
|
|
14448
|
+
return this.promptsEnabled;
|
|
14449
|
+
}
|
|
14450
|
+
get canaryObservation() {
|
|
14451
|
+
return { ...this.canaryState };
|
|
14452
|
+
}
|
|
14453
|
+
/**
|
|
14454
|
+
* Permission-boundary canary. Drives a side-effect-free probe that must
|
|
14455
|
+
* produce (1) a session/request_permission we answer with reject and
|
|
14456
|
+
* (2) a structured tool_call(_update) for that same toolCallId with a
|
|
14457
|
+
* bounded terminal deny status — never provider free-text matching.
|
|
14458
|
+
*
|
|
14459
|
+
* Ambient provider hooks remain outside this boundary — see permission.ts.
|
|
14460
|
+
* Steady-state `--permissions allow` is not proven by a deny-only canary;
|
|
14461
|
+
* allow_once is only selected after this gate, by the listener model.
|
|
14462
|
+
*/
|
|
14463
|
+
/**
|
|
14464
|
+
* D-081. ONE BOUNDED RETRY, because the canary's pass condition depends on a REMOTE MODEL
|
|
14465
|
+
* CHOOSING to attempt a tool call, and re-prompting re-samples that choice.
|
|
14466
|
+
*
|
|
14467
|
+
* `runPermissionBoundaryCanary` resets its own observation state and sends a fresh prompt, so a
|
|
14468
|
+
* second call is a genuine second sample rather than a re-read of the first verdict — that is
|
|
14469
|
+
* what makes a retry meaningful here and it was checked before this was written.
|
|
14470
|
+
*
|
|
14471
|
+
* MITIGATION, NOT A DIAGNOSIS, and deliberately so: seven mechanisms for D-081 were proposed
|
|
14472
|
+
* and refuted in a single afternoon, and the cause is still not established. The precedent is
|
|
14473
|
+
* D-076, shipped in 0.1.11 as a bounded one-shot retry with its root cause open and documented.
|
|
14474
|
+
*
|
|
14475
|
+
* The cost is real and is recorded rather than hidden: a genuinely dead host now takes up to
|
|
14476
|
+
* two canary timeouts before failing. Measured first-attempt failures on this machine were 24s,
|
|
14477
|
+
* 25s and 9s against a 30s timeout, so a doubled worst case is a minute-scale wait. That is the
|
|
14478
|
+
* price of not reporting a healthy listener as failed, which is the defect being mitigated.
|
|
14479
|
+
*
|
|
14480
|
+
* It must NOT be able to hide a deterministic failure: every attempt is reported through
|
|
14481
|
+
* `onAttempt`, and the thrown error names how many were made and why the last one failed, so
|
|
14482
|
+
* "flaky, retried, ready" and "failed twice" are distinguishable in the log rather than
|
|
14483
|
+
* collapsing into one line.
|
|
14484
|
+
*/
|
|
14485
|
+
async enablePromptsAfterCanary(options) {
|
|
14486
|
+
if (this.promptsEnabled) return;
|
|
14487
|
+
const total = Math.max(1, options?.attempts ?? 2);
|
|
14488
|
+
let last = null;
|
|
14489
|
+
for (let attempt = 1; attempt <= total; attempt += 1) {
|
|
14490
|
+
const result = await this.runPermissionBoundaryCanary(options);
|
|
14491
|
+
last = result;
|
|
14492
|
+
options?.onAttempt?.(attempt, total, result);
|
|
14493
|
+
if (result.passed) {
|
|
14494
|
+
this.promptsEnabled = true;
|
|
14495
|
+
return;
|
|
14496
|
+
}
|
|
14497
|
+
}
|
|
14498
|
+
const detail = last?.reason ?? "permission-boundary canary failed: need host reject + correlated terminal tool status";
|
|
14499
|
+
throw new AcpPermissionCanaryError(
|
|
14500
|
+
total === 1 ? detail : `${detail} (failed ${total} attempts)`,
|
|
14501
|
+
last?.reasonCode ?? null,
|
|
14502
|
+
null,
|
|
14503
|
+
last?.peerError ?? null
|
|
14504
|
+
);
|
|
14505
|
+
}
|
|
14506
|
+
/** Test/helper: force-enable prompts without canary (never used by production open path). */
|
|
14507
|
+
forceEnablePromptsForTests() {
|
|
14508
|
+
this.promptsEnabled = true;
|
|
14509
|
+
}
|
|
14510
|
+
/** Reset the canary gate (used after session/load fallback to session/new). */
|
|
14511
|
+
resetPromptGate() {
|
|
14512
|
+
this.promptsEnabled = false;
|
|
14513
|
+
this.canaryState = {
|
|
14514
|
+
sawPermissionRequest: false,
|
|
14515
|
+
sawDeniedToolResult: false,
|
|
14516
|
+
rejectedToolKeys: /* @__PURE__ */ new Set()
|
|
14517
|
+
};
|
|
14518
|
+
}
|
|
14519
|
+
/** Injective (sessionId, toolCallId) key — a delimiter alone is not, and NUL is not special. */
|
|
14520
|
+
canaryRejectKey(sessionId, toolCallId) {
|
|
14521
|
+
return JSON.stringify([sessionId, toolCallId]);
|
|
14522
|
+
}
|
|
14523
|
+
async runPermissionBoundaryCanary(options) {
|
|
14524
|
+
this.canaryState = {
|
|
14525
|
+
sawPermissionRequest: false,
|
|
14526
|
+
sawDeniedToolResult: false,
|
|
14527
|
+
rejectedToolKeys: /* @__PURE__ */ new Set()
|
|
14528
|
+
};
|
|
14529
|
+
const probe = options?.probeText ?? "cswarm-permission-boundary-canary-v2: if your policy requires a tool permission request, issue one for a no-op check only; do not create, edit, delete, or read any project files; stop after the permission path. Sentinel=CSWARM_CANARY_NOOP";
|
|
14530
|
+
try {
|
|
14531
|
+
const promptResult = await this.promptInternal(probe, {
|
|
14532
|
+
timeoutMs: options?.timeoutMs,
|
|
14533
|
+
bypassCanaryGate: true
|
|
14534
|
+
});
|
|
14535
|
+
const passed = this.canaryState.sawPermissionRequest && this.canaryState.sawDeniedToolResult;
|
|
14536
|
+
return {
|
|
14537
|
+
passed,
|
|
14538
|
+
sawPermissionRequest: this.canaryState.sawPermissionRequest,
|
|
14539
|
+
sawDeniedToolResult: this.canaryState.sawDeniedToolResult,
|
|
14540
|
+
stopReason: promptResult.stopReason,
|
|
14541
|
+
reason: passed ? void 0 : `canary incomplete: permission=${this.canaryState.sawPermissionRequest} deniedTool=${this.canaryState.sawDeniedToolResult}`
|
|
14542
|
+
};
|
|
14543
|
+
} catch (err) {
|
|
14544
|
+
return {
|
|
14545
|
+
passed: false,
|
|
14546
|
+
sawPermissionRequest: this.canaryState.sawPermissionRequest,
|
|
14547
|
+
sawDeniedToolResult: this.canaryState.sawDeniedToolResult,
|
|
14548
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
14549
|
+
...err instanceof AcpHostError ? { reasonCode: err.code } : {},
|
|
14550
|
+
...err instanceof AcpProtocolError && err.peerError ? { peerError: err.peerError } : {}
|
|
14551
|
+
};
|
|
14552
|
+
}
|
|
14553
|
+
}
|
|
14554
|
+
/**
|
|
14555
|
+
* After a successful canary on a throwaway cwd, open a new ACP session on the
|
|
14556
|
+
* real work cwd without re-probing tools in that tree. Same child/host
|
|
14557
|
+
* permission path remains in force.
|
|
14558
|
+
*/
|
|
14559
|
+
async openWorkCwd(cwd) {
|
|
14560
|
+
this.assertOpen();
|
|
14561
|
+
if (!this.promptsEnabled) {
|
|
14562
|
+
throw new AcpPromptsBlockedError();
|
|
14563
|
+
}
|
|
14564
|
+
this.cwd = assertAbsoluteExistingCwd(cwd);
|
|
14565
|
+
await this.newSession();
|
|
14566
|
+
}
|
|
14567
|
+
async prompt(text, options) {
|
|
14568
|
+
if (!this.promptsEnabled) {
|
|
14569
|
+
throw new AcpPromptsBlockedError();
|
|
14570
|
+
}
|
|
14571
|
+
return this.promptInternal(text, { ...options, bypassCanaryGate: false });
|
|
14572
|
+
}
|
|
14573
|
+
/**
|
|
14574
|
+
* session/cancel as a notification — no JSON-RPC id.
|
|
14575
|
+
*/
|
|
14576
|
+
cancel() {
|
|
14577
|
+
if (!this.sessionId) {
|
|
14578
|
+
throw new AcpProtocolError("session not opened", "no_session");
|
|
14579
|
+
}
|
|
14580
|
+
this.transport.notify("session/cancel", { sessionId: this.sessionId });
|
|
14581
|
+
}
|
|
14582
|
+
/**
|
|
14583
|
+
* session/load with fixed cwd and empty mcpServers.
|
|
14584
|
+
* On failure, falls back to session/new and returns the new session id.
|
|
14585
|
+
*/
|
|
14586
|
+
async load(sessionId) {
|
|
14587
|
+
this.assertOpen();
|
|
14588
|
+
try {
|
|
14589
|
+
const result = await this.transport.request(
|
|
14590
|
+
"session/load",
|
|
14591
|
+
{
|
|
14592
|
+
sessionId,
|
|
14593
|
+
cwd: this.cwd,
|
|
14594
|
+
mcpServers: []
|
|
14595
|
+
},
|
|
14596
|
+
this.requestTimeoutMs
|
|
14597
|
+
);
|
|
14598
|
+
const resultIsEmptySuccess = result === null || result === void 0;
|
|
14599
|
+
const resultRecord = isRecord(result) ? result : null;
|
|
14600
|
+
if (!resultIsEmptySuccess && resultRecord === null) {
|
|
14601
|
+
throw new AcpProtocolError(
|
|
14602
|
+
"session/load returned an unrecognised result shape",
|
|
14603
|
+
"session_load_malformed"
|
|
14604
|
+
);
|
|
14605
|
+
}
|
|
14606
|
+
const sessionIdAbsent = resultRecord === null || !("sessionId" in resultRecord) || resultRecord.sessionId === void 0;
|
|
14607
|
+
if (sessionIdAbsent) {
|
|
14608
|
+
this.sessionId = sessionId;
|
|
14609
|
+
await this.applyRequiredMode();
|
|
14610
|
+
return { sessionId, loaded: true };
|
|
14611
|
+
}
|
|
14612
|
+
if (typeof resultRecord.sessionId !== "string") {
|
|
14613
|
+
throw new AcpProtocolError(
|
|
14614
|
+
"session/load returned a non-string session id",
|
|
14615
|
+
"session_id_malformed"
|
|
14616
|
+
);
|
|
14617
|
+
}
|
|
14618
|
+
if (resultRecord.sessionId !== sessionId) {
|
|
14619
|
+
throw new AcpProtocolError(
|
|
14620
|
+
"session/load returned a different session id",
|
|
14621
|
+
"session_id_mismatch"
|
|
14622
|
+
);
|
|
14623
|
+
}
|
|
14624
|
+
this.sessionId = resultRecord.sessionId;
|
|
14625
|
+
await this.applyRequiredMode();
|
|
14626
|
+
return { sessionId: resultRecord.sessionId, loaded: true };
|
|
14627
|
+
} catch {
|
|
14628
|
+
this.resetPromptGate();
|
|
14629
|
+
await this.newSession();
|
|
14630
|
+
return { sessionId: this.sessionId, loaded: false };
|
|
14631
|
+
}
|
|
14632
|
+
}
|
|
14633
|
+
async close() {
|
|
14634
|
+
if (this.closed) return;
|
|
14635
|
+
this.closed = true;
|
|
14636
|
+
this.transport.close();
|
|
14637
|
+
}
|
|
14638
|
+
attachHandlers() {
|
|
14639
|
+
}
|
|
14640
|
+
/**
|
|
14641
|
+
* Install request/notification handlers on a transport for this session.
|
|
14642
|
+
* Called by factories after construction.
|
|
14643
|
+
*/
|
|
14644
|
+
bindTransportHandlers() {
|
|
14645
|
+
}
|
|
14646
|
+
/** Handle agent→client request. Public for transport wiring. */
|
|
14647
|
+
async handleAgentRequest(id, method, params) {
|
|
14648
|
+
if (method === "session/request_permission") {
|
|
14649
|
+
await this.handlePermissionRequest(id, params);
|
|
14650
|
+
return;
|
|
14651
|
+
}
|
|
14652
|
+
this.transport.respondError(id, -32601, `Method not supported by host: ${method}`);
|
|
14653
|
+
}
|
|
14654
|
+
/** Handle agent notification. Public for transport wiring. */
|
|
14655
|
+
handleAgentNotification(method, params) {
|
|
14656
|
+
if (method === "session/update") {
|
|
14657
|
+
this.handleSessionUpdate(params);
|
|
14658
|
+
return;
|
|
14659
|
+
}
|
|
14660
|
+
this.events.notification?.(method, params);
|
|
14661
|
+
}
|
|
14662
|
+
async initialize(clientName, clientVersion) {
|
|
14663
|
+
const result = await this.transport.request(
|
|
14664
|
+
"initialize",
|
|
14665
|
+
{
|
|
14666
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
14667
|
+
clientCapabilities: {
|
|
14668
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
14669
|
+
terminal: false
|
|
14670
|
+
},
|
|
14671
|
+
clientInfo: {
|
|
14672
|
+
name: clientName ?? "cswarm-host",
|
|
14673
|
+
/* "0.0.0" and not a real release number: this fallback only fires when a caller
|
|
14674
|
+
* passes no version, and a hardcoded one here silently rots (it read "0.1.4"
|
|
14675
|
+
* thirteen releases later). Callers that know the build version pass it. */
|
|
14676
|
+
version: clientVersion ?? "0.0.0"
|
|
14677
|
+
}
|
|
14678
|
+
},
|
|
14679
|
+
this.requestTimeoutMs
|
|
14680
|
+
);
|
|
14681
|
+
if (!isRecord(result)) {
|
|
14682
|
+
throw new AcpProtocolError("initialize returned non-object", "invalid_response");
|
|
14683
|
+
}
|
|
14684
|
+
if (result.protocolVersion !== ACP_PROTOCOL_VERSION) {
|
|
14685
|
+
throw new AcpProtocolError(
|
|
14686
|
+
`unsupported protocolVersion ${String(result.protocolVersion)}`,
|
|
14687
|
+
"protocol_version"
|
|
14688
|
+
);
|
|
14689
|
+
}
|
|
14690
|
+
const meta = isRecord(result._meta) ? result._meta : void 0;
|
|
14691
|
+
if (meta && typeof meta.agentVersion === "string") {
|
|
14692
|
+
this.agentVersion = meta.agentVersion;
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14695
|
+
async newSession() {
|
|
14696
|
+
const result = await this.transport.request(
|
|
14697
|
+
"session/new",
|
|
14698
|
+
{
|
|
14699
|
+
cwd: this.cwd,
|
|
14700
|
+
mcpServers: [],
|
|
14701
|
+
_meta: { yoloMode: false }
|
|
14702
|
+
},
|
|
14703
|
+
this.requestTimeoutMs
|
|
14704
|
+
);
|
|
14705
|
+
if (!isRecord(result) || typeof result.sessionId !== "string" || !result.sessionId) {
|
|
14706
|
+
throw new AcpProtocolError("session/new missing sessionId", "invalid_response");
|
|
14707
|
+
}
|
|
14708
|
+
this.sessionId = result.sessionId;
|
|
14709
|
+
await this.applyRequiredMode(result);
|
|
14710
|
+
}
|
|
14711
|
+
/** Select the provider-measured permission mode and fail closed if absent. */
|
|
14712
|
+
async applyRequiredMode(newSessionResult) {
|
|
14713
|
+
const requiredModeId = this.requiredModeId;
|
|
14714
|
+
if (!requiredModeId) return;
|
|
14715
|
+
if (!this.sessionId) {
|
|
14716
|
+
throw new AcpProtocolError("session mode requires an open session", "no_session");
|
|
14717
|
+
}
|
|
14718
|
+
if (newSessionResult) {
|
|
14719
|
+
const modes = isRecord(newSessionResult.modes) ? newSessionResult.modes : null;
|
|
14720
|
+
const availableModes = modes && Array.isArray(modes.availableModes) ? modes.availableModes : [];
|
|
14721
|
+
const available = availableModes.some(
|
|
14722
|
+
(mode3) => isRecord(mode3) && mode3.id === requiredModeId
|
|
14723
|
+
);
|
|
14724
|
+
if (!available) {
|
|
14725
|
+
throw new AcpProtocolError(
|
|
14726
|
+
`required session mode is unavailable: ${requiredModeId}`,
|
|
14727
|
+
"permission_mode_unavailable"
|
|
14728
|
+
);
|
|
14729
|
+
}
|
|
14730
|
+
}
|
|
14731
|
+
try {
|
|
14732
|
+
await this.transport.request(
|
|
14733
|
+
"session/set_mode",
|
|
14734
|
+
{ sessionId: this.sessionId, modeId: requiredModeId },
|
|
14735
|
+
this.requestTimeoutMs
|
|
14736
|
+
);
|
|
14737
|
+
} catch {
|
|
14738
|
+
throw new AcpProtocolError(
|
|
14739
|
+
`required session mode could not be selected: ${requiredModeId}`,
|
|
14740
|
+
"permission_mode_unavailable"
|
|
14741
|
+
);
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
async promptInternal(text, options) {
|
|
14745
|
+
this.assertOpen();
|
|
14746
|
+
if (!options.bypassCanaryGate && !this.promptsEnabled) {
|
|
14747
|
+
throw new AcpPromptsBlockedError();
|
|
14748
|
+
}
|
|
14749
|
+
if (this.promptInFlight) {
|
|
14750
|
+
throw new AcpProtocolError("prompt already in flight (sequential only)", "busy");
|
|
14751
|
+
}
|
|
14752
|
+
if (typeof text !== "string") {
|
|
14753
|
+
throw new AcpProtocolError("prompt text must be a string", "invalid_prompt");
|
|
14754
|
+
}
|
|
14755
|
+
this.promptInFlight = true;
|
|
14756
|
+
const updates = [];
|
|
14757
|
+
let message = "";
|
|
14758
|
+
const prev = this.events.update;
|
|
14759
|
+
this.events.update = (u) => {
|
|
14760
|
+
updates.push(u);
|
|
14761
|
+
const fromOurSession = this.sessionId === null || u.sessionId === this.sessionId;
|
|
14762
|
+
if (u.kind === "agent_message_chunk" && u.text && fromOurSession) {
|
|
14763
|
+
if (message.length + u.text.length > ACP_MAX_ACCUMULATED_TEXT_CHARS) {
|
|
14764
|
+
throw new AcpProtocolError(
|
|
14765
|
+
"accumulated agent message exceeds bound",
|
|
14766
|
+
"message_too_large"
|
|
14767
|
+
);
|
|
14768
|
+
}
|
|
14769
|
+
message += u.text;
|
|
14770
|
+
}
|
|
14771
|
+
prev?.(u);
|
|
14772
|
+
};
|
|
14773
|
+
try {
|
|
14774
|
+
const result = await this.transport.request(
|
|
14775
|
+
"session/prompt",
|
|
14776
|
+
{
|
|
14777
|
+
sessionId: this.sessionId,
|
|
14778
|
+
prompt: [{ type: "text", text }]
|
|
14779
|
+
},
|
|
14780
|
+
options.timeoutMs ?? this.requestTimeoutMs
|
|
14781
|
+
);
|
|
14782
|
+
if (!isRecord(result) || !("stopReason" in result)) {
|
|
14783
|
+
throw new AcpProtocolError("session/prompt missing stopReason", "invalid_response");
|
|
14784
|
+
}
|
|
14785
|
+
const stopReason = asStopReason(result.stopReason);
|
|
14786
|
+
return { stopReason, message, updates };
|
|
14787
|
+
} finally {
|
|
14788
|
+
this.events.update = prev;
|
|
14789
|
+
this.promptInFlight = false;
|
|
14790
|
+
}
|
|
14791
|
+
}
|
|
14792
|
+
handleSessionUpdate(params) {
|
|
14793
|
+
if (!isRecord(params)) return;
|
|
14794
|
+
const claimedSessionId = typeof params.sessionId === "string" ? params.sessionId : null;
|
|
14795
|
+
const sessionId = claimedSessionId ?? "";
|
|
14796
|
+
const update = params.update;
|
|
14797
|
+
if (!isRecord(update)) return;
|
|
14798
|
+
const kind = updateKind(update.sessionUpdate);
|
|
14799
|
+
if (kind === "unknown") {
|
|
14800
|
+
this.events.notification?.("session/update", params);
|
|
14801
|
+
return;
|
|
14802
|
+
}
|
|
14803
|
+
let text;
|
|
14804
|
+
if (kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
|
|
14805
|
+
const content = update.content;
|
|
14806
|
+
if (isRecord(content) && content.type === "text" && typeof content.text === "string") {
|
|
14807
|
+
text = sanitizeText(content.text);
|
|
14808
|
+
}
|
|
14809
|
+
}
|
|
14810
|
+
const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : void 0;
|
|
14811
|
+
const title = typeof update.title === "string" ? sanitizeText(update.title) : void 0;
|
|
14812
|
+
const status = typeof update.status === "string" ? update.status : void 0;
|
|
14813
|
+
const toolKind = typeof update.kind === "string" ? update.kind : void 0;
|
|
14814
|
+
if ((kind === "tool_call_update" || kind === "tool_call") && toolCallId && status && this.sessionId !== null && claimedSessionId !== null && claimedSessionId === this.sessionId && this.canaryState.rejectedToolKeys.has(
|
|
14815
|
+
this.canaryRejectKey(sessionId, toolCallId)
|
|
14816
|
+
) && CANARY_TERMINAL_DENY_STATUSES.has(status.toLowerCase())) {
|
|
14817
|
+
this.canaryState.sawDeniedToolResult = true;
|
|
14818
|
+
}
|
|
14819
|
+
const detail = sanitizeUpdateDetail({
|
|
14820
|
+
...toolKind ? { kind: toolKind } : {},
|
|
14821
|
+
...status ? { status } : {},
|
|
14822
|
+
...title ? { title } : {}
|
|
14823
|
+
});
|
|
14824
|
+
const sanitized = {
|
|
14825
|
+
kind,
|
|
14826
|
+
sessionId,
|
|
14827
|
+
text,
|
|
14828
|
+
toolCallId,
|
|
14829
|
+
title,
|
|
14830
|
+
status,
|
|
14831
|
+
toolKind,
|
|
14832
|
+
detail
|
|
14833
|
+
};
|
|
14834
|
+
this.events.update?.(sanitized);
|
|
14835
|
+
}
|
|
14836
|
+
async handlePermissionRequest(id, params) {
|
|
14837
|
+
const rec = isRecord(params) ? params : {};
|
|
14838
|
+
const claimedSessionId = typeof rec.sessionId === "string" ? rec.sessionId : null;
|
|
14839
|
+
const sessionId = claimedSessionId ?? "";
|
|
14840
|
+
const options = parsePermissionOptions(rec.options);
|
|
14841
|
+
const toolCall = isRecord(rec.toolCall) ? rec.toolCall : {};
|
|
14842
|
+
const toolCallId = typeof toolCall.toolCallId === "string" ? toolCall.toolCallId : void 0;
|
|
14843
|
+
const title = typeof toolCall.title === "string" ? toolCall.title : void 0;
|
|
14844
|
+
const kind = typeof toolCall.kind === "string" ? toolCall.kind : void 0;
|
|
14845
|
+
const sessionMatches = this.sessionId !== null && claimedSessionId !== null && claimedSessionId === this.sessionId;
|
|
14846
|
+
if (sessionMatches) {
|
|
14847
|
+
this.canaryState.sawPermissionRequest = true;
|
|
14848
|
+
}
|
|
14849
|
+
if (!sessionMatches) {
|
|
14850
|
+
this.transport.respond(
|
|
14851
|
+
id,
|
|
14852
|
+
permissionDecisionToResult(defaultPermissionCallback({
|
|
14853
|
+
sessionId,
|
|
14854
|
+
toolCallId,
|
|
14855
|
+
title,
|
|
14856
|
+
kind,
|
|
14857
|
+
options,
|
|
14858
|
+
summary: sanitizeText(
|
|
14859
|
+
[kind, title, toolCallId].filter(Boolean).join(" ") || "permission request"
|
|
14860
|
+
)
|
|
14861
|
+
}))
|
|
14862
|
+
);
|
|
14863
|
+
return;
|
|
14864
|
+
}
|
|
14865
|
+
const summary = sanitizeText(
|
|
14866
|
+
[kind, title, toolCallId].filter(Boolean).join(" ") || "permission request"
|
|
14867
|
+
);
|
|
14868
|
+
let decision;
|
|
14869
|
+
try {
|
|
14870
|
+
decision = await this.permissionCallback({
|
|
14871
|
+
sessionId,
|
|
14872
|
+
toolCallId,
|
|
14873
|
+
title,
|
|
14874
|
+
kind,
|
|
14875
|
+
options,
|
|
14876
|
+
summary
|
|
14877
|
+
});
|
|
14878
|
+
} catch {
|
|
14879
|
+
decision = defaultPermissionCallback({
|
|
14880
|
+
sessionId,
|
|
14881
|
+
toolCallId,
|
|
14882
|
+
title,
|
|
14883
|
+
kind,
|
|
14884
|
+
options,
|
|
14885
|
+
summary
|
|
14886
|
+
});
|
|
14887
|
+
}
|
|
14888
|
+
if (!decision || decision.outcome !== "cancelled" && decision.outcome !== "selected") {
|
|
14889
|
+
decision = defaultPermissionCallback({
|
|
14890
|
+
sessionId,
|
|
14891
|
+
toolCallId,
|
|
14892
|
+
title,
|
|
14893
|
+
kind,
|
|
14894
|
+
options,
|
|
14895
|
+
summary
|
|
14896
|
+
});
|
|
14897
|
+
}
|
|
14898
|
+
if (sessionMatches && toolCallId && isHostRejectDecision(decision, options)) {
|
|
14899
|
+
this.canaryState.rejectedToolKeys.add(
|
|
14900
|
+
this.canaryRejectKey(sessionId, toolCallId)
|
|
14901
|
+
);
|
|
14902
|
+
}
|
|
14903
|
+
const result = permissionDecisionToResult(decision);
|
|
14904
|
+
this.transport.respond(id, result);
|
|
14905
|
+
}
|
|
14906
|
+
assertOpen() {
|
|
14907
|
+
if (this.closed) {
|
|
14908
|
+
throw new AcpProtocolError("session closed", "closed");
|
|
14909
|
+
}
|
|
14910
|
+
if (!this.sessionId) {
|
|
14911
|
+
throw new AcpProtocolError("session not opened", "no_session");
|
|
14912
|
+
}
|
|
14913
|
+
}
|
|
14914
|
+
};
|
|
14915
|
+
}
|
|
14916
|
+
});
|
|
14917
|
+
|
|
14918
|
+
// src/host/claude.ts
|
|
14919
|
+
var claude_exports = {};
|
|
14920
|
+
__export(claude_exports, {
|
|
14921
|
+
CLAUDE_ACP_LAST_MEASURED_VERSION: () => CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
14922
|
+
CLAUDE_ACP_MIN_VERSION: () => CLAUDE_ACP_MIN_VERSION,
|
|
14923
|
+
CLAUDE_PERMISSION_MODE_ID: () => CLAUDE_PERMISSION_MODE_ID,
|
|
14924
|
+
assertClaudeVersionFloor: () => assertClaudeVersionFloor,
|
|
14925
|
+
buildClaudeAcpArgs: () => buildClaudeAcpArgs,
|
|
14926
|
+
buildClaudeChildEnv: () => buildClaudeChildEnv,
|
|
14927
|
+
buildClaudeLaunch: () => buildClaudeLaunch,
|
|
14928
|
+
inspectClaudeBridgeExecutable: () => inspectClaudeBridgeExecutable,
|
|
14929
|
+
measureClaudeBundleVersions: () => measureClaudeBundleVersions,
|
|
14930
|
+
openClaudeAcpSession: () => openClaudeAcpSession,
|
|
14931
|
+
parseClaudeCodeVersionOutput: () => parseClaudeCodeVersionOutput,
|
|
14932
|
+
parseClaudeVersionOutput: () => parseClaudeVersionOutput,
|
|
14933
|
+
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
14934
|
+
resolvePackagedClaudeBridge: () => resolvePackagedClaudeBridge,
|
|
14935
|
+
terminateClaudeChild: () => terminateClaudeChild
|
|
14936
|
+
});
|
|
14937
|
+
function isPackagedClaudeBridge(executable) {
|
|
14938
|
+
const normalized = executable.replaceAll("\\", "/");
|
|
14939
|
+
return normalized.endsWith(
|
|
14940
|
+
"/node_modules/@agentclientprotocol/claude-agent-acp/dist/index.js"
|
|
14941
|
+
);
|
|
14942
|
+
}
|
|
14943
|
+
function resolvePackagedClaudeBridge(pathEnv, platform = process.platform) {
|
|
14944
|
+
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
14945
|
+
const names = platform === "win32" ? ["claude-agent-acp.cmd"] : ["claude-agent-acp"];
|
|
14946
|
+
for (const dir of pathValue.split(import_node_path14.delimiter)) {
|
|
14947
|
+
if (!dir) continue;
|
|
14948
|
+
for (const name of names) {
|
|
14949
|
+
try {
|
|
14950
|
+
const candidate = resolvedClaudeCandidate((0, import_node_path14.join)(dir, name), platform);
|
|
14951
|
+
if (isPackagedClaudeBridge(candidate)) return candidate;
|
|
14952
|
+
} catch {
|
|
14953
|
+
}
|
|
14954
|
+
}
|
|
14955
|
+
}
|
|
14956
|
+
throw new AcpHostError(
|
|
14957
|
+
"executable_missing",
|
|
14958
|
+
"packaged claude-agent-acp executable not found; install @agentclientprotocol/claude-agent-acp@latest (minimum 0.64.2)"
|
|
14959
|
+
);
|
|
14960
|
+
}
|
|
14961
|
+
function resolveWindowsNpmShim(shim) {
|
|
14962
|
+
let source;
|
|
14963
|
+
try {
|
|
14964
|
+
source = (0, import_node_fs3.readFileSync)(shim, "utf8");
|
|
14965
|
+
} catch {
|
|
14966
|
+
throw new AcpHostError(
|
|
14967
|
+
"executable_missing",
|
|
14968
|
+
`could not read claude-agent-acp npm shim: ${shim}`
|
|
14969
|
+
);
|
|
14970
|
+
}
|
|
14971
|
+
if (Buffer.byteLength(source, "utf8") > WINDOWS_NPM_SHIM_MAX_BYTES || !source.includes(
|
|
14972
|
+
String.raw`"%dp0%\node_modules\@agentclientprotocol\claude-agent-acp\dist\index.js"`
|
|
14973
|
+
)) {
|
|
14974
|
+
throw new AcpHostError(
|
|
14975
|
+
"executable_missing",
|
|
14976
|
+
`unrecognized claude-agent-acp npm shim: ${shim}`
|
|
14977
|
+
);
|
|
14978
|
+
}
|
|
14979
|
+
const target2 = (0, import_node_path14.join)((0, import_node_path14.dirname)(shim), ...WINDOWS_NPM_ENTRYPOINT);
|
|
14980
|
+
try {
|
|
14981
|
+
(0, import_node_fs3.accessSync)(target2, import_node_fs3.constants.R_OK);
|
|
14982
|
+
return (0, import_node_fs3.realpathSync)(target2);
|
|
14983
|
+
} catch {
|
|
14984
|
+
throw new AcpHostError(
|
|
14985
|
+
"executable_missing",
|
|
14986
|
+
`claude-agent-acp package entrypoint is missing beside npm shim: ${shim}`
|
|
14987
|
+
);
|
|
14988
|
+
}
|
|
14989
|
+
}
|
|
14990
|
+
function resolvedClaudeCandidate(candidate, platform) {
|
|
14991
|
+
(0, import_node_fs3.accessSync)(candidate, import_node_fs3.constants.X_OK);
|
|
14992
|
+
const real = (0, import_node_fs3.realpathSync)(candidate);
|
|
14993
|
+
return platform === "win32" && (0, import_node_path14.extname)(real).toLowerCase() === ".cmd" ? resolveWindowsNpmShim(real) : real;
|
|
14994
|
+
}
|
|
14995
|
+
function resolveClaudeExecutable(executable = "claude-agent-acp", pathEnv, platform = process.platform) {
|
|
14996
|
+
if ((0, import_node_path14.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
14997
|
+
const abs = (0, import_node_path14.resolve)(executable);
|
|
14998
|
+
const candidates = platform === "win32" && (0, import_node_path14.extname)(abs) === "" ? [`${abs}.cmd`] : [abs];
|
|
14999
|
+
for (const candidate of candidates) {
|
|
15000
|
+
try {
|
|
15001
|
+
return resolvedClaudeCandidate(candidate, platform);
|
|
15002
|
+
} catch (error) {
|
|
15003
|
+
if (error instanceof AcpHostError) throw error;
|
|
15004
|
+
}
|
|
15005
|
+
}
|
|
15006
|
+
throw new AcpHostError("executable_missing", `not executable: ${abs}`);
|
|
15007
|
+
}
|
|
15008
|
+
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
15009
|
+
const names = platform === "win32" && (0, import_node_path14.extname)(executable) === "" ? [`${executable}.cmd`] : [executable];
|
|
15010
|
+
for (const dir of pathValue.split(import_node_path14.delimiter)) {
|
|
15011
|
+
if (!dir) continue;
|
|
15012
|
+
for (const name of names) {
|
|
15013
|
+
const candidate = (0, import_node_path14.join)(dir, name);
|
|
15014
|
+
try {
|
|
15015
|
+
return resolvedClaudeCandidate(candidate, platform);
|
|
15016
|
+
} catch (error) {
|
|
15017
|
+
if (error instanceof AcpHostError) throw error;
|
|
15018
|
+
}
|
|
15019
|
+
}
|
|
15020
|
+
}
|
|
15021
|
+
throw new AcpHostError(
|
|
15022
|
+
"executable_missing",
|
|
15023
|
+
`claude-agent-acp executable not found on PATH: ${executable}`
|
|
15024
|
+
);
|
|
15025
|
+
}
|
|
15026
|
+
function buildClaudeLaunch(executable, args, platform = process.platform) {
|
|
15027
|
+
return platform === "win32" && (0, import_node_path14.extname)(executable).toLowerCase() === ".js" ? { command: process.execPath, args: [executable, ...args] } : { command: executable, args: [...args] };
|
|
15028
|
+
}
|
|
15029
|
+
function parseClaudeVersionOutput(stdout) {
|
|
15030
|
+
return parseProviderVersionOutput(stdout, /\bclaude-agent-acp\b/i);
|
|
15031
|
+
}
|
|
15032
|
+
function parseClaudeCodeVersionOutput(stdout) {
|
|
15033
|
+
return parseProviderVersionOutput(stdout, /\bClaude Code\b/i, false);
|
|
15034
|
+
}
|
|
15035
|
+
function semanticVersion(value) {
|
|
15036
|
+
if (typeof value !== "string") return null;
|
|
15037
|
+
return parseProviderVersionOutput(`${value}
|
|
15038
|
+
`, /\bnever-a-product-name\b/i);
|
|
15039
|
+
}
|
|
15040
|
+
function readPackageAtOrAbove(entrypoint, expectedName) {
|
|
15041
|
+
let directory = (0, import_node_path14.dirname)(entrypoint);
|
|
15042
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
15043
|
+
const path = (0, import_node_path14.join)(directory, "package.json");
|
|
15044
|
+
try {
|
|
15045
|
+
const row = JSON.parse((0, import_node_fs3.readFileSync)(path, "utf8"));
|
|
15046
|
+
if (row && typeof row === "object" && !Array.isArray(row) && row.name === expectedName) {
|
|
15047
|
+
return { path, row };
|
|
15048
|
+
}
|
|
15049
|
+
} catch {
|
|
15050
|
+
}
|
|
15051
|
+
const parent = (0, import_node_path14.dirname)(directory);
|
|
15052
|
+
if (parent === directory) break;
|
|
15053
|
+
directory = parent;
|
|
15054
|
+
}
|
|
15055
|
+
return null;
|
|
15056
|
+
}
|
|
15057
|
+
function measureClaudeBundleVersions(executable) {
|
|
15058
|
+
const adapter = readPackageAtOrAbove(
|
|
15059
|
+
executable,
|
|
15060
|
+
"@agentclientprotocol/claude-agent-acp"
|
|
15061
|
+
);
|
|
15062
|
+
if (!adapter) return { agentSdkVersion: null, claudeCodeVersion: null };
|
|
15063
|
+
try {
|
|
15064
|
+
const sdkEntrypoint = (0, import_node_module.createRequire)(adapter.path).resolve(
|
|
15065
|
+
"@anthropic-ai/claude-agent-sdk"
|
|
15066
|
+
);
|
|
15067
|
+
const sdk = readPackageAtOrAbove(
|
|
15068
|
+
sdkEntrypoint,
|
|
15069
|
+
"@anthropic-ai/claude-agent-sdk"
|
|
15070
|
+
);
|
|
15071
|
+
if (!sdk) return { agentSdkVersion: null, claudeCodeVersion: null };
|
|
15072
|
+
return {
|
|
15073
|
+
agentSdkVersion: semanticVersion(sdk.row.version),
|
|
15074
|
+
claudeCodeVersion: semanticVersion(sdk.row.claudeCodeVersion)
|
|
15075
|
+
};
|
|
15076
|
+
} catch {
|
|
15077
|
+
return { agentSdkVersion: null, claudeCodeVersion: null };
|
|
15078
|
+
}
|
|
15079
|
+
}
|
|
15080
|
+
async function readClaudeVersionOutput(executable, options) {
|
|
15081
|
+
const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
|
|
15082
|
+
const env = options?.env ?? sanitizeChildEnv(process.env);
|
|
15083
|
+
const launch = buildClaudeLaunch(executable, ["--version"], options?.platform);
|
|
15084
|
+
return await new Promise((resolve4, reject) => {
|
|
15085
|
+
(0, import_node_child_process5.execFile)(
|
|
15086
|
+
launch.command,
|
|
15087
|
+
launch.args,
|
|
15088
|
+
{ timeout: timeoutMs, encoding: "utf8", env },
|
|
15089
|
+
(error, out, stderr) => {
|
|
15090
|
+
if (error) {
|
|
15091
|
+
reject(
|
|
15092
|
+
new AcpVersionError(
|
|
15093
|
+
`failed to run ${executable} --version: ${error.message}${stderr ? ` (${stderr.trim()})` : ""}`
|
|
15094
|
+
)
|
|
15095
|
+
);
|
|
15096
|
+
return;
|
|
15097
|
+
}
|
|
15098
|
+
resolve4(out);
|
|
15099
|
+
}
|
|
15100
|
+
);
|
|
15101
|
+
});
|
|
15102
|
+
}
|
|
15103
|
+
async function inspectClaudeBridgeExecutable(executable = "claude-agent-acp", options) {
|
|
15104
|
+
const resolved = resolveClaudeExecutable(
|
|
15105
|
+
executable,
|
|
15106
|
+
options?.pathEnv,
|
|
15107
|
+
options?.platform
|
|
15108
|
+
);
|
|
15109
|
+
const output = await readClaudeVersionOutput(resolved, options);
|
|
15110
|
+
const bundle = measureClaudeBundleVersions(resolved);
|
|
15111
|
+
return {
|
|
15112
|
+
executable: resolved,
|
|
15113
|
+
providerVersion: parseClaudeVersionOutput(output),
|
|
15114
|
+
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
15115
|
+
bundledAgentSdkVersion: bundle.agentSdkVersion,
|
|
15116
|
+
bundledClaudeCodeVersion: bundle.claudeCodeVersion
|
|
15117
|
+
};
|
|
15118
|
+
}
|
|
15119
|
+
async function assertClaudeVersionFloor(executable, options) {
|
|
15120
|
+
const minimumVersion = options?.minimumVersion ?? CLAUDE_ACP_MIN_VERSION;
|
|
15121
|
+
const lastMeasuredVersion = options?.lastMeasuredVersion ?? CLAUDE_ACP_LAST_MEASURED_VERSION;
|
|
15122
|
+
const stdout = await readClaudeVersionOutput(executable, options);
|
|
15123
|
+
const version3 = parseClaudeVersionOutput(stdout);
|
|
15124
|
+
if (!version3) {
|
|
15125
|
+
throw new AcpVersionParseError(
|
|
15126
|
+
`could not parse claude-agent-acp version from: ${stdout.trim().slice(0, 200)}`
|
|
15127
|
+
);
|
|
15128
|
+
}
|
|
15129
|
+
assertProviderVersionFloor({
|
|
15130
|
+
provider: "claude-agent-acp",
|
|
15131
|
+
version: version3,
|
|
15132
|
+
minimumVersion,
|
|
15133
|
+
lastMeasuredVersion,
|
|
15134
|
+
...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
|
|
15135
|
+
});
|
|
15136
|
+
return version3;
|
|
15137
|
+
}
|
|
15138
|
+
function buildClaudeAcpArgs() {
|
|
15139
|
+
return [];
|
|
15140
|
+
}
|
|
15141
|
+
function buildClaudeChildEnv(parent, claudeCodeExecutable) {
|
|
15142
|
+
const env = sanitizeChildEnv(parent);
|
|
15143
|
+
if (claudeCodeExecutable) {
|
|
15144
|
+
env.CLAUDE_CODE_EXECUTABLE = claudeCodeExecutable;
|
|
15145
|
+
}
|
|
15146
|
+
return env;
|
|
15147
|
+
}
|
|
15148
|
+
function waitForChildExit(child, timeoutMs) {
|
|
15149
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
15150
|
+
return Promise.resolve();
|
|
15151
|
+
}
|
|
15152
|
+
return new Promise((resolve4) => {
|
|
15153
|
+
const timer2 = setTimeout(resolve4, timeoutMs);
|
|
15154
|
+
child.once("exit", () => {
|
|
15155
|
+
clearTimeout(timer2);
|
|
15156
|
+
resolve4();
|
|
15157
|
+
});
|
|
15158
|
+
});
|
|
15159
|
+
}
|
|
15160
|
+
async function terminateClaudeChild(child) {
|
|
15161
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
15162
|
+
try {
|
|
15163
|
+
child.kill("SIGTERM");
|
|
15164
|
+
} catch {
|
|
15165
|
+
}
|
|
15166
|
+
await waitForChildExit(child, CHILD_EXIT_WAIT_MS);
|
|
15167
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
15168
|
+
try {
|
|
15169
|
+
child.kill("SIGKILL");
|
|
15170
|
+
} catch {
|
|
15171
|
+
}
|
|
15172
|
+
await waitForChildExit(child, CHILD_KILL_WAIT_MS);
|
|
15173
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
15174
|
+
throw new AcpHostError(
|
|
15175
|
+
"child_exit_timeout",
|
|
15176
|
+
"Claude ACP bridge did not exit after SIGTERM and SIGKILL"
|
|
15177
|
+
);
|
|
15178
|
+
}
|
|
15179
|
+
}
|
|
15180
|
+
async function openClaudeAcpSession(options) {
|
|
15181
|
+
const parentEnv = options.env ?? process.env;
|
|
15182
|
+
if (options.signal?.aborted) {
|
|
15183
|
+
throw new AcpHostError(
|
|
15184
|
+
"cancelled",
|
|
15185
|
+
"Claude ACP bridge opening was cancelled"
|
|
15186
|
+
);
|
|
15187
|
+
}
|
|
15188
|
+
const pathEnv = parentEnv.PATH;
|
|
15189
|
+
const resolvedPathEnv = typeof pathEnv === "string" ? pathEnv : void 0;
|
|
15190
|
+
if (options.skipVersionCheck && options.executable) {
|
|
15191
|
+
throw new AcpHostError(
|
|
15192
|
+
"version_check_required",
|
|
15193
|
+
"skipVersionCheck cannot classify an explicit Claude executable"
|
|
15194
|
+
);
|
|
15195
|
+
}
|
|
15196
|
+
const requestedExecutable = options.executable ? resolveClaudeExecutable(options.executable, resolvedPathEnv) : null;
|
|
15197
|
+
const baseEnv = buildClaudeChildEnv(parentEnv);
|
|
15198
|
+
let executable;
|
|
15199
|
+
let env = baseEnv;
|
|
15200
|
+
let claudeCodeExecutable;
|
|
15201
|
+
let providerVersion;
|
|
15202
|
+
let bundleVersions = {
|
|
15203
|
+
agentSdkVersion: null,
|
|
15204
|
+
claudeCodeVersion: null
|
|
15205
|
+
};
|
|
15206
|
+
const reportRuntime = (resolved, version3) => {
|
|
15207
|
+
bundleVersions = measureClaudeBundleVersions(resolved);
|
|
15208
|
+
options.onRuntimeNotice?.({
|
|
15209
|
+
executable: resolved,
|
|
15210
|
+
providerVersion: version3,
|
|
15211
|
+
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
15212
|
+
bundledAgentSdkVersion: bundleVersions.agentSdkVersion,
|
|
15213
|
+
bundledClaudeCodeVersion: bundleVersions.claudeCodeVersion
|
|
15214
|
+
});
|
|
15215
|
+
};
|
|
15216
|
+
const admitBridgeVersion = (resolved, version3) => {
|
|
15217
|
+
providerVersion = version3;
|
|
15218
|
+
reportRuntime(resolved, version3);
|
|
15219
|
+
assertProviderVersionFloor({
|
|
15220
|
+
provider: "claude-agent-acp",
|
|
15221
|
+
version: version3,
|
|
15222
|
+
minimumVersion: CLAUDE_ACP_MIN_VERSION,
|
|
15223
|
+
lastMeasuredVersion: CLAUDE_ACP_LAST_MEASURED_VERSION,
|
|
15224
|
+
...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
|
|
15225
|
+
});
|
|
15226
|
+
};
|
|
15227
|
+
if (requestedExecutable) {
|
|
15228
|
+
const output = await readClaudeVersionOutput(requestedExecutable, {
|
|
15229
|
+
env: baseEnv
|
|
15230
|
+
});
|
|
15231
|
+
const bridgeVersion = parseClaudeVersionOutput(output);
|
|
15232
|
+
if (bridgeVersion) {
|
|
15233
|
+
executable = requestedExecutable;
|
|
15234
|
+
admitBridgeVersion(executable, bridgeVersion);
|
|
15235
|
+
} else if (parseClaudeCodeVersionOutput(output)) {
|
|
15236
|
+
claudeCodeExecutable = requestedExecutable;
|
|
15237
|
+
executable = resolvePackagedClaudeBridge(resolvedPathEnv);
|
|
15238
|
+
env = buildClaudeChildEnv(parentEnv, claudeCodeExecutable);
|
|
15239
|
+
const bridgeOutput = await readClaudeVersionOutput(executable, {
|
|
15240
|
+
env
|
|
15241
|
+
});
|
|
15242
|
+
const packagedVersion = parseClaudeVersionOutput(bridgeOutput);
|
|
15243
|
+
if (!packagedVersion) {
|
|
15244
|
+
reportRuntime(executable, null);
|
|
15245
|
+
throw new AcpVersionParseError(
|
|
15246
|
+
`could not parse claude-agent-acp version from: ${bridgeOutput.trim().slice(0, 200)}`
|
|
15247
|
+
);
|
|
15248
|
+
}
|
|
15249
|
+
admitBridgeVersion(executable, packagedVersion);
|
|
15250
|
+
} else {
|
|
15251
|
+
reportRuntime(requestedExecutable, null);
|
|
15252
|
+
throw new AcpVersionError(
|
|
15253
|
+
`could not identify Claude executable from: ${output.trim().slice(0, 200)}`
|
|
15254
|
+
);
|
|
15255
|
+
}
|
|
15256
|
+
} else {
|
|
15257
|
+
executable = requestedExecutable ?? resolveClaudeExecutable(
|
|
15258
|
+
"claude-agent-acp",
|
|
15259
|
+
resolvedPathEnv
|
|
15260
|
+
);
|
|
15261
|
+
if (!options.skipVersionCheck) {
|
|
15262
|
+
const output = await readClaudeVersionOutput(executable, {
|
|
15263
|
+
env: baseEnv
|
|
15264
|
+
});
|
|
15265
|
+
const version3 = parseClaudeVersionOutput(output);
|
|
15266
|
+
if (!version3) {
|
|
15267
|
+
reportRuntime(executable, null);
|
|
15268
|
+
throw new AcpVersionParseError(
|
|
15269
|
+
`could not parse claude-agent-acp version from: ${output.trim().slice(0, 200)}`
|
|
15270
|
+
);
|
|
15271
|
+
}
|
|
15272
|
+
admitBridgeVersion(executable, version3);
|
|
15273
|
+
} else {
|
|
15274
|
+
reportRuntime(executable, null);
|
|
15275
|
+
}
|
|
15276
|
+
}
|
|
15277
|
+
if (options.signal?.aborted) {
|
|
15278
|
+
throw new AcpHostError(
|
|
15279
|
+
"cancelled",
|
|
15280
|
+
"Claude ACP bridge opening was cancelled"
|
|
15281
|
+
);
|
|
15282
|
+
}
|
|
15283
|
+
const args = buildClaudeAcpArgs();
|
|
15284
|
+
const launch = buildClaudeLaunch(executable, args);
|
|
15285
|
+
const child = (0, import_node_child_process5.spawn)(launch.command, launch.args, {
|
|
15286
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
15287
|
+
env,
|
|
15288
|
+
cwd: options.cwd
|
|
15289
|
+
});
|
|
15290
|
+
const spawnError = new Promise((_resolve, reject) => {
|
|
15291
|
+
child.once("error", () => {
|
|
15292
|
+
reject(
|
|
15293
|
+
new AcpHostError(
|
|
15294
|
+
"spawn_failed",
|
|
15295
|
+
"failed to spawn the Claude ACP bridge"
|
|
15296
|
+
)
|
|
15297
|
+
);
|
|
15298
|
+
});
|
|
15299
|
+
});
|
|
15300
|
+
let removeAbortListener = () => void 0;
|
|
15301
|
+
const abortError = new Promise((_resolve, reject) => {
|
|
15302
|
+
const signal = options.signal;
|
|
15303
|
+
if (!signal) return;
|
|
15304
|
+
const onAbort = () => {
|
|
15305
|
+
reject(
|
|
15306
|
+
new AcpHostError(
|
|
15307
|
+
"cancelled",
|
|
15308
|
+
"Claude ACP bridge opening was cancelled"
|
|
15309
|
+
)
|
|
15310
|
+
);
|
|
15311
|
+
};
|
|
15312
|
+
if (signal.aborted) {
|
|
15313
|
+
onAbort();
|
|
15314
|
+
return;
|
|
15315
|
+
}
|
|
15316
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
15317
|
+
removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
15318
|
+
});
|
|
15319
|
+
if (!child.stdin || !child.stdout) {
|
|
15320
|
+
await terminateClaudeChild(child);
|
|
15321
|
+
throw new AcpHostError("spawn_failed", "child missing stdio pipes");
|
|
15322
|
+
}
|
|
15323
|
+
const observeStderrTailOnExit = attachStderrTailExitObserver(
|
|
15324
|
+
child,
|
|
15325
|
+
options.onStderrTail
|
|
15326
|
+
);
|
|
15327
|
+
let sessionRef = null;
|
|
15328
|
+
const transport = createBoundTransport({
|
|
15329
|
+
readable: child.stdout,
|
|
15330
|
+
writable: child.stdin,
|
|
15331
|
+
requestTimeoutMs: options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS,
|
|
15332
|
+
readableEndGraceMs: STDERR_READABLE_END_GRACE_MS,
|
|
15333
|
+
getSession: () => sessionRef,
|
|
15334
|
+
onChildExit: observeStderrTailOnExit
|
|
15335
|
+
});
|
|
15336
|
+
try {
|
|
15337
|
+
const session = await Promise.race([
|
|
15338
|
+
AcpHostSession.connect({
|
|
15339
|
+
transport,
|
|
15340
|
+
cwd: options.cwd,
|
|
15341
|
+
requiredModeId: CLAUDE_PERMISSION_MODE_ID,
|
|
15342
|
+
permissionCallback: options.permissionCallback,
|
|
15343
|
+
events: options.events,
|
|
15344
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
15345
|
+
clientName: options.clientName,
|
|
15346
|
+
clientVersion: options.clientVersion,
|
|
15347
|
+
promptsEnabled: options.promptsEnabled
|
|
15348
|
+
}),
|
|
15349
|
+
spawnError,
|
|
15350
|
+
abortError
|
|
15351
|
+
]);
|
|
15352
|
+
removeAbortListener();
|
|
15353
|
+
sessionRef = session;
|
|
15354
|
+
let closePromise = null;
|
|
15355
|
+
const close = async () => {
|
|
15356
|
+
if (closePromise) return closePromise;
|
|
15357
|
+
closePromise = (async () => {
|
|
15358
|
+
try {
|
|
15359
|
+
await session.close();
|
|
15360
|
+
} finally {
|
|
15361
|
+
await terminateClaudeChild(child);
|
|
15362
|
+
}
|
|
15363
|
+
})();
|
|
15364
|
+
return closePromise;
|
|
15365
|
+
};
|
|
15366
|
+
return {
|
|
15367
|
+
session,
|
|
15368
|
+
child,
|
|
15369
|
+
executable,
|
|
15370
|
+
args,
|
|
15371
|
+
env,
|
|
15372
|
+
...providerVersion ? { providerVersion } : {},
|
|
15373
|
+
...bundleVersions.agentSdkVersion ? { bundledAgentSdkVersion: bundleVersions.agentSdkVersion } : {},
|
|
15374
|
+
...bundleVersions.claudeCodeVersion ? { bundledClaudeCodeVersion: bundleVersions.claudeCodeVersion } : {},
|
|
15375
|
+
close
|
|
15376
|
+
};
|
|
15377
|
+
} catch (error) {
|
|
15378
|
+
removeAbortListener();
|
|
15379
|
+
transport.close();
|
|
15380
|
+
await terminateClaudeChild(child);
|
|
15381
|
+
throw error;
|
|
15382
|
+
}
|
|
15383
|
+
}
|
|
15384
|
+
var import_node_child_process5, import_node_module, import_node_fs3, import_node_path14, CHILD_EXIT_WAIT_MS, CHILD_KILL_WAIT_MS, WINDOWS_NPM_SHIM_MAX_BYTES, WINDOWS_NPM_ENTRYPOINT;
|
|
15385
|
+
var init_claude = __esm({
|
|
15386
|
+
"src/host/claude.ts"() {
|
|
15387
|
+
"use strict";
|
|
15388
|
+
init_stderr_tail();
|
|
15389
|
+
import_node_child_process5 = require("node:child_process");
|
|
15390
|
+
import_node_module = require("node:module");
|
|
15391
|
+
import_node_fs3 = require("node:fs");
|
|
15392
|
+
import_node_path14 = require("node:path");
|
|
15393
|
+
init_bounds();
|
|
15394
|
+
init_env();
|
|
15395
|
+
init_session();
|
|
15396
|
+
init_types();
|
|
15397
|
+
init_version();
|
|
15398
|
+
CHILD_EXIT_WAIT_MS = 3e3;
|
|
15399
|
+
CHILD_KILL_WAIT_MS = 1e3;
|
|
15400
|
+
WINDOWS_NPM_SHIM_MAX_BYTES = 64 * 1024;
|
|
15401
|
+
WINDOWS_NPM_ENTRYPOINT = [
|
|
15402
|
+
"node_modules",
|
|
15403
|
+
"@agentclientprotocol",
|
|
15404
|
+
"claude-agent-acp",
|
|
15405
|
+
"dist",
|
|
15406
|
+
"index.js"
|
|
15407
|
+
];
|
|
15408
|
+
}
|
|
15409
|
+
});
|
|
15410
|
+
|
|
15411
|
+
// src/host/codex.ts
|
|
15412
|
+
var codex_exports = {};
|
|
15413
|
+
__export(codex_exports, {
|
|
15414
|
+
CODEX_ACP_LAST_MEASURED_VERSION: () => CODEX_ACP_LAST_MEASURED_VERSION,
|
|
15415
|
+
CODEX_ACP_MIN_VERSION: () => CODEX_ACP_MIN_VERSION,
|
|
15416
|
+
CODEX_PERMISSION_MODE_ID: () => CODEX_PERMISSION_MODE_ID,
|
|
15417
|
+
assertCodexVersionFloor: () => assertCodexVersionFloor,
|
|
15418
|
+
buildCodexAcpArgs: () => buildCodexAcpArgs,
|
|
15419
|
+
buildCodexChildEnv: () => buildCodexChildEnv,
|
|
15420
|
+
buildCodexLaunch: () => buildCodexLaunch,
|
|
15421
|
+
openCodexAcpSession: () => openCodexAcpSession,
|
|
15422
|
+
parseCodexVersionOutput: () => parseCodexVersionOutput,
|
|
15423
|
+
resolveCodexExecutable: () => resolveCodexExecutable,
|
|
15424
|
+
terminateCodexChild: () => terminateCodexChild
|
|
15425
|
+
});
|
|
15426
|
+
function resolveWindowsNpmShim2(shim) {
|
|
15427
|
+
let source;
|
|
15428
|
+
try {
|
|
15429
|
+
source = (0, import_node_fs4.readFileSync)(shim, "utf8");
|
|
15430
|
+
} catch {
|
|
15431
|
+
throw new AcpHostError(
|
|
15432
|
+
"executable_missing",
|
|
15433
|
+
`could not read codex-acp npm shim: ${shim}`
|
|
15434
|
+
);
|
|
15435
|
+
}
|
|
15436
|
+
if (Buffer.byteLength(source, "utf8") > WINDOWS_NPM_SHIM_MAX_BYTES2 || !source.includes(
|
|
15437
|
+
String.raw`"%dp0%\node_modules\@agentclientprotocol\codex-acp\dist\index.js"`
|
|
15438
|
+
)) {
|
|
15439
|
+
throw new AcpHostError(
|
|
15440
|
+
"executable_missing",
|
|
15441
|
+
`unrecognized codex-acp npm shim: ${shim}`
|
|
15442
|
+
);
|
|
15443
|
+
}
|
|
15444
|
+
const target2 = (0, import_node_path15.join)((0, import_node_path15.dirname)(shim), ...WINDOWS_NPM_ENTRYPOINT2);
|
|
15445
|
+
try {
|
|
15446
|
+
(0, import_node_fs4.accessSync)(target2, import_node_fs4.constants.R_OK);
|
|
15447
|
+
return (0, import_node_fs4.realpathSync)(target2);
|
|
15448
|
+
} catch {
|
|
15449
|
+
throw new AcpHostError(
|
|
15450
|
+
"executable_missing",
|
|
15451
|
+
`codex-acp package entrypoint is missing beside npm shim: ${shim}`
|
|
15452
|
+
);
|
|
15453
|
+
}
|
|
15454
|
+
}
|
|
15455
|
+
function resolvedCodexCandidate(candidate, platform) {
|
|
15456
|
+
(0, import_node_fs4.accessSync)(candidate, import_node_fs4.constants.X_OK);
|
|
15457
|
+
const real = (0, import_node_fs4.realpathSync)(candidate);
|
|
15458
|
+
return platform === "win32" && (0, import_node_path15.extname)(real).toLowerCase() === ".cmd" ? resolveWindowsNpmShim2(real) : real;
|
|
15459
|
+
}
|
|
15460
|
+
function resolveCodexExecutable(executable = "codex-acp", pathEnv, platform = process.platform) {
|
|
15461
|
+
if ((0, import_node_path15.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
15462
|
+
const abs = (0, import_node_path15.resolve)(executable);
|
|
15463
|
+
const candidates = platform === "win32" && (0, import_node_path15.extname)(abs) === "" ? [`${abs}.cmd`] : [abs];
|
|
15464
|
+
for (const candidate of candidates) {
|
|
15465
|
+
try {
|
|
15466
|
+
return resolvedCodexCandidate(candidate, platform);
|
|
15467
|
+
} catch (error) {
|
|
15468
|
+
if (error instanceof AcpHostError) throw error;
|
|
15469
|
+
}
|
|
15470
|
+
}
|
|
15471
|
+
throw new AcpHostError("executable_missing", `not executable: ${abs}`);
|
|
15472
|
+
}
|
|
15473
|
+
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
15474
|
+
const names = platform === "win32" && (0, import_node_path15.extname)(executable) === "" ? [`${executable}.cmd`] : [executable];
|
|
15475
|
+
for (const dir of pathValue.split(import_node_path15.delimiter)) {
|
|
15476
|
+
if (!dir) continue;
|
|
15477
|
+
for (const name of names) {
|
|
15478
|
+
const candidate = (0, import_node_path15.join)(dir, name);
|
|
15479
|
+
try {
|
|
15480
|
+
return resolvedCodexCandidate(candidate, platform);
|
|
15481
|
+
} catch (error) {
|
|
15482
|
+
if (error instanceof AcpHostError) throw error;
|
|
15483
|
+
}
|
|
15484
|
+
}
|
|
15485
|
+
}
|
|
15486
|
+
throw new AcpHostError(
|
|
15487
|
+
"executable_missing",
|
|
15488
|
+
`codex-acp executable not found on PATH: ${executable}`
|
|
15489
|
+
);
|
|
15490
|
+
}
|
|
15491
|
+
function buildCodexLaunch(executable, args, platform = process.platform) {
|
|
15492
|
+
return platform === "win32" && (0, import_node_path15.extname)(executable).toLowerCase() === ".js" ? { command: process.execPath, args: [executable, ...args] } : { command: executable, args: [...args] };
|
|
15493
|
+
}
|
|
15494
|
+
function parseCodexVersionOutput(stdout) {
|
|
15495
|
+
return parseProviderVersionOutput(stdout, /@agentclientprotocol\/codex-acp\b/i);
|
|
15496
|
+
}
|
|
15497
|
+
async function assertCodexVersionFloor(executable, options) {
|
|
15498
|
+
const minimumVersion = options?.minimumVersion ?? CODEX_ACP_MIN_VERSION;
|
|
15499
|
+
const lastMeasuredVersion = options?.lastMeasuredVersion ?? CODEX_ACP_LAST_MEASURED_VERSION;
|
|
15500
|
+
const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
|
|
15501
|
+
const env = options?.env ?? sanitizeChildEnv(process.env);
|
|
15502
|
+
const launch = buildCodexLaunch(executable, ["--version"], options?.platform);
|
|
15503
|
+
const stdout = await new Promise((resolve4, reject) => {
|
|
15504
|
+
(0, import_node_child_process6.execFile)(
|
|
15505
|
+
launch.command,
|
|
15506
|
+
launch.args,
|
|
15507
|
+
{ timeout: timeoutMs, encoding: "utf8", env },
|
|
15508
|
+
(error, out, stderr) => {
|
|
15509
|
+
if (error) {
|
|
15510
|
+
reject(
|
|
15511
|
+
new AcpVersionError(
|
|
15512
|
+
`failed to run ${executable} --version: ${error.message}${stderr ? ` (${stderr.trim()})` : ""}`
|
|
15513
|
+
)
|
|
15514
|
+
);
|
|
15515
|
+
return;
|
|
15516
|
+
}
|
|
15517
|
+
resolve4(out);
|
|
15518
|
+
}
|
|
15519
|
+
);
|
|
15520
|
+
});
|
|
15521
|
+
const version3 = parseCodexVersionOutput(stdout);
|
|
15522
|
+
if (!version3) {
|
|
15523
|
+
const codexCliVersion = parseProviderVersionOutput(
|
|
15524
|
+
stdout,
|
|
15525
|
+
/\bcodex-cli\b/i,
|
|
15526
|
+
false
|
|
15527
|
+
);
|
|
15528
|
+
if (codexCliVersion) {
|
|
15529
|
+
throw new AcpVersionError(
|
|
15530
|
+
"this is the Codex CLI; --codex-executable takes the codex-acp bridge (npm i -g @agentclientprotocol/codex-acp)",
|
|
15531
|
+
"executable_not_bridge"
|
|
15532
|
+
);
|
|
15533
|
+
}
|
|
15534
|
+
throw new AcpVersionParseError(
|
|
15535
|
+
`could not parse codex-acp version from: ${stdout.trim().slice(0, 200)}`
|
|
15536
|
+
);
|
|
15537
|
+
}
|
|
15538
|
+
assertProviderVersionFloor({
|
|
15539
|
+
provider: "codex-acp",
|
|
15540
|
+
version: version3,
|
|
15541
|
+
minimumVersion,
|
|
15542
|
+
lastMeasuredVersion,
|
|
15543
|
+
...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
|
|
15544
|
+
});
|
|
15545
|
+
return version3;
|
|
15546
|
+
}
|
|
15547
|
+
function buildCodexAcpArgs() {
|
|
15548
|
+
return [];
|
|
15549
|
+
}
|
|
15550
|
+
function buildCodexChildEnv(parent) {
|
|
15551
|
+
return sanitizeChildEnv(parent);
|
|
15552
|
+
}
|
|
15553
|
+
function waitForChildExit2(child, timeoutMs) {
|
|
15554
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
15555
|
+
return Promise.resolve();
|
|
15556
|
+
}
|
|
15557
|
+
return new Promise((resolve4) => {
|
|
15558
|
+
const timer2 = setTimeout(resolve4, timeoutMs);
|
|
15559
|
+
child.once("exit", () => {
|
|
15560
|
+
clearTimeout(timer2);
|
|
15561
|
+
resolve4();
|
|
15562
|
+
});
|
|
15563
|
+
});
|
|
15564
|
+
}
|
|
15565
|
+
async function terminateCodexChild(child) {
|
|
15566
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
15567
|
+
try {
|
|
15568
|
+
child.kill("SIGTERM");
|
|
15569
|
+
} catch {
|
|
15570
|
+
}
|
|
15571
|
+
await waitForChildExit2(child, CHILD_EXIT_WAIT_MS2);
|
|
15572
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
15573
|
+
try {
|
|
15574
|
+
child.kill("SIGKILL");
|
|
15575
|
+
} catch {
|
|
15576
|
+
}
|
|
15577
|
+
await waitForChildExit2(child, CHILD_KILL_WAIT_MS2);
|
|
15578
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
15579
|
+
throw new AcpHostError(
|
|
15580
|
+
"child_exit_timeout",
|
|
15581
|
+
"Codex ACP bridge did not exit after SIGTERM and SIGKILL"
|
|
15582
|
+
);
|
|
15583
|
+
}
|
|
15584
|
+
}
|
|
15585
|
+
async function openCodexAcpSession(options) {
|
|
15586
|
+
const parentEnv = options.env ?? process.env;
|
|
15587
|
+
if (options.signal?.aborted) {
|
|
15588
|
+
throw new AcpHostError(
|
|
15589
|
+
"cancelled",
|
|
15590
|
+
"Codex ACP bridge opening was cancelled"
|
|
15591
|
+
);
|
|
15592
|
+
}
|
|
15593
|
+
const pathEnv = parentEnv.PATH;
|
|
15594
|
+
const executable = resolveCodexExecutable(
|
|
15595
|
+
options.executable ?? "codex-acp",
|
|
15596
|
+
typeof pathEnv === "string" ? pathEnv : void 0
|
|
15597
|
+
);
|
|
15598
|
+
const env = buildCodexChildEnv(parentEnv);
|
|
15599
|
+
let providerVersion;
|
|
15600
|
+
if (!options.skipVersionCheck) {
|
|
15601
|
+
providerVersion = await assertCodexVersionFloor(executable, { env });
|
|
15602
|
+
options.onVersionNotice?.({
|
|
15603
|
+
provider: "codex-acp",
|
|
15604
|
+
runningVersion: providerVersion,
|
|
15605
|
+
lastMeasuredVersion: CODEX_ACP_LAST_MEASURED_VERSION
|
|
15606
|
+
});
|
|
15607
|
+
}
|
|
15608
|
+
if (options.signal?.aborted) {
|
|
15609
|
+
throw new AcpHostError(
|
|
15610
|
+
"cancelled",
|
|
15611
|
+
"Codex ACP bridge opening was cancelled"
|
|
15612
|
+
);
|
|
15613
|
+
}
|
|
15614
|
+
const args = buildCodexAcpArgs();
|
|
15615
|
+
const launch = buildCodexLaunch(executable, args);
|
|
15616
|
+
const child = (0, import_node_child_process6.spawn)(launch.command, launch.args, {
|
|
15617
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
15618
|
+
env,
|
|
15619
|
+
cwd: options.cwd
|
|
15620
|
+
});
|
|
15621
|
+
const spawnError = new Promise((_resolve, reject) => {
|
|
15622
|
+
child.once("error", () => {
|
|
15623
|
+
reject(
|
|
15624
|
+
new AcpHostError(
|
|
15625
|
+
"spawn_failed",
|
|
15626
|
+
"failed to spawn the Codex ACP bridge"
|
|
15627
|
+
)
|
|
15628
|
+
);
|
|
15629
|
+
});
|
|
15630
|
+
});
|
|
15631
|
+
let removeAbortListener = () => void 0;
|
|
15632
|
+
const abortError = new Promise((_resolve, reject) => {
|
|
15633
|
+
const signal = options.signal;
|
|
15634
|
+
if (!signal) return;
|
|
15635
|
+
const onAbort = () => {
|
|
15636
|
+
reject(
|
|
15637
|
+
new AcpHostError(
|
|
15638
|
+
"cancelled",
|
|
15639
|
+
"Codex ACP bridge opening was cancelled"
|
|
15640
|
+
)
|
|
15641
|
+
);
|
|
15642
|
+
};
|
|
15643
|
+
if (signal.aborted) {
|
|
15644
|
+
onAbort();
|
|
15645
|
+
return;
|
|
15646
|
+
}
|
|
15647
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
15648
|
+
removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
15649
|
+
});
|
|
15650
|
+
if (!child.stdin || !child.stdout) {
|
|
15651
|
+
await terminateCodexChild(child);
|
|
15652
|
+
throw new AcpHostError("spawn_failed", "child missing stdio pipes");
|
|
15653
|
+
}
|
|
15654
|
+
const observeStderrTailOnExit = attachStderrTailExitObserver(
|
|
15655
|
+
child,
|
|
15656
|
+
options.onStderrTail
|
|
15657
|
+
);
|
|
15658
|
+
let sessionRef = null;
|
|
15659
|
+
const transport = createBoundTransport({
|
|
15660
|
+
readable: child.stdout,
|
|
15661
|
+
writable: child.stdin,
|
|
15662
|
+
requestTimeoutMs: options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS,
|
|
15663
|
+
readableEndGraceMs: STDERR_READABLE_END_GRACE_MS,
|
|
15664
|
+
getSession: () => sessionRef,
|
|
15665
|
+
onChildExit: observeStderrTailOnExit
|
|
15666
|
+
});
|
|
15667
|
+
try {
|
|
15668
|
+
const session = await Promise.race([
|
|
15669
|
+
AcpHostSession.connect({
|
|
15670
|
+
transport,
|
|
15671
|
+
cwd: options.cwd,
|
|
15672
|
+
requiredModeId: CODEX_PERMISSION_MODE_ID,
|
|
15673
|
+
permissionCallback: options.permissionCallback,
|
|
15674
|
+
events: options.events,
|
|
15675
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
15676
|
+
clientName: options.clientName,
|
|
15677
|
+
clientVersion: options.clientVersion,
|
|
15678
|
+
promptsEnabled: options.promptsEnabled
|
|
15679
|
+
}),
|
|
15680
|
+
spawnError,
|
|
15681
|
+
abortError
|
|
15682
|
+
]);
|
|
15683
|
+
removeAbortListener();
|
|
15684
|
+
sessionRef = session;
|
|
15685
|
+
let closePromise = null;
|
|
15686
|
+
const close = async () => {
|
|
15687
|
+
if (closePromise) return closePromise;
|
|
15688
|
+
closePromise = (async () => {
|
|
15689
|
+
try {
|
|
15690
|
+
await session.close();
|
|
15691
|
+
} finally {
|
|
15692
|
+
await terminateCodexChild(child);
|
|
15693
|
+
}
|
|
15694
|
+
})();
|
|
15695
|
+
return closePromise;
|
|
15696
|
+
};
|
|
15697
|
+
return {
|
|
15698
|
+
session,
|
|
15699
|
+
child,
|
|
15700
|
+
executable,
|
|
15701
|
+
args,
|
|
15702
|
+
env,
|
|
15703
|
+
...providerVersion ? { providerVersion } : {},
|
|
15704
|
+
close
|
|
15705
|
+
};
|
|
15706
|
+
} catch (error) {
|
|
15707
|
+
removeAbortListener();
|
|
15708
|
+
transport.close();
|
|
15709
|
+
await terminateCodexChild(child);
|
|
15710
|
+
throw error;
|
|
15711
|
+
}
|
|
15712
|
+
}
|
|
15713
|
+
var import_node_child_process6, import_node_fs4, import_node_path15, CHILD_EXIT_WAIT_MS2, CHILD_KILL_WAIT_MS2, WINDOWS_NPM_SHIM_MAX_BYTES2, WINDOWS_NPM_ENTRYPOINT2;
|
|
15714
|
+
var init_codex = __esm({
|
|
15715
|
+
"src/host/codex.ts"() {
|
|
15716
|
+
"use strict";
|
|
15717
|
+
init_stderr_tail();
|
|
15718
|
+
import_node_child_process6 = require("node:child_process");
|
|
15719
|
+
import_node_fs4 = require("node:fs");
|
|
15720
|
+
import_node_path15 = require("node:path");
|
|
15721
|
+
init_bounds();
|
|
15722
|
+
init_env();
|
|
15723
|
+
init_session();
|
|
15724
|
+
init_types();
|
|
15725
|
+
init_version();
|
|
15726
|
+
CHILD_EXIT_WAIT_MS2 = 3e3;
|
|
15727
|
+
CHILD_KILL_WAIT_MS2 = 1e3;
|
|
15728
|
+
WINDOWS_NPM_SHIM_MAX_BYTES2 = 64 * 1024;
|
|
15729
|
+
WINDOWS_NPM_ENTRYPOINT2 = [
|
|
15730
|
+
"node_modules",
|
|
15731
|
+
"@agentclientprotocol",
|
|
15732
|
+
"codex-acp",
|
|
15733
|
+
"dist",
|
|
15734
|
+
"index.js"
|
|
15735
|
+
];
|
|
15736
|
+
}
|
|
15737
|
+
});
|
|
15738
|
+
|
|
15739
|
+
// src/host/opencode.ts
|
|
15740
|
+
var opencode_exports = {};
|
|
15741
|
+
__export(opencode_exports, {
|
|
15742
|
+
OPENCODE_FORCED_PERMISSION_TOOLS: () => OPENCODE_FORCED_PERMISSION_TOOLS,
|
|
15743
|
+
OPENCODE_HOME_OWNER_FILE: () => OPENCODE_HOME_OWNER_FILE,
|
|
15744
|
+
OPENCODE_HOME_PREFIX: () => OPENCODE_HOME_PREFIX,
|
|
15745
|
+
OPENCODE_LAST_MEASURED_VERSION: () => OPENCODE_LAST_MEASURED_VERSION,
|
|
15746
|
+
OPENCODE_MIN_VERSION: () => OPENCODE_MIN_VERSION,
|
|
15747
|
+
assertForcedAskPermissionMap: () => assertForcedAskPermissionMap,
|
|
15748
|
+
assertOpenCodeEffectiveConfig: () => assertOpenCodeEffectiveConfig,
|
|
15749
|
+
assertOpenCodeVersionFloor: () => assertOpenCodeVersionFloor,
|
|
15750
|
+
buildOpenCodeAcpArgs: () => buildOpenCodeAcpArgs,
|
|
15751
|
+
buildOpenCodeChildEnv: () => buildOpenCodeChildEnv,
|
|
15752
|
+
buildOpenCodeForcedPermissionConfig: () => buildOpenCodeForcedPermissionConfig,
|
|
15753
|
+
buildOpenCodeHomeOwner: () => buildOpenCodeHomeOwner,
|
|
15754
|
+
buildOpenCodeSafeConfigJson: () => buildOpenCodeSafeConfigJson,
|
|
15755
|
+
isProcessAlive: () => isProcessAlive,
|
|
15756
|
+
openOpenCodeAcpSession: () => openOpenCodeAcpSession,
|
|
15757
|
+
parseOpenCodeVersionOutput: () => parseOpenCodeVersionOutput,
|
|
15758
|
+
prepareOpenCodeIsolatedHome: () => prepareOpenCodeIsolatedHome,
|
|
15759
|
+
readOpenCodeHomeOwner: () => readOpenCodeHomeOwner,
|
|
15760
|
+
readValidatedOpenCodeAuth: () => readValidatedOpenCodeAuth,
|
|
15761
|
+
releaseOpenCodeHome: () => releaseOpenCodeHome,
|
|
15762
|
+
resolveOpenCodeAuthSourcePath: () => resolveOpenCodeAuthSourcePath,
|
|
15763
|
+
resolveOpenCodeExecutable: () => resolveOpenCodeExecutable,
|
|
15764
|
+
sweepStaleOpenCodeHomes: () => sweepStaleOpenCodeHomes,
|
|
15765
|
+
terminateOpenCodeChild: () => terminateOpenCodeChild,
|
|
15766
|
+
writeOpenCodeHomeOwner: () => writeOpenCodeHomeOwner
|
|
15767
|
+
});
|
|
15768
|
+
function isProcessAlive(pid) {
|
|
15769
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
15770
|
+
try {
|
|
15771
|
+
process.kill(pid, 0);
|
|
15772
|
+
return true;
|
|
15773
|
+
} catch {
|
|
15774
|
+
return false;
|
|
15775
|
+
}
|
|
15776
|
+
}
|
|
15777
|
+
function resolveOpenCodeExecutable(executable = "opencode", pathEnv) {
|
|
15778
|
+
if ((0, import_node_path16.isAbsolute)(executable) || executable.includes("/")) {
|
|
15779
|
+
const abs = (0, import_node_path16.resolve)(executable);
|
|
15780
|
+
try {
|
|
15781
|
+
(0, import_node_fs5.accessSync)(abs, import_node_fs5.constants.X_OK);
|
|
15782
|
+
} catch {
|
|
15783
|
+
throw new AcpHostError("executable_missing", `not executable: ${abs}`);
|
|
15784
|
+
}
|
|
15785
|
+
try {
|
|
15786
|
+
return (0, import_node_fs5.realpathSync)(abs);
|
|
15787
|
+
} catch {
|
|
15788
|
+
throw new AcpHostError(
|
|
15789
|
+
"executable_missing",
|
|
15790
|
+
`could not realpath opencode executable: ${abs}`
|
|
15791
|
+
);
|
|
15792
|
+
}
|
|
15793
|
+
}
|
|
15794
|
+
const pathValue = pathEnv ?? process.env.PATH ?? "";
|
|
15795
|
+
for (const dir of pathValue.split(":")) {
|
|
15796
|
+
if (!dir) continue;
|
|
15797
|
+
const candidate = (0, import_node_path16.join)(dir, executable);
|
|
15798
|
+
try {
|
|
15799
|
+
(0, import_node_fs5.accessSync)(candidate, import_node_fs5.constants.X_OK);
|
|
15800
|
+
try {
|
|
15801
|
+
return (0, import_node_fs5.realpathSync)(candidate);
|
|
15802
|
+
} catch {
|
|
15803
|
+
throw new AcpHostError(
|
|
15804
|
+
"executable_missing",
|
|
15805
|
+
`could not realpath opencode executable: ${candidate}`
|
|
15806
|
+
);
|
|
15807
|
+
}
|
|
15808
|
+
} catch (err) {
|
|
15809
|
+
if (err instanceof AcpHostError) throw err;
|
|
15810
|
+
}
|
|
15811
|
+
}
|
|
15812
|
+
throw new AcpHostError(
|
|
15813
|
+
"executable_missing",
|
|
15814
|
+
`opencode executable not found on PATH: ${executable}`
|
|
15815
|
+
);
|
|
15816
|
+
}
|
|
15817
|
+
function buildOpenCodeHomeOwner(options) {
|
|
15818
|
+
const uid2 = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
15819
|
+
return {
|
|
15820
|
+
version: 1,
|
|
15821
|
+
pid: options.pid ?? process.pid,
|
|
15822
|
+
uid: uid2,
|
|
15823
|
+
instanceId: options.instanceId ?? (0, import_node_crypto19.randomUUID)(),
|
|
15824
|
+
role: options.role,
|
|
15825
|
+
createdAt: new Date((options.now ?? Date.now)()).toISOString()
|
|
15826
|
+
};
|
|
15827
|
+
}
|
|
15828
|
+
async function writeOpenCodeHomeOwner(home, owner) {
|
|
15829
|
+
const path = (0, import_node_path16.join)(home, OPENCODE_HOME_OWNER_FILE);
|
|
15830
|
+
await (0, import_promises9.writeFile)(path, `${JSON.stringify(owner)}
|
|
15831
|
+
`, {
|
|
15832
|
+
flag: "wx",
|
|
15833
|
+
mode: 384
|
|
15834
|
+
});
|
|
15835
|
+
await (0, import_promises9.chmod)(path, 384);
|
|
15836
|
+
}
|
|
15837
|
+
async function readOpenCodeHomeOwner(home) {
|
|
15838
|
+
const path = (0, import_node_path16.join)(home, OPENCODE_HOME_OWNER_FILE);
|
|
15839
|
+
let raw;
|
|
15840
|
+
try {
|
|
15841
|
+
raw = await (0, import_promises9.readFile)(path, "utf8");
|
|
15842
|
+
} catch {
|
|
15843
|
+
return null;
|
|
15844
|
+
}
|
|
15845
|
+
try {
|
|
15846
|
+
const value = JSON.parse(raw);
|
|
15847
|
+
if (value.version !== 1 || !Number.isSafeInteger(value.pid) || !Number.isSafeInteger(value.uid) || typeof value.instanceId !== "string" || !value.instanceId || value.role !== "worker" && value.role !== "isolated" && value.role !== "ephemeral" || typeof value.createdAt !== "string") {
|
|
15848
|
+
return null;
|
|
15849
|
+
}
|
|
15850
|
+
return value;
|
|
15851
|
+
} catch {
|
|
15852
|
+
return null;
|
|
15853
|
+
}
|
|
15854
|
+
}
|
|
15855
|
+
async function releaseOpenCodeHome(home, instanceId) {
|
|
15856
|
+
if (!(0, import_node_path16.isAbsolute)(home)) return;
|
|
15857
|
+
const owner = await readOpenCodeHomeOwner(home);
|
|
15858
|
+
if (owner && owner.instanceId !== instanceId) {
|
|
15859
|
+
return;
|
|
15860
|
+
}
|
|
15861
|
+
try {
|
|
15862
|
+
await (0, import_promises9.rm)(home, { recursive: true, force: true });
|
|
15863
|
+
} catch {
|
|
15864
|
+
await (0, import_promises9.chmod)(home, 448);
|
|
15865
|
+
await (0, import_promises9.rm)(home, { recursive: true, force: true });
|
|
15866
|
+
}
|
|
15867
|
+
}
|
|
15868
|
+
function parseOpenCodeVersionOutput(stdout) {
|
|
15869
|
+
return parseProviderVersionOutput(stdout, /\bopencode\b/i);
|
|
15870
|
+
}
|
|
15871
|
+
async function assertOpenCodeVersionFloor(executable, options) {
|
|
15872
|
+
const minimumVersion = options?.minimumVersion ?? OPENCODE_MIN_VERSION;
|
|
15873
|
+
const lastMeasuredVersion = options?.lastMeasuredVersion ?? OPENCODE_LAST_MEASURED_VERSION;
|
|
15874
|
+
const timeoutMs = options?.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS;
|
|
15875
|
+
const env = sanitizeChildEnv(options?.env ?? process.env);
|
|
15876
|
+
const stdout = await new Promise((resolve4, reject) => {
|
|
15877
|
+
(0, import_node_child_process7.execFile)(
|
|
15878
|
+
executable,
|
|
15879
|
+
["--version"],
|
|
15880
|
+
{ timeout: timeoutMs, encoding: "utf8", env },
|
|
15881
|
+
(err, out, stderr) => {
|
|
15882
|
+
if (err) {
|
|
15883
|
+
reject(
|
|
15884
|
+
new AcpVersionError(
|
|
15885
|
+
`failed to run ${executable} --version: ${err.message}${stderr ? ` (${stderr.trim()})` : ""}`
|
|
15886
|
+
)
|
|
15887
|
+
);
|
|
15888
|
+
return;
|
|
15889
|
+
}
|
|
15890
|
+
resolve4(out);
|
|
15891
|
+
}
|
|
15892
|
+
);
|
|
15893
|
+
});
|
|
15894
|
+
const version3 = parseOpenCodeVersionOutput(stdout);
|
|
15895
|
+
if (!version3) {
|
|
15896
|
+
throw new AcpVersionParseError(
|
|
15897
|
+
`could not parse opencode version from: ${stdout.trim().slice(0, 200)}`
|
|
15898
|
+
);
|
|
15899
|
+
}
|
|
15900
|
+
assertProviderVersionFloor({
|
|
15901
|
+
provider: "opencode",
|
|
15902
|
+
version: version3,
|
|
15903
|
+
minimumVersion,
|
|
15904
|
+
lastMeasuredVersion,
|
|
15905
|
+
...options?.onNewerVersion ? { onNewerVersion: options.onNewerVersion } : {}
|
|
15906
|
+
});
|
|
15907
|
+
return version3;
|
|
15908
|
+
}
|
|
15909
|
+
function buildOpenCodeAcpArgs() {
|
|
15910
|
+
return ["acp", "--pure"];
|
|
15911
|
+
}
|
|
15912
|
+
function buildOpenCodeForcedPermissionConfig() {
|
|
15913
|
+
const permission = {};
|
|
15914
|
+
for (const tool of OPENCODE_FORCED_PERMISSION_TOOLS) {
|
|
15915
|
+
permission[tool] = "ask";
|
|
15916
|
+
}
|
|
15917
|
+
return permission;
|
|
15918
|
+
}
|
|
15919
|
+
function buildOpenCodeSafeConfigJson(options) {
|
|
15920
|
+
const body = {
|
|
15921
|
+
$schema: "https://opencode.ai/config.json",
|
|
15922
|
+
permission: buildOpenCodeForcedPermissionConfig()
|
|
15923
|
+
};
|
|
15924
|
+
if (options?.model) {
|
|
15925
|
+
body.model = options.model;
|
|
15926
|
+
}
|
|
15927
|
+
return `${JSON.stringify(body, null, 2)}
|
|
15928
|
+
`;
|
|
15929
|
+
}
|
|
15930
|
+
async function readValidatedOpenCodeAuth(sourceAuthPath, options) {
|
|
15931
|
+
let info;
|
|
15932
|
+
try {
|
|
15933
|
+
info = await (0, import_promises9.lstat)(sourceAuthPath);
|
|
15934
|
+
} catch (error) {
|
|
15935
|
+
if (error.code === "ENOENT") {
|
|
15936
|
+
if (options?.allowMissing) return null;
|
|
15937
|
+
throw new AcpHostError(
|
|
15938
|
+
"opencode_auth_missing",
|
|
15939
|
+
"OpenCode is not signed in; run opencode auth before starting the listener"
|
|
15940
|
+
);
|
|
15941
|
+
}
|
|
15942
|
+
throw error;
|
|
15943
|
+
}
|
|
15944
|
+
if (!info.isFile() || info.isSymbolicLink()) {
|
|
15945
|
+
throw new AcpHostError(
|
|
15946
|
+
"opencode_auth_insecure",
|
|
15947
|
+
"OpenCode auth must be a secure regular file"
|
|
15948
|
+
);
|
|
15949
|
+
}
|
|
15950
|
+
if (typeof process.getuid === "function" && (Number(info.uid) !== process.getuid() || (Number(info.mode) & 511) !== 384)) {
|
|
15951
|
+
throw new AcpHostError(
|
|
15952
|
+
"opencode_auth_insecure",
|
|
15953
|
+
"OpenCode auth must be owned by this user with mode 0600"
|
|
15954
|
+
);
|
|
15955
|
+
}
|
|
15956
|
+
if (Number(info.size) > MAX_OPENCODE_AUTH_BYTES) {
|
|
15957
|
+
throw new AcpHostError(
|
|
15958
|
+
"opencode_auth_too_large",
|
|
15959
|
+
"OpenCode auth file exceeds the listener safety bound"
|
|
15960
|
+
);
|
|
15961
|
+
}
|
|
15962
|
+
const raw = await (0, import_promises9.readFile)(sourceAuthPath);
|
|
15963
|
+
if (raw.byteLength > MAX_OPENCODE_AUTH_BYTES) {
|
|
15964
|
+
throw new AcpHostError(
|
|
15965
|
+
"opencode_auth_too_large",
|
|
15966
|
+
"OpenCode auth file exceeds the listener safety bound"
|
|
15967
|
+
);
|
|
15968
|
+
}
|
|
15969
|
+
try {
|
|
15970
|
+
JSON.parse(raw.toString("utf8"));
|
|
15971
|
+
} catch {
|
|
15972
|
+
throw new AcpHostError(
|
|
15973
|
+
"opencode_auth_malformed",
|
|
15974
|
+
"OpenCode auth file is malformed; run opencode auth again"
|
|
15975
|
+
);
|
|
15976
|
+
}
|
|
15977
|
+
return raw;
|
|
15978
|
+
}
|
|
15979
|
+
function resolveOpenCodeAuthSourcePath(parent = process.env) {
|
|
15980
|
+
const xdgData = parent.XDG_DATA_HOME;
|
|
15981
|
+
if (typeof xdgData === "string" && (0, import_node_path16.isAbsolute)(xdgData)) {
|
|
15982
|
+
return (0, import_node_path16.join)(xdgData, "opencode", "auth.json");
|
|
15983
|
+
}
|
|
15984
|
+
const home = parent.HOME ?? (0, import_node_os7.homedir)();
|
|
15985
|
+
return (0, import_node_path16.join)(home, ".local", "share", "opencode", "auth.json");
|
|
15986
|
+
}
|
|
15987
|
+
async function prepareOpenCodeIsolatedHome(options) {
|
|
15988
|
+
const home = options.home ?? await (0, import_promises9.mkdtemp)((0, import_node_path16.join)((0, import_node_os7.tmpdir)(), OPENCODE_HOME_PREFIX));
|
|
15989
|
+
if (!(0, import_node_path16.isAbsolute)(home)) {
|
|
15990
|
+
throw new AcpHostError(
|
|
15991
|
+
"isolated_home_invalid",
|
|
15992
|
+
"isolated OpenCode home must be absolute"
|
|
15993
|
+
);
|
|
15994
|
+
}
|
|
15995
|
+
await (0, import_promises9.chmod)(home, 448);
|
|
15996
|
+
try {
|
|
15997
|
+
const xdgConfig = (0, import_node_path16.join)(home, "xdg-config");
|
|
15998
|
+
const xdgData = (0, import_node_path16.join)(home, "xdg-data");
|
|
15999
|
+
const xdgCache = (0, import_node_path16.join)(home, "xdg-cache");
|
|
16000
|
+
const xdgState = (0, import_node_path16.join)(home, "xdg-state");
|
|
16001
|
+
for (const dir of [xdgConfig, xdgData, xdgCache, xdgState]) {
|
|
16002
|
+
await (0, import_promises9.mkdir)(dir, { recursive: true, mode: 448 });
|
|
16003
|
+
await (0, import_promises9.chmod)(dir, 448);
|
|
16004
|
+
}
|
|
16005
|
+
const configDir = (0, import_node_path16.join)(xdgConfig, "opencode");
|
|
16006
|
+
const dataDir = (0, import_node_path16.join)(xdgData, "opencode");
|
|
16007
|
+
await (0, import_promises9.mkdir)(configDir, { recursive: true, mode: 448 });
|
|
16008
|
+
await (0, import_promises9.mkdir)(dataDir, { recursive: true, mode: 448 });
|
|
16009
|
+
await (0, import_promises9.chmod)(configDir, 448);
|
|
16010
|
+
await (0, import_promises9.chmod)(dataDir, 448);
|
|
16011
|
+
const configPath = (0, import_node_path16.join)(configDir, "opencode.json");
|
|
16012
|
+
await (0, import_promises9.writeFile)(
|
|
16013
|
+
configPath,
|
|
16014
|
+
buildOpenCodeSafeConfigJson(
|
|
16015
|
+
options.model ? { model: options.model } : void 0
|
|
16016
|
+
),
|
|
16017
|
+
{ flag: "wx", mode: 384 }
|
|
16018
|
+
);
|
|
16019
|
+
await (0, import_promises9.chmod)(configPath, 384);
|
|
16020
|
+
const sourceAuth = resolveOpenCodeAuthSourcePath(options.env ?? process.env);
|
|
16021
|
+
const authBytes = await readValidatedOpenCodeAuth(sourceAuth, {
|
|
16022
|
+
allowMissing: options.allowMissingAuth === true
|
|
16023
|
+
});
|
|
16024
|
+
if (authBytes) {
|
|
16025
|
+
const destAuth = (0, import_node_path16.join)(dataDir, "auth.json");
|
|
16026
|
+
await (0, import_promises9.writeFile)(destAuth, authBytes, { flag: "wx", mode: 384 });
|
|
16027
|
+
await (0, import_promises9.chmod)(destAuth, 384);
|
|
16028
|
+
}
|
|
16029
|
+
const owner = options.owner ?? buildOpenCodeHomeOwner({ role: "ephemeral" });
|
|
16030
|
+
await writeOpenCodeHomeOwner(home, owner);
|
|
16031
|
+
return home;
|
|
16032
|
+
} catch (error) {
|
|
16033
|
+
if (!options.home) {
|
|
16034
|
+
await (0, import_promises9.rm)(home, { recursive: true, force: true }).catch(() => void 0);
|
|
16035
|
+
}
|
|
16036
|
+
throw error;
|
|
16037
|
+
}
|
|
16038
|
+
}
|
|
16039
|
+
function buildOpenCodeChildEnv(parent, home) {
|
|
16040
|
+
if (!(0, import_node_path16.isAbsolute)(home)) {
|
|
16041
|
+
throw new AcpHostError(
|
|
16042
|
+
"isolated_home_invalid",
|
|
16043
|
+
"isolated OpenCode home must be absolute"
|
|
16044
|
+
);
|
|
16045
|
+
}
|
|
16046
|
+
const base = sanitizeChildEnv(parent);
|
|
16047
|
+
return {
|
|
16048
|
+
...base,
|
|
16049
|
+
HOME: home,
|
|
16050
|
+
XDG_CONFIG_HOME: (0, import_node_path16.join)(home, "xdg-config"),
|
|
16051
|
+
XDG_DATA_HOME: (0, import_node_path16.join)(home, "xdg-data"),
|
|
16052
|
+
XDG_CACHE_HOME: (0, import_node_path16.join)(home, "xdg-cache"),
|
|
16053
|
+
XDG_STATE_HOME: (0, import_node_path16.join)(home, "xdg-state"),
|
|
16054
|
+
// Measured 1.18.10: private home alone still merges project opencode.json.
|
|
16055
|
+
OPENCODE_DISABLE_PROJECT_CONFIG: "1"
|
|
16056
|
+
};
|
|
16057
|
+
}
|
|
16058
|
+
async function assertOpenCodeEffectiveConfig(options) {
|
|
16059
|
+
const hostile = await (0, import_promises9.mkdtemp)((0, import_node_path16.join)((0, import_node_os7.tmpdir)(), "cswarm-opencode-hostile-"));
|
|
16060
|
+
try {
|
|
16061
|
+
await (0, import_promises9.chmod)(hostile, 448);
|
|
16062
|
+
await (0, import_promises9.writeFile)(
|
|
16063
|
+
(0, import_node_path16.join)(hostile, "opencode.json"),
|
|
16064
|
+
`${JSON.stringify({
|
|
16065
|
+
permission: {
|
|
16066
|
+
bash: "allow",
|
|
16067
|
+
edit: "allow",
|
|
16068
|
+
write: "allow",
|
|
16069
|
+
"*": "allow"
|
|
16070
|
+
}
|
|
16071
|
+
}, null, 2)}
|
|
16072
|
+
`,
|
|
16073
|
+
{ mode: 384 }
|
|
16074
|
+
);
|
|
16075
|
+
const stdout = await new Promise((resolve4, reject) => {
|
|
16076
|
+
(0, import_node_child_process7.execFile)(
|
|
16077
|
+
options.executable,
|
|
16078
|
+
["debug", "config", "--pure"],
|
|
16079
|
+
{
|
|
16080
|
+
timeout: options.timeoutMs ?? ACP_VERSION_CHECK_TIMEOUT_MS,
|
|
16081
|
+
encoding: "utf8",
|
|
16082
|
+
env: options.env,
|
|
16083
|
+
cwd: hostile
|
|
16084
|
+
},
|
|
16085
|
+
(err, out, stderr) => {
|
|
16086
|
+
if (err) {
|
|
16087
|
+
reject(
|
|
16088
|
+
new AcpHostError(
|
|
16089
|
+
"opencode_config_probe_failed",
|
|
16090
|
+
`debug config --pure failed: ${err.message}${stderr ? ` (${stderr.trim().slice(0, 200)})` : ""}`
|
|
16091
|
+
)
|
|
16092
|
+
);
|
|
16093
|
+
return;
|
|
16094
|
+
}
|
|
16095
|
+
resolve4(out);
|
|
16096
|
+
}
|
|
16097
|
+
);
|
|
16098
|
+
});
|
|
16099
|
+
let parsed;
|
|
16100
|
+
try {
|
|
16101
|
+
parsed = JSON.parse(stdout);
|
|
16102
|
+
} catch {
|
|
16103
|
+
throw new AcpHostError(
|
|
16104
|
+
"opencode_config_probe_failed",
|
|
16105
|
+
"debug config --pure returned non-JSON"
|
|
16106
|
+
);
|
|
16107
|
+
}
|
|
16108
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16109
|
+
throw new AcpHostError(
|
|
16110
|
+
"opencode_config_probe_failed",
|
|
16111
|
+
"debug config --pure returned a non-object"
|
|
16112
|
+
);
|
|
16113
|
+
}
|
|
16114
|
+
const permission = parsed.permission;
|
|
16115
|
+
if (!permission || typeof permission !== "object" || Array.isArray(permission)) {
|
|
16116
|
+
throw new AcpHostError(
|
|
16117
|
+
"opencode_config_probe_failed",
|
|
16118
|
+
"debug config --pure missing permission map"
|
|
16119
|
+
);
|
|
16120
|
+
}
|
|
16121
|
+
const map = permission;
|
|
16122
|
+
assertForcedAskPermissionMap(map);
|
|
16123
|
+
return { permission: map };
|
|
16124
|
+
} finally {
|
|
16125
|
+
await (0, import_promises9.rm)(hostile, { recursive: true, force: true }).catch(() => void 0);
|
|
16126
|
+
}
|
|
16127
|
+
}
|
|
16128
|
+
function assertForcedAskPermissionMap(map) {
|
|
16129
|
+
for (const tool of OPENCODE_FORCED_PERMISSION_TOOLS) {
|
|
16130
|
+
const value = map[tool];
|
|
16131
|
+
if (value === "allow") {
|
|
16132
|
+
throw new AcpHostError(
|
|
16133
|
+
"opencode_project_config_active",
|
|
16134
|
+
`effective OpenCode config still allows tool ${tool}; OPENCODE_DISABLE_PROJECT_CONFIG failed`
|
|
16135
|
+
);
|
|
16136
|
+
}
|
|
16137
|
+
if (tool === "*") {
|
|
16138
|
+
if (value !== "ask") {
|
|
16139
|
+
throw new AcpHostError(
|
|
16140
|
+
"opencode_config_probe_failed",
|
|
16141
|
+
"effective OpenCode config missing forced-ask wildcard"
|
|
16142
|
+
);
|
|
16143
|
+
}
|
|
16144
|
+
continue;
|
|
16145
|
+
}
|
|
16146
|
+
const star = map["*"];
|
|
16147
|
+
const effective = value === void 0 || value === null ? star : value;
|
|
16148
|
+
if (effective !== "ask") {
|
|
16149
|
+
throw new AcpHostError(
|
|
16150
|
+
"opencode_config_probe_failed",
|
|
16151
|
+
`effective OpenCode config lacks forced-ask for tool ${tool}`
|
|
16152
|
+
);
|
|
16153
|
+
}
|
|
16154
|
+
}
|
|
16155
|
+
for (const critical of ["bash", "write", "edit", "execute", "*"]) {
|
|
16156
|
+
const value = map[critical];
|
|
16157
|
+
const star = map["*"];
|
|
16158
|
+
const effective = critical === "*" ? value : value === void 0 || value === null ? star : value;
|
|
16159
|
+
if (effective !== "ask") {
|
|
16160
|
+
throw new AcpHostError(
|
|
16161
|
+
"opencode_config_probe_failed",
|
|
16162
|
+
`critical tool ${critical} is not forced-ask`
|
|
16163
|
+
);
|
|
16164
|
+
}
|
|
16165
|
+
}
|
|
16166
|
+
}
|
|
16167
|
+
async function sweepStaleOpenCodeHomes(options) {
|
|
16168
|
+
const maxAgeMs = options?.maxAgeMs ?? STALE_HOME_MAX_AGE_MS;
|
|
16169
|
+
const now = options?.now ?? Date.now();
|
|
16170
|
+
const alive = options?.isAlive ?? isProcessAlive;
|
|
16171
|
+
const root = options?.root ?? (0, import_node_os7.tmpdir)();
|
|
16172
|
+
const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
16173
|
+
let removed = 0;
|
|
16174
|
+
let entries;
|
|
16175
|
+
try {
|
|
16176
|
+
entries = await (0, import_promises9.readdir)(root);
|
|
16177
|
+
} catch {
|
|
16178
|
+
return 0;
|
|
16179
|
+
}
|
|
16180
|
+
for (const name of entries) {
|
|
16181
|
+
if (!name.startsWith(OPENCODE_HOME_PREFIX)) continue;
|
|
16182
|
+
const full = (0, import_node_path16.join)(root, name);
|
|
16183
|
+
try {
|
|
16184
|
+
const st = await (0, import_promises9.lstat)(full);
|
|
16185
|
+
if (!st.isDirectory() || st.isSymbolicLink()) continue;
|
|
16186
|
+
if (selfUid !== null && typeof st.uid === "number" && st.uid !== selfUid) {
|
|
16187
|
+
continue;
|
|
16188
|
+
}
|
|
16189
|
+
if ((Number(st.mode) & 511) !== 448) {
|
|
16190
|
+
continue;
|
|
16191
|
+
}
|
|
16192
|
+
const owner = await readOpenCodeHomeOwner(full);
|
|
16193
|
+
if (owner) {
|
|
16194
|
+
if (selfUid !== null && owner.uid !== selfUid) continue;
|
|
16195
|
+
if (alive(owner.pid)) {
|
|
16196
|
+
continue;
|
|
16197
|
+
}
|
|
16198
|
+
await (0, import_promises9.rm)(full, { recursive: true, force: true });
|
|
16199
|
+
removed += 1;
|
|
16200
|
+
continue;
|
|
16201
|
+
}
|
|
16202
|
+
if (now - st.mtimeMs < maxAgeMs) continue;
|
|
16203
|
+
await (0, import_promises9.rm)(full, { recursive: true, force: true });
|
|
16204
|
+
removed += 1;
|
|
16205
|
+
} catch {
|
|
16206
|
+
}
|
|
16207
|
+
}
|
|
16208
|
+
return removed;
|
|
16209
|
+
}
|
|
16210
|
+
function waitForChildExit3(child, timeoutMs) {
|
|
16211
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
16212
|
+
return Promise.resolve();
|
|
16213
|
+
}
|
|
16214
|
+
return new Promise((resolve4) => {
|
|
16215
|
+
const timer2 = setTimeout(() => resolve4(), timeoutMs);
|
|
16216
|
+
child.once("exit", () => {
|
|
16217
|
+
clearTimeout(timer2);
|
|
16218
|
+
resolve4();
|
|
16219
|
+
});
|
|
16220
|
+
});
|
|
16221
|
+
}
|
|
16222
|
+
async function terminateOpenCodeChild(child) {
|
|
16223
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
16224
|
+
try {
|
|
16225
|
+
child.kill("SIGTERM");
|
|
16226
|
+
} catch {
|
|
16227
|
+
}
|
|
16228
|
+
await waitForChildExit3(child, CHILD_EXIT_WAIT_MS3);
|
|
16229
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
16230
|
+
try {
|
|
16231
|
+
child.kill("SIGKILL");
|
|
16232
|
+
} catch {
|
|
16233
|
+
}
|
|
16234
|
+
await waitForChildExit3(child, CHILD_KILL_WAIT_MS3);
|
|
16235
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
16236
|
+
throw new AcpHostError(
|
|
16237
|
+
"child_exit_timeout",
|
|
16238
|
+
"OpenCode child did not exit after SIGTERM and SIGKILL"
|
|
16239
|
+
);
|
|
16240
|
+
}
|
|
16241
|
+
}
|
|
16242
|
+
async function openOpenCodeAcpSession(options) {
|
|
16243
|
+
await sweepStaleOpenCodeHomes().catch(() => 0);
|
|
16244
|
+
const pathEnv = (options.env ?? process.env).PATH;
|
|
16245
|
+
const executable = resolveOpenCodeExecutable(
|
|
16246
|
+
options.executable ?? "opencode",
|
|
16247
|
+
typeof pathEnv === "string" ? pathEnv : void 0
|
|
16248
|
+
);
|
|
16249
|
+
let home = options.isolatedHome;
|
|
16250
|
+
let createdHome = false;
|
|
16251
|
+
if (!home) {
|
|
16252
|
+
home = await prepareOpenCodeIsolatedHome({
|
|
16253
|
+
env: options.env ?? process.env,
|
|
16254
|
+
...options.model ? { model: options.model } : {},
|
|
16255
|
+
...options.allowMissingAuth === true ? { allowMissingAuth: true } : {}
|
|
16256
|
+
});
|
|
16257
|
+
createdHome = true;
|
|
16258
|
+
}
|
|
16259
|
+
const env = buildOpenCodeChildEnv(options.env ?? process.env, home);
|
|
16260
|
+
let childStarted = false;
|
|
16261
|
+
const disposeHome = async () => {
|
|
16262
|
+
if (createdHome) {
|
|
16263
|
+
try {
|
|
16264
|
+
await (0, import_promises9.rm)(home, { recursive: true, force: true });
|
|
16265
|
+
} catch {
|
|
16266
|
+
await (0, import_promises9.chmod)(home, 448);
|
|
16267
|
+
await (0, import_promises9.rm)(home, { recursive: true, force: true });
|
|
16268
|
+
}
|
|
16269
|
+
}
|
|
16270
|
+
};
|
|
16271
|
+
try {
|
|
16272
|
+
if (!options.skipVersionCheck) {
|
|
16273
|
+
await assertOpenCodeVersionFloor(executable, {
|
|
16274
|
+
env,
|
|
16275
|
+
...options.onVersionNotice ? { onNewerVersion: options.onVersionNotice } : {}
|
|
16276
|
+
});
|
|
16277
|
+
}
|
|
16278
|
+
if (!options.skipConfigProbe) {
|
|
16279
|
+
await assertOpenCodeEffectiveConfig({
|
|
16280
|
+
executable,
|
|
16281
|
+
env
|
|
16282
|
+
});
|
|
16283
|
+
}
|
|
16284
|
+
const args = buildOpenCodeAcpArgs();
|
|
16285
|
+
const child = (0, import_node_child_process7.spawn)(executable, args, {
|
|
16286
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
16287
|
+
env,
|
|
16288
|
+
cwd: options.cwd
|
|
16289
|
+
});
|
|
16290
|
+
childStarted = true;
|
|
16291
|
+
if (!child.stdin || !child.stdout) {
|
|
16292
|
+
await terminateOpenCodeChild(child);
|
|
16293
|
+
await disposeHome();
|
|
16294
|
+
throw new AcpHostError("spawn_failed", "child missing stdio pipes");
|
|
16295
|
+
}
|
|
16296
|
+
const observeStderrTailOnExit = attachStderrTailExitObserver(
|
|
16297
|
+
child,
|
|
16298
|
+
options.onStderrTail
|
|
16299
|
+
);
|
|
16300
|
+
let sessionRef = null;
|
|
16301
|
+
const transport = createBoundTransport({
|
|
16302
|
+
readable: child.stdout,
|
|
16303
|
+
writable: child.stdin,
|
|
16304
|
+
requestTimeoutMs: options.requestTimeoutMs ?? ACP_DEFAULT_REQUEST_TIMEOUT_MS,
|
|
16305
|
+
readableEndGraceMs: STDERR_READABLE_END_GRACE_MS,
|
|
16306
|
+
getSession: () => sessionRef,
|
|
16307
|
+
onChildExit: observeStderrTailOnExit
|
|
16308
|
+
});
|
|
16309
|
+
try {
|
|
16310
|
+
const session = await AcpHostSession.connect({
|
|
16311
|
+
transport,
|
|
16312
|
+
cwd: options.cwd,
|
|
16313
|
+
permissionCallback: options.permissionCallback,
|
|
16314
|
+
events: options.events,
|
|
16315
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
16316
|
+
clientName: options.clientName,
|
|
16317
|
+
clientVersion: options.clientVersion,
|
|
16318
|
+
promptsEnabled: options.promptsEnabled
|
|
16319
|
+
});
|
|
16320
|
+
sessionRef = session;
|
|
16321
|
+
let closePromise = null;
|
|
16322
|
+
const close = async () => {
|
|
16323
|
+
if (closePromise) return closePromise;
|
|
16324
|
+
closePromise = (async () => {
|
|
16325
|
+
try {
|
|
16326
|
+
await session.close();
|
|
16327
|
+
} finally {
|
|
16328
|
+
await terminateOpenCodeChild(child);
|
|
16329
|
+
await disposeHome();
|
|
16330
|
+
}
|
|
16331
|
+
})();
|
|
16332
|
+
return closePromise;
|
|
16333
|
+
};
|
|
16334
|
+
return { session, child, executable, args, env, home, close };
|
|
16335
|
+
} catch (err) {
|
|
16336
|
+
transport.close();
|
|
16337
|
+
let termErr = null;
|
|
16338
|
+
try {
|
|
16339
|
+
await terminateOpenCodeChild(child);
|
|
16340
|
+
} catch (e) {
|
|
16341
|
+
termErr = e;
|
|
16342
|
+
}
|
|
16343
|
+
if (termErr) {
|
|
16344
|
+
throw termErr;
|
|
16345
|
+
}
|
|
16346
|
+
await disposeHome();
|
|
16347
|
+
throw err;
|
|
16348
|
+
}
|
|
16349
|
+
} catch (err) {
|
|
16350
|
+
if (!childStarted) {
|
|
16351
|
+
await disposeHome();
|
|
16352
|
+
}
|
|
16353
|
+
throw err;
|
|
16354
|
+
}
|
|
16355
|
+
}
|
|
16356
|
+
var import_node_child_process7, import_node_crypto19, import_node_fs5, import_promises9, import_node_os7, import_node_path16, OPENCODE_HOME_OWNER_FILE, MAX_OPENCODE_AUTH_BYTES, OPENCODE_HOME_PREFIX, CHILD_EXIT_WAIT_MS3, CHILD_KILL_WAIT_MS3, STALE_HOME_MAX_AGE_MS;
|
|
16357
|
+
var init_opencode = __esm({
|
|
16358
|
+
"src/host/opencode.ts"() {
|
|
16359
|
+
"use strict";
|
|
16360
|
+
import_node_child_process7 = require("node:child_process");
|
|
16361
|
+
import_node_crypto19 = require("node:crypto");
|
|
16362
|
+
init_stderr_tail();
|
|
16363
|
+
import_node_fs5 = require("node:fs");
|
|
16364
|
+
import_promises9 = require("node:fs/promises");
|
|
16365
|
+
import_node_os7 = require("node:os");
|
|
16366
|
+
import_node_path16 = require("node:path");
|
|
16367
|
+
init_bounds();
|
|
16368
|
+
init_env();
|
|
16369
|
+
init_session();
|
|
16370
|
+
init_types();
|
|
16371
|
+
init_version();
|
|
16372
|
+
OPENCODE_HOME_OWNER_FILE = ".cswarm-opencode-owner.json";
|
|
16373
|
+
MAX_OPENCODE_AUTH_BYTES = 256 * 1024;
|
|
16374
|
+
OPENCODE_HOME_PREFIX = "cswarm-opencode-home-";
|
|
16375
|
+
CHILD_EXIT_WAIT_MS3 = 3e3;
|
|
16376
|
+
CHILD_KILL_WAIT_MS3 = 1e3;
|
|
16377
|
+
STALE_HOME_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
16378
|
+
}
|
|
16379
|
+
});
|
|
16380
|
+
|
|
13502
16381
|
// src/cli.ts
|
|
13503
16382
|
var cli_exports = {};
|
|
13504
16383
|
__export(cli_exports, {
|
|
@@ -13527,14 +16406,13 @@ __export(cli_exports, {
|
|
|
13527
16406
|
usage: () => usage
|
|
13528
16407
|
});
|
|
13529
16408
|
module.exports = __toCommonJS(cli_exports);
|
|
13530
|
-
var
|
|
13531
|
-
var
|
|
13532
|
-
var
|
|
13533
|
-
var
|
|
13534
|
-
var
|
|
13535
|
-
var
|
|
13536
|
-
var
|
|
13537
|
-
var import_promises10 = require("node:readline/promises");
|
|
16409
|
+
var import_node_crypto20 = require("node:crypto");
|
|
16410
|
+
var import_node_child_process8 = require("node:child_process");
|
|
16411
|
+
var import_node_fs6 = require("node:fs");
|
|
16412
|
+
var import_promises10 = require("node:fs/promises");
|
|
16413
|
+
var import_node_os8 = require("node:os");
|
|
16414
|
+
var import_node_path17 = require("node:path");
|
|
16415
|
+
var import_promises11 = require("node:readline/promises");
|
|
13538
16416
|
|
|
13539
16417
|
// src/protocol/events.ts
|
|
13540
16418
|
var SCHEMA_VERSION = 1;
|
|
@@ -24827,9 +27705,9 @@ var arraySerializer = function arraySerializer2(xs, serializer, options, typarra
|
|
|
24827
27705
|
if (!xs.length)
|
|
24828
27706
|
return "{}";
|
|
24829
27707
|
const first = xs[0];
|
|
24830
|
-
const
|
|
27708
|
+
const delimiter3 = typarray === 1020 ? ";" : ",";
|
|
24831
27709
|
if (Array.isArray(first) && !first.type)
|
|
24832
|
-
return "{" + xs.map((x) => arraySerializer2(x, serializer, options, typarray)).join(
|
|
27710
|
+
return "{" + xs.map((x) => arraySerializer2(x, serializer, options, typarray)).join(delimiter3) + "}";
|
|
24833
27711
|
return "{" + xs.map((x) => {
|
|
24834
27712
|
if (x === void 0) {
|
|
24835
27713
|
x = options.transform.undefined;
|
|
@@ -24837,7 +27715,7 @@ var arraySerializer = function arraySerializer2(xs, serializer, options, typarra
|
|
|
24837
27715
|
throw Errors.generic("UNDEFINED_VALUE", "Undefined values are not allowed");
|
|
24838
27716
|
}
|
|
24839
27717
|
return x === null ? "null" : '"' + arrayEscape(serializer ? serializer(x.type ? x.value : x) : "" + x) + '"';
|
|
24840
|
-
}).join(
|
|
27718
|
+
}).join(delimiter3) + "}";
|
|
24841
27719
|
};
|
|
24842
27720
|
var arrayParserState = {
|
|
24843
27721
|
i: 0,
|
|
@@ -24852,7 +27730,7 @@ var arrayParser = function arrayParser2(x, parser, typarray) {
|
|
|
24852
27730
|
};
|
|
24853
27731
|
function arrayParserLoop(s, x, parser, typarray) {
|
|
24854
27732
|
const xs = [];
|
|
24855
|
-
const
|
|
27733
|
+
const delimiter3 = typarray === 1020 ? ";" : ",";
|
|
24856
27734
|
for (; s.i < x.length; s.i++) {
|
|
24857
27735
|
s.char = x[s.i];
|
|
24858
27736
|
if (s.quoted) {
|
|
@@ -24876,7 +27754,7 @@ function arrayParserLoop(s, x, parser, typarray) {
|
|
|
24876
27754
|
s.last < s.i && xs.push(parser ? parser(x.slice(s.last, s.i)) : x.slice(s.last, s.i));
|
|
24877
27755
|
s.last = s.i + 1;
|
|
24878
27756
|
break;
|
|
24879
|
-
} else if (s.char ===
|
|
27757
|
+
} else if (s.char === delimiter3 && s.p !== "}" && s.p !== '"') {
|
|
24880
27758
|
xs.push(parser ? parser(x.slice(s.last, s.i)) : x.slice(s.last, s.i));
|
|
24881
27759
|
s.last = s.i + 1;
|
|
24882
27760
|
}
|
|
@@ -33752,97 +36630,8 @@ async function reportRenderedBroadcasts(target2, token, workspaceId2, signalIds,
|
|
|
33752
36630
|
return { attempted: new Set(signalIds).size, reported, failures };
|
|
33753
36631
|
}
|
|
33754
36632
|
|
|
33755
|
-
// src/
|
|
33756
|
-
|
|
33757
|
-
"timeout",
|
|
33758
|
-
"child_exit",
|
|
33759
|
-
"transport"
|
|
33760
|
-
]);
|
|
33761
|
-
var AcpHostError = class extends Error {
|
|
33762
|
-
code;
|
|
33763
|
-
constructor(code, message) {
|
|
33764
|
-
super(message);
|
|
33765
|
-
this.name = "AcpHostError";
|
|
33766
|
-
this.code = code;
|
|
33767
|
-
}
|
|
33768
|
-
};
|
|
33769
|
-
var AcpVersionError = class extends AcpHostError {
|
|
33770
|
-
constructor(message, code = "version_refused") {
|
|
33771
|
-
super(code, message);
|
|
33772
|
-
this.name = "AcpVersionError";
|
|
33773
|
-
}
|
|
33774
|
-
};
|
|
33775
|
-
var AcpVersionParseError = class extends AcpVersionError {
|
|
33776
|
-
constructor(message) {
|
|
33777
|
-
super(message, "version_unparseable");
|
|
33778
|
-
this.name = "AcpVersionParseError";
|
|
33779
|
-
}
|
|
33780
|
-
};
|
|
33781
|
-
var AcpPermissionCanaryError = class extends AcpHostError {
|
|
33782
|
-
constructor(message, reasonCode = null, minimumRequiredVersion = null, peerError = null) {
|
|
33783
|
-
super("permission_canary_failed", message);
|
|
33784
|
-
this.reasonCode = reasonCode;
|
|
33785
|
-
this.minimumRequiredVersion = minimumRequiredVersion;
|
|
33786
|
-
this.peerError = peerError;
|
|
33787
|
-
this.name = "AcpPermissionCanaryError";
|
|
33788
|
-
}
|
|
33789
|
-
reasonCode;
|
|
33790
|
-
minimumRequiredVersion;
|
|
33791
|
-
peerError;
|
|
33792
|
-
};
|
|
33793
|
-
|
|
33794
|
-
// src/host/version.ts
|
|
33795
|
-
var CORE_IDENTIFIER = "(?:0|[1-9]\\d*)";
|
|
33796
|
-
var PRERELEASE_IDENTIFIER = "(?:0|[1-9]\\d*|[A-Za-z-][0-9A-Za-z-]*)";
|
|
33797
|
-
var BUILD_IDENTIFIER = "[0-9A-Za-z-]+";
|
|
33798
|
-
var SEMVER_SOURCE = `${CORE_IDENTIFIER}\\.${CORE_IDENTIFIER}\\.${CORE_IDENTIFIER}(?:-${PRERELEASE_IDENTIFIER}(?:\\.${PRERELEASE_IDENTIFIER})*)?(?:\\+${BUILD_IDENTIFIER}(?:\\.${BUILD_IDENTIFIER})*)?`;
|
|
33799
|
-
var SEMVER_RE = new RegExp(`^${SEMVER_SOURCE}$`);
|
|
33800
|
-
function parseSemVer(value) {
|
|
33801
|
-
if (!SEMVER_RE.test(value)) return null;
|
|
33802
|
-
const withoutBuild = value.split("+", 1)[0];
|
|
33803
|
-
const dash = withoutBuild.indexOf("-");
|
|
33804
|
-
const coreText = dash === -1 ? withoutBuild : withoutBuild.slice(0, dash);
|
|
33805
|
-
const prereleaseText = dash === -1 ? null : withoutBuild.slice(dash + 1);
|
|
33806
|
-
const coreParts = coreText.split(".");
|
|
33807
|
-
if (coreParts.length !== 3) return null;
|
|
33808
|
-
return {
|
|
33809
|
-
core: [BigInt(coreParts[0]), BigInt(coreParts[1]), BigInt(coreParts[2])],
|
|
33810
|
-
prerelease: prereleaseText === null ? null : prereleaseText.split(".")
|
|
33811
|
-
};
|
|
33812
|
-
}
|
|
33813
|
-
function compareSemVer(left, right) {
|
|
33814
|
-
const a = parseSemVer(left);
|
|
33815
|
-
const b2 = parseSemVer(right);
|
|
33816
|
-
if (!a || !b2) {
|
|
33817
|
-
throw new AcpVersionParseError(
|
|
33818
|
-
`cannot compare invalid semantic versions: ${JSON.stringify(left)} and ${JSON.stringify(right)}`
|
|
33819
|
-
);
|
|
33820
|
-
}
|
|
33821
|
-
for (let index = 0; index < 3; index += 1) {
|
|
33822
|
-
if (a.core[index] < b2.core[index]) return -1;
|
|
33823
|
-
if (a.core[index] > b2.core[index]) return 1;
|
|
33824
|
-
}
|
|
33825
|
-
if (a.prerelease === null && b2.prerelease === null) return 0;
|
|
33826
|
-
if (a.prerelease === null) return 1;
|
|
33827
|
-
if (b2.prerelease === null) return -1;
|
|
33828
|
-
const length = Math.max(a.prerelease.length, b2.prerelease.length);
|
|
33829
|
-
for (let index = 0; index < length; index += 1) {
|
|
33830
|
-
const leftPart = a.prerelease[index];
|
|
33831
|
-
const rightPart = b2.prerelease[index];
|
|
33832
|
-
if (leftPart === void 0) return -1;
|
|
33833
|
-
if (rightPart === void 0) return 1;
|
|
33834
|
-
if (leftPart === rightPart) continue;
|
|
33835
|
-
const leftNumeric = /^\d+$/.test(leftPart);
|
|
33836
|
-
const rightNumeric = /^\d+$/.test(rightPart);
|
|
33837
|
-
if (leftNumeric && rightNumeric) {
|
|
33838
|
-
return BigInt(leftPart) < BigInt(rightPart) ? -1 : 1;
|
|
33839
|
-
}
|
|
33840
|
-
if (leftNumeric) return -1;
|
|
33841
|
-
if (rightNumeric) return 1;
|
|
33842
|
-
return leftPart < rightPart ? -1 : 1;
|
|
33843
|
-
}
|
|
33844
|
-
return 0;
|
|
33845
|
-
}
|
|
36633
|
+
// src/cli.ts
|
|
36634
|
+
init_version();
|
|
33846
36635
|
|
|
33847
36636
|
// src/listener/types.ts
|
|
33848
36637
|
var LISTENER_PROMPT_TIMEOUT_MS = 6e5;
|
|
@@ -33874,6 +36663,7 @@ var LISTENER_DELIVERY_HOLD_RELEASE_REMEDIES = {
|
|
|
33874
36663
|
};
|
|
33875
36664
|
|
|
33876
36665
|
// src/listener/engine.ts
|
|
36666
|
+
init_types();
|
|
33877
36667
|
var UUID_RE16 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
33878
36668
|
function listenerReplyCommandId(signalId, effectOrdinal = 0) {
|
|
33879
36669
|
if (!UUID_RE16.test(signalId)) {
|
|
@@ -34240,9 +37030,8 @@ var FileListenerEffectStore = class {
|
|
|
34240
37030
|
|
|
34241
37031
|
// src/listener/runtime.ts
|
|
34242
37032
|
var import_node_crypto15 = require("node:crypto");
|
|
34243
|
-
|
|
34244
|
-
|
|
34245
|
-
var ACP_DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
|
|
37033
|
+
init_bounds();
|
|
37034
|
+
init_types();
|
|
34246
37035
|
|
|
34247
37036
|
// src/listener/main-routing.ts
|
|
34248
37037
|
var import_node_path7 = require("node:path");
|
|
@@ -35922,25 +38711,7 @@ var import_node_crypto16 = require("node:crypto");
|
|
|
35922
38711
|
var import_node_net = require("node:net");
|
|
35923
38712
|
var import_promises6 = require("node:fs/promises");
|
|
35924
38713
|
var import_node_path8 = require("node:path");
|
|
35925
|
-
|
|
35926
|
-
// src/host/credential-redaction.ts
|
|
35927
|
-
var EXOTIC_SEPARATORS = "\\u00a0\\u1680\\u2000-\\u200d\\u2028\\u2029\\u202a-\\u202e\\u2060\\u2066-\\u2069\\u202f\\u205f\\u3000\\ufeff";
|
|
35928
|
-
var SEPARATOR_CLASS_SOURCE = "\\t\\n\\x0b\\f\\r " + EXOTIC_SEPARATORS;
|
|
35929
|
-
var ANSI_ESCAPE_GLOBAL_RE2 = new RegExp("\\u001b\\[[0-?]*[ -\\/]*[@-~]", "g");
|
|
35930
|
-
var CONTROL_AND_SEPARATOR_STRIP_RE = new RegExp(
|
|
35931
|
-
"[\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f" + EXOTIC_SEPARATORS + "]",
|
|
35932
|
-
"g"
|
|
35933
|
-
);
|
|
35934
|
-
var SECRET_SHAPE_RE = new RegExp(
|
|
35935
|
-
`swm_(?:agt|inv|cap)_[^${SEPARATOR_CLASS_SOURCE}]*|cswarm-wake:[A-Za-z0-9_-]{43}`,
|
|
35936
|
-
"i"
|
|
35937
|
-
);
|
|
35938
|
-
var SECRET_SHAPE_GLOBAL_RE = new RegExp(SECRET_SHAPE_RE.source, "gi");
|
|
35939
|
-
function redactCredentialText(value) {
|
|
35940
|
-
return value.replace(ANSI_ESCAPE_GLOBAL_RE2, "").replace(CONTROL_AND_SEPARATOR_STRIP_RE, "").replace(SECRET_SHAPE_GLOBAL_RE, "[redacted-credential]");
|
|
35941
|
-
}
|
|
35942
|
-
|
|
35943
|
-
// src/listener/control.ts
|
|
38714
|
+
init_credential_redaction();
|
|
35944
38715
|
var UUID_RE20 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
35945
38716
|
var SEMVER_RE2 = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
35946
38717
|
var MAX_STATUS_BYTES = 32 * 1024;
|
|
@@ -36606,6 +39377,8 @@ async function queryListenerControl(paths, command2, timeoutMs = CONTROL_TIMEOUT
|
|
|
36606
39377
|
|
|
36607
39378
|
// src/listener/supervisor.ts
|
|
36608
39379
|
var import_node_crypto17 = require("node:crypto");
|
|
39380
|
+
init_credential_redaction();
|
|
39381
|
+
init_types();
|
|
36609
39382
|
var UUID_RE21 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
36610
39383
|
var LISTENER_RESTART_MAX_ATTEMPTS = 5;
|
|
36611
39384
|
var LISTENER_RESTART_INITIAL_MS = 1e3;
|
|
@@ -38117,48 +40890,7 @@ async function openListenerDeliveryJournal(options) {
|
|
|
38117
40890
|
// src/listener/detach.ts
|
|
38118
40891
|
var import_node_child_process3 = require("node:child_process");
|
|
38119
40892
|
var import_node_path10 = require("node:path");
|
|
38120
|
-
|
|
38121
|
-
// src/host/env.ts
|
|
38122
|
-
var ALLOWED_EXACT = /* @__PURE__ */ new Set([
|
|
38123
|
-
"PATH",
|
|
38124
|
-
"HOME",
|
|
38125
|
-
"USER",
|
|
38126
|
-
"LOGNAME",
|
|
38127
|
-
"SHELL",
|
|
38128
|
-
"TMPDIR",
|
|
38129
|
-
"TMP",
|
|
38130
|
-
"TEMP",
|
|
38131
|
-
"LANG",
|
|
38132
|
-
"LC_ALL",
|
|
38133
|
-
"LC_CTYPE",
|
|
38134
|
-
"LC_MESSAGES",
|
|
38135
|
-
"LC_COLLATE",
|
|
38136
|
-
"LC_TIME",
|
|
38137
|
-
"TERM",
|
|
38138
|
-
"COLORTERM",
|
|
38139
|
-
"NO_COLOR",
|
|
38140
|
-
"FORCE_COLOR",
|
|
38141
|
-
"XDG_CONFIG_HOME",
|
|
38142
|
-
"XDG_DATA_HOME",
|
|
38143
|
-
"XDG_CACHE_HOME",
|
|
38144
|
-
"XDG_RUNTIME_DIR",
|
|
38145
|
-
"XDG_STATE_HOME",
|
|
38146
|
-
"GROK_HOME"
|
|
38147
|
-
]);
|
|
38148
|
-
var DENY_NAME_RE = /(?:^|_)(?:SWARM|SECRET|TOKEN|PASSWORD|PASSWD|CREDENTIAL|PRIVATE_KEY|API_KEY|AUTH|COOKIE)(?:_|$)/i;
|
|
38149
|
-
function sanitizeChildEnv(parent = process.env) {
|
|
38150
|
-
const out = {};
|
|
38151
|
-
for (const [key2, value] of Object.entries(parent)) {
|
|
38152
|
-
if (value === void 0) continue;
|
|
38153
|
-
if (!ALLOWED_EXACT.has(key2)) continue;
|
|
38154
|
-
if (DENY_NAME_RE.test(key2)) continue;
|
|
38155
|
-
if (key2.startsWith("SWARM_")) continue;
|
|
38156
|
-
out[key2] = value;
|
|
38157
|
-
}
|
|
38158
|
-
return out;
|
|
38159
|
-
}
|
|
38160
|
-
|
|
38161
|
-
// src/listener/detach.ts
|
|
40893
|
+
init_env();
|
|
38162
40894
|
function isNativeAbsolutePath(value, platform = process.platform) {
|
|
38163
40895
|
return platform === "win32" ? import_node_path10.win32.isAbsolute(value) : import_node_path10.posix.isAbsolute(value);
|
|
38164
40896
|
}
|
|
@@ -39312,18 +42044,7 @@ function renderListenerAttendanceCanary(result, workspaceId2, principalId) {
|
|
|
39312
42044
|
|
|
39313
42045
|
// src/listener/activity.ts
|
|
39314
42046
|
var import_node_crypto18 = require("node:crypto");
|
|
39315
|
-
|
|
39316
|
-
// src/host/sanitize.ts
|
|
39317
|
-
var SECRET_VALUE_RE = /(?:(?:api[_-]?key|token|secret|password|authorization|bearer)\s*[:=]\s*)(["']?)([^\s"'\\]{8,})\1/gi;
|
|
39318
|
-
var JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
|
|
39319
|
-
function redactString(value) {
|
|
39320
|
-
return redactCredentialText(value).replace(SECRET_VALUE_RE, (_m, q) => `redacted=${q}***${q}`).replace(JWT_RE, "[redacted-jwt]");
|
|
39321
|
-
}
|
|
39322
|
-
function sanitizeText(text) {
|
|
39323
|
-
return redactString(text);
|
|
39324
|
-
}
|
|
39325
|
-
|
|
39326
|
-
// src/listener/activity.ts
|
|
42047
|
+
init_sanitize();
|
|
39327
42048
|
var ACTIVITY_FRAME_INTERVAL_MS = 750;
|
|
39328
42049
|
var ACTIVITY_HEARTBEAT_MS = 15e3;
|
|
39329
42050
|
var ACTIVITY_TOOL_TITLE_MAX = 160;
|
|
@@ -41083,20 +43804,49 @@ async function revokeAgentToken(input) {
|
|
|
41083
43804
|
};
|
|
41084
43805
|
}
|
|
41085
43806
|
|
|
43807
|
+
// src/listener/claude-canary-classify.ts
|
|
43808
|
+
var CLAUDE_CODE_VERSION_REQUIRED_RE = /\bClaude Code (\d+\.\d+\.\d+) does not support this model; version (\d+\.\d+\.\d+) or newer is required\b/;
|
|
43809
|
+
var CLAUDE_AUTH_FAILURE_RE = /\b(?:authentication failed|failed to authenticate|authentication required|not authenticated|OAuth (?:sign-in|login|token)|OAuth session (?:expired|could not be refreshed)|keychain\/OAuth|please (?:log|sign) in)\b/i;
|
|
43810
|
+
var CLAUDE_CANARY_TIMEOUT_RE = /^ACP request timed out: session\/prompt(?: \(failed \d+ attempts\))?$/;
|
|
43811
|
+
function classifyClaudeCanaryFailure(detail, typedReasonCode, peerError) {
|
|
43812
|
+
const recorded = detail?.trim() ?? "";
|
|
43813
|
+
const peerData = peerError?.data;
|
|
43814
|
+
const peerErrorKind = peerData && typeof peerData === "object" && !Array.isArray(peerData) ? peerData.errorKind : void 0;
|
|
43815
|
+
if (typedReasonCode === "claude_canary_auth_failed" || (typedReasonCode === "rpc_error" || typedReasonCode === null || typedReasonCode === void 0) && (peerError?.code === -32e3 || peerErrorKind === "authentication_failed")) {
|
|
43816
|
+
return { code: "claude_canary_auth_failed", minimumRequiredVersion: null };
|
|
43817
|
+
}
|
|
43818
|
+
const demanded = CLAUDE_CODE_VERSION_REQUIRED_RE.exec(recorded);
|
|
43819
|
+
if (demanded?.[2]) {
|
|
43820
|
+
return {
|
|
43821
|
+
code: "claude_bridge_version_required",
|
|
43822
|
+
minimumRequiredVersion: demanded[2]
|
|
43823
|
+
};
|
|
43824
|
+
}
|
|
43825
|
+
if (typedReasonCode === "claude_canary_timeout" || typedReasonCode === "timeout" || (typedReasonCode === null || typedReasonCode === void 0) && CLAUDE_CANARY_TIMEOUT_RE.test(recorded)) {
|
|
43826
|
+
return { code: "claude_canary_timeout", minimumRequiredVersion: null };
|
|
43827
|
+
}
|
|
43828
|
+
if (typedReasonCode === "claude_bridge_version_required") {
|
|
43829
|
+
return {
|
|
43830
|
+
code: "claude_bridge_version_required",
|
|
43831
|
+
minimumRequiredVersion: demanded?.[2] ?? null
|
|
43832
|
+
};
|
|
43833
|
+
}
|
|
43834
|
+
if ((typedReasonCode === "rpc_error" || typedReasonCode === null || typedReasonCode === void 0) && CLAUDE_AUTH_FAILURE_RE.test(recorded)) {
|
|
43835
|
+
return { code: "claude_canary_auth_failed", minimumRequiredVersion: null };
|
|
43836
|
+
}
|
|
43837
|
+
return { code: "claude_canary_unknown", minimumRequiredVersion: null };
|
|
43838
|
+
}
|
|
43839
|
+
|
|
41086
43840
|
// src/cli.ts
|
|
41087
43841
|
var import_meta = {};
|
|
41088
|
-
var requireFromCli = (0, import_node_module.createRequire)(import_meta.url);
|
|
41089
43842
|
function loadHostClaude() {
|
|
41090
|
-
return
|
|
43843
|
+
return Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
41091
43844
|
}
|
|
41092
43845
|
function loadHostCodex() {
|
|
41093
|
-
return
|
|
43846
|
+
return Promise.resolve().then(() => (init_codex(), codex_exports));
|
|
41094
43847
|
}
|
|
41095
43848
|
function loadHostOpenCode() {
|
|
41096
|
-
return
|
|
41097
|
-
}
|
|
41098
|
-
function loadClaudeListenerModel() {
|
|
41099
|
-
return requireFromCli("./listener/claude-model.js");
|
|
43849
|
+
return Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
41100
43850
|
}
|
|
41101
43851
|
var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
41102
43852
|
"about",
|
|
@@ -41214,12 +43964,12 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
41214
43964
|
]);
|
|
41215
43965
|
var UUID_RE25 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
41216
43966
|
function packageVersion() {
|
|
41217
|
-
if ("0.1.
|
|
41218
|
-
return "0.1.
|
|
43967
|
+
if ("0.1.63".length > 0) {
|
|
43968
|
+
return "0.1.63";
|
|
41219
43969
|
}
|
|
41220
43970
|
try {
|
|
41221
43971
|
const value = JSON.parse(
|
|
41222
|
-
(0,
|
|
43972
|
+
(0, import_node_fs6.readFileSync)(new URL("../package.json", import_meta.url), "utf8")
|
|
41223
43973
|
);
|
|
41224
43974
|
const version3 = value.version;
|
|
41225
43975
|
if (typeof version3 !== "string") return "unknown";
|
|
@@ -41692,7 +44442,7 @@ async function stdinInviteLink() {
|
|
|
41692
44442
|
return link;
|
|
41693
44443
|
}
|
|
41694
44444
|
async function confirmationLine(prompt) {
|
|
41695
|
-
const reader = (0,
|
|
44445
|
+
const reader = (0, import_promises11.createInterface)({
|
|
41696
44446
|
input: process.stdin,
|
|
41697
44447
|
output: process.stderr,
|
|
41698
44448
|
terminal: Boolean(process.stdin.isTTY)
|
|
@@ -41829,7 +44579,7 @@ async function runNew(args) {
|
|
|
41829
44579
|
assertWorkspaceName(name);
|
|
41830
44580
|
const cloud = await target(args);
|
|
41831
44581
|
const human = await humanCredential(args, cloud);
|
|
41832
|
-
const proposedId = (0,
|
|
44582
|
+
const proposedId = (0, import_node_crypto20.randomUUID)();
|
|
41833
44583
|
let result;
|
|
41834
44584
|
try {
|
|
41835
44585
|
result = await new ThinCommandClient(cloud).sendConnect({
|
|
@@ -43205,13 +45955,13 @@ function prepareSignalAttachments(localPaths) {
|
|
|
43205
45955
|
return localPaths.map((localPath) => {
|
|
43206
45956
|
let bytes;
|
|
43207
45957
|
try {
|
|
43208
|
-
bytes = (0,
|
|
45958
|
+
bytes = (0, import_node_fs6.readFileSync)(localPath);
|
|
43209
45959
|
} catch {
|
|
43210
45960
|
throw new Error(
|
|
43211
45961
|
`could not read ${localPath}; check the path and permissions; no upload was started`
|
|
43212
45962
|
);
|
|
43213
45963
|
}
|
|
43214
|
-
const name = (0,
|
|
45964
|
+
const name = (0, import_node_path17.basename)(localPath);
|
|
43215
45965
|
if (bytes.byteLength < 1) {
|
|
43216
45966
|
throw new Error(`${localPath} is empty; no upload was started`);
|
|
43217
45967
|
}
|
|
@@ -43231,8 +45981,8 @@ function prepareSignalAttachments(localPaths) {
|
|
|
43231
45981
|
name,
|
|
43232
45982
|
bytes,
|
|
43233
45983
|
contentType,
|
|
43234
|
-
fileId: (0,
|
|
43235
|
-
versionId: (0,
|
|
45984
|
+
fileId: (0, import_node_crypto20.randomUUID)(),
|
|
45985
|
+
versionId: (0, import_node_crypto20.randomUUID)(),
|
|
43236
45986
|
createCommandId: newCommandId(),
|
|
43237
45987
|
commitCommandId: newCommandId()
|
|
43238
45988
|
};
|
|
@@ -43754,7 +46504,7 @@ async function runResume(args) {
|
|
|
43754
46504
|
if (/[\u0000-\u001f\u007f-\u009f]/.test(suppliedCredentialPath)) {
|
|
43755
46505
|
throw new Error("--agent-token-file must not contain control characters");
|
|
43756
46506
|
}
|
|
43757
|
-
const credentialFile = (0,
|
|
46507
|
+
const credentialFile = (0, import_node_path17.resolve)(suppliedCredentialPath);
|
|
43758
46508
|
const cloud = await target(args);
|
|
43759
46509
|
const workspaceId2 = listenerUuid(
|
|
43760
46510
|
args.optional("workspace-id") ?? process.env.SWARM_CLOUD_WORKSPACE_ID,
|
|
@@ -44269,7 +47019,7 @@ function listenerPermissionMode(value) {
|
|
|
44269
47019
|
function listenerStateDirectory(args) {
|
|
44270
47020
|
const value = args.optional("state-dir");
|
|
44271
47021
|
if (value === void 0) return void 0;
|
|
44272
|
-
if (!(0,
|
|
47022
|
+
if (!(0, import_node_path17.isAbsolute)(value)) {
|
|
44273
47023
|
throw new Error("--state-dir must be an absolute path");
|
|
44274
47024
|
}
|
|
44275
47025
|
return value;
|
|
@@ -44526,7 +47276,7 @@ function listenerLapseNotices(status, summary) {
|
|
|
44526
47276
|
async function listenerProviderInstallEvidence(status) {
|
|
44527
47277
|
if (status.provider !== "claude") return null;
|
|
44528
47278
|
try {
|
|
44529
|
-
const notice = await loadHostClaude().inspectClaudeBridgeExecutable(
|
|
47279
|
+
const notice = await (await loadHostClaude()).inspectClaudeBridgeExecutable(
|
|
44530
47280
|
status.providerExecutable ?? "claude-agent-acp",
|
|
44531
47281
|
{ pathEnv: process.env.PATH, env: process.env }
|
|
44532
47282
|
);
|
|
@@ -44914,7 +47664,7 @@ function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequi
|
|
|
44914
47664
|
}
|
|
44915
47665
|
if (code === "permission_canary_failed") {
|
|
44916
47666
|
if (provider === "claude") {
|
|
44917
|
-
const shape =
|
|
47667
|
+
const shape = classifyClaudeCanaryFailure(detail, reasonCode);
|
|
44918
47668
|
const ran = "the Claude ACP permission canary ran, but no workspace signal prompt was delivered";
|
|
44919
47669
|
const response = `bridge response [${shape.code}]: ${quotedListenerFailureDetail(detail)}`;
|
|
44920
47670
|
if (shape.code === "claude_bridge_version_required") {
|
|
@@ -44947,13 +47697,13 @@ function listenerFailureMessage(code, provider, detail, reasonCode, minimumRequi
|
|
|
44947
47697
|
}
|
|
44948
47698
|
return `listener failed (${code}); check cswarm listen status before starting another`;
|
|
44949
47699
|
}
|
|
44950
|
-
function resolveDetachedClaudeExecutable(executable = "claude-agent-acp", pathEnv = process.env.PATH) {
|
|
47700
|
+
async function resolveDetachedClaudeExecutable(executable = "claude-agent-acp", pathEnv = process.env.PATH) {
|
|
44951
47701
|
try {
|
|
44952
|
-
return loadHostClaude().resolveClaudeExecutable(executable, pathEnv);
|
|
47702
|
+
return (await loadHostClaude()).resolveClaudeExecutable(executable, pathEnv);
|
|
44953
47703
|
} catch (error) {
|
|
44954
47704
|
const code = error.code;
|
|
44955
47705
|
if (typeof code === "string") {
|
|
44956
|
-
if ((0,
|
|
47706
|
+
if ((0, import_node_path17.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
44957
47707
|
const detail = error instanceof Error ? error.message : code;
|
|
44958
47708
|
throw new Error(
|
|
44959
47709
|
`could not use --claude-executable: ${detail}; install the current bridge with npm install -g @agentclientprotocol/claude-agent-acp@latest if this path should be replaced`
|
|
@@ -44964,13 +47714,13 @@ function resolveDetachedClaudeExecutable(executable = "claude-agent-acp", pathEn
|
|
|
44964
47714
|
throw error;
|
|
44965
47715
|
}
|
|
44966
47716
|
}
|
|
44967
|
-
function resolveDetachedCodexExecutable(executable = "codex-acp", pathEnv = process.env.PATH) {
|
|
47717
|
+
async function resolveDetachedCodexExecutable(executable = "codex-acp", pathEnv = process.env.PATH) {
|
|
44968
47718
|
try {
|
|
44969
|
-
return loadHostCodex().resolveCodexExecutable(executable, pathEnv);
|
|
47719
|
+
return (await loadHostCodex()).resolveCodexExecutable(executable, pathEnv);
|
|
44970
47720
|
} catch (error) {
|
|
44971
47721
|
const code = error.code;
|
|
44972
47722
|
if (typeof code === "string") {
|
|
44973
|
-
if ((0,
|
|
47723
|
+
if ((0, import_node_path17.isAbsolute)(executable) || executable.includes("/") || executable.includes("\\")) {
|
|
44974
47724
|
const detail = error instanceof Error ? error.message : code;
|
|
44975
47725
|
throw new Error(
|
|
44976
47726
|
`could not use --codex-executable: ${detail}; install the current bridge with npm install -g @agentclientprotocol/codex-acp@latest if this path should be replaced`
|
|
@@ -45392,7 +48142,7 @@ async function runListenStart(args) {
|
|
|
45392
48142
|
assertDurableListenerCredential(agent);
|
|
45393
48143
|
const principalId = agent.principalId;
|
|
45394
48144
|
const cwd = args.optional("cwd") ?? process.cwd();
|
|
45395
|
-
if (!(0,
|
|
48145
|
+
if (!(0, import_node_path17.isAbsolute)(cwd)) throw new Error("--cwd must be an absolute path");
|
|
45396
48146
|
const permissionMode = listenerPermissionMode(args.optional("permissions"));
|
|
45397
48147
|
const stateDirectory2 = listenerStateDirectory(args);
|
|
45398
48148
|
const paths = listenerPaths({
|
|
@@ -45439,7 +48189,7 @@ async function runListenStart(args) {
|
|
|
45439
48189
|
});
|
|
45440
48190
|
} else {
|
|
45441
48191
|
const entrypoint = process.argv[1];
|
|
45442
|
-
if (!entrypoint || !(0,
|
|
48192
|
+
if (!entrypoint || !(0, import_node_path17.isAbsolute)(entrypoint)) {
|
|
45443
48193
|
throw new Error("cannot locate the cswarm executable for detached start");
|
|
45444
48194
|
}
|
|
45445
48195
|
const artifact = JSON.stringify(agentCredentialArtifact({
|
|
@@ -45449,14 +48199,14 @@ async function runListenStart(args) {
|
|
|
45449
48199
|
token: agent.token,
|
|
45450
48200
|
expiresAt: agent.expiresAt
|
|
45451
48201
|
}));
|
|
45452
|
-
const opencodeExecutable = provider === "opencode" && args.optional("opencode-executable") !== void 0 ? loadHostOpenCode().resolveOpenCodeExecutable(args.required("opencode-executable")) : void 0;
|
|
48202
|
+
const opencodeExecutable = provider === "opencode" && args.optional("opencode-executable") !== void 0 ? (await loadHostOpenCode()).resolveOpenCodeExecutable(args.required("opencode-executable")) : void 0;
|
|
45453
48203
|
let claudeExecutable;
|
|
45454
48204
|
if (provider === "claude" && args.optional("claude-executable") !== void 0) {
|
|
45455
|
-
claudeExecutable = resolveDetachedClaudeExecutable(args.required("claude-executable"));
|
|
48205
|
+
claudeExecutable = await resolveDetachedClaudeExecutable(args.required("claude-executable"));
|
|
45456
48206
|
}
|
|
45457
48207
|
let codexExecutable;
|
|
45458
48208
|
if (provider === "codex" && args.optional("codex-executable") !== void 0) {
|
|
45459
|
-
codexExecutable = resolveDetachedCodexExecutable(args.required("codex-executable"));
|
|
48209
|
+
codexExecutable = await resolveDetachedCodexExecutable(args.required("codex-executable"));
|
|
45460
48210
|
}
|
|
45461
48211
|
const startedAtFloorMs = Date.now();
|
|
45462
48212
|
const child = await spawnDetachedListener({
|
|
@@ -45606,7 +48356,7 @@ async function runListenSupervisor(args) {
|
|
|
45606
48356
|
const agent = await agentCredential(args, { implicitStdin: true });
|
|
45607
48357
|
assertDurableListenerCredential(agent, principalId);
|
|
45608
48358
|
const cwd = args.required("cwd");
|
|
45609
|
-
if (!(0,
|
|
48359
|
+
if (!(0, import_node_path17.isAbsolute)(cwd)) throw new Error("--cwd must be an absolute path");
|
|
45610
48360
|
const status = await runConfiguredListener({
|
|
45611
48361
|
cloud,
|
|
45612
48362
|
workspaceId: workspaceId2,
|
|
@@ -45919,7 +48669,7 @@ local ${local.state} server-live ${server.is_live} server-session ${server.sessi
|
|
|
45919
48669
|
const customContextPath = args.optional("session-context");
|
|
45920
48670
|
if (customContextPath !== void 0) {
|
|
45921
48671
|
const root = defaultSessionRootDirectory();
|
|
45922
|
-
if (!(0,
|
|
48672
|
+
if (!(0, import_node_path17.resolve)(customContextPath).startsWith(`${root}${import_node_path17.sep}`)) {
|
|
45923
48673
|
throw new SessionContextError(
|
|
45924
48674
|
"session_context_outside_default_tree",
|
|
45925
48675
|
`--session-context must lie under ${root} so listen start and hook check can find it; omit the flag to use the default path`
|
|
@@ -45933,7 +48683,7 @@ local ${local.state} server-live ${server.is_live} server-session ${server.sessi
|
|
|
45933
48683
|
);
|
|
45934
48684
|
const agent = await agentCredential(args);
|
|
45935
48685
|
const tokenFile = args.optional("agent-token-file");
|
|
45936
|
-
if (tokenFile === void 0 || !(0,
|
|
48686
|
+
if (tokenFile === void 0 || !(0, import_node_path17.isAbsolute)(tokenFile)) {
|
|
45937
48687
|
throw new Error(
|
|
45938
48688
|
"session start needs --agent-token-file <absolute-path> so the context can reference the sole token file"
|
|
45939
48689
|
);
|
|
@@ -45943,7 +48693,7 @@ local ${local.state} server-live ${server.is_live} server-session ${server.sessi
|
|
|
45943
48693
|
target: cloud,
|
|
45944
48694
|
workspaceId: selectedWorkspace,
|
|
45945
48695
|
credential: agent.token,
|
|
45946
|
-
tokenFile: (0,
|
|
48696
|
+
tokenFile: (0, import_node_path17.resolve)(tokenFile),
|
|
45947
48697
|
tokenPrincipalId: agent.principalId,
|
|
45948
48698
|
mode: mode3,
|
|
45949
48699
|
provider,
|
|
@@ -46029,8 +48779,8 @@ async function listenerHookSurfacePresent(instanceDirectory, cwd, principalId) {
|
|
|
46029
48779
|
if (surface.exists) return true;
|
|
46030
48780
|
const repositoryRoot = gitRepositoryRoot(cwd) ?? cwd;
|
|
46031
48781
|
const settingsPaths = /* @__PURE__ */ new Set([
|
|
46032
|
-
(0,
|
|
46033
|
-
(0,
|
|
48782
|
+
(0, import_node_path17.join)(repositoryRoot, CLAUDE_PROJECT_SETTINGS_IGNORE_LINE),
|
|
48783
|
+
(0, import_node_path17.join)(repositoryRoot, CLAUDE_REPO_SETTINGS_IGNORE_LINE),
|
|
46034
48784
|
userClaudeSettingsTarget().path
|
|
46035
48785
|
]);
|
|
46036
48786
|
for (const path of settingsPaths) {
|
|
@@ -46096,19 +48846,19 @@ function claudeUserPromptHookSnippet(principalId) {
|
|
|
46096
48846
|
var CLAUDE_PROJECT_SETTINGS_IGNORE_LINE = ".claude/settings.local.json";
|
|
46097
48847
|
var CLAUDE_REPO_SETTINGS_IGNORE_LINE = ".claude/settings.json";
|
|
46098
48848
|
function claudeUserScopeWarning(settingsPath) {
|
|
46099
|
-
return `Warning: --user scope writes settings to ${(0,
|
|
48849
|
+
return `Warning: --user scope writes settings to ${(0, import_node_path17.dirname)(settingsPath)} and applies to every Claude Code session that reads that directory.`;
|
|
46100
48850
|
}
|
|
46101
48851
|
function userClaudeSettingsTarget() {
|
|
46102
48852
|
const configured = process.env.CLAUDE_CONFIG_DIR;
|
|
46103
|
-
const directory = configured && configured.length > 0 ? (0,
|
|
48853
|
+
const directory = configured && configured.length > 0 ? (0, import_node_path17.resolve)(configured) : (0, import_node_path17.join)((0, import_node_os8.homedir)(), ".claude");
|
|
46104
48854
|
return {
|
|
46105
|
-
path: (0,
|
|
48855
|
+
path: (0, import_node_path17.join)(directory, "settings.json"),
|
|
46106
48856
|
scope: "user",
|
|
46107
48857
|
projectRoot: null
|
|
46108
48858
|
};
|
|
46109
48859
|
}
|
|
46110
48860
|
function gitRepositoryRoot(cwd) {
|
|
46111
|
-
const result = (0,
|
|
48861
|
+
const result = (0, import_node_child_process8.spawnSync)(
|
|
46112
48862
|
"git",
|
|
46113
48863
|
["-C", cwd, "rev-parse", "--show-toplevel"],
|
|
46114
48864
|
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
@@ -46118,7 +48868,7 @@ function gitRepositoryRoot(cwd) {
|
|
|
46118
48868
|
}
|
|
46119
48869
|
if (result.status !== 0) return null;
|
|
46120
48870
|
const root = result.stdout.trim();
|
|
46121
|
-
if (!(0,
|
|
48871
|
+
if (!(0, import_node_path17.isAbsolute)(root)) {
|
|
46122
48872
|
throw new Error("hook could not resolve an absolute repository root");
|
|
46123
48873
|
}
|
|
46124
48874
|
return root;
|
|
@@ -46126,14 +48876,14 @@ function gitRepositoryRoot(cwd) {
|
|
|
46126
48876
|
function projectClaudeSettingsTarget(scope, ignoreLine) {
|
|
46127
48877
|
const root = gitRepositoryRoot(process.cwd());
|
|
46128
48878
|
const base = root ?? process.cwd();
|
|
46129
|
-
const path = (0,
|
|
48879
|
+
const path = (0, import_node_path17.join)(base, ignoreLine);
|
|
46130
48880
|
if (root === null) return { path, scope, projectRoot: base };
|
|
46131
|
-
const tracked = (0,
|
|
48881
|
+
const tracked = (0, import_node_child_process8.spawnSync)(
|
|
46132
48882
|
"git",
|
|
46133
48883
|
["-C", root, "ls-files", "--error-unmatch", "--", ignoreLine],
|
|
46134
48884
|
{ encoding: "utf8", stdio: ["ignore", "ignore", "ignore"] }
|
|
46135
48885
|
);
|
|
46136
|
-
const ignored = (0,
|
|
48886
|
+
const ignored = (0, import_node_child_process8.spawnSync)(
|
|
46137
48887
|
"git",
|
|
46138
48888
|
["-C", root, "check-ignore", "--quiet", "--", ignoreLine],
|
|
46139
48889
|
{ encoding: "utf8", stdio: ["ignore", "ignore", "ignore"] }
|
|
@@ -46143,7 +48893,7 @@ function projectClaudeSettingsTarget(scope, ignoreLine) {
|
|
|
46143
48893
|
}
|
|
46144
48894
|
if (tracked.status === 0 || ignored.status !== 0) {
|
|
46145
48895
|
throw new Error(
|
|
46146
|
-
`Refusing to write ${path}: repository Claude settings could be staged and shared with every checkout. ` + (tracked.status === 0 ? "It is already tracked; remove it from Git tracking first. " : "") + `Add this exact line to ${(0,
|
|
48896
|
+
`Refusing to write ${path}: repository Claude settings could be staged and shared with every checkout. ` + (tracked.status === 0 ? "It is already tracked; remove it from Git tracking first. " : "") + `Add this exact line to ${(0, import_node_path17.join)(root, ".gitignore")}: ${ignoreLine}`
|
|
46147
48897
|
);
|
|
46148
48898
|
}
|
|
46149
48899
|
return { path, scope, projectRoot: root };
|
|
@@ -46158,7 +48908,7 @@ function claudeSettingsTarget(args) {
|
|
|
46158
48908
|
function readClaudeSettings(path) {
|
|
46159
48909
|
let raw;
|
|
46160
48910
|
try {
|
|
46161
|
-
raw = (0,
|
|
48911
|
+
raw = (0, import_node_fs6.readFileSync)(path, "utf8");
|
|
46162
48912
|
} catch (error) {
|
|
46163
48913
|
if (error.code === "ENOENT") return {};
|
|
46164
48914
|
throw error;
|
|
@@ -46358,8 +49108,8 @@ async function runHook(args) {
|
|
|
46358
49108
|
process.stdout.write(`${claudeUserScopeWarning(path)}
|
|
46359
49109
|
`);
|
|
46360
49110
|
}
|
|
46361
|
-
(0,
|
|
46362
|
-
(0,
|
|
49111
|
+
(0, import_node_fs6.mkdirSync)((0, import_node_path17.dirname)(path), { recursive: true });
|
|
49112
|
+
(0, import_node_fs6.writeFileSync)(path, `${JSON.stringify(updated, null, 2)}
|
|
46363
49113
|
`, {
|
|
46364
49114
|
encoding: "utf8",
|
|
46365
49115
|
mode: 384
|
|
@@ -46445,8 +49195,8 @@ async function uploadNamedFile(context, name, bytes, options = {}) {
|
|
|
46445
49195
|
credential: context.selected.bearer,
|
|
46446
49196
|
fetcher: context.selected.fetcher
|
|
46447
49197
|
};
|
|
46448
|
-
const fileId = (0,
|
|
46449
|
-
const versionId = (0,
|
|
49198
|
+
const fileId = (0, import_node_crypto20.randomUUID)();
|
|
49199
|
+
const versionId = (0, import_node_crypto20.randomUUID)();
|
|
46450
49200
|
const createCommandId = newCommandId();
|
|
46451
49201
|
const commitCommandId = newCommandId();
|
|
46452
49202
|
const created = await onceRetried(
|
|
@@ -46476,11 +49226,11 @@ async function runFilePut(args) {
|
|
|
46476
49226
|
const context = await fileContext(args, ["name"], 3);
|
|
46477
49227
|
let bytes;
|
|
46478
49228
|
try {
|
|
46479
|
-
bytes = (0,
|
|
49229
|
+
bytes = (0, import_node_fs6.readFileSync)(localPath);
|
|
46480
49230
|
} catch {
|
|
46481
49231
|
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
46482
49232
|
}
|
|
46483
|
-
const name = args.optional("name") ?? (0,
|
|
49233
|
+
const name = args.optional("name") ?? (0, import_node_path17.basename)(localPath);
|
|
46484
49234
|
const committed = await uploadNamedFile(context, name, bytes);
|
|
46485
49235
|
if (args.has("json")) {
|
|
46486
49236
|
process.stdout.write(`${JSON.stringify(committed, null, 2)}
|
|
@@ -46545,12 +49295,12 @@ async function runFileGet(args) {
|
|
|
46545
49295
|
fetcher: context.selected.fetcher
|
|
46546
49296
|
};
|
|
46547
49297
|
const grant = await fileDownloadUrl(send, { fileId, versionN });
|
|
46548
|
-
const destination = args.optional("out") ?? (0,
|
|
49298
|
+
const destination = args.optional("out") ?? (0, import_node_path17.basename)(grant.name);
|
|
46549
49299
|
const bytes = await onceRetried(
|
|
46550
49300
|
(attempt) => getObject(context.cloud, grant.download_path, fetch, attempt),
|
|
46551
49301
|
{}
|
|
46552
49302
|
);
|
|
46553
|
-
writeDestination(destination, bytes, args.has("force"),
|
|
49303
|
+
writeDestination(destination, bytes, args.has("force"), import_node_fs6.writeFileSync);
|
|
46554
49304
|
if (args.has("json")) {
|
|
46555
49305
|
process.stdout.write(
|
|
46556
49306
|
`${JSON.stringify(
|
|
@@ -46757,7 +49507,7 @@ async function runBrainPut(args) {
|
|
|
46757
49507
|
let bytes;
|
|
46758
49508
|
if (localPath) {
|
|
46759
49509
|
try {
|
|
46760
|
-
bytes = (0,
|
|
49510
|
+
bytes = (0, import_node_fs6.readFileSync)(localPath);
|
|
46761
49511
|
} catch {
|
|
46762
49512
|
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
46763
49513
|
}
|
|
@@ -47065,7 +49815,7 @@ async function runDogfood(args) {
|
|
|
47065
49815
|
const { selectedWorkspace, bearer } = await commandWorkspaceAndCredential(args, cloud);
|
|
47066
49816
|
const client = new ThinCommandClient(cloud);
|
|
47067
49817
|
const route = stream(args);
|
|
47068
|
-
const taskId = args.optional("task-id") ?? (0,
|
|
49818
|
+
const taskId = args.optional("task-id") ?? (0, import_node_crypto20.randomUUID)();
|
|
47069
49819
|
const ttl = Number(args.optional("ttl-ms") ?? "3600000");
|
|
47070
49820
|
if (!Number.isSafeInteger(ttl) || ttl <= 0 || ttl > 144e5) {
|
|
47071
49821
|
throw new Error("--ttl-ms must be an integer in 1..14400000");
|
|
@@ -47128,10 +49878,10 @@ async function runSeed(args) {
|
|
|
47128
49878
|
throw new Error("DATABASE_URL is required for the fixture bridge");
|
|
47129
49879
|
}
|
|
47130
49880
|
const tokenOut = process.env.SEED_TOKEN_OUT;
|
|
47131
|
-
if (!tokenOut || !(0,
|
|
49881
|
+
if (!tokenOut || !(0, import_node_path17.isAbsolute)(tokenOut)) {
|
|
47132
49882
|
throw new Error("SEED_TOKEN_OUT must be an absolute path");
|
|
47133
49883
|
}
|
|
47134
|
-
const tokenFile = await (0,
|
|
49884
|
+
const tokenFile = await (0, import_promises10.open)(tokenOut, "wx", 384).catch((error) => {
|
|
47135
49885
|
if (error.code === "EEXIST") {
|
|
47136
49886
|
throw new Error("SEED_TOKEN_OUT already exists; refusing to overwrite it");
|
|
47137
49887
|
}
|
|
@@ -47170,7 +49920,7 @@ async function runSeed(args) {
|
|
|
47170
49920
|
tokenWritten = true;
|
|
47171
49921
|
}
|
|
47172
49922
|
await tokenFile.close();
|
|
47173
|
-
if (!tokenWritten) await (0,
|
|
49923
|
+
if (!tokenWritten) await (0, import_promises10.unlink)(tokenOut);
|
|
47174
49924
|
process.stdout.write(`${JSON.stringify({
|
|
47175
49925
|
userId: result.userId,
|
|
47176
49926
|
membershipRole: result.membershipRole,
|
|
@@ -47183,7 +49933,7 @@ async function runSeed(args) {
|
|
|
47183
49933
|
`);
|
|
47184
49934
|
} catch (error) {
|
|
47185
49935
|
await tokenFile.close().catch(() => void 0);
|
|
47186
|
-
if (!tokenWritten) await (0,
|
|
49936
|
+
if (!tokenWritten) await (0, import_promises10.unlink)(tokenOut).catch(() => void 0);
|
|
47187
49937
|
throw error;
|
|
47188
49938
|
}
|
|
47189
49939
|
}
|