@creatornader/agent-bridge 0.6.4 → 0.7.0
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/CHANGELOG.md +22 -0
- package/README.md +19 -1
- package/ROADMAP.md +16 -0
- package/SKILL.md +7 -1
- package/dist/bridge.js +2 -2
- package/dist/{chunk-DBQ5PNQW.js → chunk-FDDLNAOY.js} +3 -3
- package/dist/{chunk-HIYXPNBE.js → chunk-J6GHXVHC.js} +11 -3
- package/dist/chunk-J6GHXVHC.js.map +1 -0
- package/dist/{chunk-2UBJ6DHC.js → chunk-JEOOF3E4.js} +2 -2
- package/dist/{chunk-NSQE6YRP.js → chunk-SBVZVXGK.js} +23 -2
- package/dist/chunk-SBVZVXGK.js.map +1 -0
- package/dist/{chunk-H2UAY6AV.js → chunk-SHJGICQ5.js} +2 -2
- package/dist/cli.js +340 -59
- package/dist/cli.js.map +1 -1
- package/dist/client-runtime.js +3 -3
- package/dist/dr.js +2 -2
- package/dist/index.js +4 -4
- package/dist/server.js +4 -4
- package/dist/sqlite.js +2 -2
- package/docs/architecture-v2.md +15 -3
- package/docs/troubleshooting.md +28 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/chunk-HIYXPNBE.js.map +0 -1
- package/dist/chunk-NSQE6YRP.js.map +0 -1
- /package/dist/{chunk-DBQ5PNQW.js.map → chunk-FDDLNAOY.js.map} +0 -0
- /package/dist/{chunk-2UBJ6DHC.js.map → chunk-JEOOF3E4.js.map} +0 -0
- /package/dist/{chunk-H2UAY6AV.js.map → chunk-SHJGICQ5.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "./chunk-UKFRCP6X.js";
|
|
18
18
|
import {
|
|
19
19
|
createClientRuntime
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-FDDLNAOY.js";
|
|
21
21
|
import {
|
|
22
22
|
BridgeHttpError,
|
|
23
23
|
HttpBridgeStore
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
SyncingBridgeStore,
|
|
28
28
|
edgeScopeKey,
|
|
29
29
|
inspectEdgeScopeReadOnly
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-J6GHXVHC.js";
|
|
31
31
|
import {
|
|
32
32
|
NativeDrBundleError,
|
|
33
33
|
NativeDrBundleReader,
|
|
@@ -42,8 +42,8 @@ import {
|
|
|
42
42
|
verifyNativeDrBundle,
|
|
43
43
|
verifyPostgresNativeDrArtifacts,
|
|
44
44
|
writeNativeDrBundle
|
|
45
|
-
} from "./chunk-
|
|
46
|
-
import "./chunk-
|
|
45
|
+
} from "./chunk-SHJGICQ5.js";
|
|
46
|
+
import "./chunk-SBVZVXGK.js";
|
|
47
47
|
import {
|
|
48
48
|
PrivatePathError,
|
|
49
49
|
createPrivateDirectoryAccessCache,
|
|
@@ -1584,6 +1584,31 @@ var defaultExecutor = (command, args, context) => spawnSync2(command, args, {
|
|
|
1584
1584
|
cwd: context?.cwd,
|
|
1585
1585
|
env: context?.env
|
|
1586
1586
|
});
|
|
1587
|
+
function validateLoopbackMcpUrl(value, field = "--mcp-url") {
|
|
1588
|
+
const text4 = strictText(value, field, 2048);
|
|
1589
|
+
let parsed;
|
|
1590
|
+
try {
|
|
1591
|
+
parsed = new URL(text4);
|
|
1592
|
+
} catch {
|
|
1593
|
+
throw new Error(`${field} must be a loopback HTTP MCP URL`);
|
|
1594
|
+
}
|
|
1595
|
+
if (parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.search || parsed.hash || !["127.0.0.1", "localhost", "[::1]"].includes(parsed.hostname) || !parsed.port || parsed.pathname !== "/mcp") {
|
|
1596
|
+
throw new Error(`${field} must be a loopback HTTP MCP URL ending in /mcp`);
|
|
1597
|
+
}
|
|
1598
|
+
return parsed.toString();
|
|
1599
|
+
}
|
|
1600
|
+
function managedClientTarget(metadata) {
|
|
1601
|
+
return metadata.version === 2 ? metadata.target : {
|
|
1602
|
+
version: 1,
|
|
1603
|
+
transport: "stdio",
|
|
1604
|
+
command: metadata.launch.command,
|
|
1605
|
+
args: [...metadata.launch.args],
|
|
1606
|
+
scope: metadata.launch.scope
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function targetLaunchProjection(target) {
|
|
1610
|
+
return target.transport === "stdio" ? { command: target.command, args: [...target.args], scope: target.scope } : { command: target.url, args: [], scope: target.scope };
|
|
1611
|
+
}
|
|
1587
1612
|
function safeComponent2(value) {
|
|
1588
1613
|
const readable = value.replace(/[^A-Za-z0-9._-]+/g, "_").slice(0, 48) || "client";
|
|
1589
1614
|
const suffix = createHash3("sha256").update(value).digest("hex").slice(0, 12);
|
|
@@ -1632,8 +1657,8 @@ function expectedManagedClientMetadata(runtime2, identity2, options, env) {
|
|
|
1632
1657
|
locator: clientRegistrationLocator(runtime2, scope, options, env)
|
|
1633
1658
|
};
|
|
1634
1659
|
}
|
|
1635
|
-
function
|
|
1636
|
-
return isDeepStrictEqual(actual, expected);
|
|
1660
|
+
function exactManagedTarget(actual, expected) {
|
|
1661
|
+
return actual != null && actual.schema === expected.schema && actual.runtime === expected.runtime && actual.identity === expected.identity && actual.instance === expected.instance && actual.backendConfigPath === expected.backendConfigPath && isDeepStrictEqual(actual.locator, expected.locator);
|
|
1637
1662
|
}
|
|
1638
1663
|
var REGISTRATION_ENV_KEYS = [
|
|
1639
1664
|
"AGENT_BRIDGE_AGENT",
|
|
@@ -1685,6 +1710,10 @@ function safeEnvironment(value) {
|
|
|
1685
1710
|
return environment;
|
|
1686
1711
|
}
|
|
1687
1712
|
function exactRegistration(expected, command, args, environment) {
|
|
1713
|
+
const target = managedClientTarget(expected);
|
|
1714
|
+
if (target.transport === "streamable-http") {
|
|
1715
|
+
return command === target.url && args.length === 0 && Object.keys(environment).length === 0;
|
|
1716
|
+
}
|
|
1688
1717
|
return command === expected.launch.command && isDeepStrictEqual(args, expected.launch.args) && isDeepStrictEqual(environment, {
|
|
1689
1718
|
AGENT_BRIDGE_AGENT: expected.identity,
|
|
1690
1719
|
AGENT_BRIDGE_INSTANCE: expected.instance,
|
|
@@ -1776,14 +1805,29 @@ ${result.stderr}`.trim();
|
|
|
1776
1805
|
"tool_timeout_sec",
|
|
1777
1806
|
"status"
|
|
1778
1807
|
]);
|
|
1779
|
-
const
|
|
1808
|
+
const target = managedClientTarget(expected);
|
|
1809
|
+
const transportFields = transport?.type === "streamable_http" ? /* @__PURE__ */ new Set(["type", "url", "bearer_token_env_var", "http_headers", "env_http_headers"]) : /* @__PURE__ */ new Set(["type", "command", "args", "env", "env_vars", "cwd"]);
|
|
1780
1810
|
const hasNonNullSetting = (field) => field in server && server[field] !== null;
|
|
1781
|
-
if (!server || typeof server !== "object" || Array.isArray(server) || server.name !== "agent-bridge" || server.enabled !== true || !transport || typeof transport !== "object" || Array.isArray(transport)
|
|
1782
|
-
throw new Error("Codex MCP registration is not a supported
|
|
1811
|
+
if (!server || typeof server !== "object" || Array.isArray(server) || server.name !== "agent-bridge" || server.enabled !== true || !transport || typeof transport !== "object" || Array.isArray(transport)) {
|
|
1812
|
+
throw new Error("Codex MCP registration is not a supported contract");
|
|
1783
1813
|
}
|
|
1784
1814
|
if (Object.keys(server).some((field) => !serverFields.has(field)) || Object.keys(transport).some((field) => !transportFields.has(field)) || hasNonNullSetting("enabled_tools") || hasNonNullSetting("disabled_tools")) {
|
|
1785
1815
|
throw new Error("Codex MCP registration contains an unsupported execution setting");
|
|
1786
1816
|
}
|
|
1817
|
+
if (transport.type === "streamable_http") {
|
|
1818
|
+
if (transport.type !== "streamable_http" || transport.bearer_token_env_var !== null || transport.http_headers !== null || transport.env_http_headers !== null) {
|
|
1819
|
+
throw new Error("Codex MCP registration contains an unsupported HTTP setting");
|
|
1820
|
+
}
|
|
1821
|
+
const url = validateLoopbackMcpUrl(transport.url, "Codex MCP URL");
|
|
1822
|
+
return {
|
|
1823
|
+
state: exactRegistration(expected, url, [], {}) ? "exact" : "inexact",
|
|
1824
|
+
target: registrationTarget(expected),
|
|
1825
|
+
observed: { state: "present", command: url, args: [], env: {} }
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
if (transport.type !== "stdio") {
|
|
1829
|
+
throw new Error("Codex MCP registration is not a supported stdio contract");
|
|
1830
|
+
}
|
|
1787
1831
|
if ("cwd" in transport && transport.cwd !== null || "env_vars" in transport && (!Array.isArray(transport.env_vars) || transport.env_vars.length !== 0)) {
|
|
1788
1832
|
throw new Error("Codex MCP registration contains an unsupported execution setting");
|
|
1789
1833
|
}
|
|
@@ -1822,6 +1866,28 @@ ${result.stderr}`.trim();
|
|
|
1822
1866
|
if (lines[0] !== "agent-bridge:" || lines[1] !== scope) {
|
|
1823
1867
|
throw new Error("Claude Code MCP registration scope is not the recorded scope");
|
|
1824
1868
|
}
|
|
1869
|
+
const target = managedClientTarget(expected);
|
|
1870
|
+
const httpTypeIndex = lines.indexOf(" Type: http", 2);
|
|
1871
|
+
if (httpTypeIndex >= 0) {
|
|
1872
|
+
const urlLine = lines[httpTypeIndex + 1];
|
|
1873
|
+
if (!urlLine?.startsWith(" URL: ")) {
|
|
1874
|
+
throw new Error("Claude Code MCP registration cannot be represented safely");
|
|
1875
|
+
}
|
|
1876
|
+
const url = validateLoopbackMcpUrl(urlLine.slice(" URL: ".length), "Claude Code MCP URL");
|
|
1877
|
+
let index2 = httpTypeIndex + 2;
|
|
1878
|
+
while (lines[index2] === "") index2 += 1;
|
|
1879
|
+
if (lines[index2] !== `To remove this server, run: claude mcp remove agent-bridge -s ${expected.launch.scope}`) {
|
|
1880
|
+
throw new Error("Claude Code MCP registration cannot be represented safely");
|
|
1881
|
+
}
|
|
1882
|
+
if (lines.slice(index2 + 1).some((line) => line !== "")) {
|
|
1883
|
+
throw new Error("Claude Code MCP registration cannot be represented safely");
|
|
1884
|
+
}
|
|
1885
|
+
return {
|
|
1886
|
+
state: target.transport === "streamable-http" && target.url === url ? "exact" : "inexact",
|
|
1887
|
+
target: registrationTarget(expected),
|
|
1888
|
+
observed: { state: "present", command: url, args: [], env: {} }
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1825
1891
|
const typeIndex = lines.indexOf(" Type: stdio", 2);
|
|
1826
1892
|
if (typeIndex < 0 || lines[typeIndex + 1] !== ` Command: ${expected.launch.command}` || lines[typeIndex + 2] !== " Args:" || lines[typeIndex + 3] !== " Environment:") {
|
|
1827
1893
|
if (typeIndex < 0 || lines[typeIndex + 2] !== " Args:" || lines[typeIndex + 3] !== " Environment:") {
|
|
@@ -1987,7 +2053,9 @@ function strictLocator(runtime2, value) {
|
|
|
1987
2053
|
function strictManagedMetadata(value, runtime2, instance) {
|
|
1988
2054
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("managed metadata is invalid");
|
|
1989
2055
|
const metadata = value;
|
|
1990
|
-
|
|
2056
|
+
const version = metadata.version;
|
|
2057
|
+
const expectedKeys = version === 2 ? "backendConfigPath,identity,instance,launch,locator,runtime,schema,target,version" : "backendConfigPath,identity,instance,launch,locator,runtime,schema,version";
|
|
2058
|
+
if (Object.keys(metadata).sort().join(",") !== expectedKeys || metadata.schema !== "agent-bridge.client-management" || version !== 1 && version !== 2 || metadata.runtime !== runtime2) throw new Error("managed metadata is invalid");
|
|
1991
2059
|
const identity2 = strictText(metadata.identity, "identity", 128);
|
|
1992
2060
|
const recordedInstance = strictText(metadata.instance, "instance", 128);
|
|
1993
2061
|
if (recordedInstance !== instance) throw new Error("managed metadata instance does not match its locator");
|
|
@@ -2008,7 +2076,7 @@ function strictManagedMetadata(value, runtime2, instance) {
|
|
|
2008
2076
|
}
|
|
2009
2077
|
const locator = strictLocator(runtime2, metadata.locator);
|
|
2010
2078
|
const scope = launch.scope;
|
|
2011
|
-
if (runtime2 !== "claude-code" && scope !== null || runtime2 === "claude-code" && scope !== locator.scope || (runtime2 === "codex" || runtime2 === "claude-code") && launch.args.length !== 0 || runtime2 === "claude-desktop" && !isAbsolute2(strictText(launch.command, "launch command", 4096)) || (runtime2 === "codex" || runtime2 === "claude-code") && (() => {
|
|
2079
|
+
if (runtime2 !== "claude-code" && scope !== null || runtime2 === "claude-code" && scope !== locator.scope || (runtime2 === "codex" || runtime2 === "claude-code") && launch.args.length !== 0 || runtime2 === "claude-desktop" && !isAbsolute2(strictText(launch.command, "launch command", 4096)) || (runtime2 === "codex" || runtime2 === "claude-code") && !(version === 2 && metadata.target?.transport === "streamable-http") && (() => {
|
|
2012
2080
|
try {
|
|
2013
2081
|
safeNativeExecutableContract(launch.command, "launch command");
|
|
2014
2082
|
return false;
|
|
@@ -2018,14 +2086,69 @@ function strictManagedMetadata(value, runtime2, instance) {
|
|
|
2018
2086
|
})()) {
|
|
2019
2087
|
throw new Error("managed metadata launch is invalid");
|
|
2020
2088
|
}
|
|
2089
|
+
let target;
|
|
2090
|
+
if (version === 2) {
|
|
2091
|
+
if (!metadata.target || typeof metadata.target !== "object" || Array.isArray(metadata.target)) {
|
|
2092
|
+
throw new Error("managed metadata target is invalid");
|
|
2093
|
+
}
|
|
2094
|
+
const candidate = metadata.target;
|
|
2095
|
+
const targetScope = candidate.scope;
|
|
2096
|
+
if (candidate.version !== 1 || ![null, "local", "user", "project"].includes(targetScope)) {
|
|
2097
|
+
throw new Error("managed metadata target is invalid");
|
|
2098
|
+
}
|
|
2099
|
+
if (candidate.transport === "streamable-http") {
|
|
2100
|
+
if (Object.keys(candidate).sort().join(",") !== "scope,transport,url,version" || runtime2 === "claude-desktop" || targetScope !== scope) {
|
|
2101
|
+
throw new Error("managed metadata target is invalid");
|
|
2102
|
+
}
|
|
2103
|
+
target = {
|
|
2104
|
+
version: 1,
|
|
2105
|
+
transport: "streamable-http",
|
|
2106
|
+
url: validateLoopbackMcpUrl(candidate.url, "managed metadata target URL"),
|
|
2107
|
+
scope: targetScope
|
|
2108
|
+
};
|
|
2109
|
+
} else if (candidate.transport === "stdio") {
|
|
2110
|
+
if (Object.keys(candidate).sort().join(",") !== "args,command,scope,transport,version" || !Array.isArray(candidate.args) || candidate.args.length > 16 || candidate.args.some((arg) => {
|
|
2111
|
+
try {
|
|
2112
|
+
strictText(arg, "target argument", 1024);
|
|
2113
|
+
return false;
|
|
2114
|
+
} catch {
|
|
2115
|
+
return true;
|
|
2116
|
+
}
|
|
2117
|
+
})) {
|
|
2118
|
+
throw new Error("managed metadata target is invalid");
|
|
2119
|
+
}
|
|
2120
|
+
target = {
|
|
2121
|
+
version: 1,
|
|
2122
|
+
transport: "stdio",
|
|
2123
|
+
command: strictText(candidate.command, "target command", 4096),
|
|
2124
|
+
args: [...candidate.args],
|
|
2125
|
+
scope: targetScope
|
|
2126
|
+
};
|
|
2127
|
+
if (runtime2 === "claude-desktop") {
|
|
2128
|
+
if (!isAbsolute2(target.command)) throw new Error("managed metadata target is invalid");
|
|
2129
|
+
} else {
|
|
2130
|
+
safeNativeExecutableContract(target.command, "managed metadata target command");
|
|
2131
|
+
}
|
|
2132
|
+
} else {
|
|
2133
|
+
throw new Error("managed metadata target is invalid");
|
|
2134
|
+
}
|
|
2135
|
+
if (!isDeepStrictEqual(targetLaunchProjection(target), {
|
|
2136
|
+
command: strictText(launch.command, "launch command", 4096),
|
|
2137
|
+
args: [...launch.args],
|
|
2138
|
+
scope
|
|
2139
|
+
})) {
|
|
2140
|
+
throw new Error("managed metadata target does not match its launch projection");
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2021
2143
|
return {
|
|
2022
2144
|
schema: "agent-bridge.client-management",
|
|
2023
|
-
version
|
|
2145
|
+
version,
|
|
2024
2146
|
runtime: runtime2,
|
|
2025
2147
|
identity: identity2,
|
|
2026
2148
|
instance: recordedInstance,
|
|
2027
2149
|
backendConfigPath,
|
|
2028
2150
|
launch: { command: strictText(launch.command, "launch command", 4096), args: [...launch.args], scope },
|
|
2151
|
+
...target ? { target } : {},
|
|
2029
2152
|
locator
|
|
2030
2153
|
};
|
|
2031
2154
|
}
|
|
@@ -2134,8 +2257,8 @@ function inspectClient(runtime2, identity2, options, execute = defaultExecutor)
|
|
|
2134
2257
|
const normalizedIdentity = identity2.trim();
|
|
2135
2258
|
const expected = expectedManagedClientMetadata(runtime2, normalizedIdentity, options, env);
|
|
2136
2259
|
const path = managedClientMetadataPath(runtime2, expected.instance, env);
|
|
2137
|
-
const registration = inspectManagedRegistration(expected, execute, env);
|
|
2138
2260
|
const metadata = readMetadata(path, runtime2, expected.instance);
|
|
2261
|
+
const registration = inspectManagedRegistration(metadata ?? expected, execute, env);
|
|
2139
2262
|
const backend = backendState(expected.backendConfigPath);
|
|
2140
2263
|
let state;
|
|
2141
2264
|
let reason;
|
|
@@ -2145,7 +2268,7 @@ function inspectClient(runtime2, identity2, options, execute = defaultExecutor)
|
|
|
2145
2268
|
} else if (registration === "exact" && backend === "private-file" && metadata === void 0) {
|
|
2146
2269
|
state = "unmanaged";
|
|
2147
2270
|
reason = "exact registration has no managed metadata";
|
|
2148
|
-
} else if (registration === "exact" && backend === "private-file" &&
|
|
2271
|
+
} else if (registration === "exact" && backend === "private-file" && exactManagedTarget(metadata, expected)) {
|
|
2149
2272
|
state = "managed";
|
|
2150
2273
|
reason = "registration and managed metadata are exact";
|
|
2151
2274
|
} else {
|
|
@@ -2496,7 +2619,9 @@ function validateOperationLocator(runtime2, value) {
|
|
|
2496
2619
|
function validateOperationMetadata(value, runtime2, instance) {
|
|
2497
2620
|
if (!value || typeof value !== "object" || Array.isArray(value)) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2498
2621
|
const metadata = value;
|
|
2499
|
-
|
|
2622
|
+
const metadataVersion = metadata.version;
|
|
2623
|
+
const metadataKeys = metadataVersion === 2 ? "backendConfigPath,identity,instance,launch,locator,runtime,schema,target,version" : "backendConfigPath,identity,instance,launch,locator,runtime,schema,version";
|
|
2624
|
+
if (Object.keys(metadata).sort().join(",") !== metadataKeys || metadata.schema !== "agent-bridge.client-management" || metadataVersion !== 1 && metadataVersion !== 2 || !RUNTIMES.includes(metadata.runtime) || runtime2 !== void 0 && metadata.runtime !== runtime2) {
|
|
2500
2625
|
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2501
2626
|
}
|
|
2502
2627
|
const recordedRuntime = metadata.runtime;
|
|
@@ -2521,19 +2646,52 @@ function validateOperationMetadata(value, runtime2, instance) {
|
|
|
2521
2646
|
const locator = validateOperationLocator(recordedRuntime, metadata.locator);
|
|
2522
2647
|
const command = safeText(launch.command, "launch command", 4096);
|
|
2523
2648
|
const scope = launch.scope;
|
|
2649
|
+
const httpTarget = metadataVersion === 2 && metadata.target?.transport === "streamable-http";
|
|
2524
2650
|
if (recordedRuntime !== "claude-code" && scope !== null || recordedRuntime === "claude-code" && scope !== locator.scope || (recordedRuntime === "codex" || recordedRuntime === "claude-code") && launch.args.length !== 0) {
|
|
2525
2651
|
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2526
2652
|
}
|
|
2527
|
-
if (recordedRuntime === "codex" || recordedRuntime === "claude-code")
|
|
2653
|
+
if ((recordedRuntime === "codex" || recordedRuntime === "claude-code") && !httpTarget) {
|
|
2654
|
+
safeNativeLaunchCommand(command);
|
|
2655
|
+
}
|
|
2528
2656
|
if (recordedRuntime === "claude-desktop" && !isAbsolute3(command)) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2657
|
+
let target;
|
|
2658
|
+
if (metadataVersion === 2) {
|
|
2659
|
+
if (!metadata.target || typeof metadata.target !== "object" || Array.isArray(metadata.target)) {
|
|
2660
|
+
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2661
|
+
}
|
|
2662
|
+
const candidate = metadata.target;
|
|
2663
|
+
if (candidate.version !== 1 || candidate.scope !== scope) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2664
|
+
if (candidate.transport === "streamable-http") {
|
|
2665
|
+
if (recordedRuntime === "claude-desktop" || Object.keys(candidate).sort().join(",") !== "scope,transport,url,version") {
|
|
2666
|
+
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2667
|
+
}
|
|
2668
|
+
const url = validateLoopbackMcpUrl(candidate.url, "managed operation target URL");
|
|
2669
|
+
if (command !== url || launch.args.length !== 0) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2670
|
+
target = { version: 1, transport: "streamable-http", url, scope };
|
|
2671
|
+
} else if (candidate.transport === "stdio") {
|
|
2672
|
+
if (Object.keys(candidate).sort().join(",") !== "args,command,scope,transport,version" || candidate.command !== command || !sameCanonicalValue(candidate.args, launch.args)) {
|
|
2673
|
+
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2674
|
+
}
|
|
2675
|
+
target = {
|
|
2676
|
+
version: 1,
|
|
2677
|
+
transport: "stdio",
|
|
2678
|
+
command,
|
|
2679
|
+
args: [...launch.args],
|
|
2680
|
+
scope
|
|
2681
|
+
};
|
|
2682
|
+
} else {
|
|
2683
|
+
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2529
2686
|
return {
|
|
2530
2687
|
schema: "agent-bridge.client-management",
|
|
2531
|
-
version:
|
|
2688
|
+
version: metadataVersion,
|
|
2532
2689
|
runtime: recordedRuntime,
|
|
2533
2690
|
identity: identity2,
|
|
2534
2691
|
instance: recordedInstance,
|
|
2535
2692
|
backendConfigPath,
|
|
2536
2693
|
launch: { command, args: [...launch.args], scope },
|
|
2694
|
+
...target ? { target } : {},
|
|
2537
2695
|
locator
|
|
2538
2696
|
};
|
|
2539
2697
|
}
|
|
@@ -2549,6 +2707,7 @@ function canonicalDigest(value) {
|
|
|
2549
2707
|
return createHash4("sha256").update(canonical(value)).digest("hex");
|
|
2550
2708
|
}
|
|
2551
2709
|
function exactRegistrationProof(metadata) {
|
|
2710
|
+
const http = metadata.version === 2 && metadata.target?.transport === "streamable-http";
|
|
2552
2711
|
return {
|
|
2553
2712
|
state: "exact",
|
|
2554
2713
|
target: {
|
|
@@ -2563,7 +2722,7 @@ function exactRegistrationProof(metadata) {
|
|
|
2563
2722
|
state: "present",
|
|
2564
2723
|
command: metadata.launch.command,
|
|
2565
2724
|
args: [...metadata.launch.args],
|
|
2566
|
-
env: {
|
|
2725
|
+
env: http ? {} : {
|
|
2567
2726
|
AGENT_BRIDGE_AGENT: metadata.identity,
|
|
2568
2727
|
AGENT_BRIDGE_INSTANCE: metadata.instance,
|
|
2569
2728
|
AGENT_BRIDGE_CONFIG: metadata.backendConfigPath
|
|
@@ -2572,9 +2731,23 @@ function exactRegistrationProof(metadata) {
|
|
|
2572
2731
|
};
|
|
2573
2732
|
}
|
|
2574
2733
|
function forwardMetadataFromUpdate(prior, launch) {
|
|
2734
|
+
const target = prior.runtime !== "claude-desktop" && launch.command.startsWith("http://") ? {
|
|
2735
|
+
version: 1,
|
|
2736
|
+
transport: "streamable-http",
|
|
2737
|
+
url: validateLoopbackMcpUrl(launch.command, "managed operation target URL"),
|
|
2738
|
+
scope: launch.scope
|
|
2739
|
+
} : {
|
|
2740
|
+
version: 1,
|
|
2741
|
+
transport: "stdio",
|
|
2742
|
+
command: launch.command,
|
|
2743
|
+
args: [...launch.args],
|
|
2744
|
+
scope: launch.scope
|
|
2745
|
+
};
|
|
2575
2746
|
return {
|
|
2576
2747
|
...prior,
|
|
2577
|
-
|
|
2748
|
+
version: 2,
|
|
2749
|
+
launch: { command: launch.command, args: [...launch.args], scope: launch.scope },
|
|
2750
|
+
target
|
|
2578
2751
|
};
|
|
2579
2752
|
}
|
|
2580
2753
|
function validateRegistrationProof(value, metadata) {
|
|
@@ -2589,12 +2762,13 @@ function validateRegistrationProof(value, metadata) {
|
|
|
2589
2762
|
}
|
|
2590
2763
|
const targetMetadata = validateOperationMetadata({
|
|
2591
2764
|
schema: "agent-bridge.client-management",
|
|
2592
|
-
version:
|
|
2765
|
+
version: metadata.version,
|
|
2593
2766
|
runtime: target.runtime,
|
|
2594
2767
|
identity: target.identity,
|
|
2595
2768
|
instance: target.instance,
|
|
2596
2769
|
backendConfigPath: target.backendConfigPath,
|
|
2597
2770
|
launch: target.launch,
|
|
2771
|
+
...metadata.version === 2 ? { target: metadata.target } : {},
|
|
2598
2772
|
locator: target.locator
|
|
2599
2773
|
}, metadata.runtime, metadata.instance);
|
|
2600
2774
|
if (!sameCanonicalValue(targetMetadata, metadata)) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
@@ -2603,7 +2777,8 @@ function validateRegistrationProof(value, metadata) {
|
|
|
2603
2777
|
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2604
2778
|
}
|
|
2605
2779
|
const env = observed.env;
|
|
2606
|
-
|
|
2780
|
+
const http = metadata.version === 2 && metadata.target?.transport === "streamable-http";
|
|
2781
|
+
if (http ? Object.keys(env).length !== 0 : Object.keys(env).sort().join(",") !== "AGENT_BRIDGE_AGENT,AGENT_BRIDGE_CONFIG,AGENT_BRIDGE_INSTANCE" || env.AGENT_BRIDGE_AGENT !== metadata.identity || env.AGENT_BRIDGE_CONFIG !== metadata.backendConfigPath || env.AGENT_BRIDGE_INSTANCE !== metadata.instance) {
|
|
2607
2782
|
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2608
2783
|
}
|
|
2609
2784
|
return {
|
|
@@ -2620,7 +2795,7 @@ function validateRegistrationProof(value, metadata) {
|
|
|
2620
2795
|
state: "present",
|
|
2621
2796
|
command: metadata.launch.command,
|
|
2622
2797
|
args: [...metadata.launch.args],
|
|
2623
|
-
env: {
|
|
2798
|
+
env: http ? {} : {
|
|
2624
2799
|
AGENT_BRIDGE_AGENT: metadata.identity,
|
|
2625
2800
|
AGENT_BRIDGE_CONFIG: metadata.backendConfigPath,
|
|
2626
2801
|
AGENT_BRIDGE_INSTANCE: metadata.instance
|
|
@@ -2720,7 +2895,10 @@ function validateRequest2(value, runtime2, version = CLIENT_OPERATION_VERSION, i
|
|
|
2720
2895
|
if ((runtime2 === "codex" || runtime2 === "claude-code") && (launch.args.length !== 0 || (runtime2 === "claude-code" ? !["local", "user", "project"].includes(launch.scope) : launch.scope !== null))) {
|
|
2721
2896
|
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2722
2897
|
}
|
|
2723
|
-
if (runtime2 === "codex" || runtime2 === "claude-code")
|
|
2898
|
+
if (runtime2 === "codex" || runtime2 === "claude-code") {
|
|
2899
|
+
if (command.startsWith("http://")) validateLoopbackMcpUrl(command, "managed operation target URL");
|
|
2900
|
+
else safeNativeLaunchCommand(command);
|
|
2901
|
+
}
|
|
2724
2902
|
if (runtime2 === "claude-desktop" && (launch.scope !== null || !isAbsolute3(command))) {
|
|
2725
2903
|
fail("CORRUPT_OPERATION", "operation manifest is corrupt");
|
|
2726
2904
|
}
|
|
@@ -3826,6 +4004,13 @@ function assertIdentity(metadata, assertion) {
|
|
|
3826
4004
|
function registrationProof(observation) {
|
|
3827
4005
|
return canonical2({ role: "registration", observation });
|
|
3828
4006
|
}
|
|
4007
|
+
function registrationEnvironment(metadata) {
|
|
4008
|
+
return managedClientTarget(metadata).transport === "streamable-http" ? {} : {
|
|
4009
|
+
AGENT_BRIDGE_AGENT: metadata.identity,
|
|
4010
|
+
AGENT_BRIDGE_INSTANCE: metadata.instance,
|
|
4011
|
+
AGENT_BRIDGE_CONFIG: metadata.backendConfigPath
|
|
4012
|
+
};
|
|
4013
|
+
}
|
|
3829
4014
|
function exactRegistrationObservation(metadata) {
|
|
3830
4015
|
return {
|
|
3831
4016
|
state: "exact",
|
|
@@ -3841,11 +4026,7 @@ function exactRegistrationObservation(metadata) {
|
|
|
3841
4026
|
state: "present",
|
|
3842
4027
|
command: metadata.launch.command,
|
|
3843
4028
|
args: [...metadata.launch.args],
|
|
3844
|
-
env:
|
|
3845
|
-
AGENT_BRIDGE_AGENT: metadata.identity,
|
|
3846
|
-
AGENT_BRIDGE_INSTANCE: metadata.instance,
|
|
3847
|
-
AGENT_BRIDGE_CONFIG: metadata.backendConfigPath
|
|
3848
|
-
}
|
|
4029
|
+
env: registrationEnvironment(metadata)
|
|
3849
4030
|
}
|
|
3850
4031
|
};
|
|
3851
4032
|
}
|
|
@@ -3887,24 +4068,88 @@ function operationLaunch(metadata) {
|
|
|
3887
4068
|
envKeys: fixedEnvKeys()
|
|
3888
4069
|
};
|
|
3889
4070
|
}
|
|
3890
|
-
function updateMetadata(metadata, launch) {
|
|
4071
|
+
function updateMetadata(metadata, launch, target) {
|
|
3891
4072
|
return {
|
|
3892
4073
|
...metadata,
|
|
3893
|
-
|
|
4074
|
+
version: 2,
|
|
4075
|
+
launch: { command: launch.command, args: [...launch.args], scope: launch.scope },
|
|
4076
|
+
target
|
|
3894
4077
|
};
|
|
3895
4078
|
}
|
|
3896
|
-
function
|
|
4079
|
+
function safeProxyArgs(args, endpoint) {
|
|
4080
|
+
if (args.length === 0 || args.length > 16 || args.some((arg) => !arg || arg.length > 1024 || arg.trim() !== arg || /[\u0000-\u001f\u007f]/.test(arg)) || !args.includes(endpoint)) {
|
|
4081
|
+
throw new Error("--proxy-args-json must include the exact --mcp-url value");
|
|
4082
|
+
}
|
|
4083
|
+
if (args.some((arg, index) => {
|
|
4084
|
+
const previous = index > 0 ? args[index - 1] : "";
|
|
4085
|
+
const sensitive = /^--?(?:token|key|secret|password|authorization|credential)(?:=.*)?$/i;
|
|
4086
|
+
return sensitive.test(arg) || sensitive.test(previous) || /\bBearer\s+\S+/i.test(arg) || /^https?:\/\/\S*[?#]\S+/i.test(arg);
|
|
4087
|
+
})) {
|
|
4088
|
+
throw new Error("--proxy-args-json cannot contain credential-like values");
|
|
4089
|
+
}
|
|
4090
|
+
return [...args];
|
|
4091
|
+
}
|
|
4092
|
+
function resolveUpdateTarget(metadata, options, env) {
|
|
4093
|
+
if (options.mcpUrl !== void 0) {
|
|
4094
|
+
const url = validateLoopbackMcpUrl(options.mcpUrl);
|
|
4095
|
+
if (metadata.runtime !== "claude-desktop") {
|
|
4096
|
+
if (options.command !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
|
|
4097
|
+
throw new Error("--command, --proxy-command, and --proxy-args-json are not valid for a native HTTP target");
|
|
4098
|
+
}
|
|
4099
|
+
const scope2 = metadata.runtime === "claude-code" ? metadata.launch.scope : null;
|
|
4100
|
+
return {
|
|
4101
|
+
launch: { command: url, args: [], scope: scope2, envKeys: fixedEnvKeys() },
|
|
4102
|
+
target: { version: 1, transport: "streamable-http", url, scope: scope2 }
|
|
4103
|
+
};
|
|
4104
|
+
}
|
|
4105
|
+
if (options.command !== void 0) throw new Error("--command cannot be combined with --mcp-url");
|
|
4106
|
+
if (!options.proxyCommand) throw new Error("--proxy-command is required for a Desktop HTTP target");
|
|
4107
|
+
const command = newNativeExecutableContract(options.proxyCommand, "--proxy-command");
|
|
4108
|
+
const args = safeProxyArgs(options.proxyArgs ?? [], url);
|
|
4109
|
+
return {
|
|
4110
|
+
launch: { command, args, scope: null, envKeys: fixedEnvKeys() },
|
|
4111
|
+
target: { version: 1, transport: "stdio", command, args, scope: null }
|
|
4112
|
+
};
|
|
4113
|
+
}
|
|
4114
|
+
if (options.proxyCommand !== void 0 || options.proxyArgs?.length) {
|
|
4115
|
+
throw new Error("--proxy-command and --proxy-args-json require --mcp-url");
|
|
4116
|
+
}
|
|
3897
4117
|
if (metadata.runtime === "claude-desktop") {
|
|
3898
|
-
const launch = resolveDesktopLaunchContract(command, env);
|
|
3899
|
-
return {
|
|
4118
|
+
const launch = resolveDesktopLaunchContract(options.command, env);
|
|
4119
|
+
return {
|
|
4120
|
+
launch: { command: launch.command, args: [...launch.args], scope: null, envKeys: fixedEnvKeys() },
|
|
4121
|
+
target: {
|
|
4122
|
+
version: 1,
|
|
4123
|
+
transport: "stdio",
|
|
4124
|
+
command: launch.command,
|
|
4125
|
+
args: [...launch.args],
|
|
4126
|
+
scope: null
|
|
4127
|
+
}
|
|
4128
|
+
};
|
|
3900
4129
|
}
|
|
3901
|
-
const requested = command === void 0 ? "agent-bridge-mcp" : command;
|
|
4130
|
+
const requested = options.command === void 0 ? "agent-bridge-mcp" : options.command;
|
|
3902
4131
|
newNativeExecutableContract(requested, "--command");
|
|
4132
|
+
const scope = metadata.runtime === "claude-code" ? metadata.launch.scope : null;
|
|
3903
4133
|
return {
|
|
3904
|
-
command: requested,
|
|
3905
|
-
args: [],
|
|
3906
|
-
|
|
3907
|
-
|
|
4134
|
+
launch: { command: requested, args: [], scope, envKeys: fixedEnvKeys() },
|
|
4135
|
+
target: { version: 1, transport: "stdio", command: requested, args: [], scope }
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
function targetFromRecordedLaunch(metadata, launch) {
|
|
4139
|
+
if (metadata.runtime !== "claude-desktop" && launch.command.startsWith("http://")) {
|
|
4140
|
+
return {
|
|
4141
|
+
version: 1,
|
|
4142
|
+
transport: "streamable-http",
|
|
4143
|
+
url: validateLoopbackMcpUrl(launch.command, "recorded MCP URL"),
|
|
4144
|
+
scope: launch.scope
|
|
4145
|
+
};
|
|
4146
|
+
}
|
|
4147
|
+
return {
|
|
4148
|
+
version: 1,
|
|
4149
|
+
transport: "stdio",
|
|
4150
|
+
command: launch.command,
|
|
4151
|
+
args: [...launch.args],
|
|
4152
|
+
scope: launch.scope
|
|
3908
4153
|
};
|
|
3909
4154
|
}
|
|
3910
4155
|
function backendPolicy(path) {
|
|
@@ -4122,7 +4367,17 @@ function nativeContext(metadata, env) {
|
|
|
4122
4367
|
function nativeExecute(metadata, action, execute, env) {
|
|
4123
4368
|
const context = nativeContext(metadata, env);
|
|
4124
4369
|
const executable = metadata.runtime === "codex" ? "codex" : "claude";
|
|
4125
|
-
const
|
|
4370
|
+
const target = managedClientTarget(metadata);
|
|
4371
|
+
const args = action === "remove" ? metadata.runtime === "codex" ? ["mcp", "remove", "agent-bridge"] : ["mcp", "remove", "agent-bridge", "-s", metadata.launch.scope] : target.transport === "streamable-http" ? metadata.runtime === "codex" ? ["mcp", "add", "agent-bridge", "--url", target.url] : [
|
|
4372
|
+
"mcp",
|
|
4373
|
+
"add",
|
|
4374
|
+
"--scope",
|
|
4375
|
+
metadata.launch.scope,
|
|
4376
|
+
"--transport",
|
|
4377
|
+
"http",
|
|
4378
|
+
"agent-bridge",
|
|
4379
|
+
target.url
|
|
4380
|
+
] : metadata.runtime === "codex" ? [
|
|
4126
4381
|
"mcp",
|
|
4127
4382
|
"add",
|
|
4128
4383
|
"agent-bridge",
|
|
@@ -4348,11 +4603,7 @@ function buildPlan(metadata, target, execute, env) {
|
|
|
4348
4603
|
state: "present",
|
|
4349
4604
|
command: target.launch.command,
|
|
4350
4605
|
args: [...target.launch.args],
|
|
4351
|
-
env:
|
|
4352
|
-
AGENT_BRIDGE_AGENT: target.identity,
|
|
4353
|
-
AGENT_BRIDGE_INSTANCE: target.instance,
|
|
4354
|
-
AGENT_BRIDGE_CONFIG: target.backendConfigPath
|
|
4355
|
-
}
|
|
4606
|
+
env: registrationEnvironment(target)
|
|
4356
4607
|
}
|
|
4357
4608
|
};
|
|
4358
4609
|
if (target.runtime === "claude-desktop") {
|
|
@@ -4389,7 +4640,7 @@ function buildPlan(metadata, target, execute, env) {
|
|
|
4389
4640
|
after: registrationProof(exactRegistration2)
|
|
4390
4641
|
});
|
|
4391
4642
|
}
|
|
4392
|
-
if (!isDeepStrictEqual2(metadata.launch, target.launch)) {
|
|
4643
|
+
if (metadata.version !== target.version || !isDeepStrictEqual2(metadata.launch, target.launch) || !isDeepStrictEqual2(metadata.target, target.target)) {
|
|
4393
4644
|
steps.push({
|
|
4394
4645
|
kind: "metadata",
|
|
4395
4646
|
target: "metadata",
|
|
@@ -4592,6 +4843,13 @@ function assertForwardRollbackMetadataState(forward, contract) {
|
|
|
4592
4843
|
throw new Error("managed client no longer matches the forward update metadata state");
|
|
4593
4844
|
}
|
|
4594
4845
|
}
|
|
4846
|
+
function loadForwardRollbackMetadata(runtime2, instance, env) {
|
|
4847
|
+
try {
|
|
4848
|
+
return loadManagedClientMetadata(runtime2, instance, env);
|
|
4849
|
+
} catch {
|
|
4850
|
+
throw new Error("managed client no longer matches the forward update metadata state");
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4595
4853
|
function assertForwardRollbackRegistrationState(forward, contract, execute, env) {
|
|
4596
4854
|
const registration = observeManagedRegistration(forward, execute, env);
|
|
4597
4855
|
if (digest(registrationProof(registration)) !== contract.forwardRegistrationSha256) {
|
|
@@ -4729,7 +4987,7 @@ function switchManagedClientMetadata(before, after, env = process.env) {
|
|
|
4729
4987
|
throw new Error("managed metadata switch was not verified");
|
|
4730
4988
|
}
|
|
4731
4989
|
}
|
|
4732
|
-
function resumeAction(metadata, manifest,
|
|
4990
|
+
function resumeAction(metadata, manifest, options, env, action) {
|
|
4733
4991
|
if (manifest.runtime !== metadata.runtime || manifest.instance !== metadata.instance) {
|
|
4734
4992
|
throw new Error("--resume does not match this client action, runtime, or instance");
|
|
4735
4993
|
}
|
|
@@ -4740,14 +4998,18 @@ function resumeAction(metadata, manifest, command, env, action) {
|
|
|
4740
4998
|
if (manifest.version !== 3 || !manifest.request || manifest.request.kind !== "repair" || !("identity" in manifest.request) || manifest.request.identity !== metadata.identity) {
|
|
4741
4999
|
throw new Error("--resume does not match this client action, runtime, or instance");
|
|
4742
5000
|
}
|
|
4743
|
-
if (command !== void 0
|
|
5001
|
+
if (options.command !== void 0 || options.mcpUrl !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
|
|
5002
|
+
throw new Error("target options are not valid for clients repair");
|
|
5003
|
+
}
|
|
4744
5004
|
return metadata;
|
|
4745
5005
|
}
|
|
4746
5006
|
if (action === "uninstall") {
|
|
4747
5007
|
if (manifest.version !== 3 || !manifest.request || manifest.request.kind !== "uninstall" || !("identity" in manifest.request) || manifest.request.identity !== metadata.identity) {
|
|
4748
5008
|
throw new Error("--resume does not match this client action, runtime, or instance");
|
|
4749
5009
|
}
|
|
4750
|
-
if (command !== void 0
|
|
5010
|
+
if (options.command !== void 0 || options.mcpUrl !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
|
|
5011
|
+
throw new Error("target options are not valid for clients uninstall");
|
|
5012
|
+
}
|
|
4751
5013
|
return metadata;
|
|
4752
5014
|
}
|
|
4753
5015
|
if (!manifest.request || manifest.request.kind !== "update" || !("identity" in manifest.request) || !("launch" in manifest.request) || manifest.request.identity !== metadata.identity) {
|
|
@@ -4761,13 +5023,13 @@ function resumeAction(metadata, manifest, command, env, action) {
|
|
|
4761
5023
|
throw new Error("recorded Desktop update launch is invalid");
|
|
4762
5024
|
}
|
|
4763
5025
|
}
|
|
4764
|
-
if (command !== void 0) {
|
|
4765
|
-
const supplied =
|
|
4766
|
-
if (!isDeepStrictEqual2(supplied, recorded)) {
|
|
4767
|
-
throw new Error("
|
|
5026
|
+
if (options.command !== void 0 || options.mcpUrl !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
|
|
5027
|
+
const supplied = resolveUpdateTarget(metadata, options, env);
|
|
5028
|
+
if (!isDeepStrictEqual2(supplied.launch, recorded)) {
|
|
5029
|
+
throw new Error("target options do not match the recorded update request");
|
|
4768
5030
|
}
|
|
4769
5031
|
}
|
|
4770
|
-
return updateMetadata(metadata, recorded);
|
|
5032
|
+
return updateMetadata(metadata, recorded, targetFromRecordedLaunch(metadata, recorded));
|
|
4771
5033
|
}
|
|
4772
5034
|
function planResult(action, applied, metadata, steps, env, operationId) {
|
|
4773
5035
|
return {
|
|
@@ -4796,7 +5058,10 @@ function maintainManagedClient(options) {
|
|
|
4796
5058
|
const requestedAction = options.action;
|
|
4797
5059
|
if (requestedAction === "uninstall") assertUninstallPathIsolation(metadata, env);
|
|
4798
5060
|
const manifest = options.resume ? readClientOperation(options.resume, env) : void 0;
|
|
4799
|
-
const target = manifest ? resumeAction(metadata, manifest, options
|
|
5061
|
+
const target = manifest ? resumeAction(metadata, manifest, options, env, requestedAction) : requestedAction === "update" ? (() => {
|
|
5062
|
+
const resolved = resolveUpdateTarget(metadata, options, env);
|
|
5063
|
+
return updateMetadata(metadata, resolved.launch, resolved.target);
|
|
5064
|
+
})() : metadata;
|
|
4800
5065
|
const steps = options.resume && options.apply ? [] : requestedAction === "uninstall" ? buildUninstallPlan(metadata, execute, env) : buildPlan(metadata, target, execute, env);
|
|
4801
5066
|
if (!options.apply) {
|
|
4802
5067
|
if (options.resume || options.recoverLock) throw new Error("--resume and --recover-lock require --apply");
|
|
@@ -5012,7 +5277,7 @@ function runManagedClientRollback(options, resumeOperationId) {
|
|
|
5012
5277
|
throw new Error("rollback request no longer matches its committed source");
|
|
5013
5278
|
}
|
|
5014
5279
|
const metadataStepApplied = rollbackMetadataReachedAfterState(reverse, prior, env);
|
|
5015
|
-
const forward = metadataStepApplied ? prior :
|
|
5280
|
+
const forward = metadataStepApplied ? prior : loadForwardRollbackMetadata(source.runtime, source.instance, env);
|
|
5016
5281
|
const registrationHasProgress = rollbackRegistrationHasProgress(reverse);
|
|
5017
5282
|
if (!metadataStepApplied) {
|
|
5018
5283
|
assertForwardRollbackMetadataState(forward, contract);
|
|
@@ -5049,7 +5314,7 @@ function runManagedClientRollback(options, resumeOperationId) {
|
|
|
5049
5314
|
throw new Error("rollback source changed while acquiring the client lock");
|
|
5050
5315
|
}
|
|
5051
5316
|
const metadataApplied = rollbackMetadataReachedAfterState(current, prior, env);
|
|
5052
|
-
const currentForward = metadataApplied ? prior :
|
|
5317
|
+
const currentForward = metadataApplied ? prior : loadForwardRollbackMetadata(source.runtime, source.instance, env);
|
|
5053
5318
|
const currentRegistrationHasProgress = rollbackRegistrationHasProgress(current);
|
|
5054
5319
|
if (!metadataApplied) {
|
|
5055
5320
|
assertForwardRollbackMetadataState(currentForward, contract);
|
|
@@ -8598,6 +8863,9 @@ var SUPPORTED_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
8598
8863
|
"scope",
|
|
8599
8864
|
"backend-config",
|
|
8600
8865
|
"config-path",
|
|
8866
|
+
"mcp-url",
|
|
8867
|
+
"proxy-command",
|
|
8868
|
+
"proxy-args-json",
|
|
8601
8869
|
"recipient",
|
|
8602
8870
|
"retry-base-ms",
|
|
8603
8871
|
"retry-jitter",
|
|
@@ -8770,7 +9038,7 @@ Commands:
|
|
|
8770
9038
|
clients inspect <codex|claude-code|claude-desktop> --identity <name> --instance <key> --backend-config <path>
|
|
8771
9039
|
clients adopt <codex|claude-code|claude-desktop> --identity <name> --instance <key> --backend-config <path> [--apply]
|
|
8772
9040
|
clients repair <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--apply] [--resume <uuid>] [--recover-lock]
|
|
8773
|
-
clients update <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--command <command>] [--apply] [--resume <uuid>] [--recover-lock]
|
|
9041
|
+
clients update <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--command <command> | --mcp-url <loopback-url>] [--proxy-command <command> --proxy-args-json <json-array>] [--apply] [--resume <uuid>] [--recover-lock]
|
|
8774
9042
|
clients uninstall <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--apply] [--resume <uuid>] [--recover-lock]
|
|
8775
9043
|
clients rollback <source-operation-id> --identity <name> [--apply] [--recover-lock]
|
|
8776
9044
|
clients migrate stage <codex|claude-code|claude-desktop> --identity <name> --instance <key> --enrollment-file <path> [--apply] [--recover-lock]
|
|
@@ -9086,12 +9354,15 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
9086
9354
|
"identity",
|
|
9087
9355
|
"instance",
|
|
9088
9356
|
"command",
|
|
9357
|
+
"mcp-url",
|
|
9358
|
+
"proxy-command",
|
|
9359
|
+
"proxy-args-json",
|
|
9089
9360
|
"apply",
|
|
9090
9361
|
"resume",
|
|
9091
9362
|
"recover-lock"
|
|
9092
9363
|
]), `clients ${action}`);
|
|
9093
|
-
if (action !== "update" && options.command !== void 0) {
|
|
9094
|
-
throw new Error(
|
|
9364
|
+
if (action !== "update" && (options.command !== void 0 || options["mcp-url"] !== void 0 || options["proxy-command"] !== void 0 || options["proxy-args-json"] !== void 0)) {
|
|
9365
|
+
throw new Error(`target options are not valid for clients ${action}`);
|
|
9095
9366
|
}
|
|
9096
9367
|
if (!boolean(options, "apply") && (options.resume !== void 0 || options["recover-lock"] !== void 0)) {
|
|
9097
9368
|
throw new Error("--resume and --recover-lock require --apply");
|
|
@@ -9102,6 +9373,16 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
9102
9373
|
instance: one(options, "instance") ?? "",
|
|
9103
9374
|
command: one(options, "command"),
|
|
9104
9375
|
apply: boolean(options, "apply"),
|
|
9376
|
+
mcpUrl: one(options, "mcp-url"),
|
|
9377
|
+
proxyCommand: one(options, "proxy-command"),
|
|
9378
|
+
proxyArgs: (() => {
|
|
9379
|
+
const value = json(options, "proxy-args-json");
|
|
9380
|
+
if (value === void 0) return [];
|
|
9381
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
9382
|
+
throw new Error("--proxy-args-json must be a JSON array of strings");
|
|
9383
|
+
}
|
|
9384
|
+
return value;
|
|
9385
|
+
})(),
|
|
9105
9386
|
resume: one(options, "resume"),
|
|
9106
9387
|
recoverLock: boolean(options, "recover-lock"),
|
|
9107
9388
|
env: process.env
|