@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.
- package/README.md +20 -0
- package/convex/logs.d.ts +3 -3
- package/convex/memory.d.ts +21 -21
- package/convex/schema.d.ts +9 -9
- package/convex/skills.d.ts +3 -3
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/config-cmd.d.ts +12 -19
- package/dist/cli/commands/config-cmd.d.ts.map +1 -1
- package/dist/cli/commands/config-cmd.js +14 -197
- package/dist/cli/commands/config-cmd.js.map +1 -1
- package/dist/cli/commands/connect.d.ts +6 -0
- package/dist/cli/commands/connect.d.ts.map +1 -1
- package/dist/cli/commands/connect.js +7 -0
- package/dist/cli/commands/connect.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +2 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/expose.d.ts.map +1 -1
- package/dist/cli/commands/expose.js +22 -3
- package/dist/cli/commands/expose.js.map +1 -1
- package/dist/cli/commands/gateway.d.ts +12 -0
- package/dist/cli/commands/gateway.d.ts.map +1 -1
- package/dist/cli/commands/gateway.js +114 -2
- package/dist/cli/commands/gateway.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +2 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/program/build-program.d.ts.map +1 -1
- package/dist/cli/program/build-program.js +36 -0
- package/dist/cli/program/build-program.js.map +1 -1
- package/dist/config/io.d.ts +13 -0
- package/dist/config/io.d.ts.map +1 -1
- package/dist/config/io.js.map +1 -1
- package/dist/core/agents-crud-ops.d.ts +15 -0
- package/dist/core/agents-crud-ops.d.ts.map +1 -0
- package/dist/core/agents-crud-ops.js +27 -0
- package/dist/core/agents-crud-ops.js.map +1 -0
- package/dist/core/agents-ops.d.ts +43 -0
- package/dist/core/agents-ops.d.ts.map +1 -0
- package/dist/core/agents-ops.js +117 -0
- package/dist/core/agents-ops.js.map +1 -0
- package/dist/core/channels-ops.d.ts +30 -0
- package/dist/core/channels-ops.d.ts.map +1 -0
- package/dist/core/channels-ops.js +52 -0
- package/dist/core/channels-ops.js.map +1 -0
- package/dist/core/config-ops.d.ts +77 -0
- package/dist/core/config-ops.d.ts.map +1 -0
- package/dist/core/config-ops.js +241 -0
- package/dist/core/config-ops.js.map +1 -0
- package/dist/core/exec-ops.d.ts +48 -0
- package/dist/core/exec-ops.d.ts.map +1 -0
- package/dist/core/exec-ops.js +101 -0
- package/dist/core/exec-ops.js.map +1 -0
- package/dist/core/gateway-auth.d.ts +86 -0
- package/dist/core/gateway-auth.d.ts.map +1 -0
- package/dist/core/gateway-auth.js +156 -0
- package/dist/core/gateway-auth.js.map +1 -0
- package/dist/core/gateway-probe.d.ts +5 -0
- package/dist/core/gateway-probe.d.ts.map +1 -1
- package/dist/core/gateway-probe.js +2 -1
- package/dist/core/gateway-probe.js.map +1 -1
- package/dist/core/gateway-spawn.d.ts.map +1 -1
- package/dist/core/gateway-spawn.js +5 -2
- package/dist/core/gateway-spawn.js.map +1 -1
- package/dist/core/integrations-ops.d.ts +25 -0
- package/dist/core/integrations-ops.d.ts.map +1 -0
- package/dist/core/integrations-ops.js +40 -0
- package/dist/core/integrations-ops.js.map +1 -0
- package/dist/core/memory-ops.d.ts +20 -0
- package/dist/core/memory-ops.d.ts.map +1 -0
- package/dist/core/memory-ops.js +40 -0
- package/dist/core/memory-ops.js.map +1 -0
- package/dist/core/pairing-ops.d.ts +33 -0
- package/dist/core/pairing-ops.d.ts.map +1 -0
- package/dist/core/pairing-ops.js +78 -0
- package/dist/core/pairing-ops.js.map +1 -0
- package/dist/core/provider-ops.d.ts +17 -0
- package/dist/core/provider-ops.d.ts.map +1 -0
- package/dist/core/provider-ops.js +29 -0
- package/dist/core/provider-ops.js.map +1 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +112 -1
- package/dist/core/server.js.map +1 -1
- package/dist/core/sessions-ops.d.ts +25 -0
- package/dist/core/sessions-ops.d.ts.map +1 -0
- package/dist/core/sessions-ops.js +77 -0
- package/dist/core/sessions-ops.js.map +1 -0
- package/dist/core/skills-ops.d.ts +14 -0
- package/dist/core/skills-ops.d.ts.map +1 -0
- package/dist/core/skills-ops.js +28 -0
- package/dist/core/skills-ops.js.map +1 -0
- package/dist/core/tunnel/auth-proxy.d.ts +3 -2
- package/dist/core/tunnel/auth-proxy.d.ts.map +1 -1
- package/dist/core/tunnel/auth-proxy.js +8 -34
- package/dist/core/tunnel/auth-proxy.js.map +1 -1
- package/dist/core/tunnel/manager.d.ts +4 -2
- package/dist/core/tunnel/manager.d.ts.map +1 -1
- package/dist/core/tunnel/manager.js +3 -2
- package/dist/core/tunnel/manager.js.map +1 -1
- package/dist/protocol/methods.d.ts +478 -0
- package/dist/protocol/methods.d.ts.map +1 -1
- package/dist/tui/client.d.ts +8 -0
- package/dist/tui/client.d.ts.map +1 -1
- package/dist/tui/client.js +5 -1
- package/dist/tui/client.js.map +1 -1
- 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;
|
|
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"}
|
package/dist/core/server.js
CHANGED
|
@@ -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
|
-
|
|
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
|