codeam-cli 2.73.9 → 2.74.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 +12 -0
- package/dist/index.js +287 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `codeam-cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.73.10] — 2026-09-03
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Re-resolve the integrations manifest from the backend at session start (#697)
|
|
12
|
+
|
|
13
|
+
## [2.73.9] — 2026-09-03
|
|
14
|
+
|
|
15
|
+
### Tests
|
|
16
|
+
|
|
17
|
+
- **cli:** Move the MCP licence audit to apps/cli — it broke the shared publish (#695)
|
|
18
|
+
|
|
7
19
|
## [2.73.8] — 2026-09-03
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -8136,7 +8136,7 @@ function readAnonId() {
|
|
|
8136
8136
|
}
|
|
8137
8137
|
function superProperties() {
|
|
8138
8138
|
return {
|
|
8139
|
-
cliVersion: true ? "2.
|
|
8139
|
+
cliVersion: true ? "2.74.0" : "0.0.0-dev",
|
|
8140
8140
|
nodeVersion: process.version,
|
|
8141
8141
|
platform: process.platform,
|
|
8142
8142
|
arch: process.arch,
|
|
@@ -8378,7 +8378,7 @@ var http = __toESM(require("http"));
|
|
|
8378
8378
|
// package.json
|
|
8379
8379
|
var package_default = {
|
|
8380
8380
|
name: "codeam-cli",
|
|
8381
|
-
version: "2.
|
|
8381
|
+
version: "2.74.0",
|
|
8382
8382
|
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
8383
8383
|
type: "commonjs",
|
|
8384
8384
|
main: "dist/index.js",
|
|
@@ -9971,7 +9971,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9971
9971
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9972
9972
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9973
9973
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9974
|
-
..."2.
|
|
9974
|
+
..."2.74.0" ? { ideVersion: "2.74.0" } : {}
|
|
9975
9975
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9976
9976
|
}
|
|
9977
9977
|
/**
|
|
@@ -22037,11 +22037,35 @@ function readHouseProxyChildEnv() {
|
|
|
22037
22037
|
return {};
|
|
22038
22038
|
}
|
|
22039
22039
|
}
|
|
22040
|
+
var HOUSE_MODEL_CONTEXT_TOKENS = "512000";
|
|
22040
22041
|
function buildHouseProxyChildEnv(cfg) {
|
|
22041
22042
|
const env = {
|
|
22042
22043
|
ANTHROPIC_BASE_URL: cfg.baseUrl,
|
|
22043
22044
|
ANTHROPIC_AUTH_TOKEN: cfg.token,
|
|
22044
|
-
|
|
22045
|
+
// ⚠️ MAX_CONTEXT_TOKENS, not (only) AUTO_COMPACT_WINDOW — and this is
|
|
22046
|
+
// HOUSE-AGENT ONLY: it lives in this builder precisely so a user's own
|
|
22047
|
+
// Claude Code (a real Anthropic model claude knows) is never touched.
|
|
22048
|
+
//
|
|
22049
|
+
// claude does not know `MiniMax-M3`, so it falls back to its
|
|
22050
|
+
// unknown-model default (200k). `CLAUDE_CODE_AUTO_COMPACT_WINDOW` is then
|
|
22051
|
+
// applied as `Math.min(assumedWindow, env)` — it can only SHRINK the
|
|
22052
|
+
// window, never grow it — so the 512000 we used to set here was inert:
|
|
22053
|
+
// claude kept believing 200k. Meanwhile a session with a dozen linked
|
|
22054
|
+
// integrations carries ~175k tokens of MCP tool schemas on EVERY turn
|
|
22055
|
+
// (measured: 475 tools ≈ 698 KB on a real box), so the context started
|
|
22056
|
+
// ~88% full and a 28-character prompt triggered a compaction, then the
|
|
22057
|
+
// next small tool result triggered another: "Autocompact is thrashing:
|
|
22058
|
+
// the context refilled to the limit within 3 turns", 13 compactions in
|
|
22059
|
+
// one session (rafaelph90.br@gmail.com, 2026-09-03).
|
|
22060
|
+
//
|
|
22061
|
+
// claude's own unknown-model notice names the fix: "set
|
|
22062
|
+
// CLAUDE_CODE_MAX_CONTEXT_TOKENS to its real window" — that variable is
|
|
22063
|
+
// what `wL()` returns for an unrecognised model. MiniMax-M3 is a 1M-context
|
|
22064
|
+
// model with a guaranteed minimum of 512K, so 512000 here is the
|
|
22065
|
+
// conservative truth, not a wish. AUTO_COMPACT_WINDOW stays as the
|
|
22066
|
+
// compaction threshold beneath it.
|
|
22067
|
+
CLAUDE_CODE_MAX_CONTEXT_TOKENS: HOUSE_MODEL_CONTEXT_TOKENS,
|
|
22068
|
+
CLAUDE_CODE_AUTO_COMPACT_WINDOW: HOUSE_MODEL_CONTEXT_TOKENS,
|
|
22045
22069
|
API_TIMEOUT_MS: "3000000"
|
|
22046
22070
|
};
|
|
22047
22071
|
if (cfg.openRouter === true) {
|
|
@@ -22066,6 +22090,7 @@ var HOUSE_PROXY_ENV_KEYS = [
|
|
|
22066
22090
|
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
22067
22091
|
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
22068
22092
|
"CLAUDE_CODE_AUTO_COMPACT_WINDOW",
|
|
22093
|
+
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
|
|
22069
22094
|
"API_TIMEOUT_MS",
|
|
22070
22095
|
"CLAUDE_CONFIG_DIR"
|
|
22071
22096
|
];
|
|
@@ -22247,7 +22272,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22247
22272
|
if (process.env.NODE_ENV === "test") return;
|
|
22248
22273
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22249
22274
|
if (process.env.CI) return;
|
|
22250
|
-
const current2 = true ? "2.
|
|
22275
|
+
const current2 = true ? "2.74.0" : null;
|
|
22251
22276
|
if (!current2) return;
|
|
22252
22277
|
const cache = readCache();
|
|
22253
22278
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22264,7 +22289,7 @@ function checkForUpdates() {
|
|
|
22264
22289
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22265
22290
|
if (process.env.CI) return;
|
|
22266
22291
|
if (!process.stdout.isTTY) return;
|
|
22267
|
-
const current2 = true ? "2.
|
|
22292
|
+
const current2 = true ? "2.74.0" : null;
|
|
22268
22293
|
if (!current2) return;
|
|
22269
22294
|
const cache = readCache();
|
|
22270
22295
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22287,7 +22312,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
22287
22312
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
22288
22313
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
22289
22314
|
function currentCliVersion() {
|
|
22290
|
-
return true ? "2.
|
|
22315
|
+
return true ? "2.74.0" : null;
|
|
22291
22316
|
}
|
|
22292
22317
|
async function installedVersion(deps) {
|
|
22293
22318
|
const ls = await deps.run(
|
|
@@ -23464,20 +23489,7 @@ var HostAgentSupervisor = class {
|
|
|
23464
23489
|
if (payload.houseProxy) {
|
|
23465
23490
|
const { baseUrl, token, agentKind, openRouter } = payload.houseProxy;
|
|
23466
23491
|
childEnv = {
|
|
23467
|
-
|
|
23468
|
-
ANTHROPIC_AUTH_TOKEN: token,
|
|
23469
|
-
// House pins MiniMax; OpenRouter routes the real Claude model names the
|
|
23470
|
-
// agent sends, so it sets NO model overrides — but it DOES need
|
|
23471
|
-
// ANTHROPIC_API_KEY empty so a stale key can't override the Bearer auth
|
|
23472
|
-
// token (OpenRouter's Claude Code setup requires ANTHROPIC_API_KEY="").
|
|
23473
|
-
...openRouter ? { ANTHROPIC_API_KEY: "" } : {
|
|
23474
|
-
ANTHROPIC_MODEL: "MiniMax-M3",
|
|
23475
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: "MiniMax-M3",
|
|
23476
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "MiniMax-M3",
|
|
23477
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: "MiniMax-M3"
|
|
23478
|
-
},
|
|
23479
|
-
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "512000",
|
|
23480
|
-
API_TIMEOUT_MS: "3000000",
|
|
23492
|
+
...buildHouseProxyChildEnv({ baseUrl, token, openRouter: openRouter === true }),
|
|
23481
23493
|
CODEAM_AUTO_TOKEN: payload.autoPairToken
|
|
23482
23494
|
};
|
|
23483
23495
|
const houseConfigDir = path52.join(
|
|
@@ -39580,6 +39592,172 @@ var packStatusH = async (ctx) => {
|
|
|
39580
39592
|
await ctx.relay.sendResult(ctx.cmd.id, "completed", { state });
|
|
39581
39593
|
};
|
|
39582
39594
|
|
|
39595
|
+
// src/integrations/mcp-router.ts
|
|
39596
|
+
var MCP_ROUTER_SERVER_NAME = "codeam";
|
|
39597
|
+
var CALL_TIMEOUT_MS = (() => {
|
|
39598
|
+
const raw = Number(process.env.CODEAM_MCP_TOOL_TIMEOUT_MS);
|
|
39599
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 12e4;
|
|
39600
|
+
})();
|
|
39601
|
+
var CHILD_START_TIMEOUT_MS = 9e4;
|
|
39602
|
+
function qualifiedName(integration, tool) {
|
|
39603
|
+
return `${integration}/${tool}`;
|
|
39604
|
+
}
|
|
39605
|
+
function splitQualifiedName(q2) {
|
|
39606
|
+
const i = q2.indexOf("/");
|
|
39607
|
+
if (i <= 0 || i === q2.length - 1) return null;
|
|
39608
|
+
return { integration: q2.slice(0, i), tool: q2.slice(i + 1) };
|
|
39609
|
+
}
|
|
39610
|
+
function searchTools(tools, query, integration) {
|
|
39611
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
39612
|
+
return tools.filter((t2) => !integration || t2.integration === integration).filter((t2) => {
|
|
39613
|
+
if (words.length === 0) return true;
|
|
39614
|
+
const hay = `${t2.integration} ${t2.name} ${t2.description}`.toLowerCase();
|
|
39615
|
+
return words.every((w3) => hay.includes(w3));
|
|
39616
|
+
}).sort((a, b) => {
|
|
39617
|
+
const an = words.some((w3) => a.name.toLowerCase().includes(w3)) ? 0 : 1;
|
|
39618
|
+
const bn = words.some((w3) => b.name.toLowerCase().includes(w3)) ? 0 : 1;
|
|
39619
|
+
return an - bn || a.integration.localeCompare(b.integration) || a.name.localeCompare(b.name);
|
|
39620
|
+
});
|
|
39621
|
+
}
|
|
39622
|
+
function routerToolDefinitions() {
|
|
39623
|
+
return [
|
|
39624
|
+
{
|
|
39625
|
+
name: "list_integrations",
|
|
39626
|
+
description: "List the integrations attached to this session (Jira, Linear, ClickUp, Postman, \u2026) with how many tools each exposes and whether it is available. Call this first when the user asks what you can do with an external service.",
|
|
39627
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
39628
|
+
},
|
|
39629
|
+
{
|
|
39630
|
+
name: "search_tools",
|
|
39631
|
+
description: 'Search the tools of the attached integrations by keywords (e.g. "create task", "list boards", "send email"). Returns each match as `<integration>/<tool>` with a one-line description. Use `describe_tool` for the full argument schema, then `call_tool` to run it.',
|
|
39632
|
+
inputSchema: {
|
|
39633
|
+
type: "object",
|
|
39634
|
+
properties: {
|
|
39635
|
+
query: { type: "string", description: "Keywords to match against tool names and descriptions." },
|
|
39636
|
+
integration: { type: "string", description: 'Optional: restrict to one integration id, e.g. "clickup".' },
|
|
39637
|
+
limit: { type: "integer", minimum: 1, maximum: 50, default: 20 }
|
|
39638
|
+
},
|
|
39639
|
+
required: ["query"],
|
|
39640
|
+
additionalProperties: false
|
|
39641
|
+
}
|
|
39642
|
+
},
|
|
39643
|
+
{
|
|
39644
|
+
name: "describe_tool",
|
|
39645
|
+
description: "Return the full JSON input schema of one tool, given its `<integration>/<tool>` name from `search_tools`.",
|
|
39646
|
+
inputSchema: {
|
|
39647
|
+
type: "object",
|
|
39648
|
+
properties: { tool: { type: "string", description: '`<integration>/<tool>`, e.g. "clickup/create_task".' } },
|
|
39649
|
+
required: ["tool"],
|
|
39650
|
+
additionalProperties: false
|
|
39651
|
+
}
|
|
39652
|
+
},
|
|
39653
|
+
{
|
|
39654
|
+
name: "call_tool",
|
|
39655
|
+
description: "Invoke one integration tool by its `<integration>/<tool>` name with the arguments its schema expects. The result is returned verbatim from the integration.",
|
|
39656
|
+
inputSchema: {
|
|
39657
|
+
type: "object",
|
|
39658
|
+
properties: {
|
|
39659
|
+
tool: { type: "string", description: "`<integration>/<tool>`." },
|
|
39660
|
+
arguments: { type: "object", description: "Arguments matching the schema from `describe_tool`.", additionalProperties: true }
|
|
39661
|
+
},
|
|
39662
|
+
required: ["tool"],
|
|
39663
|
+
additionalProperties: false
|
|
39664
|
+
}
|
|
39665
|
+
}
|
|
39666
|
+
];
|
|
39667
|
+
}
|
|
39668
|
+
async function startChild(id, env, sdk) {
|
|
39669
|
+
const state = { id, client: null, tools: [], error: null };
|
|
39670
|
+
const timer = new Promise((_2, rej) => {
|
|
39671
|
+
const t2 = setTimeout(
|
|
39672
|
+
() => rej(new Error(`timed out after ${CHILD_START_TIMEOUT_MS / 1e3}s starting`)),
|
|
39673
|
+
CHILD_START_TIMEOUT_MS
|
|
39674
|
+
);
|
|
39675
|
+
t2.unref?.();
|
|
39676
|
+
});
|
|
39677
|
+
try {
|
|
39678
|
+
const transport = new sdk.StdioClientTransport({
|
|
39679
|
+
command: process.execPath,
|
|
39680
|
+
args: [process.argv[1], "mcp-run", id],
|
|
39681
|
+
env: Object.fromEntries(
|
|
39682
|
+
Object.entries(env).filter((e) => typeof e[1] === "string")
|
|
39683
|
+
),
|
|
39684
|
+
stderr: "inherit"
|
|
39685
|
+
});
|
|
39686
|
+
const client3 = new sdk.Client({ name: "codeam-router", version: "1.0.0" }, { capabilities: {} });
|
|
39687
|
+
await Promise.race([client3.connect(transport), timer]);
|
|
39688
|
+
const listed = await Promise.race([client3.listTools(), timer]);
|
|
39689
|
+
state.client = client3;
|
|
39690
|
+
state.tools = listed.tools.map((t2) => ({
|
|
39691
|
+
integration: id,
|
|
39692
|
+
name: t2.name,
|
|
39693
|
+
description: t2.description ?? "",
|
|
39694
|
+
inputSchema: t2.inputSchema
|
|
39695
|
+
}));
|
|
39696
|
+
log.info("mcpRouter", `${id}: ${state.tools.length} tool(s) ready`);
|
|
39697
|
+
} catch (err) {
|
|
39698
|
+
state.error = err instanceof Error ? err.message : String(err);
|
|
39699
|
+
log.warn("mcpRouter", `${id}: unavailable \u2014 ${state.error}`);
|
|
39700
|
+
}
|
|
39701
|
+
return state;
|
|
39702
|
+
}
|
|
39703
|
+
async function mcpRouter() {
|
|
39704
|
+
const manifest = readIntegrationsManifest();
|
|
39705
|
+
const ids = (manifest?.integrations ?? []).filter((e) => e.delivery?.mcp).map((e) => e.id);
|
|
39706
|
+
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
39707
|
+
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
39708
|
+
const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
39709
|
+
const { Client: ClientCtor } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
39710
|
+
const { StdioClientTransport: TransportCtor } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
39711
|
+
const sdk = { Client: ClientCtor, StdioClientTransport: TransportCtor };
|
|
39712
|
+
const children = await Promise.all(ids.map((id) => startChild(id, process.env, sdk)));
|
|
39713
|
+
const byId = new Map(children.map((c2) => [c2.id, c2]));
|
|
39714
|
+
const allTools = children.flatMap((c2) => c2.tools);
|
|
39715
|
+
log.info("mcpRouter", `fronting ${children.filter((c2) => c2.client).length}/${ids.length} integration server(s), ${allTools.length} tools behind 4`);
|
|
39716
|
+
const server = new Server({ name: MCP_ROUTER_SERVER_NAME, version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
39717
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: routerToolDefinitions() }));
|
|
39718
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
39719
|
+
const name = req.params.name;
|
|
39720
|
+
const args2 = req.params.arguments ?? {};
|
|
39721
|
+
const text = (v, isError2 = false) => ({ content: [{ type: "text", text: typeof v === "string" ? v : JSON.stringify(v, null, 2) }], isError: isError2 });
|
|
39722
|
+
if (name === "list_integrations") {
|
|
39723
|
+
return text(children.map((c2) => ({ integration: c2.id, available: c2.client !== null, tools: c2.tools.length, ...c2.error ? { error: c2.error } : {} })));
|
|
39724
|
+
}
|
|
39725
|
+
if (name === "search_tools") {
|
|
39726
|
+
const q2 = typeof args2.query === "string" ? args2.query : "";
|
|
39727
|
+
const integration = typeof args2.integration === "string" ? args2.integration : void 0;
|
|
39728
|
+
const limit = typeof args2.limit === "number" ? Math.max(1, Math.min(50, args2.limit)) : 20;
|
|
39729
|
+
const hits = searchTools(allTools, q2, integration).slice(0, limit);
|
|
39730
|
+
return text(hits.map((t2) => ({ tool: qualifiedName(t2.integration, t2.name), description: t2.description })));
|
|
39731
|
+
}
|
|
39732
|
+
if (name === "describe_tool" || name === "call_tool") {
|
|
39733
|
+
const q2 = typeof args2.tool === "string" ? args2.tool : "";
|
|
39734
|
+
const parts = splitQualifiedName(q2);
|
|
39735
|
+
if (!parts) return text(`Expected "<integration>/<tool>", got "${q2}". Use search_tools to find the name.`, true);
|
|
39736
|
+
const child = byId.get(parts.integration);
|
|
39737
|
+
if (!child) return text(`No integration "${parts.integration}" is attached to this session. Use list_integrations.`, true);
|
|
39738
|
+
if (!child.client) return text(`Integration "${parts.integration}" is unavailable: ${child.error ?? "failed to start"}.`, true);
|
|
39739
|
+
const tool = child.tools.find((t2) => t2.name === parts.tool);
|
|
39740
|
+
if (!tool) return text(`No tool "${parts.tool}" in "${parts.integration}". Use search_tools.`, true);
|
|
39741
|
+
if (name === "describe_tool") return text({ tool: q2, description: tool.description, inputSchema: tool.inputSchema });
|
|
39742
|
+
const timer = new Promise((_2, rej) => setTimeout(() => rej(new Error(`"${q2}" did not answer within ${CALL_TIMEOUT_MS / 1e3}s`)), CALL_TIMEOUT_MS).unref?.());
|
|
39743
|
+
try {
|
|
39744
|
+
const res = await Promise.race([
|
|
39745
|
+
child.client.callTool({ name: parts.tool, arguments: args2.arguments ?? {} }),
|
|
39746
|
+
timer
|
|
39747
|
+
]);
|
|
39748
|
+
return { content: res.content ?? [], isError: res.isError === true };
|
|
39749
|
+
} catch (err) {
|
|
39750
|
+
return text(`${q2} failed: ${err instanceof Error ? err.message : String(err)}`, true);
|
|
39751
|
+
}
|
|
39752
|
+
}
|
|
39753
|
+
return text(`Unknown tool "${name}".`, true);
|
|
39754
|
+
});
|
|
39755
|
+
await server.connect(new StdioServerTransport());
|
|
39756
|
+
process.stdin.on("close", () => {
|
|
39757
|
+
for (const c2 of children) void c2.client?.close().catch(() => void 0);
|
|
39758
|
+
});
|
|
39759
|
+
}
|
|
39760
|
+
|
|
39583
39761
|
// src/integrations/provision.ts
|
|
39584
39762
|
function buildMcpServersForStart(ctx) {
|
|
39585
39763
|
const manifest = readIntegrationsManifest();
|
|
@@ -39608,6 +39786,30 @@ function buildMcpServersForStart(ctx) {
|
|
|
39608
39786
|
env
|
|
39609
39787
|
});
|
|
39610
39788
|
}
|
|
39789
|
+
if (manifest.toolRouter === true && servers.length > 0) {
|
|
39790
|
+
const routed = servers.map((s) => s.name);
|
|
39791
|
+
log.info(
|
|
39792
|
+
"integrations",
|
|
39793
|
+
`tool router ON \u2014 fronting ${routed.length} MCP server(s) behind one: ${routed.join(", ")}`
|
|
39794
|
+
);
|
|
39795
|
+
return [
|
|
39796
|
+
{
|
|
39797
|
+
name: MCP_ROUTER_SERVER_NAME,
|
|
39798
|
+
command: process.execPath,
|
|
39799
|
+
args: [process.argv[1], "mcp-router"],
|
|
39800
|
+
// The router re-spawns each shim with THIS env: the same session /
|
|
39801
|
+
// plugin id / token / secret the shims got when injected directly.
|
|
39802
|
+
// No per-integration id — each child receives its own via
|
|
39803
|
+
// `mcp-run <id>` argv, which the shim prefers.
|
|
39804
|
+
env: [
|
|
39805
|
+
{ name: "CODEAM_MCP_SESSION_ID", value: ctx.sessionId },
|
|
39806
|
+
{ name: "CODEAM_MCP_PLUGIN_ID", value: ctx.pluginId },
|
|
39807
|
+
{ name: "CODEAM_MCP_PLUGIN_TOKEN", value: ctx.pluginAuthToken },
|
|
39808
|
+
...ctx.pollSecret ? [{ name: "CODEAM_MCP_POLL_SECRET", value: ctx.pollSecret }] : []
|
|
39809
|
+
]
|
|
39810
|
+
}
|
|
39811
|
+
];
|
|
39812
|
+
}
|
|
39611
39813
|
if (servers.length) {
|
|
39612
39814
|
log.info(
|
|
39613
39815
|
"integrations",
|
|
@@ -43268,6 +43470,54 @@ ${BEADS_HINT}
|
|
|
43268
43470
|
}
|
|
43269
43471
|
}
|
|
43270
43472
|
|
|
43473
|
+
// src/integrations/refresh-manifest.ts
|
|
43474
|
+
function fingerprint(m) {
|
|
43475
|
+
const norm = [...m.integrations].map((e) => ({ id: e.id, delivery: e.delivery })).sort((a, b) => a.id.localeCompare(b.id));
|
|
43476
|
+
return JSON.stringify(norm);
|
|
43477
|
+
}
|
|
43478
|
+
async function refreshIntegrationsManifest(ctx, fetchImpl = fetch, timeoutMs = 8e3) {
|
|
43479
|
+
const url2 = `${resolveApiBaseUrl()}/api/plugin/integrations/manifest`;
|
|
43480
|
+
const ac = new AbortController();
|
|
43481
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
43482
|
+
try {
|
|
43483
|
+
const resp = await fetchImpl(url2, {
|
|
43484
|
+
method: "POST",
|
|
43485
|
+
headers: { "Content-Type": "application/json", "X-Plugin-Auth-Token": ctx.pluginAuthToken },
|
|
43486
|
+
body: JSON.stringify({
|
|
43487
|
+
sessionId: ctx.sessionId,
|
|
43488
|
+
pluginId: ctx.pluginId,
|
|
43489
|
+
...ctx.agent ? { agent: ctx.agent } : {}
|
|
43490
|
+
}),
|
|
43491
|
+
signal: ac.signal
|
|
43492
|
+
});
|
|
43493
|
+
if (!resp.ok) {
|
|
43494
|
+
return { status: "skipped", reason: `HTTP ${resp.status}` };
|
|
43495
|
+
}
|
|
43496
|
+
const json = await resp.json();
|
|
43497
|
+
const fresh = json.data;
|
|
43498
|
+
if (!json.success || !fresh || !Array.isArray(fresh.integrations)) {
|
|
43499
|
+
return { status: "skipped", reason: "malformed response" };
|
|
43500
|
+
}
|
|
43501
|
+
const onDisk = readIntegrationsManifest();
|
|
43502
|
+
if (onDisk && fingerprint(onDisk) === fingerprint(fresh)) return { status: "unchanged" };
|
|
43503
|
+
persistIntegrationsManifest(fresh);
|
|
43504
|
+
const result = {
|
|
43505
|
+
status: "rewritten",
|
|
43506
|
+
before: onDisk?.integrations.length ?? 0,
|
|
43507
|
+
after: fresh.integrations.length
|
|
43508
|
+
};
|
|
43509
|
+
log.info(
|
|
43510
|
+
"integrations",
|
|
43511
|
+
`manifest refreshed from backend at start (${result.before} \u2192 ${result.after} entr${result.after === 1 ? "y" : "ies"}) \u2014 the on-disk copy was stale`
|
|
43512
|
+
);
|
|
43513
|
+
return result;
|
|
43514
|
+
} catch (err) {
|
|
43515
|
+
return { status: "skipped", reason: err instanceof Error ? err.message : String(err) };
|
|
43516
|
+
} finally {
|
|
43517
|
+
clearTimeout(timer);
|
|
43518
|
+
}
|
|
43519
|
+
}
|
|
43520
|
+
|
|
43271
43521
|
// src/skills/provision.ts
|
|
43272
43522
|
var import_node_os13 = __toESM(require("os"));
|
|
43273
43523
|
function provisionSkillsForStart(home = import_node_os13.default.homedir()) {
|
|
@@ -44328,6 +44578,17 @@ async function start(requestedAgent, presetSession) {
|
|
|
44328
44578
|
beads = started;
|
|
44329
44579
|
return started;
|
|
44330
44580
|
});
|
|
44581
|
+
if (session.pluginAuthToken) {
|
|
44582
|
+
await refreshIntegrationsManifest({
|
|
44583
|
+
sessionId: session.id,
|
|
44584
|
+
pluginId,
|
|
44585
|
+
pluginAuthToken: session.pluginAuthToken,
|
|
44586
|
+
// The house agent runs the `claude` runtime, so `session.agent` cannot
|
|
44587
|
+
// tell it apart; the managed-proxy env can (`isHouseProxyEnv`). The
|
|
44588
|
+
// backend turns the tool router on for the house agent ONLY.
|
|
44589
|
+
agent: isHouseProxyEnv(process.env) ? HOUSE_AGENT_ID : session.agent
|
|
44590
|
+
});
|
|
44591
|
+
}
|
|
44331
44592
|
const mcpServers = mergeWithLocalMcpServers(
|
|
44332
44593
|
buildMcpServersForStart({
|
|
44333
44594
|
sessionId: session.id,
|
|
@@ -47176,7 +47437,7 @@ function checkChokidar() {
|
|
|
47176
47437
|
}
|
|
47177
47438
|
async function doctor(args2 = []) {
|
|
47178
47439
|
const json = args2.includes("--json");
|
|
47179
|
-
const cliVersion = true ? "2.
|
|
47440
|
+
const cliVersion = true ? "2.74.0" : "0.0.0-dev";
|
|
47180
47441
|
const apiBase2 = resolveApiBaseUrl();
|
|
47181
47442
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
47182
47443
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -47647,7 +47908,7 @@ async function mcpWarm(argv = []) {
|
|
|
47647
47908
|
// src/commands/version.ts
|
|
47648
47909
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
47649
47910
|
function version2() {
|
|
47650
|
-
const v = true ? "2.
|
|
47911
|
+
const v = true ? "2.74.0" : "unknown";
|
|
47651
47912
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
47652
47913
|
}
|
|
47653
47914
|
|
|
@@ -47851,6 +48112,9 @@ async function main() {
|
|
|
47851
48112
|
if (command === "mcp-warm") {
|
|
47852
48113
|
return mcpWarm(args);
|
|
47853
48114
|
}
|
|
48115
|
+
if (command === "mcp-router") {
|
|
48116
|
+
return mcpRouter();
|
|
48117
|
+
}
|
|
47854
48118
|
if (command === "deploy") {
|
|
47855
48119
|
if (args[0] === "ls" || args[0] === "list") return deployList();
|
|
47856
48120
|
if (args[0] === "stop" || args[0] === "remove") return deployStop();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.74.0",
|
|
4
4
|
"description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|