@columenlabs/lumen-ai 0.1.133 → 0.1.135
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 +1 -1
- package/dist/assistant/engine.d.ts +9 -3
- package/dist/assistant/engine.js +24 -11
- package/dist/assistant/routes.js +4 -0
- package/dist/assistant/web.d.ts +5 -2
- package/dist/assistant/web.js +12 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ app.all('/api/assistant/*', (c) => routes(c.req.raw));
|
|
|
99
99
|
- **ChatGPT sign-in.** In place of their own OpenAI key a person may sign in with their ChatGPT plan (`POST /my-signin`, then `POST /my-signin/finish {url}`): the sign-in opens in a new tab and ends on `http://127.0.0.1:1455/auth/callback`, a page that does not load, whose address the person pastes back. The tokens are sealed as their own key and refreshed before they run out; answers on the plan count tokens and no dollars.
|
|
100
100
|
- **Who pays.** The person's pick (the shared keys by default) while it has a key for the tier's provider and money left this month under its cap (an admin sets the shared keys' cap per person, `PUT /app`), else the other; with neither the panel says why.
|
|
101
101
|
- **Tools.** Each group is Off, Read, Read + draft or Read + write per person. A tool is a read when its MCP `readOnlyHint` is set. On Read + draft a write only drafts a card, and runs as the person when they press Save, after access is checked again. Read + write is offered only for a group with `writes: true`: its writes run at once, and with `undo` set each one's line in the chat has Undo, which calls `undo(caller, { tool, args, result })` (`POST /chats/:id/writes/:step/undo`); the model is told what was undone. Cards drafted one after another show as one stack with Save all. Use `mcpServerTools` (an SDK `McpServer`), `mcpFetchTools` (a server served as a fetch handler, any SDK), `remoteMcpTools` or `plainTools`.
|
|
102
|
-
- **Web.** `fetch` is a plain GET from the Worker (10 s, 2 MB, never the app's own hosts or private addresses). ScrapeQL's own tools join over its MCP API once an admin pastes a Cloudflare Access service token (`PUT /web/scrapeql`).
|
|
102
|
+
- **Web.** `fetch` is a plain GET from the Worker (10 s, 2 MB, never the app's own hosts or private addresses). ScrapeQL's own tools join over its MCP API once an admin pastes a Cloudflare Access service token (`PUT /web/scrapeql`). A Worker on ScrapeQL's own Cloudflare account passes its service binding instead, `scrapeql: { url: 'https://scrapeql/mcp', fetch: env.SCRAPEQL.fetch.bind(env.SCRAPEQL) }`: no token is stored, the tools are always on and Settings shows the link with a Test.
|
|
103
103
|
- **Hand off and advice.** The `escalate` tool hands one task to a higher tier (the person sets next, any or only when asked); Anthropic's advisor tool lets a higher tier send a plan, at most two an answer.
|
|
104
104
|
- **Chats** stream as server-sent events (`POST /chat`); closing the request stops the answer and keeps what was said. Past 150k tokens, or on `/compact`, the history sent to the model becomes a summary. Run `assistant.purge()` once a day to drop chats 30 days after their last message.
|
|
105
105
|
|
|
@@ -123,9 +123,13 @@ export interface AssistantOptions {
|
|
|
123
123
|
web?: {
|
|
124
124
|
/** `fetch` options, or false for no fetch. */
|
|
125
125
|
fetch?: WebFetchOptions | false;
|
|
126
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* ScrapeQL's MCP address; its tools join once an admin adds the Access token. With `fetch`
|
|
128
|
+
* (a service binding's), ScrapeQL is reached through it: no token, its tools always on.
|
|
129
|
+
*/
|
|
127
130
|
scrapeql?: {
|
|
128
131
|
url: string;
|
|
132
|
+
fetch?: typeof fetch;
|
|
129
133
|
};
|
|
130
134
|
};
|
|
131
135
|
store: AssistantStore;
|
|
@@ -252,8 +256,10 @@ export declare class Assistant {
|
|
|
252
256
|
detail: string;
|
|
253
257
|
at: number;
|
|
254
258
|
}>;
|
|
255
|
-
/**
|
|
256
|
-
|
|
259
|
+
/** ScrapeQL is reached over a service binding, so it needs no token. */
|
|
260
|
+
get boundScrapeql(): boolean;
|
|
261
|
+
/** Lists ScrapeQL's tools with the token (none over a binding): works or why not. */
|
|
262
|
+
testScrapeql(token: AccessToken | null): Promise<{
|
|
257
263
|
ok: boolean;
|
|
258
264
|
detail: string;
|
|
259
265
|
at: number;
|
package/dist/assistant/engine.js
CHANGED
|
@@ -357,9 +357,11 @@ export class Assistant {
|
|
|
357
357
|
web: {
|
|
358
358
|
fetch: p.web ?? true,
|
|
359
359
|
scrapeql: this.o.web?.scrapeql
|
|
360
|
-
?
|
|
361
|
-
?
|
|
362
|
-
:
|
|
360
|
+
? this.o.web.scrapeql.fetch
|
|
361
|
+
? { set: true, bound: true }
|
|
362
|
+
: caller.admin
|
|
363
|
+
? keyState(scrapeql, (d) => d.client_id)
|
|
364
|
+
: { set: !!scrapeql }
|
|
363
365
|
: null,
|
|
364
366
|
},
|
|
365
367
|
providers,
|
|
@@ -525,12 +527,13 @@ export class Assistant {
|
|
|
525
527
|
access: 'read',
|
|
526
528
|
source: webFetchTools(this.o.web?.fetch || {}),
|
|
527
529
|
});
|
|
528
|
-
const sq = this.o.web?.scrapeql
|
|
529
|
-
|
|
530
|
+
const sq = this.o.web?.scrapeql;
|
|
531
|
+
const token = sq && !sq.fetch ? await this.state.scrapeql() : null;
|
|
532
|
+
if (sq && (sq.fetch || token))
|
|
530
533
|
sources.push({
|
|
531
534
|
group: 'web',
|
|
532
535
|
access: 'read',
|
|
533
|
-
source: scrapeqlTools(
|
|
536
|
+
source: scrapeqlTools(sq.url, token, sq.fetch ?? this.o.fetch),
|
|
534
537
|
});
|
|
535
538
|
}
|
|
536
539
|
const results = await Promise.allSettled(sources.map((s) => s.source()));
|
|
@@ -1636,20 +1639,30 @@ export class Assistant {
|
|
|
1636
1639
|
return { ok: false, detail: shownError(err).message, at };
|
|
1637
1640
|
}
|
|
1638
1641
|
}
|
|
1639
|
-
/**
|
|
1642
|
+
/** ScrapeQL is reached over a service binding, so it needs no token. */
|
|
1643
|
+
get boundScrapeql() {
|
|
1644
|
+
return !!this.o.web?.scrapeql?.fetch;
|
|
1645
|
+
}
|
|
1646
|
+
/** Lists ScrapeQL's tools with the token (none over a binding): works or why not. */
|
|
1640
1647
|
async testScrapeql(token) {
|
|
1641
1648
|
const at = Date.now();
|
|
1642
|
-
const
|
|
1643
|
-
if (!
|
|
1649
|
+
const sq = this.o.web?.scrapeql;
|
|
1650
|
+
if (!sq)
|
|
1644
1651
|
return { ok: false, detail: 'ScrapeQL is not set up in this app.', at };
|
|
1645
1652
|
try {
|
|
1646
|
-
const open = await scrapeqlTools(url, token, this.o.fetch)();
|
|
1653
|
+
const open = await scrapeqlTools(sq.url, sq.fetch ? null : token, sq.fetch ?? this.o.fetch)();
|
|
1647
1654
|
const n = open.tools.length;
|
|
1648
1655
|
await open.close();
|
|
1649
1656
|
return { ok: true, detail: `ScrapeQL answered with ${n} tool${n === 1 ? '' : 's'}.`, at };
|
|
1650
1657
|
}
|
|
1651
1658
|
catch {
|
|
1652
|
-
return {
|
|
1659
|
+
return {
|
|
1660
|
+
ok: false,
|
|
1661
|
+
detail: sq.fetch
|
|
1662
|
+
? 'ScrapeQL could not be reached over the link.'
|
|
1663
|
+
: 'ScrapeQL refused the token or could not be reached.',
|
|
1664
|
+
at,
|
|
1665
|
+
};
|
|
1653
1666
|
}
|
|
1654
1667
|
}
|
|
1655
1668
|
// ---------------------------------------------------------------- drafts
|
package/dist/assistant/routes.js
CHANGED
|
@@ -273,6 +273,8 @@ export function assistantRoutes(app, o) {
|
|
|
273
273
|
const no = admin();
|
|
274
274
|
if (no)
|
|
275
275
|
return no;
|
|
276
|
+
if (a.boundScrapeql)
|
|
277
|
+
return fail("ScrapeQL is reached over the app's link; it needs no token.", 409, 'bound');
|
|
276
278
|
const b = await body(req);
|
|
277
279
|
const client_id = str(b.client_id, 200);
|
|
278
280
|
const client_secret = str(b.client_secret, 400);
|
|
@@ -300,6 +302,8 @@ export function assistantRoutes(app, o) {
|
|
|
300
302
|
const no = admin();
|
|
301
303
|
if (no)
|
|
302
304
|
return no;
|
|
305
|
+
if (a.boundScrapeql)
|
|
306
|
+
return json(await a.testScrapeql(null));
|
|
303
307
|
const d = await a.state.scrapeql();
|
|
304
308
|
if (!d)
|
|
305
309
|
return fail('No ScrapeQL token is set.', 404);
|
package/dist/assistant/web.d.ts
CHANGED
|
@@ -41,5 +41,8 @@ export interface AccessToken {
|
|
|
41
41
|
client_id: string;
|
|
42
42
|
client_secret: string;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* ScrapeQL's MCP tools over its API, with an Access service token, or none over a service
|
|
46
|
+
* binding (`f` is then the binding's fetch). Every one of them reads.
|
|
47
|
+
*/
|
|
48
|
+
export declare function scrapeqlTools(url: string, token: AccessToken | null, f?: typeof fetch): ToolSource;
|
package/dist/assistant/web.js
CHANGED
|
@@ -155,14 +155,21 @@ export function webFetchTools(o = {}) {
|
|
|
155
155
|
return open;
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* ScrapeQL's MCP tools over its API, with an Access service token, or none over a service
|
|
160
|
+
* binding (`f` is then the binding's fetch). Every one of them reads.
|
|
161
|
+
*/
|
|
159
162
|
export function scrapeqlTools(url, token, f) {
|
|
160
163
|
const remote = remoteMcpTools({
|
|
161
164
|
url,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
...(token
|
|
166
|
+
? {
|
|
167
|
+
headers: {
|
|
168
|
+
'CF-Access-Client-Id': token.client_id,
|
|
169
|
+
'CF-Access-Client-Secret': token.client_secret,
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
: {}),
|
|
166
173
|
...(f ? { fetch: f } : {}),
|
|
167
174
|
});
|
|
168
175
|
return async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.135",
|
|
4
4
|
"description": "Lumen's assistant and agent parts: a chat assistant's server (Claude tiers, tools over MCP, the web, hand off, spend caps), serving an MCP server from a Worker, read-only tool sets and metered tool calls.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@anthropic-ai/sdk": "0.133.0",
|
|
44
|
-
"@columenlabs/lumen-ops": "0.1.
|
|
45
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
44
|
+
"@columenlabs/lumen-ops": "0.1.135",
|
|
45
|
+
"@columenlabs/lumen-ui-types": "0.1.135",
|
|
46
46
|
"openai": "7.32.0",
|
|
47
47
|
"zod": "^4.3.6"
|
|
48
48
|
},
|