@agentorg/cli 0.0.6 → 0.0.8
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 +10 -7
- package/dist/node_modules/@agentorg/computer/LICENSE +201 -0
- package/dist/node_modules/@agentorg/computer/NOTICE +2 -0
- package/dist/node_modules/@agentorg/computer/dist/THIRD_PARTY_LICENSES.txt +1321 -0
- package/dist/node_modules/@agentorg/computer/dist/sdk.js +24909 -0
- package/dist/node_modules/@agentorg/computer/dist/types/generated/schema.d.ts +860 -0
- package/dist/node_modules/@agentorg/computer/dist/types/sdk.d.ts +242 -0
- package/dist/node_modules/@agentorg/computer/package.json +12 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/index.d.ts +1 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/index.js +1 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.d.ts +24 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.js +482 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.d.ts +19 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.js +149 -0
- package/dist/node_modules/@agentorg/computer-connector/package.json +33 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/browser/SKILL.md +31 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/computer/SKILL.md +30 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/files/SKILL.md +28 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/terminal/SKILL.md +39 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/web-research/SKILL.md +29 -0
- package/dist/node_modules/@agentorg/hub/dist/src/router.d.ts +4 -2
- package/dist/node_modules/@agentorg/hub/dist/src/router.js +3 -1
- package/dist/node_modules/@agentorg/hub/dist/src/validate.js +16 -2
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.d.ts +34 -47
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.js +25 -34
- package/dist/node_modules/@agentorg/interfaces/dist/src/chat.d.ts +90 -50
- package/dist/node_modules/@agentorg/interfaces/dist/src/chat.js +69 -34
- package/dist/node_modules/@agentorg/interfaces/dist/src/connection.d.ts +36 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/connection.js +31 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/folders.d.ts +19 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/folders.js +25 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.d.ts +6 -4
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.js +6 -4
- package/dist/node_modules/@agentorg/interfaces/dist/src/llm-session.d.ts +2 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/llm-session.js +2 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.d.ts +381 -27
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.js +179 -15
- package/dist/node_modules/@agentorg/interfaces/dist/src/units.d.ts +460 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/units.js +204 -0
- package/dist/node_modules/@agentorg/looper/dist/src/index.d.ts +5 -0
- package/dist/node_modules/@agentorg/looper/dist/src/index.js +5 -0
- package/dist/node_modules/@agentorg/looper/dist/src/loop.d.ts +89 -0
- package/dist/node_modules/@agentorg/looper/dist/src/loop.js +366 -0
- package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +81 -0
- package/dist/node_modules/@agentorg/looper/dist/src/looper.js +574 -0
- package/dist/node_modules/@agentorg/looper/dist/src/main.d.ts +34 -0
- package/dist/node_modules/@agentorg/looper/dist/src/main.js +116 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +181 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.js +433 -0
- package/dist/node_modules/@agentorg/looper/dist/src/tools.d.ts +11 -0
- package/dist/node_modules/@agentorg/looper/dist/src/tools.js +42 -0
- package/dist/node_modules/@agentorg/looper/dist/src/types.d.ts +147 -0
- package/dist/node_modules/@agentorg/looper/dist/src/types.js +32 -0
- package/dist/node_modules/@agentorg/looper/package.json +24 -0
- package/dist/node_modules/@agentorg/manager/dist/src/activity.d.ts +18 -0
- package/dist/node_modules/@agentorg/manager/dist/src/activity.js +65 -0
- package/dist/node_modules/@agentorg/manager/dist/src/control.d.ts +134 -0
- package/dist/node_modules/@agentorg/manager/dist/src/control.js +1115 -0
- package/dist/node_modules/@agentorg/manager/dist/src/form.d.ts +40 -0
- package/dist/node_modules/@agentorg/manager/dist/src/form.js +191 -0
- package/dist/node_modules/@agentorg/manager/dist/src/manager.d.ts +4 -0
- package/dist/node_modules/@agentorg/manager/dist/src/manager.js +341 -189
- package/dist/node_modules/@agentorg/manager/dist/src/skills.d.ts +2 -0
- package/dist/node_modules/@agentorg/manager/dist/src/skills.js +7 -0
- package/dist/node_modules/@agentorg/manager/dist/src/stack.d.ts +454 -0
- package/dist/node_modules/@agentorg/manager/dist/src/stack.js +917 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/connection.d.ts +2 -1
- package/dist/node_modules/@agentorg/mcp/dist/src/connection.js +8 -2
- package/dist/node_modules/@agentorg/mcp/dist/src/index.d.ts +3 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/index.js +3 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/kinds.d.ts +108 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/kinds.js +95 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/provider.d.ts +2 -2
- package/dist/node_modules/@agentorg/mcp/dist/src/provider.js +48 -18
- package/dist/node_modules/@agentorg/mcp/dist/src/public.d.ts +6 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/public.js +72 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/filled.d.ts +21 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/filled.js +24 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/index.d.ts +1 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/index.js +1 -0
- package/dist/node_modules/@agentorg/shell/dist/app.css +1306 -181
- package/dist/node_modules/@agentorg/shell/dist/app.js +207 -166
- package/dist/node_modules/@agentorg/shell/package.json +6 -1
- package/dist/node_modules/assistant/dist/src/chats.d.ts +94 -0
- package/dist/node_modules/assistant/dist/src/chats.js +355 -0
- package/dist/node_modules/assistant/dist/src/engine.d.ts +26 -7
- package/dist/node_modules/assistant/dist/src/engine.js +107 -35
- package/dist/node_modules/assistant/dist/src/local-store.js +1 -1
- package/dist/node_modules/assistant/dist/src/provider.d.ts +1 -1
- package/dist/node_modules/assistant/dist/src/provider.js +47 -19
- package/dist/node_modules/assistant/dist/src/tools.d.ts +11 -28
- package/dist/node_modules/assistant/dist/src/tools.js +19 -40
- package/dist/node_modules/assistant/package.json +1 -0
- package/dist/src/api.js +4 -0
- package/dist/src/folders.d.ts +2 -0
- package/dist/src/folders.js +45 -0
- package/dist/src/gateway.d.ts +7 -1
- package/dist/src/gateway.js +81 -12
- package/dist/src/live.d.ts +17 -0
- package/dist/src/live.js +109 -0
- package/dist/src/programs.d.ts +7 -0
- package/dist/src/programs.js +29 -2
- package/dist/src/signin.d.ts +12 -3
- package/dist/src/signin.js +30 -6
- package/package.json +2 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/mcp-provider.d.ts +0 -88
- package/dist/node_modules/@agentorg/interfaces/dist/src/mcp-provider.js +0 -78
- package/dist/node_modules/assistant/dist/src/agent.d.ts +0 -62
- package/dist/node_modules/assistant/dist/src/agent.js +0 -226
- package/dist/node_modules/assistant/dist/src/history.d.ts +0 -97
- package/dist/node_modules/assistant/dist/src/history.js +0 -235
package/dist/src/gateway.js
CHANGED
|
@@ -4,11 +4,13 @@ import { createRequire } from "node:module";
|
|
|
4
4
|
import { dirname, join, normalize, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { Hub, listen, PERSON } from "@agentorg/hub";
|
|
7
|
-
import { accountLogin, agents, connections, hubActors, hubAudit, hubBindings, hubExports, hubGrants, skills, } from "@agentorg/interfaces";
|
|
8
|
-
import { Refused } from "@agentorg/sdk";
|
|
7
|
+
import { accountLogin, agents, connections, hubActors, hubAudit, hubBindings, hubExports, hubGrants, llmSession, skills, } from "@agentorg/interfaces";
|
|
8
|
+
import { describeInterface, Refused } from "@agentorg/sdk";
|
|
9
9
|
import { connect } from "@agentorg/sdk/client";
|
|
10
10
|
import { WebSocketServer } from "ws";
|
|
11
11
|
import { serveSocket } from "./api.js";
|
|
12
|
+
import { callFolders } from "./folders.js";
|
|
13
|
+
import { LIVE_MAX, pipeLive } from "./live.js";
|
|
12
14
|
import { Notes } from "./notes.js";
|
|
13
15
|
import { oauthRoute } from "./oauth.js";
|
|
14
16
|
import { closePackageActors, startPrograms, } from "./programs.js";
|
|
@@ -39,6 +41,21 @@ const MAX_MESSAGE = 1024 * 1024;
|
|
|
39
41
|
const HELLO_TIMEOUT_MS = 10_000;
|
|
40
42
|
/** The close code for a socket with no live session: the tab signs in again. */
|
|
41
43
|
const SIGN_IN_AGAIN = 4401;
|
|
44
|
+
/** A model's reply can take minutes; every other interface keeps the Hub's own limit. */
|
|
45
|
+
export const HUB_OPTIONS = {
|
|
46
|
+
answerTimeoutsMs: { [describeInterface(llmSession).id]: 10 * 60_000 },
|
|
47
|
+
};
|
|
48
|
+
/** The port the kept sessions were given at, if any are kept. */
|
|
49
|
+
function keptPort(sessions) {
|
|
50
|
+
try {
|
|
51
|
+
const { at } = JSON.parse(readFileSync(sessions, "utf8"));
|
|
52
|
+
const port = /^http:\/\/127\.0\.0\.1:(\d+)$/.exec(String(at))?.[1];
|
|
53
|
+
return port === undefined ? undefined : Number(port);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
42
59
|
/** Write a file only its owner reads, all at once. */
|
|
43
60
|
function writePrivate(file, text) {
|
|
44
61
|
writeFileSync(`${file}.tmp`, text, { mode: 0o600 });
|
|
@@ -55,13 +72,38 @@ export async function startApp(options) {
|
|
|
55
72
|
const home = resolve(options.home);
|
|
56
73
|
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
57
74
|
closePackageActors(home);
|
|
58
|
-
const hub = Hub.open(home);
|
|
75
|
+
const hub = Hub.open(home, HUB_OPTIONS);
|
|
59
76
|
const listener = await listen(hub, {}).catch((error) => {
|
|
60
77
|
hub.close();
|
|
61
78
|
throw error;
|
|
62
79
|
});
|
|
63
80
|
writePrivate(join(home, "url"), `${listener.url}\n`);
|
|
64
|
-
|
|
81
|
+
// A tab signed in before the app last stopped is still signed in, if the app is at the address
|
|
82
|
+
// it was then: what is kept is each session's hash, in a file only its owner reads. At another
|
|
83
|
+
// address they are dropped. A tab keeps its session by address, so it couldn't say one here, and
|
|
84
|
+
// a tab left open on the old address goes on saying its session to whatever answers there.
|
|
85
|
+
const sessions = join(home, "sessions.json");
|
|
86
|
+
const signIn = new SignIn({
|
|
87
|
+
kept: {
|
|
88
|
+
read() {
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(readFileSync(sessions, "utf8")).hashes;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// None kept, or none that reads back.
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
write(hashes) {
|
|
98
|
+
try {
|
|
99
|
+
writePrivate(sessions, `${JSON.stringify({ at: origin, hashes })}\n`);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// A home that can't be written still signs the tab in: only the next start forgets it.
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
65
107
|
const credential = readFileSync(join(home, "person-credential"), "utf8").trim();
|
|
66
108
|
/** The app's own notes: what the shell remembers. */
|
|
67
109
|
const notes = new Notes(join(home, "app-notes.json"));
|
|
@@ -81,6 +123,8 @@ export async function startApp(options) {
|
|
|
81
123
|
async call(slot, method, input) {
|
|
82
124
|
if (slot === "store")
|
|
83
125
|
return notes.call(method, input);
|
|
126
|
+
if (slot === "folders")
|
|
127
|
+
return callFolders(method, input);
|
|
84
128
|
const exposed = Object.hasOwn(SLOTS, slot) ? undefined : exposedSlot(slot);
|
|
85
129
|
if (!exposed && !Object.hasOwn(SLOTS, slot))
|
|
86
130
|
throw new Refused("invalid", `The person has no slot ${slot}.`);
|
|
@@ -197,18 +241,32 @@ export async function startApp(options) {
|
|
|
197
241
|
response.end(request.method === "HEAD" ? undefined : body);
|
|
198
242
|
};
|
|
199
243
|
const sockets = new WebSocketServer({ noServer: true, maxPayload: MAX_MESSAGE });
|
|
244
|
+
const lives = new WebSocketServer({ noServer: true, maxPayload: LIVE_MAX });
|
|
200
245
|
server.on("upgrade", (request, socket, head) => {
|
|
201
246
|
// An upgraded socket has no error handler of its own: a client that resets would crash the app.
|
|
202
247
|
socket.on("error", () => socket.destroy());
|
|
203
248
|
const url = new URL(request.url ?? "/", origin);
|
|
204
249
|
const cli = signIn.isCli(request.headers.authorization);
|
|
205
250
|
const allowed = request.headers.host === host() &&
|
|
206
|
-
url.pathname === "/api" &&
|
|
251
|
+
(url.pathname === "/api" || url.pathname === "/live") &&
|
|
207
252
|
(cli || request.headers.origin === origin);
|
|
208
253
|
if (!allowed) {
|
|
209
254
|
socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
210
255
|
return;
|
|
211
256
|
}
|
|
257
|
+
if (url.pathname === "/live") {
|
|
258
|
+
const query = url.searchParams;
|
|
259
|
+
return lives.handleUpgrade(request, socket, head, (ws) => pipeLive(ws, {
|
|
260
|
+
cli,
|
|
261
|
+
isSession: (session) => signIn.isSession(session),
|
|
262
|
+
ticket: async () => (await personSide.call("agents", "live", {
|
|
263
|
+
agent: query.get("agent"),
|
|
264
|
+
instance: query.get("instance"),
|
|
265
|
+
surface: query.get("surface"),
|
|
266
|
+
mode: query.get("mode") ?? "watch",
|
|
267
|
+
})),
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
212
270
|
sockets.handleUpgrade(request, socket, head, (ws) => {
|
|
213
271
|
let served;
|
|
214
272
|
const serve = () => {
|
|
@@ -244,16 +302,25 @@ export async function startApp(options) {
|
|
|
244
302
|
});
|
|
245
303
|
});
|
|
246
304
|
});
|
|
247
|
-
|
|
305
|
+
const listenOn = (port) => new Promise((done, failed) => {
|
|
248
306
|
server.once("error", failed);
|
|
249
|
-
server.listen(
|
|
250
|
-
})
|
|
307
|
+
server.listen(port, "127.0.0.1", () => done());
|
|
308
|
+
});
|
|
309
|
+
// With no port asked for, the one the kept sessions were given at, so a tab left open is still
|
|
310
|
+
// signed in. If that one can't be had, any free one, as on a first start.
|
|
311
|
+
await (options.port === undefined
|
|
312
|
+
? listenOn(keptPort(sessions) ?? 0).catch(() => listenOn(0))
|
|
313
|
+
: listenOn(options.port)).catch(async (error) => {
|
|
251
314
|
person.close();
|
|
252
315
|
await listener.close();
|
|
253
316
|
hub.close();
|
|
254
317
|
throw error;
|
|
255
318
|
});
|
|
256
|
-
|
|
319
|
+
const { port } = server.address();
|
|
320
|
+
// At another address the kept sessions are dropped, before anything is answered.
|
|
321
|
+
if (keptPort(sessions) !== port)
|
|
322
|
+
rmSync(sessions, { force: true });
|
|
323
|
+
origin = `http://127.0.0.1:${port}`;
|
|
257
324
|
writePrivate(join(home, "cli-token"), `${signIn.cliToken}\n`);
|
|
258
325
|
writePrivate(join(home, "app.json"), `${JSON.stringify({ url: origin, pid: process.pid })}\n`);
|
|
259
326
|
let closing;
|
|
@@ -294,9 +361,11 @@ export async function startApp(options) {
|
|
|
294
361
|
catch {
|
|
295
362
|
// Already gone.
|
|
296
363
|
}
|
|
297
|
-
for (const
|
|
298
|
-
client.
|
|
299
|
-
|
|
364
|
+
for (const server of [sockets, lives]) {
|
|
365
|
+
for (const client of server.clients)
|
|
366
|
+
client.terminate();
|
|
367
|
+
server.close();
|
|
368
|
+
}
|
|
300
369
|
// server.close waits for every open connection; a tab still connected (a keep-alive request,
|
|
301
370
|
// a socket reconnecting) would hold the app open, so they are all closed now.
|
|
302
371
|
const serverClosed = new Promise((done) => server.close(() => done()));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
/** What one live frame can be, either way. */
|
|
3
|
+
export declare const LIVE_MAX: number;
|
|
4
|
+
export interface Ticket {
|
|
5
|
+
url: string;
|
|
6
|
+
protocol: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Pipe a page's socket to a surface. A tab says its session first, as on `/api`, and is then
|
|
10
|
+
* answered `{"op":"live","protocol"}`; after that every frame is the surface's own. The CLI is in
|
|
11
|
+
* by its token.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pipeLive(page: WebSocket, options: {
|
|
14
|
+
cli: boolean;
|
|
15
|
+
isSession(session: unknown): boolean;
|
|
16
|
+
ticket(): Promise<Ticket>;
|
|
17
|
+
}): void;
|
package/dist/src/live.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// A live surface of an agent's unit, for a page: the page opens a socket here, same-origin, and
|
|
2
|
+
// this asks management for a one-time ticket, dials it, and pipes the bytes both ways. The ticket
|
|
3
|
+
// and every token stay out of the page.
|
|
4
|
+
import WebSocket from "ws";
|
|
5
|
+
/** What one live frame can be, either way. */
|
|
6
|
+
export const LIVE_MAX = 4 * 1024 * 1024;
|
|
7
|
+
/** The page didn't say its session, or the session is gone. */
|
|
8
|
+
const SIGN_IN_AGAIN = 4401;
|
|
9
|
+
/** The surface couldn't be opened; the reason says why. */
|
|
10
|
+
const NOT_OPENED = 4404;
|
|
11
|
+
const HELLO_TIMEOUT_MS = 10_000;
|
|
12
|
+
/** What a page may send before the surface opens; past it, the socket closes rather than drop. */
|
|
13
|
+
const WAITING_MAX = 1024 * 1024;
|
|
14
|
+
/** Past this much unsent to the page, the surface waits; it goes on below the lower mark. */
|
|
15
|
+
const BEHIND = 8 * 1024 * 1024;
|
|
16
|
+
const CAUGHT_UP = 1024 * 1024;
|
|
17
|
+
/** A close code a page may be given: the peer's when it's an ordinary one. */
|
|
18
|
+
const codeOf = (code) => code === 1000 || code === 1001 || (code >= 3000 && code <= 4999) ? code : 1011;
|
|
19
|
+
/** A close reason fits in 123 bytes. */
|
|
20
|
+
const reasonOf = (text) => {
|
|
21
|
+
let reason = text;
|
|
22
|
+
while (Buffer.byteLength(reason) > 123)
|
|
23
|
+
reason = reason.slice(0, -1);
|
|
24
|
+
return reason;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Pipe a page's socket to a surface. A tab says its session first, as on `/api`, and is then
|
|
28
|
+
* answered `{"op":"live","protocol"}`; after that every frame is the surface's own. The CLI is in
|
|
29
|
+
* by its token.
|
|
30
|
+
*/
|
|
31
|
+
export function pipeLive(page, options) {
|
|
32
|
+
let upstream;
|
|
33
|
+
let opening = false;
|
|
34
|
+
const waiting = [];
|
|
35
|
+
let waitingBytes = 0;
|
|
36
|
+
const sizeOf = (data) => Array.isArray(data)
|
|
37
|
+
? data.reduce((sum, one) => sum + one.length, 0)
|
|
38
|
+
: data.byteLength;
|
|
39
|
+
const open = async () => {
|
|
40
|
+
opening = true;
|
|
41
|
+
let ticket;
|
|
42
|
+
try {
|
|
43
|
+
ticket = await options.ticket();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return page.close(NOT_OPENED, reasonOf(error instanceof Error ? error.message : "Not opened."));
|
|
47
|
+
}
|
|
48
|
+
if (page.readyState !== WebSocket.OPEN)
|
|
49
|
+
return;
|
|
50
|
+
let socket;
|
|
51
|
+
try {
|
|
52
|
+
// A server's socket, dialled from here: no browser Origin goes with it.
|
|
53
|
+
socket = new WebSocket(ticket.url, { maxPayload: LIVE_MAX });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Its message would hold the ticket.
|
|
57
|
+
return page.close(NOT_OPENED, "The surface didn't open.");
|
|
58
|
+
}
|
|
59
|
+
upstream = socket;
|
|
60
|
+
socket.on("open", () => {
|
|
61
|
+
page.send(JSON.stringify({ op: "live", protocol: ticket.protocol }));
|
|
62
|
+
for (const [data, binary] of waiting.splice(0))
|
|
63
|
+
socket.send(data, { binary });
|
|
64
|
+
});
|
|
65
|
+
socket.on("message", (data, binary) => {
|
|
66
|
+
page.send(data, { binary }, () => {
|
|
67
|
+
if (page.bufferedAmount < CAUGHT_UP)
|
|
68
|
+
socket.resume();
|
|
69
|
+
});
|
|
70
|
+
// A page that can't keep up isn't sent more until it has.
|
|
71
|
+
if (page.bufferedAmount > BEHIND)
|
|
72
|
+
socket.pause();
|
|
73
|
+
});
|
|
74
|
+
socket.on("close", (code, reason) => page.close(codeOf(code), reasonOf(String(reason))));
|
|
75
|
+
socket.on("error", () => page.close(NOT_OPENED, "The surface didn't open."));
|
|
76
|
+
};
|
|
77
|
+
const timer = options.cli
|
|
78
|
+
? undefined
|
|
79
|
+
: setTimeout(() => page.close(SIGN_IN_AGAIN, "No session"), HELLO_TIMEOUT_MS);
|
|
80
|
+
const opened = () => open().catch(() => page.close(NOT_OPENED, "The surface didn't open."));
|
|
81
|
+
if (options.cli)
|
|
82
|
+
void opened();
|
|
83
|
+
page.on("message", (data, binary) => {
|
|
84
|
+
if (!opening) {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
let hello;
|
|
87
|
+
try {
|
|
88
|
+
hello = binary ? undefined : JSON.parse(String(data));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Not a hello.
|
|
92
|
+
}
|
|
93
|
+
if (hello?.op !== "hello" || !options.isSession(hello.session))
|
|
94
|
+
return page.close(SIGN_IN_AGAIN, "Sign in again");
|
|
95
|
+
return void opened();
|
|
96
|
+
}
|
|
97
|
+
if (upstream?.readyState === WebSocket.OPEN)
|
|
98
|
+
return upstream.send(data, { binary });
|
|
99
|
+
waitingBytes += sizeOf(data);
|
|
100
|
+
if (waitingBytes > WAITING_MAX)
|
|
101
|
+
return page.close(1009, "Too much before the surface opened.");
|
|
102
|
+
waiting.push([data, binary]);
|
|
103
|
+
});
|
|
104
|
+
page.on("close", () => {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
upstream?.close();
|
|
107
|
+
});
|
|
108
|
+
page.on("error", () => page.terminate());
|
|
109
|
+
}
|
package/dist/src/programs.d.ts
CHANGED
|
@@ -6,12 +6,19 @@ export interface ProgramsOptions {
|
|
|
6
6
|
log?: (message: string) => void;
|
|
7
7
|
/** How ChatGPT's requests leave, instead of the network. For tests. */
|
|
8
8
|
fetch?: (request: Request, init: RequestInit) => Promise<Response>;
|
|
9
|
+
/** Where the computer connector looks for a local service, the web unit's server, and agents' folders. For tests. */
|
|
10
|
+
computer?: {
|
|
11
|
+
dataFolder?: string;
|
|
12
|
+
webUrl?: string;
|
|
13
|
+
agentsFolder?: string;
|
|
14
|
+
};
|
|
9
15
|
}
|
|
10
16
|
export interface Programs {
|
|
11
17
|
/** The Actors running now, by program. */
|
|
12
18
|
chatgpt?: string;
|
|
13
19
|
agents?: string;
|
|
14
20
|
mcp?: string;
|
|
21
|
+
computer?: string;
|
|
15
22
|
management?: string;
|
|
16
23
|
/** Leave the Hub, in the order that lets each finish. What each keeps stays. */
|
|
17
24
|
close(): Promise<void>;
|
package/dist/src/programs.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
// The app's programs: ChatGPT, the agent provider, the MCP provider and management, each an Actor
|
|
1
|
+
// The app's programs: ChatGPT, the agent provider, the MCP provider, the computer connector and management, each an Actor
|
|
2
2
|
// of its own that this process runs on its own channel, with its own private folder under the home.
|
|
3
3
|
// Every step reads what is there before it acts, so each start converges on the same system.
|
|
4
4
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
+
import { startComputerProvider } from "@agentorg/computer-connector";
|
|
6
7
|
import { HubState, PERSON } from "@agentorg/hub";
|
|
7
8
|
import { startManager } from "@agentorg/manager";
|
|
8
9
|
import { startMcpProvider } from "@agentorg/mcp";
|
|
@@ -25,6 +26,17 @@ export function closePackageActors(home) {
|
|
|
25
26
|
state.close();
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
/** The bundled Assistant: the base, a computer on a service the person chooses, and the web. */
|
|
30
|
+
const ASSISTANT_TEMPLATE = {
|
|
31
|
+
id: "assistant",
|
|
32
|
+
title: "Assistant",
|
|
33
|
+
description: "An agent with a computer of its own and the web.",
|
|
34
|
+
base: { provider: "AgentOrg", creatable: "assistant" },
|
|
35
|
+
units: [
|
|
36
|
+
{ provider: "Computers", unit: "computer", values: { access: "terminal", browser: true } },
|
|
37
|
+
{ provider: "Computers", unit: "web", values: { service: "exa" } },
|
|
38
|
+
],
|
|
39
|
+
};
|
|
28
40
|
/** The Hub's exports each program holds. */
|
|
29
41
|
const hubBinding = (name) => ({ slot: name, provider: "hub", export: name });
|
|
30
42
|
/**
|
|
@@ -131,6 +143,19 @@ export async function startPrograms(person, options) {
|
|
|
131
143
|
closers.unshift(() => running.close());
|
|
132
144
|
return actorId;
|
|
133
145
|
});
|
|
146
|
+
const computer = await attempt("the computer connector", async () => {
|
|
147
|
+
const { actorId, credential } = await identity("computer", "Computers", [hubBinding("actors")]);
|
|
148
|
+
const running = await startComputerProvider({
|
|
149
|
+
hubUrl,
|
|
150
|
+
actorId,
|
|
151
|
+
credential,
|
|
152
|
+
dir: dirOf("computer"),
|
|
153
|
+
log: programLog,
|
|
154
|
+
...options.computer,
|
|
155
|
+
});
|
|
156
|
+
closers.unshift(() => running.close());
|
|
157
|
+
return actorId;
|
|
158
|
+
});
|
|
134
159
|
const management = await attempt("management", async () => {
|
|
135
160
|
const { actorId, credential } = await identity("management", "Management", [
|
|
136
161
|
hubBinding("actors"),
|
|
@@ -144,6 +169,7 @@ export async function startPrograms(person, options) {
|
|
|
144
169
|
credential,
|
|
145
170
|
dir: dirOf("management"),
|
|
146
171
|
log: programLog,
|
|
172
|
+
templates: [ASSISTANT_TEMPLATE],
|
|
147
173
|
});
|
|
148
174
|
closers.push(() => running.close());
|
|
149
175
|
for (const [slot, exported] of [
|
|
@@ -155,13 +181,14 @@ export async function startPrograms(person, options) {
|
|
|
155
181
|
return actorId;
|
|
156
182
|
});
|
|
157
183
|
if (management)
|
|
158
|
-
for (const provider of [agents, mcp])
|
|
184
|
+
for (const provider of [agents, mcp, computer])
|
|
159
185
|
if (provider)
|
|
160
186
|
await attempt("connecting a provider to management", () => call("grants", "expose", { provider, export: "provider", to: management }));
|
|
161
187
|
return {
|
|
162
188
|
...(chatgpt ? { chatgpt } : {}),
|
|
163
189
|
...(agents ? { agents } : {}),
|
|
164
190
|
...(mcp ? { mcp } : {}),
|
|
191
|
+
...(computer ? { computer } : {}),
|
|
165
192
|
...(management ? { management } : {}),
|
|
166
193
|
async close() {
|
|
167
194
|
for (const close of closers)
|
package/dist/src/signin.d.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
+
/** Where the sessions' hashes outlast the app: read once, when first needed, and written at each sign-in. */
|
|
2
|
+
export interface KeptSessions {
|
|
3
|
+
/** What was written last; anything that isn't a list is read as none. */
|
|
4
|
+
read(): unknown;
|
|
5
|
+
write(hashes: string[]): void;
|
|
6
|
+
}
|
|
1
7
|
/**
|
|
2
8
|
* Who may use the gateway: browser tabs with a session, made from a one-time code, and the CLI
|
|
3
9
|
* with the token in the home. A code works once, within 60 s, and travels only in a URL's
|
|
4
|
-
* fragment, which no request carries. A session is a random id the gateway holds
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* fragment, which no request carries. A session is a random id of which the gateway holds only a
|
|
11
|
+
* hash. With `kept`, the hashes outlast the app, so a tab is still signed in when the app starts
|
|
12
|
+
* again; a session ends only when `SESSIONS_MAX` newer ones have been given.
|
|
13
|
+
* It is no cookie, which every port on 127.0.0.1 would receive: the shell keeps it in its origin's
|
|
14
|
+
* own storage and says it in its socket's first message.
|
|
7
15
|
*/
|
|
8
16
|
export declare class SignIn {
|
|
9
17
|
#private;
|
|
10
18
|
readonly cliToken: string;
|
|
11
19
|
constructor(options?: {
|
|
12
20
|
now?: () => number;
|
|
21
|
+
kept?: KeptSessions;
|
|
13
22
|
});
|
|
14
23
|
/** A new one-time code, for `/signin#<code>`. */
|
|
15
24
|
code(): string;
|
package/dist/src/signin.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
1
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
2
|
/** How long a sign-in code works. */
|
|
3
3
|
const CODE_TTL_MS = 60_000;
|
|
4
4
|
/** Codes not yet used; past it, the oldest go, so nothing grows without bound. */
|
|
5
5
|
const MAX_CODES = 16;
|
|
6
6
|
const SESSIONS_MAX = 256;
|
|
7
7
|
const token = () => randomBytes(32).toString("base64url");
|
|
8
|
+
/** What is kept of a session: enough to know it again, and nothing that signs anyone in. */
|
|
9
|
+
const hashOf = (session) => createHash("sha256").update(session).digest("base64url");
|
|
8
10
|
const same = (a, b) => {
|
|
9
11
|
const left = Buffer.from(a);
|
|
10
12
|
const right = Buffer.from(b);
|
|
@@ -13,17 +15,34 @@ const same = (a, b) => {
|
|
|
13
15
|
/**
|
|
14
16
|
* Who may use the gateway: browser tabs with a session, made from a one-time code, and the CLI
|
|
15
17
|
* with the token in the home. A code works once, within 60 s, and travels only in a URL's
|
|
16
|
-
* fragment, which no request carries. A session is a random id the gateway holds
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* fragment, which no request carries. A session is a random id of which the gateway holds only a
|
|
19
|
+
* hash. With `kept`, the hashes outlast the app, so a tab is still signed in when the app starts
|
|
20
|
+
* again; a session ends only when `SESSIONS_MAX` newer ones have been given.
|
|
21
|
+
* It is no cookie, which every port on 127.0.0.1 would receive: the shell keeps it in its origin's
|
|
22
|
+
* own storage and says it in its socket's first message.
|
|
19
23
|
*/
|
|
20
24
|
export class SignIn {
|
|
21
25
|
cliToken = token();
|
|
22
26
|
#codes = new Map();
|
|
27
|
+
/** The hash of each session, the oldest first. */
|
|
23
28
|
#sessions = new Set();
|
|
24
29
|
#now;
|
|
30
|
+
#kept;
|
|
31
|
+
#restored = false;
|
|
25
32
|
constructor(options = {}) {
|
|
26
33
|
this.#now = options.now ?? Date.now;
|
|
34
|
+
this.#kept = options.kept;
|
|
35
|
+
}
|
|
36
|
+
/** Take back what was kept, the first time a session is given or asked about. */
|
|
37
|
+
#restore() {
|
|
38
|
+
if (this.#restored)
|
|
39
|
+
return;
|
|
40
|
+
this.#restored = true;
|
|
41
|
+
const read = this.#kept?.read();
|
|
42
|
+
const hashes = Array.isArray(read) ? read : [];
|
|
43
|
+
for (const hash of hashes.slice(-SESSIONS_MAX))
|
|
44
|
+
if (typeof hash === "string")
|
|
45
|
+
this.#sessions.add(hash);
|
|
27
46
|
}
|
|
28
47
|
/** A new one-time code, for `/signin#<code>`. */
|
|
29
48
|
code() {
|
|
@@ -48,17 +67,22 @@ export class SignIn {
|
|
|
48
67
|
if (this.#now() > expires)
|
|
49
68
|
return undefined;
|
|
50
69
|
const session = token();
|
|
51
|
-
this.#
|
|
70
|
+
this.#restore();
|
|
71
|
+
this.#sessions.add(hashOf(session));
|
|
52
72
|
while (this.#sessions.size > SESSIONS_MAX)
|
|
53
73
|
this.#sessions.delete(this.#sessions.values().next().value);
|
|
74
|
+
// Kept before it is given: a session the tab holds is one a restart still knows.
|
|
75
|
+
this.#kept?.write([...this.#sessions]);
|
|
54
76
|
return session;
|
|
55
77
|
}
|
|
56
78
|
/** Whether a session is live. */
|
|
57
79
|
isSession(session) {
|
|
58
80
|
if (typeof session !== "string")
|
|
59
81
|
return false;
|
|
82
|
+
this.#restore();
|
|
83
|
+
const hash = hashOf(session);
|
|
60
84
|
for (const known of this.#sessions)
|
|
61
|
-
if (same(known,
|
|
85
|
+
if (same(known, hash))
|
|
62
86
|
return true;
|
|
63
87
|
return false;
|
|
64
88
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentorg/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "AgentOrg as one app: the Hub, the person's gateway and the shell, on loopback.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@modelcontextprotocol/client": "2.4.0",
|
|
31
31
|
"ajv": "^8.20.0",
|
|
32
|
+
"react-diff-view": "3.3.3",
|
|
32
33
|
"ws": "^8.22.1",
|
|
33
34
|
"yaml": "2.9.1",
|
|
34
35
|
"zod": "^4.6.7"
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
/** A way a provider makes connections, such as `url` or `command`. */
|
|
3
|
-
export declare const ConnectionKind: z.ZodString;
|
|
4
|
-
/** `connecting` before its first success, `connected`, or `down` and being tried again. */
|
|
5
|
-
export declare const ConnectionState: z.ZodEnum<{
|
|
6
|
-
connected: "connected";
|
|
7
|
-
connecting: "connecting";
|
|
8
|
-
down: "down";
|
|
9
|
-
}>;
|
|
10
|
-
/** A connection's tools, for people to read; a provider cuts a long description to fit. */
|
|
11
|
-
export declare const ConnectionTool: z.ZodObject<{
|
|
12
|
-
name: z.ZodString;
|
|
13
|
-
description: z.ZodOptional<z.ZodString>;
|
|
14
|
-
}, z.core.$strip>;
|
|
15
|
-
/** How a connection stands now: `detail` says why it is down, and never holds a secret. */
|
|
16
|
-
export declare const ConnectionStatus: z.ZodObject<{
|
|
17
|
-
state: z.ZodEnum<{
|
|
18
|
-
connected: "connected";
|
|
19
|
-
connecting: "connecting";
|
|
20
|
-
down: "down";
|
|
21
|
-
}>;
|
|
22
|
-
detail: z.ZodOptional<z.ZodString>;
|
|
23
|
-
tools: z.ZodArray<z.ZodObject<{
|
|
24
|
-
name: z.ZodString;
|
|
25
|
-
description: z.ZodOptional<z.ZodString>;
|
|
26
|
-
}, z.core.$strip>>;
|
|
27
|
-
}, z.core.$strip>;
|
|
28
|
-
/** The parameters of a connection, fitting its kind's `params` schema. */
|
|
29
|
-
export declare const ConnectionParams: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
30
|
-
/**
|
|
31
|
-
* What a server needs and shouldn't be seen, by name, such as a bearer token. Write-only: the values
|
|
32
|
-
* cross once, in `create`, and go only to the provider's own private file.
|
|
33
|
-
*/
|
|
34
|
-
export declare const ConnectionSecrets: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
35
|
-
/** What a connection attaches to or runs, for people to read: never a secret. */
|
|
36
|
-
export declare const ConnectionTarget: z.ZodString;
|
|
37
|
-
/**
|
|
38
|
-
* `mcp.provider@1`: a program that makes connections to MCP servers. Each connection joins the
|
|
39
|
-
* Hub as its own Actor, whose id is `connection`, exporting `tools@1` as `tools`. MCP itself stays
|
|
40
|
-
* behind this interface.
|
|
41
|
-
*
|
|
42
|
-
* A secret a server needs is given once, in `create`'s `secrets`, and stays with the provider: its
|
|
43
|
-
* value is never in `params`, `target`, `detail`, or anything else read back. `status` and `remove` are refused `unknown` for a connection the
|
|
44
|
-
* provider doesn't hold.
|
|
45
|
-
*/
|
|
46
|
-
export declare const mcpProvider: import("@agentorg/sdk").RpcInterface<"mcp.provider", {
|
|
47
|
-
/** The kinds it offers; `params` and `secrets` are the JSON Schemas of what each one takes. */
|
|
48
|
-
kinds: import("@agentorg/sdk").MethodSpec<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
|
|
49
|
-
kinds: z.ZodArray<z.ZodObject<{
|
|
50
|
-
kind: z.ZodString;
|
|
51
|
-
title: z.ZodString;
|
|
52
|
-
params: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
53
|
-
secrets: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
54
|
-
}, z.core.$strip>>;
|
|
55
|
-
}, z.core.$strip>>;
|
|
56
|
-
/**
|
|
57
|
-
* Make a connection and its Actor, named `name`. It returns once the Actor has joined the Hub,
|
|
58
|
-
* whether or not the server has answered yet; `status` says how it stands. Refused `invalid`
|
|
59
|
-
* when `kind` isn't offered or `params` or `secrets` don't fit it.
|
|
60
|
-
*/
|
|
61
|
-
create: import("@agentorg/sdk").MethodSpec<z.ZodObject<{
|
|
62
|
-
kind: z.ZodString;
|
|
63
|
-
params: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
|
|
64
|
-
secrets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
65
|
-
name: z.ZodString;
|
|
66
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
67
|
-
connection: z.ZodString;
|
|
68
|
-
target: z.ZodString;
|
|
69
|
-
}, z.core.$strip>>;
|
|
70
|
-
status: import("@agentorg/sdk").MethodSpec<z.ZodObject<{
|
|
71
|
-
connection: z.ZodString;
|
|
72
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
73
|
-
state: z.ZodEnum<{
|
|
74
|
-
connected: "connected";
|
|
75
|
-
connecting: "connecting";
|
|
76
|
-
down: "down";
|
|
77
|
-
}>;
|
|
78
|
-
detail: z.ZodOptional<z.ZodString>;
|
|
79
|
-
tools: z.ZodArray<z.ZodObject<{
|
|
80
|
-
name: z.ZodString;
|
|
81
|
-
description: z.ZodOptional<z.ZodString>;
|
|
82
|
-
}, z.core.$strip>>;
|
|
83
|
-
}, z.core.$strip>>;
|
|
84
|
-
/** End the connection and close its Actor, which removes what was bound to it. */
|
|
85
|
-
remove: import("@agentorg/sdk").MethodSpec<z.ZodObject<{
|
|
86
|
-
connection: z.ZodString;
|
|
87
|
-
}, z.core.$strip>, z.ZodObject<{}, z.core.$strip>>;
|
|
88
|
-
}, {}, 1>;
|