@cotal-ai/connector-claude-code 0.3.2 → 0.5.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 +11 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +49 -11
- package/dist/extension.js.map +1 -1
- package/dist/hook.cjs +143 -19
- package/dist/mcp.cjs +1754 -312
- package/dist/mcp.js +20 -8
- package/dist/mcp.js.map +1 -1
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @cotal-ai/connector-claude-code
|
|
2
|
+
|
|
3
|
+
The Claude Code adapter: a bundled, installed plugin plus `claude/channel` push that turns a
|
|
4
|
+
real `claude` session into a Cotal mesh peer. A thin client over
|
|
5
|
+
[`@cotal-ai/connector-core`](../connector-core).
|
|
6
|
+
|
|
7
|
+
**Tier:** `extensions/`. Peer-depends [`@cotal-ai/core`](../../packages/core); self-registers on
|
|
8
|
+
import.
|
|
9
|
+
|
|
10
|
+
See [docs/claude-code-integration.md](../../docs/claude-code-integration.md) for the full
|
|
11
|
+
integration, and the [root AGENTS.md](../../AGENTS.md) for the tier rules.
|
package/dist/extension.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAoB3G;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SA2F7B,CAAC"}
|
package/dist/extension.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
2
4
|
import { fileURLToPath } from "node:url";
|
|
3
5
|
import { loadAgentFile, registry } from "@cotal-ai/core";
|
|
6
|
+
import { launchEnv, mcpServerEnvKeys } from "@cotal-ai/connector-core";
|
|
4
7
|
/** Name the cotal MCP server is registered under via --mcp-config (see buildLaunch). */
|
|
5
8
|
const MCP_SERVER_NAME = "cotal";
|
|
6
9
|
/** Channel ref for `--dangerously-load-development-channels`, which turns on the cotal MCP server's
|
|
@@ -27,16 +30,25 @@ export const claudeConnector = {
|
|
|
27
30
|
name: "claude",
|
|
28
31
|
pluginRoot: PLUGIN_ROOT,
|
|
29
32
|
buildLaunch(opts) {
|
|
33
|
+
// Operator MCP servers shared with this agent (default none — see the --mcp-config block).
|
|
34
|
+
const shared = opts.mcpServers ?? {};
|
|
35
|
+
// claude auths via macOS Keychain / an OAuth token, not an env key → forward NO provider key.
|
|
36
|
+
// The OS allow-list (PATH/HOME/TERM/…) is the only thing inherited from the manager env, plus
|
|
37
|
+
// — only when a shared server declares them via `${VAR}` — the named secrets it needs (mcpKeys,
|
|
38
|
+
// by name). The operator's unrelated secrets don't reach the child (P3).
|
|
30
39
|
const env = {
|
|
40
|
+
...launchEnv({ mcpKeys: mcpServerEnvKeys(shared) }),
|
|
31
41
|
COTAL_SPACE: opts.space,
|
|
32
42
|
COTAL_NAME: opts.name,
|
|
33
43
|
// Force the connector to emit channel wake-nudges: Claude doesn't advertise the
|
|
34
44
|
// `claude/channel` capability back over MCP, so auto-detection would see it "off".
|
|
35
45
|
COTAL_CHANNEL: "1",
|
|
36
|
-
// Managed sessions mirror their own transcript to `tr-<name>` so peers can read
|
|
37
|
-
// what the agent actually did. Personal sessions (no buildLaunch) never mirror.
|
|
38
|
-
COTAL_TRANSCRIPT: "1",
|
|
39
46
|
};
|
|
47
|
+
// A session can mirror its own transcript to `tr-<name>` so peers can read what the
|
|
48
|
+
// agent actually did — OFF by default (transcripts are verbose and may carry sensitive
|
|
49
|
+
// content); `--transcript` (opts.transcript === true) opts in. Personal sessions never mirror.
|
|
50
|
+
if (opts.transcript === true)
|
|
51
|
+
env.COTAL_TRANSCRIPT = "1";
|
|
40
52
|
if (opts.role)
|
|
41
53
|
env.COTAL_ROLE = opts.role;
|
|
42
54
|
if (opts.id)
|
|
@@ -54,13 +66,39 @@ export const claudeConnector = {
|
|
|
54
66
|
// can look something up under `npx` (no repo on disk) without prompting the operator
|
|
55
67
|
// mid-demo. Additive under the default permission mode — leaves other tools as-is.
|
|
56
68
|
args.push("--allowedTools", "WebFetch(domain:github.com),WebFetch(domain:raw.githubusercontent.com)");
|
|
57
|
-
// Isolate the spawned session's MCP
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
69
|
+
// Isolate the spawned session's MCP. --strict-mcp-config drops every ambient MCP source —
|
|
70
|
+
// including the operator's personal ~/.claude.json servers (e.g. a headless Chromium, a DB
|
|
71
|
+
// server) that a meshed teammate never needs and that, multiplied across several spawns on a
|
|
72
|
+
// busy machine, starve memory and kill the session before it registers presence — so the ONLY
|
|
73
|
+
// servers that load are the ones we name in --mcp-config: cotal (always, for its tools +
|
|
74
|
+
// presence) plus any the operator explicitly opted to share (`shared`, from the cotal config).
|
|
75
|
+
// The plugin itself stays enabled (its hooks + the dev-channels wake path are unaffected).
|
|
76
|
+
// cotal is spread LAST so a shared server can never shadow the mesh server by reusing its name.
|
|
77
|
+
const mcpServers = { ...shared, [MCP_SERVER_NAME]: { command: "node", args: [MCP_CJS] } };
|
|
78
|
+
// Default (no shared servers): pass the config inline, unchanged. With shared servers, write it
|
|
79
|
+
// to a file instead and pass the path. Either way the secret stays a `${VAR}` reference (Claude
|
|
80
|
+
// expands it from the child env at launch — see the mcpKeys forwarding above), never the resolved
|
|
81
|
+
// value, so nothing secret reaches disk or argv. We prefer the file when sharing because env
|
|
82
|
+
// expansion is only *documented* for --mcp-config files (inline expansion does work today, but
|
|
83
|
+
// isn't contracted), and a file keeps a potentially multi-server config off the process argv.
|
|
84
|
+
// Verified end-to-end on claude 2.1.183: ${VAR} expands in the --mcp-config file and the value
|
|
85
|
+
// is handed to the shared server. This is host-version behavior — if a future claude stops
|
|
86
|
+
// expanding here, a shared server would receive a literal `${VAR}`; re-check on host upgrades.
|
|
87
|
+
let mcpConfig;
|
|
88
|
+
if (Object.keys(shared).length === 0) {
|
|
89
|
+
mcpConfig = JSON.stringify({ mcpServers });
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// A private 0700 temp dir (unique per spawn) holds the 0600 config. mkdtemp can't be raced
|
|
93
|
+
// by a pre-created or symlinked path the way a predictable name in the world-writable tmpdir
|
|
94
|
+
// could, and a fresh file guarantees the 0600 mode applies on creation (mode is ignored on an
|
|
95
|
+
// overwrite). Left for the OS to reap: the file must outlive this call (Claude reads it at
|
|
96
|
+
// startup and on /mcp reconnect), and buildLaunch doesn't own the child's lifecycle.
|
|
97
|
+
const dir = mkdtempSync(join(tmpdir(), "cotal-mcp-"));
|
|
98
|
+
mcpConfig = join(dir, "mcp.json");
|
|
99
|
+
writeFileSync(mcpConfig, JSON.stringify({ mcpServers }, null, 2), { mode: 0o600 });
|
|
100
|
+
}
|
|
101
|
+
args.push("--strict-mcp-config", "--mcp-config", mcpConfig);
|
|
64
102
|
// An agent file carries identity (read in-session via COTAL_AGENT_FILE) plus
|
|
65
103
|
// persona + model, which can only be applied to a `claude` session at launch.
|
|
66
104
|
if (opts.configPath) {
|
package/dist/extension.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAoD,MAAM,gBAAgB,CAAC;AAC3G,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEvE,wFAAwF;AACxF,MAAM,eAAe,GAAG,OAAO,CAAC;AAChC;;;;;+EAK+E;AAC/E,MAAM,WAAW,GAAG,UAAU,eAAe,EAAE,CAAC;AAEhD;qEACqE;AACrE,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAClE;0DAC0D;AAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,WAAW;IACvB,WAAW,CAAC,IAAgB;QAC1B,2FAA2F;QAC3F,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QACrC,8FAA8F;QAC9F,8FAA8F;QAC9F,gGAAgG;QAChG,yEAAyE;QACzE,MAAM,GAAG,GAA2B;YAClC,GAAG,SAAS,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,WAAW,EAAE,IAAI,CAAC,KAAK;YACvB,UAAU,EAAE,IAAI,CAAC,IAAI;YACrB,gFAAgF;YAChF,mFAAmF;YACnF,aAAa,EAAE,GAAG;SACnB,CAAC;QACF,oFAAoF;QACpF,uFAAuF;QACvF,+FAA+F;QAC/F,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI;YAAE,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC;QACzD,IAAI,IAAI,CAAC,IAAI;YAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1C,IAAI,IAAI,CAAC,EAAE;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7C,IAAI,IAAI,CAAC,OAAO;YAAE,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC;QAEnD,4EAA4E;QAC5E,mEAAmE;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM;YACtB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,yCAAyC,EAAE,WAAW,CAAC;YACvE,CAAC,CAAC,CAAC,yCAAyC,EAAE,WAAW,CAAC,CAAC;QAE7D,kFAAkF;QAClF,qFAAqF;QACrF,mFAAmF;QACnF,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,wEAAwE,CAAC,CAAC;QAEtG,0FAA0F;QAC1F,2FAA2F;QAC3F,6FAA6F;QAC7F,8FAA8F;QAC9F,yFAAyF;QACzF,+FAA+F;QAC/F,2FAA2F;QAC3F,gGAAgG;QAChG,MAAM,UAAU,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAC1F,gGAAgG;QAChG,gGAAgG;QAChG,kGAAkG;QAClG,6FAA6F;QAC7F,+FAA+F;QAC/F,8FAA8F;QAC9F,+FAA+F;QAC/F,2FAA2F;QAC3F,+FAA+F;QAC/F,IAAI,SAAiB,CAAC;QACtB,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,2FAA2F;YAC3F,6FAA6F;YAC7F,8FAA8F;YAC9F,2FAA2F;YAC3F,qFAAqF;YACrF,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;YACtD,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAClC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;QAE5D,6EAA6E;QAC7E,8EAA8E;QAC9E,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACtC,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,GAAG,CAAC,OAAO;gBAAE,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,GAAG,CAAC,KAAK;gBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QAED,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,IAAI;YACJ,GAAG;YACH,wEAAwE;YACxE,yEAAyE;YACzE,OAAO,EAAE,kBAAkB;SAC5B,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC"}
|
package/dist/hook.cjs
CHANGED
|
@@ -3504,16 +3504,16 @@ var require_errors = __commonJS({
|
|
|
3504
3504
|
}
|
|
3505
3505
|
};
|
|
3506
3506
|
exports2.ProtocolError = ProtocolError;
|
|
3507
|
-
var
|
|
3507
|
+
var RequestError2 = class extends Error {
|
|
3508
3508
|
constructor(message = "", options) {
|
|
3509
3509
|
super(message, options);
|
|
3510
3510
|
this.name = "RequestError";
|
|
3511
3511
|
}
|
|
3512
3512
|
isNoResponders() {
|
|
3513
|
-
return this.cause instanceof
|
|
3513
|
+
return this.cause instanceof NoRespondersError2;
|
|
3514
3514
|
}
|
|
3515
3515
|
};
|
|
3516
|
-
exports2.RequestError =
|
|
3516
|
+
exports2.RequestError = RequestError2;
|
|
3517
3517
|
var TimeoutError = class extends Error {
|
|
3518
3518
|
constructor(options) {
|
|
3519
3519
|
super("timeout", options);
|
|
@@ -3521,7 +3521,7 @@ var require_errors = __commonJS({
|
|
|
3521
3521
|
}
|
|
3522
3522
|
};
|
|
3523
3523
|
exports2.TimeoutError = TimeoutError;
|
|
3524
|
-
var
|
|
3524
|
+
var NoRespondersError2 = class extends Error {
|
|
3525
3525
|
subject;
|
|
3526
3526
|
constructor(subject, options) {
|
|
3527
3527
|
super(`no responders: '${subject}'`, options);
|
|
@@ -3529,7 +3529,7 @@ var require_errors = __commonJS({
|
|
|
3529
3529
|
this.name = "NoResponders";
|
|
3530
3530
|
}
|
|
3531
3531
|
};
|
|
3532
|
-
exports2.NoRespondersError =
|
|
3532
|
+
exports2.NoRespondersError = NoRespondersError2;
|
|
3533
3533
|
var PermissionViolationError2 = class _PermissionViolationError extends Error {
|
|
3534
3534
|
operation;
|
|
3535
3535
|
subject;
|
|
@@ -3572,10 +3572,10 @@ var require_errors = __commonJS({
|
|
|
3572
3572
|
InvalidArgumentError,
|
|
3573
3573
|
InvalidOperationError,
|
|
3574
3574
|
InvalidSubjectError,
|
|
3575
|
-
NoRespondersError,
|
|
3575
|
+
NoRespondersError: NoRespondersError2,
|
|
3576
3576
|
PermissionViolationError: PermissionViolationError2,
|
|
3577
3577
|
ProtocolError,
|
|
3578
|
-
RequestError,
|
|
3578
|
+
RequestError: RequestError2,
|
|
3579
3579
|
TimeoutError,
|
|
3580
3580
|
UserAuthenticationExpiredError: UserAuthenticationExpiredError2
|
|
3581
3581
|
};
|
|
@@ -13797,7 +13797,7 @@ var require_kv = __commonJS({
|
|
|
13797
13797
|
throw new Error(`invalid bucket name: ${name}`);
|
|
13798
13798
|
}
|
|
13799
13799
|
}
|
|
13800
|
-
var
|
|
13800
|
+
var Kvm6 = class {
|
|
13801
13801
|
js;
|
|
13802
13802
|
/**
|
|
13803
13803
|
* Creates an instance of the Kv that allows you to create and access KV stores.
|
|
@@ -13863,7 +13863,7 @@ var require_kv = __commonJS({
|
|
|
13863
13863
|
return new internal_2.ListerImpl(subj, filter, this.js);
|
|
13864
13864
|
}
|
|
13865
13865
|
};
|
|
13866
|
-
exports2.Kvm =
|
|
13866
|
+
exports2.Kvm = Kvm6;
|
|
13867
13867
|
var Bucket = class _Bucket {
|
|
13868
13868
|
js;
|
|
13869
13869
|
jsm;
|
|
@@ -14708,6 +14708,56 @@ var require_mod6 = __commonJS({
|
|
|
14708
14708
|
var import_node_os = require("node:os");
|
|
14709
14709
|
var import_node_fs2 = require("node:fs");
|
|
14710
14710
|
|
|
14711
|
+
// ../../packages/core/dist/subjects.js
|
|
14712
|
+
function isConcreteChannel(channel) {
|
|
14713
|
+
return !channel.split(".").some((s) => s.trim() === "*" || s.trim() === ">");
|
|
14714
|
+
}
|
|
14715
|
+
function subjectMatches(pattern, subject) {
|
|
14716
|
+
const p = pattern.split(".");
|
|
14717
|
+
const s = subject.split(".");
|
|
14718
|
+
for (let i = 0; i < p.length; i++) {
|
|
14719
|
+
if (p[i] === ">")
|
|
14720
|
+
return i < s.length;
|
|
14721
|
+
if (i >= s.length)
|
|
14722
|
+
return false;
|
|
14723
|
+
if (p[i] === "*")
|
|
14724
|
+
continue;
|
|
14725
|
+
if (p[i] !== s[i])
|
|
14726
|
+
return false;
|
|
14727
|
+
}
|
|
14728
|
+
return p.length === s.length;
|
|
14729
|
+
}
|
|
14730
|
+
function assertValidChannel(channel) {
|
|
14731
|
+
const segs = channel.split(".");
|
|
14732
|
+
if (!channel.length || segs.some((s) => s.length === 0))
|
|
14733
|
+
throw new Error(`invalid channel "${channel}": empty segment (no leading/trailing/double dots)`);
|
|
14734
|
+
segs.forEach((s, i) => {
|
|
14735
|
+
if (s === ">") {
|
|
14736
|
+
if (i !== segs.length - 1)
|
|
14737
|
+
throw new Error(`invalid channel "${channel}": '>' is only valid as the last segment`);
|
|
14738
|
+
return;
|
|
14739
|
+
}
|
|
14740
|
+
if (s === "*")
|
|
14741
|
+
return;
|
|
14742
|
+
if (!/^[A-Za-z0-9_-]+$/.test(s))
|
|
14743
|
+
throw new Error(`invalid channel "${channel}": segment "${s}" must be a NATS-safe token ([A-Za-z0-9_-]), '*', or '>' \u2014 policy channel names can't contain characters the wire layer would rewrite`);
|
|
14744
|
+
});
|
|
14745
|
+
return channel;
|
|
14746
|
+
}
|
|
14747
|
+
function channelInAllow(allow, channel) {
|
|
14748
|
+
return allow.some((a) => subjectMatches(a, channel));
|
|
14749
|
+
}
|
|
14750
|
+
|
|
14751
|
+
// ../../packages/core/dist/resolve.js
|
|
14752
|
+
function assertValidName(name) {
|
|
14753
|
+
if (name.length === 0 || name !== name.trim())
|
|
14754
|
+
throw new Error(`invalid name ${JSON.stringify(name)}: must be non-empty with no surrounding whitespace`);
|
|
14755
|
+
if (/[\r\n]/.test(name))
|
|
14756
|
+
throw new Error(`invalid name ${JSON.stringify(name)}: must be a single line`);
|
|
14757
|
+
if (name.includes("/"))
|
|
14758
|
+
throw new Error(`invalid name ${JSON.stringify(name)}: "/" is reserved (the owner/name separator)`);
|
|
14759
|
+
}
|
|
14760
|
+
|
|
14711
14761
|
// ../../packages/core/dist/link.js
|
|
14712
14762
|
function parseJoinLink(link) {
|
|
14713
14763
|
const tls = link.startsWith("cotals://");
|
|
@@ -16364,11 +16414,15 @@ var SYS_LIMITS = { ...BASE_LIMITS, mem_storage: 0, disk_storage: 0 };
|
|
|
16364
16414
|
var import_jetstream = __toESM(require_mod4(), 1);
|
|
16365
16415
|
var import_transport_node = __toESM(require_transport_node(), 1);
|
|
16366
16416
|
var import_kv = __toESM(require_mod6(), 1);
|
|
16417
|
+
var PLANE3_DEDUP_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
16367
16418
|
|
|
16368
16419
|
// ../../packages/core/dist/channels.js
|
|
16369
16420
|
var import_kv2 = __toESM(require_mod6(), 1);
|
|
16370
16421
|
var import_transport_node2 = __toESM(require_transport_node(), 1);
|
|
16371
16422
|
|
|
16423
|
+
// ../../packages/core/dist/members.js
|
|
16424
|
+
var import_kv3 = __toESM(require_mod6(), 1);
|
|
16425
|
+
|
|
16372
16426
|
// ../../packages/core/dist/agent-file.js
|
|
16373
16427
|
var import_node_fs = require("node:fs");
|
|
16374
16428
|
function unquote(v) {
|
|
@@ -16419,10 +16473,45 @@ function loadAgentFile(path) {
|
|
|
16419
16473
|
const name = str("name");
|
|
16420
16474
|
if (!name)
|
|
16421
16475
|
throw new Error(`agent file ${path}: "name" is required`);
|
|
16476
|
+
assertValidName(name);
|
|
16422
16477
|
const kind = str("kind");
|
|
16423
16478
|
if (kind && kind !== "agent" && kind !== "endpoint")
|
|
16424
16479
|
throw new Error(`agent file ${path}: "kind" must be "agent" or "endpoint"`);
|
|
16425
|
-
const
|
|
16480
|
+
for (const old of ["channels", "publish"])
|
|
16481
|
+
if (old in fm)
|
|
16482
|
+
throw new Error(`agent file ${path}: "${old}" was renamed \u2014 use "subscribe"/"allowSubscribe" (read) and "allowPublish" (post)`);
|
|
16483
|
+
const subscribe = list("subscribe");
|
|
16484
|
+
const allowSubscribe = list("allowSubscribe");
|
|
16485
|
+
const allowPublish = list("allowPublish");
|
|
16486
|
+
const quiet = list("quiet");
|
|
16487
|
+
const muted = list("muted");
|
|
16488
|
+
for (const ch of [...subscribe ?? [], ...allowSubscribe ?? [], ...allowPublish ?? []])
|
|
16489
|
+
try {
|
|
16490
|
+
assertValidChannel(ch);
|
|
16491
|
+
} catch (e) {
|
|
16492
|
+
throw new Error(`agent file ${path}: ${e.message}`);
|
|
16493
|
+
}
|
|
16494
|
+
const effSubscribe = subscribe?.length ? subscribe : ["general"];
|
|
16495
|
+
const effAllow = allowSubscribe?.length ? allowSubscribe : effSubscribe;
|
|
16496
|
+
for (const ch of effSubscribe)
|
|
16497
|
+
if (!channelInAllow(effAllow, ch))
|
|
16498
|
+
throw new Error(`agent file ${path}: subscribe channel "${ch}" is not within allowSubscribe [${effAllow.join(", ")}]`);
|
|
16499
|
+
const both = (quiet ?? []).filter((c) => (muted ?? []).includes(c));
|
|
16500
|
+
if (both.length)
|
|
16501
|
+
throw new Error(`agent file ${path}: channel(s) [${both.join(", ")}] are in both quiet and muted \u2014 pick one`);
|
|
16502
|
+
for (const [field, chans] of [["quiet", quiet], ["muted", muted]])
|
|
16503
|
+
for (const ch of chans ?? []) {
|
|
16504
|
+
try {
|
|
16505
|
+
assertValidChannel(ch);
|
|
16506
|
+
} catch (e) {
|
|
16507
|
+
throw new Error(`agent file ${path}: ${e.message}`);
|
|
16508
|
+
}
|
|
16509
|
+
if (!isConcreteChannel(ch))
|
|
16510
|
+
throw new Error(`agent file ${path}: ${field} channel "${ch}" must be a concrete channel (no wildcard)`);
|
|
16511
|
+
if (!channelInAllow(effAllow, ch))
|
|
16512
|
+
throw new Error(`agent file ${path}: ${field} channel "${ch}" is not within your read ACL / allowSubscribe [${effAllow.join(", ")}]`);
|
|
16513
|
+
}
|
|
16514
|
+
const known = /* @__PURE__ */ new Set(["name", "role", "kind", "description", "tags", "subscribe", "allowSubscribe", "allowPublish", "quiet", "muted", "model", "capabilities", "owner"]);
|
|
16426
16515
|
const meta = {};
|
|
16427
16516
|
for (const [k, v] of Object.entries(fm))
|
|
16428
16517
|
if (!known.has(k) && typeof v === "string")
|
|
@@ -16433,9 +16522,14 @@ function loadAgentFile(path) {
|
|
|
16433
16522
|
kind,
|
|
16434
16523
|
description: str("description"),
|
|
16435
16524
|
tags: list("tags"),
|
|
16436
|
-
|
|
16437
|
-
|
|
16525
|
+
subscribe,
|
|
16526
|
+
allowSubscribe,
|
|
16527
|
+
allowPublish,
|
|
16528
|
+
quiet,
|
|
16529
|
+
muted,
|
|
16438
16530
|
model: str("model"),
|
|
16531
|
+
capabilities: list("capabilities"),
|
|
16532
|
+
owner: str("owner"),
|
|
16439
16533
|
meta: Object.keys(meta).length ? meta : void 0,
|
|
16440
16534
|
persona: persona || void 0
|
|
16441
16535
|
};
|
|
@@ -16444,13 +16538,13 @@ function loadAgentFile(path) {
|
|
|
16444
16538
|
// ../../packages/core/dist/endpoint.js
|
|
16445
16539
|
var import_transport_node3 = __toESM(require_transport_node(), 1);
|
|
16446
16540
|
var import_jetstream2 = __toESM(require_mod4(), 1);
|
|
16447
|
-
var
|
|
16541
|
+
var import_kv4 = __toESM(require_mod6(), 1);
|
|
16448
16542
|
var DEFAULT_SERVER = "nats://127.0.0.1:4222";
|
|
16449
16543
|
|
|
16450
16544
|
// ../../packages/core/dist/spaces.js
|
|
16451
16545
|
var import_transport_node4 = __toESM(require_transport_node(), 1);
|
|
16452
16546
|
var import_jetstream3 = __toESM(require_mod4(), 1);
|
|
16453
|
-
var
|
|
16547
|
+
var import_kv5 = __toESM(require_mod6(), 1);
|
|
16454
16548
|
|
|
16455
16549
|
// ../../packages/core/dist/registry.js
|
|
16456
16550
|
var Registry = class {
|
|
@@ -16493,9 +16587,31 @@ function configFromEnv(env = process.env) {
|
|
|
16493
16587
|
const name = env.COTAL_NAME?.trim() || def?.name || (link ? (0, import_node_os.userInfo)().username : void 0);
|
|
16494
16588
|
if (!name)
|
|
16495
16589
|
throw new Error("COTAL_NAME, COTAL_AGENT_FILE or COTAL_LINK is required \u2014 a Cotal session needs an explicit identity from its launcher");
|
|
16496
|
-
const
|
|
16497
|
-
const
|
|
16498
|
-
const
|
|
16590
|
+
const subscribe = splitList(env.COTAL_SUBSCRIBE);
|
|
16591
|
+
const resolvedSubscribe = subscribe.length ? subscribe : def?.subscribe ?? link?.channels ?? ["general"];
|
|
16592
|
+
const allowSub = splitList(env.COTAL_ALLOW_SUBSCRIBE);
|
|
16593
|
+
const resolvedAllowSub = allowSub.length ? allowSub : def?.allowSubscribe ?? resolvedSubscribe;
|
|
16594
|
+
for (const ch of resolvedSubscribe)
|
|
16595
|
+
if (!channelInAllow(resolvedAllowSub, ch))
|
|
16596
|
+
throw new Error(`COTAL config: subscribe channel "${ch}" is not within allowSubscribe [${resolvedAllowSub.join(", ")}]`);
|
|
16597
|
+
const allowPub = splitList(env.COTAL_ALLOW_PUBLISH);
|
|
16598
|
+
const resolvedAllowPub = allowPub.length ? allowPub : def?.allowPublish ?? [];
|
|
16599
|
+
for (const ch of [...resolvedSubscribe, ...resolvedAllowSub, ...resolvedAllowPub])
|
|
16600
|
+
assertValidChannel(ch);
|
|
16601
|
+
const qEnv = splitList(env.COTAL_QUIET), mEnv = splitList(env.COTAL_MUTED);
|
|
16602
|
+
const resolvedQuiet = qEnv.length ? qEnv : def?.quiet ?? [];
|
|
16603
|
+
const resolvedMuted = mEnv.length ? mEnv : def?.muted ?? [];
|
|
16604
|
+
const bothModes = resolvedQuiet.filter((c) => resolvedMuted.includes(c));
|
|
16605
|
+
if (bothModes.length)
|
|
16606
|
+
throw new Error(`COTAL config: channel(s) [${bothModes.join(", ")}] are in both quiet and muted`);
|
|
16607
|
+
for (const [field, chans] of [["quiet", resolvedQuiet], ["muted", resolvedMuted]])
|
|
16608
|
+
for (const ch of chans) {
|
|
16609
|
+
assertValidChannel(ch);
|
|
16610
|
+
if (!isConcreteChannel(ch))
|
|
16611
|
+
throw new Error(`COTAL config: ${field} channel "${ch}" must be concrete (no wildcard)`);
|
|
16612
|
+
if (!channelInAllow(resolvedAllowSub, ch))
|
|
16613
|
+
throw new Error(`COTAL config: ${field} channel "${ch}" is not within allowSubscribe [${resolvedAllowSub.join(", ")}]`);
|
|
16614
|
+
}
|
|
16499
16615
|
const credsPath = env.COTAL_CREDS?.trim();
|
|
16500
16616
|
return {
|
|
16501
16617
|
space: env.COTAL_SPACE?.trim() || link?.space || "demo",
|
|
@@ -16505,9 +16621,17 @@ function configFromEnv(env = process.env) {
|
|
|
16505
16621
|
role: env.COTAL_ROLE?.trim() || def?.role || void 0,
|
|
16506
16622
|
description: def?.description,
|
|
16507
16623
|
tags: def?.tags,
|
|
16624
|
+
meta: def?.meta,
|
|
16625
|
+
capabilities: def?.capabilities,
|
|
16626
|
+
model: env.COTAL_MODEL?.trim() || def?.model || void 0,
|
|
16508
16627
|
servers: env.COTAL_SERVERS?.trim() || link?.servers || DEFAULT_SERVER,
|
|
16509
|
-
|
|
16510
|
-
|
|
16628
|
+
subscribe: resolvedSubscribe,
|
|
16629
|
+
allowSubscribe: resolvedAllowSub,
|
|
16630
|
+
// Post ACL is default-DENY: only what's explicitly declared (env > agent-file). The broker
|
|
16631
|
+
// enforces it under auth; in open mode posting is unrestricted regardless (see laneLine).
|
|
16632
|
+
allowPublish: resolvedAllowPub,
|
|
16633
|
+
quiet: resolvedQuiet,
|
|
16634
|
+
muted: resolvedMuted,
|
|
16511
16635
|
kind: env.COTAL_KIND?.trim() || def?.kind || "agent",
|
|
16512
16636
|
token: env.COTAL_TOKEN?.trim() || link?.token,
|
|
16513
16637
|
user: link?.user,
|