agent-relay-server 0.120.3 → 0.120.5

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.
Files changed (88) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +3 -3
  3. package/public/assets/{MessageBubble-Cqi3XsZ7.js → MessageBubble-BVff6PSi.js} +2 -2
  4. package/public/assets/{MessageBubble-Cqi3XsZ7.js.map → MessageBubble-BVff6PSi.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-D6Um05oW.js → PlanGraphPanel-BjrVU5Tg.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-D6Um05oW.js.map → PlanGraphPanel-BjrVU5Tg.js.map} +1 -1
  7. package/public/assets/{activity-B0LsQHEz.js → activity-Bk9LEUr4.js} +2 -2
  8. package/public/assets/{activity-B0LsQHEz.js.map → activity-Bk9LEUr4.js.map} +1 -1
  9. package/public/assets/{agent-profiles-O--pe5Ic.js → agent-profiles-DSVDoQ_N.js} +2 -2
  10. package/public/assets/{agent-profiles-O--pe5Ic.js.map → agent-profiles-DSVDoQ_N.js.map} +1 -1
  11. package/public/assets/{agents-Ii6XlVwR.js → agents-8rQuJqii.js} +2 -2
  12. package/public/assets/{agents-Ii6XlVwR.js.map → agents-8rQuJqii.js.map} +1 -1
  13. package/public/assets/{analytics-BCzdYWRT.js → analytics-DifGnk7q.js} +2 -2
  14. package/public/assets/{analytics-BCzdYWRT.js.map → analytics-DifGnk7q.js.map} +1 -1
  15. package/public/assets/{automation-Crr3Xzui.js → automation-CMxQAf3l.js} +2 -2
  16. package/public/assets/{automation-Crr3Xzui.js.map → automation-CMxQAf3l.js.map} +1 -1
  17. package/public/assets/{branch-state-badge-Bhw2rloy.js → branch-state-badge-CyC5Qrhc.js} +2 -2
  18. package/public/assets/{branch-state-badge-Bhw2rloy.js.map → branch-state-badge-CyC5Qrhc.js.map} +1 -1
  19. package/public/assets/{channels-CKs9rvsn.js → channels-BLkdt2XR.js} +2 -2
  20. package/public/assets/{channels-CKs9rvsn.js.map → channels-BLkdt2XR.js.map} +1 -1
  21. package/public/assets/{chat-BCwEUj5R.js → chat-jlXywb9G.js} +2 -2
  22. package/public/assets/{chat-BCwEUj5R.js.map → chat-jlXywb9G.js.map} +1 -1
  23. package/public/assets/{connectors-D_kQR_vw.js → connectors-C0n12Je8.js} +2 -2
  24. package/public/assets/{connectors-D_kQR_vw.js.map → connectors-C0n12Je8.js.map} +1 -1
  25. package/public/assets/{formatted-body-q_qzt1L-.js → formatted-body-CzehtPtx.js} +3 -3
  26. package/public/assets/{formatted-body-q_qzt1L-.js.map → formatted-body-CzehtPtx.js.map} +1 -1
  27. package/public/assets/{formatted-body-impl-CSRGuMcM.js → formatted-body-impl-D3Bnt0Fj.js} +2 -2
  28. package/public/assets/{formatted-body-impl-CSRGuMcM.js.map → formatted-body-impl-D3Bnt0Fj.js.map} +1 -1
  29. package/public/assets/{index-Ceiwd02X.js → index-4g3PbGVN.js} +6 -6
  30. package/public/assets/{index-Ceiwd02X.js.map → index-4g3PbGVN.js.map} +1 -1
  31. package/public/assets/{insights-DUhXqZTZ.js → insights-3H6KttBi.js} +2 -2
  32. package/public/assets/{insights-DUhXqZTZ.js.map → insights-3H6KttBi.js.map} +1 -1
  33. package/public/assets/{integrations-lbg5CjtE.js → integrations-v14ehskT.js} +2 -2
  34. package/public/assets/{integrations-lbg5CjtE.js.map → integrations-v14ehskT.js.map} +1 -1
  35. package/public/assets/{maintenance-CQgneSVf.js → maintenance-BANYcBcn.js} +2 -2
  36. package/public/assets/{maintenance-CQgneSVf.js.map → maintenance-BANYcBcn.js.map} +1 -1
  37. package/public/assets/{managed-agents-DsnYf6fm.js → managed-agents-DCFMWKqt.js} +2 -2
  38. package/public/assets/{managed-agents-DsnYf6fm.js.map → managed-agents-DCFMWKqt.js.map} +1 -1
  39. package/public/assets/{markdown-preview-impl-C1ZtGJp6.js → markdown-preview-impl-CfaR-GRX.js} +2 -2
  40. package/public/assets/{markdown-preview-impl-C1ZtGJp6.js.map → markdown-preview-impl-CfaR-GRX.js.map} +1 -1
  41. package/public/assets/{memory-SSLUTOI2.js → memory-BmkUEyd5.js} +2 -2
  42. package/public/assets/{memory-SSLUTOI2.js.map → memory-BmkUEyd5.js.map} +1 -1
  43. package/public/assets/{messages-BY26OP4K.js → messages-BQ_fVnSA.js} +2 -2
  44. package/public/assets/{messages-BY26OP4K.js.map → messages-BQ_fVnSA.js.map} +1 -1
  45. package/public/assets/{orchestrators-OY7yPzVI.js → orchestrators-DC7y9vQD.js} +2 -2
  46. package/public/assets/{orchestrators-OY7yPzVI.js.map → orchestrators-DC7y9vQD.js.map} +1 -1
  47. package/public/assets/{overview-Dj0u1tPG.js → overview-6lzywFHq.js} +2 -2
  48. package/public/assets/{overview-Dj0u1tPG.js.map → overview-6lzywFHq.js.map} +1 -1
  49. package/public/assets/{pairs-CrUR50ou.js → pairs-CRrRIaHI.js} +2 -2
  50. package/public/assets/{pairs-CrUR50ou.js.map → pairs-CRrRIaHI.js.map} +1 -1
  51. package/public/assets/{projects-B_AtxHtH.js → projects-CZJW35C7.js} +2 -2
  52. package/public/assets/{projects-B_AtxHtH.js.map → projects-CZJW35C7.js.map} +1 -1
  53. package/public/assets/{prompt-settings-CPtYSban.js → prompt-settings-DvlnMq8H.js} +2 -2
  54. package/public/assets/{prompt-settings-CPtYSban.js.map → prompt-settings-DvlnMq8H.js.map} +1 -1
  55. package/public/assets/{registry-BgUALWn5.js → registry-3TWg4zbE.js} +2 -2
  56. package/public/assets/{registry-BgUALWn5.js.map → registry-3TWg4zbE.js.map} +1 -1
  57. package/public/assets/{security-5lHfGk7g.js → security-A2VYKHeI.js} +2 -2
  58. package/public/assets/{security-5lHfGk7g.js.map → security-A2VYKHeI.js.map} +1 -1
  59. package/public/assets/{select-Cgx4QSLj.js → select-_gK5Ojjc.js} +2 -2
  60. package/public/assets/{select-Cgx4QSLj.js.map → select-_gK5Ojjc.js.map} +1 -1
  61. package/public/assets/{settings-DFGgxyun.js → settings-BZ26ewcu.js} +2 -2
  62. package/public/assets/{settings-DFGgxyun.js.map → settings-BZ26ewcu.js.map} +1 -1
  63. package/public/assets/store-nXGxgz2Q.js +9 -0
  64. package/public/assets/store-nXGxgz2Q.js.map +1 -0
  65. package/public/assets/{tasks-BvH5rluu.js → tasks-DlokSl_P.js} +2 -2
  66. package/public/assets/{tasks-BvH5rluu.js.map → tasks-DlokSl_P.js.map} +1 -1
  67. package/public/assets/{teams-VZOIOdoE.js → teams-t7M1HMgS.js} +2 -2
  68. package/public/assets/{teams-VZOIOdoE.js.map → teams-t7M1HMgS.js.map} +1 -1
  69. package/public/assets/{terminal-viewer-impl-DYY3Wmoe.js → terminal-viewer-impl-DxgrO-jH.js} +2 -2
  70. package/public/assets/{terminal-viewer-impl-DYY3Wmoe.js.map → terminal-viewer-impl-DxgrO-jH.js.map} +1 -1
  71. package/public/assets/types-uVOXgvMT.js.map +1 -1
  72. package/public/assets/{work-queue-B6Xk7QNe.js → work-queue-V8LR-3Gi.js} +2 -2
  73. package/public/assets/{work-queue-B6Xk7QNe.js.map → work-queue-V8LR-3Gi.js.map} +1 -1
  74. package/public/assets/{workspaces-CPIzAHAe.js → workspaces-DI317fMk.js} +2 -2
  75. package/public/assets/{workspaces-CPIzAHAe.js.map → workspaces-DI317fMk.js.map} +1 -1
  76. package/public/index.html +3 -3
  77. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  78. package/src/auto-spawn-dispatch.ts +2 -2
  79. package/src/db/index.ts +2 -0
  80. package/src/db/migrations.ts +2 -1
  81. package/src/db/provisioning-callmux.ts +180 -0
  82. package/src/db/provisioning-registry.ts +45 -374
  83. package/src/db/provisioning-resolve.ts +245 -0
  84. package/src/routes/workspaces.ts +58 -22
  85. package/src/task-dispatcher.ts +12 -3
  86. package/src/workspace-orphans.ts +5 -1
  87. package/public/assets/store-BM4zM89B.js +0 -9
  88. package/public/assets/store-BM4zM89B.js.map +0 -1
@@ -0,0 +1,180 @@
1
+ // Shared-callmux registry seeding (#849/#871) — split out of provisioning-registry.ts
2
+ // under the file-size ratchet (epic #291). Reads the host callmux source config,
3
+ // scrubs secrets into env placeholders (literal non-secret values survive only via
4
+ // the per-server configEnv allowlist), and seeds/updates the relay-owned registry
5
+ // rows for the shared callmux MCP servers. The validation invariants the scrub must
6
+ // satisfy (assertConfigEnv / assertCallmuxServerPlaceholders + the allowlist) stay
7
+ // in provisioning-registry.ts beside the definition validator that enforces them.
8
+
9
+ import { existsSync, readFileSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { errMessage, isRecord } from "agent-relay-sdk";
13
+ import type { ProvisioningAsset, ProvisioningMcpServer } from "agent-relay-sdk";
14
+ import { getDb, ValidationError } from "./connection.ts";
15
+ import { parseJson } from "../utils";
16
+ import {
17
+ isEnvPlaceholder,
18
+ SHARED_CALLMUX_CONFIG_ENV_ALLOWLIST,
19
+ upsertProvisioningAsset,
20
+ } from "./provisioning-registry.ts";
21
+ import { stringRecord } from "./provisioning-resolve.ts";
22
+
23
+ const HOST_CALLMUX_SOURCE_TYPE = "host-callmux";
24
+ export const SHARED_CALLMUX_SEED_SERVER_NAMES = ["tokenlean", "github", "vent", "reasoning", "scribe", "searxng", "qmd"] as const;
25
+ export const SHARED_CALLMUX_SOURCE_CONFIG_ENV = "AGENT_RELAY_SHARED_CALLMUX_SOURCE_CONFIG";
26
+ // Servers seeded from the host callmux config but NOT eligible for the shared listener (deno not provisioned, etc.).
27
+ const SHARED_CALLMUX_SEED_NON_LISTENER_NAMES: ReadonlySet<string> = new Set(["scribe"]);
28
+
29
+ export interface SharedCallmuxSeedResult {
30
+ sourceConfigPath: string;
31
+ seeded: string[];
32
+ skipped: Array<{ name: string; reason: string }>;
33
+ }
34
+
35
+ export function sharedCallmuxSourceConfigPath(env: Record<string, string | undefined> = process.env): string {
36
+ return env[SHARED_CALLMUX_SOURCE_CONFIG_ENV] || env.CALLMUX_CONFIG || join(homedir(), ".config", "callmux", "config.json");
37
+ }
38
+
39
+ function cloneJsonRecord(value: Record<string, unknown>): Record<string, unknown> {
40
+ return JSON.parse(JSON.stringify(value)) as Record<string, unknown>;
41
+ }
42
+
43
+ function scrubEnvPlaceholders(serverName: string, value: unknown): { env: Record<string, string> | undefined; configEnv: Record<string, string> | undefined } {
44
+ if (!isRecord(value)) return { env: undefined, configEnv: undefined };
45
+ const env: Record<string, string> = {};
46
+ const configEnv: Record<string, string> = {};
47
+ for (const [key, raw] of Object.entries(value)) {
48
+ if (typeof raw !== "string") continue;
49
+ if (isEnvPlaceholder(raw)) {
50
+ env[key] = raw;
51
+ } else if (SHARED_CALLMUX_CONFIG_ENV_ALLOWLIST[serverName]?.includes(key)) {
52
+ configEnv[key] = raw;
53
+ } else {
54
+ env[key] = `$${key}`;
55
+ }
56
+ }
57
+ return {
58
+ env: Object.keys(env).length ? env : undefined,
59
+ configEnv: Object.keys(configEnv).length ? configEnv : undefined,
60
+ };
61
+ }
62
+
63
+ function envPlaceholderForLiteral(value: string, env: Record<string, string | undefined>): string | undefined {
64
+ for (const [key, candidate] of Object.entries(env)) {
65
+ if (!candidate || candidate !== value || !/^[A-Z_][A-Z0-9_]*$/.test(key)) continue;
66
+ return `$${key}`;
67
+ }
68
+ return undefined;
69
+ }
70
+
71
+ function envPlaceholderName(value: string): string | undefined {
72
+ const match = value.match(/^\$?\{?([A-Z_][A-Z0-9_]*)\}?$/);
73
+ return match?.[1];
74
+ }
75
+
76
+ function scrubHeaderPlaceholders(value: unknown, env: Record<string, string | undefined>): Record<string, string> | undefined {
77
+ if (!isRecord(value)) return undefined;
78
+ const headers: Record<string, string> = {};
79
+ for (const [key, raw] of Object.entries(value)) {
80
+ if (typeof raw !== "string") continue;
81
+ if (!/authorization|token|secret|key|api/i.test(key) || isEnvPlaceholder(raw) || /^Bearer \$\{[A-Z_][A-Z0-9_]*\}$/.test(raw)) {
82
+ headers[key] = raw;
83
+ continue;
84
+ }
85
+ const bearer = raw.match(/^Bearer\s+(.+)$/i);
86
+ const bearerValue = bearer?.[1];
87
+ const bearerPlaceholderName = bearerValue ? envPlaceholderName(bearerValue) : undefined;
88
+ if (bearerPlaceholderName) {
89
+ headers[key] = `Bearer \${${bearerPlaceholderName}}`;
90
+ continue;
91
+ }
92
+ const placeholder = envPlaceholderForLiteral(bearerValue ?? raw, env);
93
+ if (placeholder) headers[key] = bearer ? `Bearer ${placeholder.startsWith("${") ? placeholder : `\${${placeholder.slice(1)}}`}` : placeholder;
94
+ }
95
+ return Object.keys(headers).length ? headers : undefined;
96
+ }
97
+
98
+ function scrubCallmuxServerSecrets(serverName: string, server: Record<string, unknown>, env: Record<string, string | undefined>): Record<string, unknown> {
99
+ const scrubbed = cloneJsonRecord(server);
100
+ const { env: scrubbedEnv, configEnv } = scrubEnvPlaceholders(serverName, scrubbed.env);
101
+ const scrubbedHeaders = scrubHeaderPlaceholders(scrubbed.headers, env);
102
+ if (scrubbedEnv !== undefined) scrubbed.env = scrubbedEnv;
103
+ else delete scrubbed.env;
104
+ if (configEnv !== undefined) scrubbed.configEnv = configEnv;
105
+ else delete scrubbed.configEnv;
106
+ if (scrubbedHeaders) scrubbed.headers = scrubbedHeaders;
107
+ else delete scrubbed.headers;
108
+ return scrubbed;
109
+ }
110
+
111
+ function mcpServerFromCallmuxServer(server: Record<string, unknown>): ProvisioningMcpServer {
112
+ const projected: ProvisioningMcpServer = {};
113
+ if (typeof server.command === "string") {
114
+ projected.type = "stdio";
115
+ projected.command = server.command;
116
+ if (Array.isArray(server.args)) projected.args = server.args.filter((arg): arg is string => typeof arg === "string");
117
+ if (isRecord(server.env)) projected.env = stringRecord(server.env);
118
+ if (isRecord(server.configEnv)) projected.configEnv = stringRecord(server.configEnv);
119
+ } else if (typeof server.url === "string") {
120
+ projected.type = server.transport === "sse" ? "sse" : "http";
121
+ projected.url = server.url;
122
+ if (isRecord(server.headers)) projected.headers = stringRecord(server.headers);
123
+ if (isRecord(server.configEnv)) projected.configEnv = stringRecord(server.configEnv);
124
+ }
125
+ return projected;
126
+ }
127
+
128
+ function updateSharedCallmuxAssetMetadata(asset: ProvisioningAsset, sourceConfigPath: string, callmuxServer: Record<string, unknown>, sharedListenerEligible = true): void {
129
+ if (!asset.capabilityId) throw new ValidationError("shared callmux seed missing capability id");
130
+ const now = Date.now();
131
+ const provenance = {
132
+ importedBy: "agent-relay-shared-callmux-seed",
133
+ sharedListenerEligible,
134
+ callmux: { sourceConfigPath, serverName: asset.name },
135
+ };
136
+ getDb().query(`
137
+ UPDATE provisioning_assets
138
+ SET source_type = ?, source_ref = ?, provenance = ?, updated_at = ?
139
+ WHERE id = ?
140
+ `).run(HOST_CALLMUX_SOURCE_TYPE, sourceConfigPath, JSON.stringify(provenance), now, asset.id);
141
+ getDb().query("UPDATE provisioning_capabilities SET title = ?, description = ?, updated_at = ? WHERE id = ?")
142
+ .run(asset.name, `Host callmux MCP server ${asset.name}`, now, asset.capabilityId);
143
+ }
144
+
145
+ export function seedSharedCallmuxProvisioningAssets(
146
+ input: { sourceConfigPath?: string; env?: Record<string, string | undefined> } = {},
147
+ ): SharedCallmuxSeedResult {
148
+ const sourceConfigPath = input.sourceConfigPath ?? sharedCallmuxSourceConfigPath(input.env);
149
+ const result: SharedCallmuxSeedResult = { sourceConfigPath, seeded: [], skipped: [] };
150
+ if (!existsSync(sourceConfigPath)) return result;
151
+ const source = parseJson(readFileSync(sourceConfigPath, "utf8"), {}) as unknown;
152
+ const sourceServers = isRecord(source) && isRecord(source.servers) ? source.servers : {};
153
+ for (const name of SHARED_CALLMUX_SEED_SERVER_NAMES) {
154
+ const rawServer = sourceServers[name];
155
+ if (!isRecord(rawServer)) {
156
+ result.skipped.push({ name, reason: "missing" });
157
+ continue;
158
+ }
159
+ const callmuxServer = scrubCallmuxServerSecrets(name, rawServer, input.env ?? process.env);
160
+ const server = mcpServerFromCallmuxServer(callmuxServer);
161
+ try {
162
+ const sharedListenerEligible = !SHARED_CALLMUX_SEED_NON_LISTENER_NAMES.has(name);
163
+ const asset = upsertProvisioningAsset({
164
+ kind: "mcp",
165
+ name,
166
+ definition: {
167
+ kind: "mcp",
168
+ name,
169
+ server,
170
+ metadata: { sharedListenerEligible, callmuxServer },
171
+ },
172
+ });
173
+ updateSharedCallmuxAssetMetadata(asset, sourceConfigPath, callmuxServer, sharedListenerEligible);
174
+ result.seeded.push(name);
175
+ } catch (err) {
176
+ result.skipped.push({ name, reason: errMessage(err) });
177
+ }
178
+ }
179
+ return result;
180
+ }