@spinabot/brigade 1.12.0 → 1.14.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.
Files changed (106) hide show
  1. package/README.md +20 -0
  2. package/convex/logs.d.ts +3 -3
  3. package/convex/memory.d.ts +21 -21
  4. package/convex/schema.d.ts +9 -9
  5. package/convex/skills.d.ts +3 -3
  6. package/dist/buildstamp.json +1 -1
  7. package/dist/cli/commands/config-cmd.d.ts +12 -19
  8. package/dist/cli/commands/config-cmd.d.ts.map +1 -1
  9. package/dist/cli/commands/config-cmd.js +14 -197
  10. package/dist/cli/commands/config-cmd.js.map +1 -1
  11. package/dist/cli/commands/connect.d.ts +6 -0
  12. package/dist/cli/commands/connect.d.ts.map +1 -1
  13. package/dist/cli/commands/connect.js +7 -0
  14. package/dist/cli/commands/connect.js.map +1 -1
  15. package/dist/cli/commands/doctor.d.ts.map +1 -1
  16. package/dist/cli/commands/doctor.js +2 -1
  17. package/dist/cli/commands/doctor.js.map +1 -1
  18. package/dist/cli/commands/expose.d.ts.map +1 -1
  19. package/dist/cli/commands/expose.js +22 -3
  20. package/dist/cli/commands/expose.js.map +1 -1
  21. package/dist/cli/commands/gateway.d.ts +12 -0
  22. package/dist/cli/commands/gateway.d.ts.map +1 -1
  23. package/dist/cli/commands/gateway.js +114 -2
  24. package/dist/cli/commands/gateway.js.map +1 -1
  25. package/dist/cli/commands/status.d.ts.map +1 -1
  26. package/dist/cli/commands/status.js +2 -1
  27. package/dist/cli/commands/status.js.map +1 -1
  28. package/dist/cli/program/build-program.d.ts.map +1 -1
  29. package/dist/cli/program/build-program.js +36 -0
  30. package/dist/cli/program/build-program.js.map +1 -1
  31. package/dist/config/io.d.ts +13 -0
  32. package/dist/config/io.d.ts.map +1 -1
  33. package/dist/config/io.js.map +1 -1
  34. package/dist/core/agents-crud-ops.d.ts +15 -0
  35. package/dist/core/agents-crud-ops.d.ts.map +1 -0
  36. package/dist/core/agents-crud-ops.js +27 -0
  37. package/dist/core/agents-crud-ops.js.map +1 -0
  38. package/dist/core/agents-ops.d.ts +43 -0
  39. package/dist/core/agents-ops.d.ts.map +1 -0
  40. package/dist/core/agents-ops.js +117 -0
  41. package/dist/core/agents-ops.js.map +1 -0
  42. package/dist/core/channels-ops.d.ts +30 -0
  43. package/dist/core/channels-ops.d.ts.map +1 -0
  44. package/dist/core/channels-ops.js +52 -0
  45. package/dist/core/channels-ops.js.map +1 -0
  46. package/dist/core/config-ops.d.ts +77 -0
  47. package/dist/core/config-ops.d.ts.map +1 -0
  48. package/dist/core/config-ops.js +241 -0
  49. package/dist/core/config-ops.js.map +1 -0
  50. package/dist/core/exec-ops.d.ts +48 -0
  51. package/dist/core/exec-ops.d.ts.map +1 -0
  52. package/dist/core/exec-ops.js +101 -0
  53. package/dist/core/exec-ops.js.map +1 -0
  54. package/dist/core/gateway-auth.d.ts +86 -0
  55. package/dist/core/gateway-auth.d.ts.map +1 -0
  56. package/dist/core/gateway-auth.js +156 -0
  57. package/dist/core/gateway-auth.js.map +1 -0
  58. package/dist/core/gateway-probe.d.ts +5 -0
  59. package/dist/core/gateway-probe.d.ts.map +1 -1
  60. package/dist/core/gateway-probe.js +2 -1
  61. package/dist/core/gateway-probe.js.map +1 -1
  62. package/dist/core/gateway-spawn.d.ts.map +1 -1
  63. package/dist/core/gateway-spawn.js +5 -2
  64. package/dist/core/gateway-spawn.js.map +1 -1
  65. package/dist/core/integrations-ops.d.ts +25 -0
  66. package/dist/core/integrations-ops.d.ts.map +1 -0
  67. package/dist/core/integrations-ops.js +40 -0
  68. package/dist/core/integrations-ops.js.map +1 -0
  69. package/dist/core/memory-ops.d.ts +20 -0
  70. package/dist/core/memory-ops.d.ts.map +1 -0
  71. package/dist/core/memory-ops.js +40 -0
  72. package/dist/core/memory-ops.js.map +1 -0
  73. package/dist/core/pairing-ops.d.ts +33 -0
  74. package/dist/core/pairing-ops.d.ts.map +1 -0
  75. package/dist/core/pairing-ops.js +78 -0
  76. package/dist/core/pairing-ops.js.map +1 -0
  77. package/dist/core/provider-ops.d.ts +17 -0
  78. package/dist/core/provider-ops.d.ts.map +1 -0
  79. package/dist/core/provider-ops.js +29 -0
  80. package/dist/core/provider-ops.js.map +1 -0
  81. package/dist/core/server.d.ts.map +1 -1
  82. package/dist/core/server.js +112 -1
  83. package/dist/core/server.js.map +1 -1
  84. package/dist/core/sessions-ops.d.ts +25 -0
  85. package/dist/core/sessions-ops.d.ts.map +1 -0
  86. package/dist/core/sessions-ops.js +77 -0
  87. package/dist/core/sessions-ops.js.map +1 -0
  88. package/dist/core/skills-ops.d.ts +14 -0
  89. package/dist/core/skills-ops.d.ts.map +1 -0
  90. package/dist/core/skills-ops.js +28 -0
  91. package/dist/core/skills-ops.js.map +1 -0
  92. package/dist/core/tunnel/auth-proxy.d.ts +3 -2
  93. package/dist/core/tunnel/auth-proxy.d.ts.map +1 -1
  94. package/dist/core/tunnel/auth-proxy.js +8 -34
  95. package/dist/core/tunnel/auth-proxy.js.map +1 -1
  96. package/dist/core/tunnel/manager.d.ts +4 -2
  97. package/dist/core/tunnel/manager.d.ts.map +1 -1
  98. package/dist/core/tunnel/manager.js +3 -2
  99. package/dist/core/tunnel/manager.js.map +1 -1
  100. package/dist/protocol/methods.d.ts +478 -0
  101. package/dist/protocol/methods.d.ts.map +1 -1
  102. package/dist/tui/client.d.ts +8 -0
  103. package/dist/tui/client.d.ts.map +1 -1
  104. package/dist/tui/client.js +5 -1
  105. package/dist/tui/client.js.map +1 -1
  106. package/package.json +1 -1
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Memory (Tideline) write + governance behind the `memory.*` gateway RPCs — the
3
+ * write_memory + manage_memory surface, reachable from a remote client.
4
+ *
5
+ * Memory lives in `facts.jsonl` (a store, NOT brigade.json), so `config.set`
6
+ * cannot reach it — these RPCs are the only typed remote path to MUTATE memory.
7
+ * READ is already covered by the `memory-query` (list/search/inspect/stats) and
8
+ * `memory-graph` RPCs.
9
+ *
10
+ * OPERATOR-SCOPED: operates on the OWNER origin over the agent's workspace,
11
+ * exactly like the tools (filesystem AND Convex modes). Reuses the SAME
12
+ * write_memory / manage_memory tool logic — their `execute()` is pure (owner-
13
+ * gating is a session wrapper, not inside execute), so invoking them with an
14
+ * owner scope from the gateway is correct and byte-identical to a turn.
15
+ */
16
+ import { FactStore } from "../agents/memory/records.js";
17
+ import { makeManageMemoryTool } from "../agents/tools/manage-memory-tool.js";
18
+ import { makeWriteMemoryTool } from "../agents/tools/memory-tools.js";
19
+ import { DEFAULT_AGENT_ID, resolveAgentWorkspaceDir } from "../config/paths.js";
20
+ function workspaceFor(agentId) {
21
+ const id = (agentId ?? "").trim() || DEFAULT_AGENT_ID;
22
+ return resolveAgentWorkspaceDir(id);
23
+ }
24
+ /** `memory.write` — persist a durable fact. Params mirror the write_memory tool. */
25
+ export async function handleMemoryWrite(params) {
26
+ const p = (params ?? {});
27
+ const store = new FactStore(workspaceFor(p.agentId));
28
+ const tool = makeWriteMemoryTool(store, { senderIsOwner: true });
29
+ const res = await tool.execute("gateway", params);
30
+ return res.details;
31
+ }
32
+ /** `memory.manage` — dream/purge/inspect/export/retention/vault/retract/restore/relink. */
33
+ export async function handleMemoryManage(params) {
34
+ const p = (params ?? {});
35
+ const agentId = (p.agentId ?? "").trim() || DEFAULT_AGENT_ID;
36
+ const tool = makeManageMemoryTool(workspaceFor(agentId), { agentId });
37
+ const res = await tool.execute("gateway", params);
38
+ return res.details;
39
+ }
40
+ //# sourceMappingURL=memory-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ops.js","sourceRoot":"","sources":["../../src/core/memory-ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,SAAS,YAAY,CAAC,OAAgB;IACrC,MAAM,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC;IACtD,OAAO,wBAAwB,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAe;IACtD,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAyB,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAe,CAAC,CAAC;IAC3D,OAAO,GAAG,CAAC,OAAO,CAAC;AACpB,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAe;IACvD,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAyB,CAAC;IACjD,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC;IAC7D,MAAM,IAAI,GAAG,oBAAoB,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACtE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAe,CAAC,CAAC;IAC3D,OAAO,GAAG,CAAC,OAAO,CAAC;AACpB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Channel pairing operations behind the `pairing.*` gateway RPCs — the
3
+ * `brigade pairing <list|approve|revoke>` surface, reachable from a remote
4
+ * client.
5
+ *
6
+ * OPERATOR-SCOPED channel access control (approve/deny strangers who DM the
7
+ * bot). Per-channel, never a per-session target, so no per-session guard. The
8
+ * RPCs REQUIRE an explicit `channel` (no CLI-style auto-pick) — a client knows
9
+ * the channel from `system.capabilities`. Reuses the SAME access-control
10
+ * primitives the CLI calls, including the owner-bootstrap + in-channel notify
11
+ * on approve.
12
+ */
13
+ import { readPendingPairings } from "../agents/channels/access-control/index.js";
14
+ export type PairingListResult = {
15
+ channel: string;
16
+ pending: ReturnType<typeof readPendingPairings>;
17
+ };
18
+ export declare function handlePairingList(params: unknown): PairingListResult;
19
+ export interface PairingApproveResult {
20
+ ok: boolean;
21
+ channel: string;
22
+ sender?: string;
23
+ owner?: boolean;
24
+ reason?: string;
25
+ }
26
+ export declare function handlePairingApprove(params: unknown): Promise<PairingApproveResult>;
27
+ export interface PairingRevokeResult {
28
+ ok: boolean;
29
+ channel: string;
30
+ reason?: string;
31
+ }
32
+ export declare function handlePairingRevoke(params: unknown): PairingRevokeResult;
33
+ //# sourceMappingURL=pairing-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing-ops.d.ts","sourceRoot":"","sources":["../../src/core/pairing-ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAGN,mBAAmB,EAGnB,MAAM,4CAA4C,CAAC;AAqBpD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;CAChD,CAAC;AACF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,iBAAiB,CAKpE;AAED,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0BzF;AAED,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,mBAAmB,CAQxE"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Channel pairing operations behind the `pairing.*` gateway RPCs — the
3
+ * `brigade pairing <list|approve|revoke>` surface, reachable from a remote
4
+ * client.
5
+ *
6
+ * OPERATOR-SCOPED channel access control (approve/deny strangers who DM the
7
+ * bot). Per-channel, never a per-session target, so no per-session guard. The
8
+ * RPCs REQUIRE an explicit `channel` (no CLI-style auto-pick) — a client knows
9
+ * the channel from `system.capabilities`. Reuses the SAME access-control
10
+ * primitives the CLI calls, including the owner-bootstrap + in-channel notify
11
+ * on approve.
12
+ */
13
+ import { approvePairingCode, readChannelOwner, readPendingPairings, revokePairingCode, setChannelOwner, } from "../agents/channels/access-control/index.js";
14
+ import { BUNDLED_MODULES, loadModules } from "../agents/extensions/index.js";
15
+ import { DEFAULT_AGENT_ID, resolveAgentWorkspaceDir } from "../config/paths.js";
16
+ import { loadConfig } from "./config.js";
17
+ /** Best-effort adapter lookup — only `approve` needs it (owner-bootstrap + notify). */
18
+ async function resolveAdapter(channel) {
19
+ try {
20
+ const config = loadConfig();
21
+ const workspaceDir = resolveAgentWorkspaceDir(DEFAULT_AGENT_ID);
22
+ const registry = await loadModules({
23
+ modules: BUNDLED_MODULES,
24
+ meta: { agentId: DEFAULT_AGENT_ID, workspaceDir, cwd: workspaceDir, config: config },
25
+ });
26
+ return registry.channels.find((c) => c.id === channel);
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ export function handlePairingList(params) {
33
+ const p = (params ?? {});
34
+ const channel = (p.channel ?? "").trim();
35
+ if (!channel)
36
+ throw new Error("pairing.list: missing 'channel'");
37
+ return { channel, pending: readPendingPairings(channel) };
38
+ }
39
+ export async function handlePairingApprove(params) {
40
+ const p = (params ?? {});
41
+ const channel = (p.channel ?? "").trim();
42
+ const code = (p.code ?? "").trim();
43
+ if (!channel || !code)
44
+ return { ok: false, channel, reason: "missing 'channel' or 'code'" };
45
+ const approved = approvePairingCode(channel, code);
46
+ if (!approved)
47
+ return { ok: false, channel, reason: "unknown or expired pairing code" };
48
+ // Owner bootstrap (bot-separate channels like Telegram): the first approved
49
+ // sender becomes the recorded owner so they can run admin commands. Reaching
50
+ // this RPC already proves operator access. Never overwrites an existing owner.
51
+ let becameOwner = false;
52
+ const adapter = await resolveAdapter(channel);
53
+ if (adapter?.pairing?.botIsSeparateFromOperator && !readChannelOwner(channel)) {
54
+ becameOwner = setChannelOwner(channel, approved.senderId);
55
+ }
56
+ // Best-effort in-channel "you're approved" reply when the adapter wires it.
57
+ const notify = adapter?.pairing?.notifyApproval;
58
+ if (notify) {
59
+ try {
60
+ await notify({ senderId: approved.senderId, senderName: approved.senderName });
61
+ }
62
+ catch {
63
+ /* non-fatal — the approval already landed in the allow-list */
64
+ }
65
+ }
66
+ return { ok: true, channel, sender: approved.senderId, owner: becameOwner };
67
+ }
68
+ export function handlePairingRevoke(params) {
69
+ const p = (params ?? {});
70
+ const channel = (p.channel ?? "").trim();
71
+ const code = (p.code ?? "").trim();
72
+ if (!channel || !code)
73
+ return { ok: false, channel, reason: "missing 'channel' or 'code'" };
74
+ return revokePairingCode(channel, code)
75
+ ? { ok: true, channel }
76
+ : { ok: false, channel, reason: "no matching pending code" };
77
+ }
78
+ //# sourceMappingURL=pairing-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing-ops.js","sourceRoot":"","sources":["../../src/core/pairing-ops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Provider-key removal behind the `provider.remove` gateway RPC.
3
+ *
4
+ * The genuine gap: `add-provider` adds a key, but there was NO way to REMOVE one
5
+ * over the gateway — keys live in `auth-profiles.json` (not config, so config.set
6
+ * can't reach them) and no tool exposes removal. This closes it. Operator-scoped
7
+ * per-agent (no per-session guard — allowlisted).
8
+ */
9
+ export interface ProviderRemoveResult {
10
+ ok: boolean;
11
+ providerId: string;
12
+ agentId: string;
13
+ removed: number;
14
+ reason?: string;
15
+ }
16
+ export declare function handleProviderRemove(params: unknown): ProviderRemoveResult;
17
+ //# sourceMappingURL=provider-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-ops.d.ts","sourceRoot":"","sources":["../../src/core/provider-ops.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,oBAAoB,CAe1E"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Provider-key removal behind the `provider.remove` gateway RPC.
3
+ *
4
+ * The genuine gap: `add-provider` adds a key, but there was NO way to REMOVE one
5
+ * over the gateway — keys live in `auth-profiles.json` (not config, so config.set
6
+ * can't reach them) and no tool exposes removal. This closes it. Operator-scoped
7
+ * per-agent (no per-session guard — allowlisted).
8
+ */
9
+ import { readProfiles, writeProfiles } from "../auth/profiles.js";
10
+ import { DEFAULT_AGENT_ID } from "../config/paths.js";
11
+ export function handleProviderRemove(params) {
12
+ const p = (params ?? {});
13
+ const providerId = (p.providerId ?? "").trim().toLowerCase();
14
+ const agentId = (p.agentId ?? "").trim() || DEFAULT_AGENT_ID;
15
+ if (!providerId)
16
+ return { ok: false, providerId, agentId, removed: 0, reason: "missing 'providerId'" };
17
+ const file = readProfiles(agentId);
18
+ let removed = 0;
19
+ for (const [key, profile] of Object.entries(file.profiles)) {
20
+ if ((profile.provider ?? "").trim().toLowerCase() === providerId) {
21
+ delete file.profiles[key];
22
+ removed++;
23
+ }
24
+ }
25
+ if (removed > 0)
26
+ writeProfiles(agentId, file);
27
+ return { ok: removed > 0, providerId, agentId, removed, ...(removed === 0 ? { reason: "no key found for that provider" } : {}) };
28
+ }
29
+ //# sourceMappingURL=provider-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-ops.js","sourceRoot":"","sources":["../../src/core/provider-ops.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAStD,MAAM,UAAU,oBAAoB,CAAC,MAAe;IACnD,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAA8C,CAAC;IACtE,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7D,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC;IAC7D,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACvG,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,UAAU,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC1B,OAAO,EAAE,CAAC;QACX,CAAC;IACF,CAAC;IACD,IAAI,OAAO,GAAG,CAAC;QAAE,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9C,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,gCAAgC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAClI,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA0QH,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUzD,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AA8FD,wBAAsB,WAAW,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAwKjF"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAySH,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUzD,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AA8FD,wBAAsB,WAAW,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAwKjF"}
@@ -134,6 +134,18 @@ import { getBrigadeWorkspaceDir, loadConfig, saveConfig } from "./config.js";
134
134
  import { mutateConfigAtomic } from "../config/io.js";
135
135
  import { acquireGatewayLock } from "./gateway-lock.js";
136
136
  import { clearHeartbeatFile, clearPidFile, writeHeartbeatFile, writePidFile } from "./gateway-probe.js";
137
+ import { extractToken, matchesAnyToken, resolveGatewayAuth } from "./gateway-auth.js";
138
+ import { handleConfigGet, handleConfigList, handleConfigSchema, handleConfigSet, handleConfigUnset, handleConfigValidate, } from "./config-ops.js";
139
+ import { handleExecAllow, handleExecAllowPattern, handleExecDenyTest, handleExecList, handleExecRemove, } from "./exec-ops.js";
140
+ import { handleAgentsBind, handleAgentsBindings, handleAgentsUnbind } from "./agents-ops.js";
141
+ import { handlePairingApprove, handlePairingList, handlePairingRevoke } from "./pairing-ops.js";
142
+ import { handleSessionsCleanup } from "./sessions-ops.js";
143
+ import { handleMemoryManage, handleMemoryWrite } from "./memory-ops.js";
144
+ import { handleAgentsAdd, handleAgentsDelete, handleAgentsSetIdentity } from "./agents-crud-ops.js";
145
+ import { handleSkillsCreate, handleSkillsDelete, handleSkillsWriteFile } from "./skills-ops.js";
146
+ import { handleChannelsAllowAdd, handleChannelsAllowList, handleChannelsAllowRemove, handleChannelsConnect, handleChannelsDisconnect, } from "./channels-ops.js";
147
+ import { handleProviderRemove } from "./provider-ops.js";
148
+ import { handleComposio, handleOauth } from "./integrations-ops.js";
137
149
  // Persist a model selection to brigade.json's new wizard-shape (the lifted
138
150
  // code expected the older flat `defaultProvider`/`defaultModelId` fields).
139
151
  // Writes through the same `agents.defaults.{provider, model.primary}` path
@@ -1441,7 +1453,26 @@ async function continueBoot(args) {
1441
1453
  // value the handshake's `HelloOk.policy.maxBufferedBytes` field advertises;
1442
1454
  // at 2× the payload cap a client this far behind is a stuck/slow consumer.
1443
1455
  const MAX_WS_BUFFERED_BYTES = 64 * 1024 * 1024; // 64 MiB
1444
- const wss = new WebSocketServer({ server: httpServer, maxPayload: MAX_WS_PAYLOAD_BYTES });
1456
+ // Optional, opt-in gateway authentication (see core/gateway-auth.ts).
1457
+ // DEFAULT — no tokens configured — resolves to `required:false`, so the
1458
+ // gateway stays unauthenticated + localhost-only exactly as before; this
1459
+ // feature NEVER changes behaviour until the operator sets
1460
+ // `gateway.auth.tokens` (or the BRIGADE_GATEWAY_TOKENS env var). When tokens
1461
+ // ARE present we install a `verifyClient` gate that rejects the WS upgrade
1462
+ // with 401 unless a valid token rides in via `Authorization: Bearer`,
1463
+ // `x-brigade-token`, or `?token=`. We gate ONLY the WS control surface (every
1464
+ // connection is granted operator scope) — NOT the HTTP routes, which carry
1465
+ // inbound channel webhooks that must stay reachable. Resolved once at boot;
1466
+ // token changes take effect on the next gateway start.
1467
+ const gatewayAuth = resolveGatewayAuth(loadConfig().gateway?.auth, process.env);
1468
+ const wssOptions = { server: httpServer, maxPayload: MAX_WS_PAYLOAD_BYTES };
1469
+ if (gatewayAuth.required) {
1470
+ wssOptions.verifyClient = (info) => matchesAnyToken(gatewayAuth.tokens, extractToken(info.req.url, info.req.headers));
1471
+ }
1472
+ const wss = new WebSocketServer(wssOptions);
1473
+ if (gatewayAuth.required) {
1474
+ bootLog(`authentication enabled — clients must present a valid token (${gatewayAuth.tokens.length} configured)`);
1475
+ }
1445
1476
  // `WebSocketServer` re-emits errors from the underlying httpServer (and
1446
1477
  // can emit its own — bad upgrade frame, etc). With NO 'error' listener on
1447
1478
  // wss, Node's EventEmitter throws the error, crashing the process with an
@@ -4073,6 +4104,86 @@ async function continueBoot(args) {
4073
4104
  disposeHandlers.push(registerGatewayHandler("org.snapshot", (_params) => handleOrgSnapshot(undefined, {
4074
4105
  loadConfig: () => loadConfig(),
4075
4106
  })));
4107
+ // `config.*` — operator-level config CRUD over the wire (the `brigade
4108
+ // config` CLI, reachable from a remote client). Path/value/redact shape:
4109
+ // never session-targeted, so the guard-sweep correctly needs no per-session
4110
+ // access check. Reads/writes go through the mode-aware loadConfig/saveConfig,
4111
+ // so this works in filesystem AND Convex mode.
4112
+ disposeHandlers.push(registerGatewayHandler("config.get", handleConfigGet));
4113
+ disposeHandlers.push(registerGatewayHandler("config.set", handleConfigSet));
4114
+ disposeHandlers.push(registerGatewayHandler("config.unset", handleConfigUnset));
4115
+ disposeHandlers.push(registerGatewayHandler("config.list", handleConfigList));
4116
+ disposeHandlers.push(registerGatewayHandler("config.schema", handleConfigSchema));
4117
+ disposeHandlers.push(registerGatewayHandler("config.validate", handleConfigValidate));
4118
+ // `exec.*` — operator-level exec-approval allowlist CRUD (the `brigade exec`
4119
+ // CLI over the wire). Per-agent + operator-scoped (the operator manages
4120
+ // their OWN agents' bash-approval allowlist), the same posture as the
4121
+ // allowlisted exec-allow-all / exec-grant-skill RPCs — no per-session guard
4122
+ // (see ALLOWLIST_NO_GUARD_NEEDED in server.guard-sweep.test.ts). The
4123
+ // hard-deny safety net in exec-approvals.ts still applies on every allow.
4124
+ disposeHandlers.push(registerGatewayHandler("exec.list", handleExecList));
4125
+ disposeHandlers.push(registerGatewayHandler("exec.allow", handleExecAllow));
4126
+ disposeHandlers.push(registerGatewayHandler("exec.allow-pattern", handleExecAllowPattern));
4127
+ disposeHandlers.push(registerGatewayHandler("exec.remove", handleExecRemove));
4128
+ disposeHandlers.push(registerGatewayHandler("exec.deny-test", handleExecDenyTest));
4129
+ // `agents.*` — operator-level routing-binding management (which agent owns
4130
+ // which channel/account). The genuine no-other-path gap: agent add/delete/
4131
+ // set-identity are already reachable via the `manage_agent` tool, but
4132
+ // bindings had no remote path. Operator-scoped config mutation, no per-
4133
+ // session guard (allowlisted in server.guard-sweep.test.ts).
4134
+ disposeHandlers.push(registerGatewayHandler("agents.bindings", handleAgentsBindings));
4135
+ disposeHandlers.push(registerGatewayHandler("agents.bind", handleAgentsBind));
4136
+ disposeHandlers.push(registerGatewayHandler("agents.unbind", handleAgentsUnbind));
4137
+ // `pairing.*` — operator-level channel pairing (approve/revoke strangers who
4138
+ // DM the bot). Per-channel + operator-scoped, no per-session guard. The RPCs
4139
+ // require an explicit channel (a client gets the channel list from
4140
+ // system.capabilities), unlike the CLI's single-channel auto-pick.
4141
+ disposeHandlers.push(registerGatewayHandler("pairing.list", handlePairingList));
4142
+ disposeHandlers.push(registerGatewayHandler("pairing.approve", handlePairingApprove));
4143
+ disposeHandlers.push(registerGatewayHandler("pairing.revoke", handlePairingRevoke));
4144
+ // `sessions.cleanup` — operator maintenance: delete an agent's stale idle
4145
+ // transcript files (the gateway regenerates the store entry on next access).
4146
+ // NOT session-content access (unlike sessions.list/history), so no per-
4147
+ // session guard (allowlisted in server.guard-sweep.test.ts).
4148
+ disposeHandlers.push(registerGatewayHandler("sessions.cleanup", handleSessionsCleanup));
4149
+ // `memory.*` — Tideline write + governance (write_memory / manage_memory).
4150
+ // Memory lives in facts.jsonl (NOT config), so config.set can't reach it;
4151
+ // these are the only typed remote path to MUTATE memory (read is covered by
4152
+ // memory-query / memory-graph). Operator-scoped owner origin, no per-session
4153
+ // guard (allowlisted in server.guard-sweep.test.ts).
4154
+ disposeHandlers.push(registerGatewayHandler("memory.write", handleMemoryWrite));
4155
+ disposeHandlers.push(registerGatewayHandler("memory.manage", handleMemoryManage));
4156
+ // agents.add/delete/set-identity — agent CRUD (reuses the manage_agent tool,
4157
+ // which wraps `brigade agents add/delete/set-identity`). Seeds/soft-deletes a
4158
+ // workspace, so config.set alone can't do it. Operator-scoped (allowlisted).
4159
+ disposeHandlers.push(registerGatewayHandler("agents.add", handleAgentsAdd));
4160
+ disposeHandlers.push(registerGatewayHandler("agents.delete", handleAgentsDelete));
4161
+ disposeHandlers.push(registerGatewayHandler("agents.set-identity", handleAgentsSetIdentity));
4162
+ // skills.create/delete/write-file — skill authoring (reuses the manage_skill
4163
+ // tool). SKILL.md files on disk, not config. (status/install/update already
4164
+ // cover read/install/enable.) Operator-scoped (allowlisted).
4165
+ disposeHandlers.push(registerGatewayHandler("skills.create", handleSkillsCreate));
4166
+ disposeHandlers.push(registerGatewayHandler("skills.delete", handleSkillsDelete));
4167
+ disposeHandlers.push(registerGatewayHandler("skills.write-file", handleSkillsWriteFile));
4168
+ // channels.* — LIVE connect/disconnect (runtime adapter via the global
4169
+ // channel manager) + DM allow-from (a per-channel file store, not config).
4170
+ // Channel enable/disable/policy are already config.set-reachable. Operator-
4171
+ // scoped (allowlisted). connect reuses the owner-scoped connect_channel tool.
4172
+ disposeHandlers.push(registerGatewayHandler("channels.connect", handleChannelsConnect));
4173
+ disposeHandlers.push(registerGatewayHandler("channels.disconnect", handleChannelsDisconnect));
4174
+ disposeHandlers.push(registerGatewayHandler("channels.allow-add", handleChannelsAllowAdd));
4175
+ disposeHandlers.push(registerGatewayHandler("channels.allow-remove", handleChannelsAllowRemove));
4176
+ disposeHandlers.push(registerGatewayHandler("channels.allow-list", handleChannelsAllowList));
4177
+ // provider.remove — delete a provider key (auth-profiles.json, not config;
4178
+ // add-provider exists, removal had no gateway path). Operator-scoped.
4179
+ disposeHandlers.push(registerGatewayHandler("provider.remove", handleProviderRemove));
4180
+ // composio + oauth — integrations. `composio` is remote-clean (Composio
4181
+ // hosts the OAuth callback; the gateway hands over a click-link + polls).
4182
+ // `oauth` is the DIY loopback flow (callback on the gateway host — completes
4183
+ // only for a local/tunneled operator; status/token work remotely). Both
4184
+ // reuse the owner-scoped tools. Operator-scoped (allowlisted).
4185
+ disposeHandlers.push(registerGatewayHandler("composio", handleComposio));
4186
+ disposeHandlers.push(registerGatewayHandler("oauth", handleOauth));
4076
4187
  // Wave O0.8 GAP 11 — opt the session inbox into JSONL persistence at
4077
4188
  // gateway boot. The disk write surface defaults off so the existing
4078
4189
  // unit-test fleet (which doesn't tempdir-isolate ~/.brigade) keeps