@luckydraw/cumulus 0.31.66 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -559
- package/LICENSE +150 -0
- package/README.md +27 -8
- package/dist/gateway/adapters/webchat.d.ts +2 -0
- package/dist/gateway/adapters/webchat.d.ts.map +1 -1
- package/dist/gateway/adapters/webchat.js +22 -2
- package/dist/gateway/adapters/webchat.js.map +1 -1
- package/dist/gateway/config.d.ts +17 -2
- package/dist/gateway/config.d.ts.map +1 -1
- package/dist/gateway/config.js +10 -3
- package/dist/gateway/config.js.map +1 -1
- package/dist/gateway/daemon.d.ts +3 -1
- package/dist/gateway/daemon.d.ts.map +1 -1
- package/dist/gateway/daemon.js +128 -39
- package/dist/gateway/daemon.js.map +1 -1
- package/dist/gateway/namespaces.d.ts +34 -0
- package/dist/gateway/namespaces.d.ts.map +1 -1
- package/dist/gateway/namespaces.js +58 -0
- package/dist/gateway/namespaces.js.map +1 -1
- package/dist/gateway/server.d.ts +8 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +150 -41
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/setup.d.ts +32 -0
- package/dist/gateway/setup.d.ts.map +1 -1
- package/dist/gateway/setup.js +23 -3
- package/dist/gateway/setup.js.map +1 -1
- package/dist/gateway/static/widget.js +897 -611
- package/dist/lib/gateway.d.ts +30 -8
- package/dist/lib/gateway.d.ts.map +1 -1
- package/dist/lib/gateway.js +36 -11
- package/dist/lib/gateway.js.map +1 -1
- package/dist/lib/history.d.ts +22 -0
- package/dist/lib/history.d.ts.map +1 -1
- package/dist/lib/history.js +59 -21
- package/dist/lib/history.js.map +1 -1
- package/dist/lib/huggingface-provider.d.ts.map +1 -1
- package/dist/lib/huggingface-provider.js +11 -3
- package/dist/lib/huggingface-provider.js.map +1 -1
- package/dist/lib/license.d.ts +76 -0
- package/dist/lib/license.d.ts.map +1 -0
- package/dist/lib/license.js +141 -0
- package/dist/lib/license.js.map +1 -0
- package/docs/agentic-harness-primer.md +283 -0
- package/docs/conditional-continuation.md +167 -0
- package/docs/web-app-agent-guide.md +520 -0
- package/examples/web-app-agent/README.md +187 -0
- package/examples/web-app-agent/agent/mcp-shim.js +105 -0
- package/examples/web-app-agent/gateway.config.example.json +52 -0
- package/examples/web-app-agent/package.json +13 -0
- package/examples/web-app-agent/public/agent/bridge-mount.js +75 -0
- package/examples/web-app-agent/public/agent/chat-client.js +104 -0
- package/examples/web-app-agent/public/agent/commands.js +250 -0
- package/examples/web-app-agent/public/agent/device-thread.js +48 -0
- package/examples/web-app-agent/public/agent/panel.css +107 -0
- package/examples/web-app-agent/public/agent/panel.js +369 -0
- package/examples/web-app-agent/public/app.js +250 -0
- package/examples/web-app-agent/public/index.html +111 -0
- package/examples/web-app-agent/server.js +242 -0
- package/package.json +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"namespaces.d.ts","sourceRoot":"","sources":["../../src/gateway/namespaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"namespaces.d.ts","sourceRoot":"","sources":["../../src/gateway/namespaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,eAAe,EAAE,GAC5B,MAAM,GAAG,SAAS,CAWpB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,MAAM,GAAG,SAAS,CAM9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAGT;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAGT;AAED,yFAAyF;AACzF,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,mBAAmB,EAAE,MAAM,EAAE,EAC7B,UAAU,EAAE,eAAe,EAAE,GAC5B,MAAM,CAER;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,eAAe,EAAE,EAC7B,mBAAmB,EAAE,MAAM,EAAE,EAC7B,MAAM,EAAE,aAAa,EACrB,GAAG,GAAE,MAAgC,GACpC,aAAa,GAAG,SAAS,CAS3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,eAAe,EAAE,EAC7B,mBAAmB,EAAE,MAAM,EAAE,EAC7B,GAAG,GAAE,MAAgC,GACpC,MAAM,EAAE,CAkBV;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAExE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,eAAe,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,SAAS,CAI5C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,eAAe,EAAE,GAC5B;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAchC"}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* everything but lists only non-namespaced threads (P2 keeps the owner's list
|
|
23
23
|
* clean).
|
|
24
24
|
*/
|
|
25
|
+
import { DEMO_VISITOR_THREAD_CAP, LICENSE_CONTACT, describeLicenseFailure, namespaceLicensed, } from '../lib/license.js';
|
|
25
26
|
/**
|
|
26
27
|
* Does `threadName` belong to namespace `ns`? True only for `${ns}-` prefixed
|
|
27
28
|
* children — so `pursuit` covers `pursuit-abc` but NOT bare `pursuit`, and not
|
|
@@ -101,6 +102,63 @@ export function threadAccessibleToScope(threadName, scopeNs, namespaces) {
|
|
|
101
102
|
return true;
|
|
102
103
|
return namespaceForThread(threadName, namespaces) === scopeNs;
|
|
103
104
|
}
|
|
105
|
+
/** Existing threads belonging to `ns` (longest-match, so `x-v-1` counts under `x-v`). */
|
|
106
|
+
export function countNamespaceThreads(ns, existingThreadNames, namespaces) {
|
|
107
|
+
return existingThreadNames.filter(t => namespaceForThread(t, namespaces) === ns).length;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Demo-mode capacity check (task 127). Returns a denial when a request would
|
|
111
|
+
* MINT a new thread in an unlicensed namespace that is already at the cap, or
|
|
112
|
+
* undefined when the request may proceed.
|
|
113
|
+
*
|
|
114
|
+
* Three deliberate exemptions, in the order they are checked:
|
|
115
|
+
* 1. **Non-namespaced threads are never gated.** Personal threads, the CLI, the
|
|
116
|
+
* TUI — the gate exists only where the sold capability lives.
|
|
117
|
+
* 2. **Threads that already exist always keep working.** The cap gates minting,
|
|
118
|
+
* never reads or replies, so an evaluator who reaches it loses no data.
|
|
119
|
+
* 3. **Licensed namespaces are uncapped.**
|
|
120
|
+
*
|
|
121
|
+
* Pure: the caller supplies the thread inventory, so this is testable without a
|
|
122
|
+
* filesystem and callable from both the REST and WS gates.
|
|
123
|
+
*/
|
|
124
|
+
export function demoMintDenial(threadName, namespaces, existingThreadNames, status, cap = DEMO_VISITOR_THREAD_CAP) {
|
|
125
|
+
const ns = namespaceForThread(threadName, namespaces);
|
|
126
|
+
if (!ns)
|
|
127
|
+
return undefined;
|
|
128
|
+
if (existingThreadNames.includes(threadName))
|
|
129
|
+
return undefined;
|
|
130
|
+
if (namespaceLicensed(status, ns))
|
|
131
|
+
return undefined;
|
|
132
|
+
const count = countNamespaceThreads(ns, existingThreadNames, namespaces);
|
|
133
|
+
if (count < cap)
|
|
134
|
+
return undefined;
|
|
135
|
+
return { namespace: ns, count, cap };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The startup / periodic licence banner (task 127), as lines. Pure so the
|
|
139
|
+
* wording is asserted in tests rather than eyeballed in a log.
|
|
140
|
+
*
|
|
141
|
+
* A licensed gateway gets exactly one confirmation line and nothing further. An
|
|
142
|
+
* unlicensed one gets one line per configured namespace naming its occupancy,
|
|
143
|
+
* plus the reason and the contact address — so an operator who is about to hit
|
|
144
|
+
* the cap sees it coming rather than discovering it as a 402.
|
|
145
|
+
*/
|
|
146
|
+
export function licenseBannerLines(status, namespaces, existingThreadNames, cap = DEMO_VISITOR_THREAD_CAP) {
|
|
147
|
+
if (status.licensed) {
|
|
148
|
+
const scope = status.namespaces === null ? 'all namespaces' : status.namespaces.join(', ');
|
|
149
|
+
return [`[License] Licensed to ${status.licensee} (major ${status.major}, ${scope}).`];
|
|
150
|
+
}
|
|
151
|
+
const lines = [
|
|
152
|
+
`[License] UNLICENSED (${describeLicenseFailure(status.reason)}) — running in demo mode.`,
|
|
153
|
+
];
|
|
154
|
+
for (const ns of namespaces) {
|
|
155
|
+
const count = countNamespaceThreads(ns.name, existingThreadNames, namespaces);
|
|
156
|
+
lines.push(`[License] namespace "${ns.name}": ${count}/${cap} visitor threads` +
|
|
157
|
+
(count >= cap ? ' — AT CAP, new threads are refused (402).' : '.'));
|
|
158
|
+
}
|
|
159
|
+
lines.push(`[License] Commercial and production use requires a licence: ${LICENSE_CONTACT}`);
|
|
160
|
+
return lines;
|
|
161
|
+
}
|
|
104
162
|
/**
|
|
105
163
|
* Every API key scoped to any namespace, for folding into the gateway's set of
|
|
106
164
|
* valid authentication keys (a scoped key must still authenticate).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"namespaces.js","sourceRoot":"","sources":["../../src/gateway/namespaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"namespaces.js","sourceRoot":"","sources":["../../src/gateway/namespaces.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway/server.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { type GatewayAgentsConfig } from '../lib/gateway.js';
|
|
18
18
|
import type { HooksConfig, NamespaceConfig, AppliedConfig } from './config.js';
|
|
19
|
+
import { type DemoCapDenial } from './namespaces.js';
|
|
19
20
|
import { type VapidConfig } from './push.js';
|
|
20
21
|
export interface GatewayServerOptions {
|
|
21
22
|
basePath?: string;
|
|
@@ -56,6 +57,9 @@ export interface GatewayServerOptions {
|
|
|
56
57
|
bridge?: import('./config.js').BridgeConfig;
|
|
57
58
|
/** Thread namespaces — prefix-scoped list visibility for app/test threads (task 097 P2). */
|
|
58
59
|
namespaces?: NamespaceConfig[];
|
|
60
|
+
/** Lucky Draw licence key (task 127). Absent = demo mode: namespaces capped at
|
|
61
|
+
* DEMO_VISITOR_THREAD_CAP visitor threads. Verified offline at startup. */
|
|
62
|
+
licenseKey?: string;
|
|
59
63
|
}
|
|
60
64
|
export interface GatewayServerHandle {
|
|
61
65
|
port: number;
|
|
@@ -99,6 +103,10 @@ export declare function isThreadBusy(threadName: string): boolean;
|
|
|
99
103
|
* net to swallow these without crashing while still surfacing real faults.
|
|
100
104
|
*/
|
|
101
105
|
export declare function isBenignSocketError(err: unknown): boolean;
|
|
106
|
+
/** Thread names that currently exist on disk (one `.jsonl` per thread). */
|
|
107
|
+
export declare function listExistingThreadNames(): string[];
|
|
108
|
+
/** The 402 body for a demo-cap refusal. Exported so tests assert one string. */
|
|
109
|
+
export declare function demoCapMessage(d: DemoCapDenial): string;
|
|
102
110
|
export interface AgentQueuedMessage {
|
|
103
111
|
text: string;
|
|
104
112
|
sender: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/gateway/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/gateway/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAC;AAuB3B,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAsB,aAAa,EAAE,MAAM,aAAa,CAAC;AAQnG,OAAO,EAUL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAML,KAAK,WAAW,EACjB,MAAM,WAAW,CAAC;AAKnB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,8FAA8F;IAC9F,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACjF,8CAA8C;IAC9C,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,qCAAqC;IACrC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5C,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,aAAa,EAAE,gBAAgB,EAAE,CAAC;IACxD,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;uCAGmC;IACnC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACnD,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wGAAwG;IACxG,MAAM,CAAC,EAAE,OAAO,aAAa,EAAE,YAAY,CAAC;IAC5C,4FAA4F;IAC5F,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B;+EAC2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,iFAAiF;IACjF,MAAM,EAAE,OAAO,MAAM,EAAE,MAAM,CAAC;IAC9B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,wDAAwD;IACxD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5C,sFAAsF;IACtF,oBAAoB,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC;IACjG,iFAAiF;IACjF,YAAY,EAAE,CAAC,CAAC,EAAE;QAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IACpE,6FAA6F;IAC7F,iBAAiB,EAAE,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IAChF,oEAAoE;IACpE,mBAAmB,EAAE,CAAC,MAAM,EAAE,OAAO,iBAAiB,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACnF;AAyFD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAetE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAUvF;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAExD;AA8CD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGzD;AAqQD,2EAA2E;AAC3E,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CAMlD;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAMvD;AAkPD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAKzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC3C,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GACA,MAAM,CAwER;AAsJD,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI/C;AA4mCD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAosB9B"}
|
package/dist/gateway/server.js
CHANGED
|
@@ -23,16 +23,17 @@ import * as path from 'path';
|
|
|
23
23
|
import { loadThreadConfig, saveThreadConfig, setConfigThreadsDir } from '../lib/config.js';
|
|
24
24
|
import { loadEmbeddings, generateMissingEmbeddings } from '../lib/embeddings.js';
|
|
25
25
|
import { sendMessage, getOrCreateThread, clearThreadCache, getActiveSubprocessCount, setGatewayThreadsDir, } from '../lib/gateway.js';
|
|
26
|
+
import { verifyLicense, majorOf, LICENSE_CONTACT } from '../lib/license.js';
|
|
26
27
|
import { getPromptsDirForThreadPath, isValidCaptureId, readPromptCapture, } from '../lib/prompt-capture.js';
|
|
27
28
|
import { catchUpSegmentation, loadSegmentBoundaries, saveSegmentBoundaries, } from '../lib/segments.js';
|
|
28
29
|
import { listTemplates, scaffoldFromTemplate } from '../lib/templates.js';
|
|
29
|
-
import { checkForUpdate, performUpdate, readChangelog } from '../lib/version-check.js';
|
|
30
|
+
import { checkForUpdate, getCurrentVersion, performUpdate, readChangelog, } from '../lib/version-check.js';
|
|
30
31
|
import { serveStaticFile, getMediaDir } from './adapters/webchat.js';
|
|
31
32
|
import { authenticate, getPresentedKey } from './auth.js';
|
|
32
33
|
import { BridgeGateway } from './bridge/gateway.js';
|
|
33
34
|
import { applyConfigPatch, maskedConfigView } from './config.js';
|
|
34
35
|
import { handleEmailHook, handleFormHook, handleGenericWebhook, } from './hooks.js';
|
|
35
|
-
import { allNamespaceKeys, extraMcpServersForThread, namespaceForKey, namespaceForThread, resolveExecutorProxy, threadAccessibleToScope, threadVisibleToScope, } from './namespaces.js';
|
|
36
|
+
import { allNamespaceKeys, demoMintDenial, extraMcpServersForThread, licenseBannerLines, namespaceForKey, namespaceForThread, resolveExecutorProxy, threadAccessibleToScope, threadVisibleToScope, } from './namespaces.js';
|
|
36
37
|
import { configureVapid, sendNotification, subscribe as pushSubscribe, unsubscribe as pushUnsubscribe, } from './push.js';
|
|
37
38
|
import { validateTranscriptFlush, ingestTranscriptFlush } from './transcript-ingest.js';
|
|
38
39
|
const threadQueues = new Map();
|
|
@@ -379,6 +380,39 @@ function denyOutOfNamespace(res, threadName, scope) {
|
|
|
379
380
|
jsonResponse(res, 403, { error: 'Thread not in your namespace' });
|
|
380
381
|
return true;
|
|
381
382
|
}
|
|
383
|
+
/** Thread names that currently exist on disk (one `.jsonl` per thread). */
|
|
384
|
+
export function listExistingThreadNames() {
|
|
385
|
+
if (!fs.existsSync(THREADS_DIR))
|
|
386
|
+
return [];
|
|
387
|
+
return fs
|
|
388
|
+
.readdirSync(THREADS_DIR)
|
|
389
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
390
|
+
.map(f => f.replace(/\.jsonl$/, ''));
|
|
391
|
+
}
|
|
392
|
+
/** The 402 body for a demo-cap refusal. Exported so tests assert one string. */
|
|
393
|
+
export function demoCapMessage(d) {
|
|
394
|
+
return (`Demo mode: namespace "${d.namespace}" has reached its limit of ${d.cap} visitor threads ` +
|
|
395
|
+
`(${d.count} in use). Existing threads keep working. ` +
|
|
396
|
+
`Commercial and production use requires a licence — contact ${LICENSE_CONTACT}.`);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* 402s and returns true when the request would MINT a new thread in an
|
|
400
|
+
* unlicensed namespace that is already at the demo cap (task 127). Non-namespaced
|
|
401
|
+
* threads, already-existing threads and licensed namespaces all pass through.
|
|
402
|
+
*/
|
|
403
|
+
function denyOverDemoCap(res, threadName, scope) {
|
|
404
|
+
const denial = demoMintDenial(threadName, scope.namespaces, listExistingThreadNames(), scope.license);
|
|
405
|
+
if (!denial)
|
|
406
|
+
return false;
|
|
407
|
+
jsonResponse(res, 402, {
|
|
408
|
+
error: demoCapMessage(denial),
|
|
409
|
+
demoMode: true,
|
|
410
|
+
namespace: denial.namespace,
|
|
411
|
+
cap: denial.cap,
|
|
412
|
+
contact: LICENSE_CONTACT,
|
|
413
|
+
});
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
382
416
|
async function handleAgentInject(req, res, pipelineOpts, scope) {
|
|
383
417
|
let body;
|
|
384
418
|
try {
|
|
@@ -420,6 +454,23 @@ async function handleAgentInject(req, res, pipelineOpts, scope) {
|
|
|
420
454
|
jsonResponse(res, 403, { error: `Thread not in your namespace: ${denied.join(', ')}` });
|
|
421
455
|
return;
|
|
422
456
|
}
|
|
457
|
+
// Task 127: injecting into a thread that doesn't exist yet mints it. Checked
|
|
458
|
+
// once against a single inventory read so a multi-target inject can't slip
|
|
459
|
+
// extra threads past the cap.
|
|
460
|
+
const existing = listExistingThreadNames();
|
|
461
|
+
for (const t of targetNames) {
|
|
462
|
+
const capDenial = demoMintDenial(t, scope.namespaces, existing, scope.license);
|
|
463
|
+
if (capDenial) {
|
|
464
|
+
jsonResponse(res, 402, {
|
|
465
|
+
error: demoCapMessage(capDenial),
|
|
466
|
+
demoMode: true,
|
|
467
|
+
namespace: capDenial.namespace,
|
|
468
|
+
cap: capDenial.cap,
|
|
469
|
+
contact: LICENSE_CONTACT,
|
|
470
|
+
});
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
423
474
|
const results = [];
|
|
424
475
|
for (const targetName of targetNames) {
|
|
425
476
|
const type = messageType || 'direct';
|
|
@@ -1084,9 +1135,12 @@ async function handleIngestTranscript(req, res, scope) {
|
|
|
1084
1135
|
jsonResponse(res, 400, { error: parsed.error });
|
|
1085
1136
|
return;
|
|
1086
1137
|
}
|
|
1087
|
-
// P7: the target thread arrives in the body, so gate it here.
|
|
1138
|
+
// P7: the target thread arrives in the body, so gate it here. Task 127's demo
|
|
1139
|
+
// cap rides the same spot for the same reason.
|
|
1088
1140
|
if (denyOutOfNamespace(res, parsed.flush.targetThread, scope))
|
|
1089
1141
|
return;
|
|
1142
|
+
if (denyOverDemoCap(res, parsed.flush.targetThread, scope))
|
|
1143
|
+
return;
|
|
1090
1144
|
try {
|
|
1091
1145
|
const result = await ingestTranscriptFlush(parsed.flush);
|
|
1092
1146
|
jsonResponse(res, 200, result);
|
|
@@ -1364,6 +1418,8 @@ async function handleMigrate(req, res, opts, scope) {
|
|
|
1364
1418
|
// P7: the thread arrives in the body — gate before creating/cloning anything.
|
|
1365
1419
|
if (denyOutOfNamespace(res, threadName, scope))
|
|
1366
1420
|
return;
|
|
1421
|
+
if (denyOverDemoCap(res, threadName, scope))
|
|
1422
|
+
return;
|
|
1367
1423
|
const result = {
|
|
1368
1424
|
threadName,
|
|
1369
1425
|
synced: 0,
|
|
@@ -1674,6 +1730,34 @@ export async function startGatewayServer(options) {
|
|
|
1674
1730
|
// them into the valid-key set. The namespace→key mapping stays in
|
|
1675
1731
|
// `namespaces` for scope resolution.
|
|
1676
1732
|
const namespaces = options.namespaces ?? [];
|
|
1733
|
+
// Licence verification (task 127). Offline, once at startup — SIGHUP config
|
|
1734
|
+
// reload is a full process restart, so this re-runs there too. Pasting a key
|
|
1735
|
+
// therefore requires a reload, which is the right cadence for a rare act.
|
|
1736
|
+
const licenseStatus = verifyLicense(options.licenseKey, majorOf(getCurrentVersion()));
|
|
1737
|
+
// Task 129: the licence key is editable in Settings but NEVER live-applied —
|
|
1738
|
+
// enforcement stays `licenseStatus` above, verified once at startup (127's
|
|
1739
|
+
// reload cadence). This tracks the on-disk value only, so GET /api/config can
|
|
1740
|
+
// render masked state and a truthful pendingReload flag after a PUT.
|
|
1741
|
+
let storedLicenseKey = options.licenseKey;
|
|
1742
|
+
const licenseView = () => ({
|
|
1743
|
+
licensed: licenseStatus.licensed,
|
|
1744
|
+
...(licenseStatus.licensed
|
|
1745
|
+
? { licensee: licenseStatus.licensee }
|
|
1746
|
+
: { reason: licenseStatus.reason }),
|
|
1747
|
+
pendingReload: (storedLicenseKey ?? '') !== (options.licenseKey ?? ''),
|
|
1748
|
+
});
|
|
1749
|
+
const emitLicenseBanner = () => {
|
|
1750
|
+
for (const line of licenseBannerLines(licenseStatus, namespaces, listExistingThreadNames())) {
|
|
1751
|
+
console.log(line);
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
emitLicenseBanner();
|
|
1755
|
+
// Periodic repeat while unlicensed, so a long-running demo deployment keeps
|
|
1756
|
+
// saying so. `unref` keeps it from holding the process open.
|
|
1757
|
+
const licenseBannerTimer = licenseStatus.licensed
|
|
1758
|
+
? undefined
|
|
1759
|
+
: setInterval(emitLicenseBanner, 60 * 60 * 1000);
|
|
1760
|
+
licenseBannerTimer?.unref?.();
|
|
1677
1761
|
const nsKeys = allNamespaceKeys(namespaces);
|
|
1678
1762
|
let currentApiKeys = Array.from(new Set([...options.apiKeys, ...nsKeys]));
|
|
1679
1763
|
let scheduler;
|
|
@@ -1783,6 +1867,13 @@ export async function startGatewayServer(options) {
|
|
|
1783
1867
|
jsonResponse(res, 403, { error: 'Thread not in your namespace' });
|
|
1784
1868
|
return;
|
|
1785
1869
|
}
|
|
1870
|
+
// Demo capacity gate (task 127) — rides the same choke point, one notch
|
|
1871
|
+
// later: access says WHO may touch a thread, this says whether a NEW one may
|
|
1872
|
+
// be minted. Ordered after the 403 so an out-of-namespace caller never learns
|
|
1873
|
+
// a namespace's occupancy from the status code.
|
|
1874
|
+
const scope = { ns: scopeNs, namespaces, license: licenseStatus };
|
|
1875
|
+
if (pathThread && denyOverDemoCap(res, pathThread, scope))
|
|
1876
|
+
return;
|
|
1786
1877
|
try {
|
|
1787
1878
|
// POST /bridge/call — route a tool call to the connected app tab (task
|
|
1788
1879
|
// 097). Body: { thread, command, params?, confirm?, summary? }. Commands
|
|
@@ -1812,6 +1903,8 @@ export async function startGatewayServer(options) {
|
|
|
1812
1903
|
jsonResponse(res, 403, { error: 'Thread not in your namespace' });
|
|
1813
1904
|
return;
|
|
1814
1905
|
}
|
|
1906
|
+
if (denyOverDemoCap(res, thread, scope))
|
|
1907
|
+
return;
|
|
1815
1908
|
const callParams = body.params ?? {};
|
|
1816
1909
|
const manifestEntry = bridgeGateway.manifest(thread)?.find(c => c.name === command);
|
|
1817
1910
|
const needsConfirm = Boolean(body.confirm) || manifestEntry?.risk === 'export';
|
|
@@ -1929,7 +2022,7 @@ export async function startGatewayServer(options) {
|
|
|
1929
2022
|
}
|
|
1930
2023
|
// POST /api/ingest-transcript — store-only transcript ingestion (task 096)
|
|
1931
2024
|
if (routePath === '/api/ingest-transcript' && req.method === 'POST') {
|
|
1932
|
-
await handleIngestTranscript(req, res,
|
|
2025
|
+
await handleIngestTranscript(req, res, scope);
|
|
1933
2026
|
return;
|
|
1934
2027
|
}
|
|
1935
2028
|
// GET /api/thread/:name/history
|
|
@@ -2022,15 +2115,12 @@ export async function startGatewayServer(options) {
|
|
|
2022
2115
|
// PUT /api/config reassigns pipelineOpts.* in place, so this is the single
|
|
2023
2116
|
// authoritative in-memory source (task 104).
|
|
2024
2117
|
if (routePath === '/api/models' && req.method === 'GET') {
|
|
2025
|
-
//
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
];
|
|
2032
|
-
}
|
|
2033
|
-
else {
|
|
2118
|
+
// Serve the configured catalog truthfully — task 129 deleted the
|
|
2119
|
+
// hardcoded fallbacks (a lone Claude entry / the Opus-Sonnet pair).
|
|
2120
|
+
// An unconfigured gateway gets empty arrays; setup seeds a catalog,
|
|
2121
|
+
// and the Settings gear (topbar, task 128) is the recovery path.
|
|
2122
|
+
const models = (pipelineOpts.models || []).map(m => ({ ...m }));
|
|
2123
|
+
if (models.length > 0) {
|
|
2034
2124
|
// Clear all defaults, then set the global default (or first if none specified)
|
|
2035
2125
|
const globalDefault = pipelineOpts.model || models[0].id;
|
|
2036
2126
|
for (const m of models) {
|
|
@@ -2038,31 +2128,39 @@ export async function startGatewayServer(options) {
|
|
|
2038
2128
|
}
|
|
2039
2129
|
}
|
|
2040
2130
|
// Claude CLI model variants (Opus/Sonnet/...) for the sub-model picker.
|
|
2041
|
-
//
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
{ id: 'claude-opus-5[1m]', label: 'Opus 5', default: true },
|
|
2046
|
-
{ id: 'claude-sonnet-5[1m]', label: 'Sonnet 5' },
|
|
2047
|
-
];
|
|
2048
|
-
}
|
|
2049
|
-
else if (!claudeModels.some(m => m.default)) {
|
|
2131
|
+
// First-is-default is a normalization rule for a configured list, not
|
|
2132
|
+
// model knowledge — it stays.
|
|
2133
|
+
const claudeModels = (pipelineOpts.claudeModels || []).map(m => ({ ...m }));
|
|
2134
|
+
if (claudeModels.length > 0 && !claudeModels.some(m => m.default)) {
|
|
2050
2135
|
claudeModels[0].default = true;
|
|
2051
2136
|
}
|
|
2052
2137
|
jsonResponse(res, 200, { models, claudeModels });
|
|
2053
2138
|
return;
|
|
2054
2139
|
}
|
|
2140
|
+
// GET/PUT /api/config — gateway-level settings are ADMIN-ONLY (task 128).
|
|
2141
|
+
// Task 104 opened these to "any valid key", which predated scoped app keys
|
|
2142
|
+
// becoming a public surface (097 P4 ships one to every app visitor's
|
|
2143
|
+
// browser); a scoped key must not read or rewrite the gateway's model
|
|
2144
|
+
// catalog, default model, or provider credentials.
|
|
2145
|
+
if (routePath === '/api/config' && scopeNs) {
|
|
2146
|
+
jsonResponse(res, 403, { error: 'Gateway settings require an admin key' });
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2055
2149
|
// GET /api/config — live-mutable config subset (task 104), credentials
|
|
2056
|
-
// MASKED.
|
|
2057
|
-
//
|
|
2150
|
+
// MASKED. Lets the in-app editor render current state without ever
|
|
2151
|
+
// exposing a full provider key.
|
|
2058
2152
|
if (routePath === '/api/config' && req.method === 'GET') {
|
|
2059
|
-
jsonResponse(res, 200,
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2153
|
+
jsonResponse(res, 200, {
|
|
2154
|
+
...maskedConfigView({
|
|
2155
|
+
models: pipelineOpts.models,
|
|
2156
|
+
claudeModels: pipelineOpts.claudeModels,
|
|
2157
|
+
model: pipelineOpts.model,
|
|
2158
|
+
hfApiKey: pipelineOpts.hfApiKey,
|
|
2159
|
+
openaiApiKey: pipelineOpts.openaiApiKey,
|
|
2160
|
+
licenseKey: storedLicenseKey,
|
|
2161
|
+
}),
|
|
2162
|
+
license: licenseView(),
|
|
2163
|
+
});
|
|
2066
2164
|
return;
|
|
2067
2165
|
}
|
|
2068
2166
|
// PUT /api/config — live-apply the model catalog / default / provider
|
|
@@ -2070,7 +2168,7 @@ export async function startGatewayServer(options) {
|
|
|
2070
2168
|
// in-memory options in place so /api/models and the per-turn spawn reflect
|
|
2071
2169
|
// it with NO restart; onConfigApplied propagates to the other spawn-path
|
|
2072
2170
|
// holders (webchat). PUT (not PATCH) so it rides the existing CORS
|
|
2073
|
-
// allow-methods.
|
|
2171
|
+
// allow-methods. Admin (non-namespaced) keys only — gated above.
|
|
2074
2172
|
if (routePath === '/api/config' && req.method === 'PUT') {
|
|
2075
2173
|
let patch;
|
|
2076
2174
|
try {
|
|
@@ -2094,15 +2192,24 @@ export async function startGatewayServer(options) {
|
|
|
2094
2192
|
pipelineOpts.hfApiKey = applied.hfApiKey;
|
|
2095
2193
|
if (patch.openaiApiKey !== undefined)
|
|
2096
2194
|
pipelineOpts.openaiApiKey = applied.openaiApiKey;
|
|
2195
|
+
// Task 129: licence key is persist-ONLY — track the stored value for
|
|
2196
|
+
// display, but never rewire enforcement (licenseStatus stays as
|
|
2197
|
+
// verified at startup; a reload is what applies it — task 127).
|
|
2198
|
+
if (patch.licenseKey !== undefined)
|
|
2199
|
+
storedLicenseKey = applied.licenseKey;
|
|
2097
2200
|
// Propagate to the daemon's OTHER holders (webchat pipelineOpts, etc.).
|
|
2098
2201
|
options.onConfigApplied?.(applied);
|
|
2099
|
-
jsonResponse(res, 200,
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2202
|
+
jsonResponse(res, 200, {
|
|
2203
|
+
...maskedConfigView({
|
|
2204
|
+
models: pipelineOpts.models,
|
|
2205
|
+
claudeModels: pipelineOpts.claudeModels,
|
|
2206
|
+
model: pipelineOpts.model,
|
|
2207
|
+
hfApiKey: pipelineOpts.hfApiKey,
|
|
2208
|
+
openaiApiKey: pipelineOpts.openaiApiKey,
|
|
2209
|
+
licenseKey: storedLicenseKey,
|
|
2210
|
+
}),
|
|
2211
|
+
license: licenseView(),
|
|
2212
|
+
});
|
|
2106
2213
|
}
|
|
2107
2214
|
catch (err) {
|
|
2108
2215
|
jsonResponse(res, 500, { error: String(err) });
|
|
@@ -2125,7 +2232,7 @@ export async function startGatewayServer(options) {
|
|
|
2125
2232
|
}
|
|
2126
2233
|
// POST /api/agents/inject — agent-to-agent messaging
|
|
2127
2234
|
if (routePath === '/api/agents/inject' && req.method === 'POST') {
|
|
2128
|
-
await handleAgentInject(req, res, pipelineOpts,
|
|
2235
|
+
await handleAgentInject(req, res, pipelineOpts, scope);
|
|
2129
2236
|
return;
|
|
2130
2237
|
}
|
|
2131
2238
|
// GET /api/federation/status — connected spokes and their threads (hub only)
|
|
@@ -2140,7 +2247,7 @@ export async function startGatewayServer(options) {
|
|
|
2140
2247
|
}
|
|
2141
2248
|
// POST /api/migrate — sync thread + clone repo + set projectDir
|
|
2142
2249
|
if (routePath === '/api/migrate' && req.method === 'POST') {
|
|
2143
|
-
await handleMigrate(req, res, pipelineOpts,
|
|
2250
|
+
await handleMigrate(req, res, pipelineOpts, scope);
|
|
2144
2251
|
return;
|
|
2145
2252
|
}
|
|
2146
2253
|
// POST /api/push/subscribe — register push subscription
|
|
@@ -2241,6 +2348,8 @@ export async function startGatewayServer(options) {
|
|
|
2241
2348
|
// Abort any in-flight message processing
|
|
2242
2349
|
threadQueues.clear();
|
|
2243
2350
|
threadBusy.clear();
|
|
2351
|
+
if (licenseBannerTimer)
|
|
2352
|
+
clearInterval(licenseBannerTimer);
|
|
2244
2353
|
return new Promise(resolveClose => {
|
|
2245
2354
|
server.close(() => {
|
|
2246
2355
|
clearThreadCache();
|