@agentorg/cli 0.0.5 → 0.0.7
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 +23 -104
- 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/hub-actor.d.ts +2 -7
- package/dist/node_modules/@agentorg/hub/dist/src/hub-actor.js +4 -144
- package/dist/node_modules/@agentorg/hub/dist/src/index.d.ts +1 -3
- package/dist/node_modules/@agentorg/hub/dist/src/index.js +1 -3
- package/dist/node_modules/@agentorg/hub/dist/src/router.d.ts +9 -58
- package/dist/node_modules/@agentorg/hub/dist/src/router.js +41 -414
- package/dist/node_modules/@agentorg/hub/dist/src/state.d.ts +17 -317
- package/dist/node_modules/@agentorg/hub/dist/src/state.js +42 -536
- package/dist/node_modules/@agentorg/hub/dist/src/validate.d.ts +1 -3
- package/dist/node_modules/@agentorg/hub/dist/src/validate.js +19 -17
- package/dist/node_modules/@agentorg/hub/package.json +0 -3
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.d.ts +119 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.js +96 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-settings.d.ts +42 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-settings.js +53 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/chat.d.ts +93 -38
- package/dist/node_modules/@agentorg/interfaces/dist/src/chat.js +71 -22
- 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/hub.d.ts +16 -286
- package/dist/node_modules/@agentorg/interfaces/dist/src/hub.js +17 -159
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.d.ts +9 -9
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.js +9 -9
- package/dist/node_modules/@agentorg/interfaces/dist/src/kv.d.ts +2 -2
- package/dist/node_modules/@agentorg/interfaces/dist/src/kv.js +2 -2
- 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 +626 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.js +349 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/tools.d.ts +39 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/tools.js +43 -0
- 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 +75 -0
- package/dist/node_modules/@agentorg/looper/dist/src/loop.js +332 -0
- package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +71 -0
- package/dist/node_modules/@agentorg/looper/dist/src/looper.js +282 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +99 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.js +278 -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/index.d.ts +1 -0
- package/dist/node_modules/@agentorg/manager/dist/src/index.js +1 -0
- package/dist/node_modules/@agentorg/manager/dist/src/manager.d.ts +25 -0
- package/dist/node_modules/@agentorg/manager/dist/src/manager.js +680 -0
- package/dist/node_modules/@agentorg/manager/dist/src/skills.d.ts +27 -0
- package/dist/node_modules/@agentorg/manager/dist/src/skills.js +152 -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/manager/package.json +30 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/connection.d.ts +34 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/connection.js +296 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/index.d.ts +4 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/index.js +4 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/kinds.d.ts +158 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/kinds.js +164 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/provider.d.ts +22 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/provider.js +197 -0
- 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/mcp/package.json +31 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/cli.js +3 -23
- package/dist/node_modules/@agentorg/sdk/dist/src/client.d.ts +4 -25
- package/dist/node_modules/@agentorg/sdk/dist/src/client.js +5 -55
- package/dist/node_modules/@agentorg/sdk/dist/src/errors.d.ts +1 -1
- package/dist/node_modules/@agentorg/sdk/dist/src/errors.js +3 -3
- 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 +3 -2
- package/dist/node_modules/@agentorg/sdk/dist/src/index.js +2 -1
- package/dist/node_modules/@agentorg/sdk/dist/src/interface.d.ts +6 -34
- package/dist/node_modules/@agentorg/sdk/dist/src/interface.js +3 -24
- package/dist/node_modules/@agentorg/sdk/dist/src/module-host.d.ts +8 -17
- package/dist/node_modules/@agentorg/sdk/dist/src/module-host.js +15 -87
- package/dist/node_modules/@agentorg/sdk/dist/src/module.d.ts +10 -76
- package/dist/node_modules/@agentorg/sdk/dist/src/module.js +3 -23
- package/dist/node_modules/@agentorg/sdk/dist/src/person-commands.js +0 -5
- package/dist/node_modules/@agentorg/sdk/dist/src/runtime.d.ts +3 -27
- package/dist/node_modules/@agentorg/sdk/dist/src/runtime.js +5 -100
- package/dist/node_modules/@agentorg/sdk/dist/src/testing.d.ts +7 -8
- package/dist/node_modules/@agentorg/sdk/dist/src/testing.js +12 -24
- package/dist/node_modules/@agentorg/sdk/package.json +4 -37
- package/dist/node_modules/@agentorg/shell/dist/app.css +4624 -739
- package/dist/node_modules/@agentorg/shell/dist/app.js +245 -202
- package/dist/node_modules/@agentorg/shell/package.json +23 -10
- package/dist/node_modules/@agentorg/wire/dist/src/frames.d.ts +2 -3
- package/dist/node_modules/@agentorg/wire/dist/src/frames.js +2 -3
- package/dist/node_modules/@agentorg/wire/dist/src/ops.d.ts +5 -34
- package/dist/node_modules/@agentorg/wire/dist/src/ops.js +2 -17
- package/dist/node_modules/assistant/dist/src/chats.d.ts +92 -0
- package/dist/node_modules/assistant/dist/src/chats.js +357 -0
- package/dist/node_modules/assistant/dist/src/engine.d.ts +42 -0
- package/dist/node_modules/assistant/dist/src/engine.js +160 -0
- package/dist/node_modules/assistant/dist/src/local-store.d.ts +3 -0
- package/dist/node_modules/assistant/dist/src/local-store.js +58 -0
- package/dist/node_modules/assistant/dist/src/provider.d.ts +21 -0
- package/dist/node_modules/assistant/dist/src/provider.js +259 -0
- package/dist/node_modules/assistant/dist/src/settings.d.ts +17 -0
- package/dist/node_modules/assistant/dist/src/settings.js +43 -0
- package/dist/node_modules/assistant/dist/src/tools.d.ts +57 -0
- package/dist/node_modules/assistant/dist/src/tools.js +102 -0
- package/dist/node_modules/assistant/package.json +30 -0
- package/dist/node_modules/chatgpt/dist/src/account.d.ts +52 -0
- package/dist/node_modules/chatgpt/dist/src/account.js +270 -0
- package/dist/node_modules/chatgpt/dist/src/encoding.d.ts +7 -0
- package/dist/node_modules/chatgpt/dist/src/encoding.js +20 -0
- package/dist/node_modules/chatgpt/dist/src/file-store.d.ts +9 -0
- package/dist/node_modules/chatgpt/dist/src/file-store.js +68 -0
- package/dist/node_modules/chatgpt/dist/src/index.d.ts +1 -0
- package/dist/node_modules/chatgpt/dist/src/index.js +1 -0
- package/dist/node_modules/chatgpt/dist/src/jwt.d.ts +15 -0
- package/dist/node_modules/chatgpt/dist/src/jwt.js +37 -0
- package/dist/node_modules/chatgpt/dist/src/oauth.d.ts +43 -0
- package/dist/node_modules/chatgpt/dist/src/oauth.js +104 -0
- package/dist/node_modules/chatgpt/dist/src/program.d.ts +23 -0
- package/dist/node_modules/chatgpt/dist/src/program.js +41 -0
- package/dist/node_modules/chatgpt/dist/src/responses.d.ts +54 -0
- package/dist/node_modules/chatgpt/dist/src/responses.js +300 -0
- package/dist/node_modules/chatgpt/dist/src/serve.d.ts +65 -0
- package/dist/node_modules/chatgpt/dist/src/serve.js +46 -0
- package/dist/node_modules/chatgpt/package.json +33 -0
- package/dist/src/api.d.ts +5 -34
- package/dist/src/api.js +10 -68
- package/dist/src/cli.js +3 -7
- package/dist/src/folders.d.ts +2 -0
- package/dist/src/folders.js +45 -0
- package/dist/src/gateway.d.ts +13 -8
- package/dist/src/gateway.js +119 -296
- package/dist/src/index.d.ts +4 -5
- package/dist/src/index.js +3 -4
- package/dist/src/live.d.ts +17 -0
- package/dist/src/live.js +109 -0
- package/dist/src/notes.d.ts +11 -0
- package/dist/src/notes.js +64 -0
- package/dist/src/oauth.d.ts +16 -0
- package/dist/src/oauth.js +46 -0
- package/dist/src/programs.d.ts +36 -0
- package/dist/src/programs.js +198 -0
- package/dist/src/signin.d.ts +12 -3
- package/dist/src/signin.js +30 -6
- package/package.json +4 -11
- package/dist/bundled/index.json +0 -6
- package/dist/bundled/sha256-0e91cc3d0e9762b81abc2427b5496b20f383abcd6fb97d928a8d50e956b54d1f.agentorg +0 -0
- package/dist/bundled/sha256-1f64ca62a0209d767d6aa6c4feb50b0653d60f10bebbc301f79bd3a37e0153c1.agentorg +0 -0
- package/dist/bundled/sha256-4055f8fddf6b90f3d356543f554b2a9e9a2c4a8827a8774f8f87952dc1692331.agentorg +0 -0
- package/dist/bundled/sha256-e356ea3c1899138a8494baf521556831006f38534fa9e92ae873e7e6956c3a46.agentorg +0 -0
- package/dist/node_modules/@agentorg/hub/dist/src/build.d.ts +0 -17
- package/dist/node_modules/@agentorg/hub/dist/src/build.js +0 -120
- package/dist/node_modules/@agentorg/hub/dist/src/logs.d.ts +0 -33
- package/dist/node_modules/@agentorg/hub/dist/src/logs.js +0 -162
- package/dist/node_modules/@agentorg/hub/dist/src/net.d.ts +0 -41
- package/dist/node_modules/@agentorg/hub/dist/src/net.js +0 -265
- package/dist/node_modules/@agentorg/hub/dist/src/processes.d.ts +0 -78
- package/dist/node_modules/@agentorg/hub/dist/src/processes.js +0 -485
- package/dist/node_modules/@agentorg/hub/dist/src/storage.d.ts +0 -38
- package/dist/node_modules/@agentorg/hub/dist/src/storage.js +0 -194
- package/dist/node_modules/@agentorg/interfaces/dist/src/http-egress.d.ts +0 -6
- package/dist/node_modules/@agentorg/interfaces/dist/src/http-egress.js +0 -7
- package/dist/node_modules/@agentorg/interfaces/dist/src/installer.d.ts +0 -191
- package/dist/node_modules/@agentorg/interfaces/dist/src/installer.js +0 -136
- package/dist/node_modules/@agentorg/interfaces/dist/src/layout.d.ts +0 -47
- package/dist/node_modules/@agentorg/interfaces/dist/src/layout.js +0 -52
- package/dist/node_modules/@agentorg/interfaces/dist/src/rules.d.ts +0 -35
- package/dist/node_modules/@agentorg/interfaces/dist/src/rules.js +0 -37
- package/dist/node_modules/@agentorg/interfaces/dist/src/storage-admin.d.ts +0 -23
- package/dist/node_modules/@agentorg/interfaces/dist/src/storage-admin.js +0 -31
- package/dist/node_modules/@agentorg/interfaces/dist/src/ui-shell.d.ts +0 -14
- package/dist/node_modules/@agentorg/interfaces/dist/src/ui-shell.js +0 -26
- package/dist/node_modules/@agentorg/interfaces/dist/src/ui-view.d.ts +0 -27
- package/dist/node_modules/@agentorg/interfaces/dist/src/ui-view.js +0 -26
- package/dist/node_modules/@agentorg/kit/dist/src/chat.d.ts +0 -37
- package/dist/node_modules/@agentorg/kit/dist/src/chat.js +0 -131
- package/dist/node_modules/@agentorg/kit/dist/src/components.d.ts +0 -94
- package/dist/node_modules/@agentorg/kit/dist/src/components.js +0 -66
- package/dist/node_modules/@agentorg/kit/dist/src/icons.d.ts +0 -17
- package/dist/node_modules/@agentorg/kit/dist/src/icons.js +0 -30
- package/dist/node_modules/@agentorg/kit/dist/src/index.d.ts +0 -6
- package/dist/node_modules/@agentorg/kit/dist/src/index.js +0 -10
- package/dist/node_modules/@agentorg/kit/dist/src/markdown.d.ts +0 -9
- package/dist/node_modules/@agentorg/kit/dist/src/markdown.js +0 -14
- package/dist/node_modules/@agentorg/kit/dist/src/resizable.d.ts +0 -5
- package/dist/node_modules/@agentorg/kit/dist/src/resizable.js +0 -8
- package/dist/node_modules/@agentorg/kit/dist/src/styles.d.ts +0 -11
- package/dist/node_modules/@agentorg/kit/dist/src/styles.js +0 -166
- package/dist/node_modules/@agentorg/kit/package.json +0 -44
- package/dist/node_modules/@agentorg/sdk/dist/frame/runtime.js +0 -753
- package/dist/node_modules/@agentorg/sdk/dist/host/runner.js +0 -21963
- package/dist/node_modules/@agentorg/sdk/dist/src/build.d.ts +0 -2
- package/dist/node_modules/@agentorg/sdk/dist/src/build.js +0 -2
- package/dist/node_modules/@agentorg/sdk/dist/src/client-http.d.ts +0 -24
- package/dist/node_modules/@agentorg/sdk/dist/src/client-http.js +0 -247
- package/dist/node_modules/@agentorg/sdk/dist/src/describe.d.ts +0 -13
- package/dist/node_modules/@agentorg/sdk/dist/src/describe.js +0 -16
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-base.d.ts +0 -8
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-base.js +0 -64
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-entry.d.ts +0 -1
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-entry.js +0 -35
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-host.d.ts +0 -57
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-host.js +0 -293
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-protocol.d.ts +0 -124
- package/dist/node_modules/@agentorg/sdk/dist/src/frame-protocol.js +0 -16
- package/dist/node_modules/@agentorg/sdk/dist/src/frame.d.ts +0 -25
- package/dist/node_modules/@agentorg/sdk/dist/src/frame.js +0 -248
- package/dist/node_modules/@agentorg/sdk/dist/src/host-main.d.ts +0 -1
- package/dist/node_modules/@agentorg/sdk/dist/src/host-main.js +0 -60
- package/dist/node_modules/@agentorg/sdk/dist/src/host.d.ts +0 -25
- package/dist/node_modules/@agentorg/sdk/dist/src/host.js +0 -92
- package/dist/node_modules/@agentorg/sdk/dist/src/manifest-api.d.ts +0 -2
- package/dist/node_modules/@agentorg/sdk/dist/src/manifest-api.js +0 -2
- package/dist/node_modules/@agentorg/sdk/dist/src/manifest.d.ts +0 -56
- package/dist/node_modules/@agentorg/sdk/dist/src/manifest.js +0 -165
- package/dist/node_modules/@agentorg/sdk/dist/src/package.d.ts +0 -60
- package/dist/node_modules/@agentorg/sdk/dist/src/package.js +0 -538
- package/dist/node_modules/@agentorg/sdk/dist/src/tar.d.ts +0 -7
- package/dist/node_modules/@agentorg/sdk/dist/src/tar.js +0 -71
- package/dist/node_modules/@agentorg/shell/dist/agent-bindings.js +0 -59
- package/dist/node_modules/@agentorg/shell/src/agent-bindings.ts +0 -107
- package/dist/src/frames.d.ts +0 -23
- package/dist/src/frames.js +0 -78
- package/dist/src/layout.d.ts +0 -44
- package/dist/src/layout.js +0 -90
- package/dist/src/model.d.ts +0 -54
- package/dist/src/model.js +0 -299
- package/dist/src/rules.d.ts +0 -71
- package/dist/src/rules.js +0 -157
- package/dist/src/transfer.d.ts +0 -30
- package/dist/src/transfer.js +0 -110
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { chmodSync, closeSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync, writeSync, } from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import { join } from "node:path";
|
|
4
4
|
import { DatabaseSync } from "node:sqlite";
|
|
5
|
-
import {
|
|
5
|
+
import { hubActors, hubAudit, hubBindings, hubExports, hubGrants } from "@agentorg/interfaces";
|
|
6
6
|
import { compareInterfaces, describeInterface, hashInterface, Refused, } from "@agentorg/sdk";
|
|
7
|
-
import { readTar, treeHash, verifyArchive } from "@agentorg/sdk/manifest";
|
|
8
7
|
/** The built-in Actor that exports management. */
|
|
9
8
|
export const HUB = "hub";
|
|
10
|
-
/** The built-in Actor that keeps state: `kv@1`, one store per consumer slot. */
|
|
11
|
-
export const STORAGE = "storage";
|
|
12
|
-
/** The built-in Actor that reaches the public internet: `http.egress@1`, over HTTPS only. */
|
|
13
|
-
export const NET = "net";
|
|
14
9
|
/** The person's Actor, created at first start with every `hub.*` binding. */
|
|
15
10
|
export const PERSON = "person";
|
|
16
11
|
/** The hub's exports, each the authority for its own part of management. */
|
|
17
12
|
const HUB_EXPORTS = {
|
|
18
|
-
packages: hubPackages,
|
|
19
13
|
actors: hubActors,
|
|
20
14
|
bindings: hubBindings,
|
|
21
15
|
grants: hubGrants,
|
|
@@ -25,10 +19,6 @@ const HUB_EXPORTS = {
|
|
|
25
19
|
const NAME = /^[a-z][A-Za-z0-9_]*$/;
|
|
26
20
|
/** The longest name of a slot made at run time, so a tool named `<slot>__<method>` fits. */
|
|
27
21
|
const EXPOSED_MAX = 48;
|
|
28
|
-
/** Exposure requests one Actor may have open toward one other at a time. */
|
|
29
|
-
const MAX_OPEN_EXPOSURES = 32;
|
|
30
|
-
/** The Actors that are the Hub itself: they take no slots. */
|
|
31
|
-
const BUILTIN = new Set(["hub", "storage", "net"]);
|
|
32
22
|
/**
|
|
33
23
|
* The default name of a slot exposing `exportName` of the Actor named `providerName`: the name's
|
|
34
24
|
* ASCII words camelCased, `_`, and the export's last part; the export's part alone when the name
|
|
@@ -44,7 +34,6 @@ export function exposedName(providerName, exportName) {
|
|
|
44
34
|
const part = exportName.slice(exportName.lastIndexOf(".") + 1);
|
|
45
35
|
return (camel ? `${camel}_${part}` : part).slice(0, EXPOSED_MAX);
|
|
46
36
|
}
|
|
47
|
-
const MEMBER = /^[a-z][A-Za-z0-9_]*(\.[a-z][A-Za-z0-9_]*)?$/;
|
|
48
37
|
const now = () => new Date().toISOString();
|
|
49
38
|
const digest = (secret) => createHash("sha256").update(secret).digest();
|
|
50
39
|
/** Each entry moves the home's database up one version; `user_version` records where it is. */
|
|
@@ -76,6 +65,14 @@ const MIGRATIONS = [
|
|
|
76
65
|
// Exposure: an Actor made for another (provenance only), and requests to expose.
|
|
77
66
|
`ALTER TABLE actors ADD COLUMN installed_for TEXT;
|
|
78
67
|
ALTER TABLE requests ADD COLUMN exposed INTEGER NOT NULL DEFAULT 0;`,
|
|
68
|
+
// The built-in storage and network and the package store are gone; an earlier home's records
|
|
69
|
+
// of them go too. The packages table stays, unread.
|
|
70
|
+
`DELETE FROM bindings WHERE provider IN ('storage', 'net');
|
|
71
|
+
DELETE FROM bindings WHERE provider = 'hub' AND export = 'packages';
|
|
72
|
+
DELETE FROM declarations WHERE actor = 'hub' AND side = 'export' AND name = 'packages';
|
|
73
|
+
DELETE FROM declarations WHERE actor = 'person' AND side = 'slot' AND name = 'packages';
|
|
74
|
+
DELETE FROM declarations WHERE actor IN ('storage', 'net');
|
|
75
|
+
UPDATE actors SET state = 'closed' WHERE id IN ('storage', 'net');`,
|
|
79
76
|
];
|
|
80
77
|
/** Whether a process with this pid runs now. */
|
|
81
78
|
function alive(pid) {
|
|
@@ -130,33 +127,8 @@ const bindingOf = (row) => ({
|
|
|
130
127
|
/** A binding's columns, with whether its slot was made at run time. */
|
|
131
128
|
const BINDING = `b.*, (SELECT d.dynamic FROM declarations d
|
|
132
129
|
WHERE d.actor = b.consumer AND d.side = 'slot' AND d.name = b.slot) AS exposed`;
|
|
133
|
-
const requestOf = (row) => ({
|
|
134
|
-
requestId: String(row.id),
|
|
135
|
-
consumer: String(row.consumer),
|
|
136
|
-
slot: String(row.slot),
|
|
137
|
-
provider: String(row.provider),
|
|
138
|
-
export: String(row.export),
|
|
139
|
-
reason: String(row.reason),
|
|
140
|
-
requestedBy: String(row.requested_by),
|
|
141
|
-
at: String(row.at),
|
|
142
|
-
needs: row.needs === "slot" ? "slot" : "export",
|
|
143
|
-
...(row.exposed ? { exposed: true } : {}),
|
|
144
|
-
});
|
|
145
|
-
const other = (side) => (side === "export" ? "slot" : "export");
|
|
146
|
-
/** What a package's Actor declares: each module's slots and exports, by `module.name`. */
|
|
147
|
-
function packageDeclarations(manifest) {
|
|
148
|
-
return Object.entries(manifest.modules).flatMap(([module, m]) => [
|
|
149
|
-
["slot", m.slots],
|
|
150
|
-
["export", m.exports],
|
|
151
|
-
].flatMap(([side, members]) => Object.entries(members).map(([member, id]) => {
|
|
152
|
-
const entry = manifest.interfaces[id];
|
|
153
|
-
if (!entry)
|
|
154
|
-
throw new Refused("invalid", `The package doesn't record ${id}.`);
|
|
155
|
-
return { side, name: `${module}.${member}`, iface: { id, ...entry } };
|
|
156
|
-
})));
|
|
157
|
-
}
|
|
158
130
|
/**
|
|
159
|
-
* The core's state in its home folder: Actors,
|
|
131
|
+
* The core's state in its home folder: Actors, declarations, bindings
|
|
160
132
|
* and the audit trail, in `node:sqlite`. Every method that changes something takes its caller, and
|
|
161
133
|
* each is checked against bindings: there is no other authority.
|
|
162
134
|
*/
|
|
@@ -169,7 +141,6 @@ export class HubState {
|
|
|
169
141
|
constructor(home, events) {
|
|
170
142
|
this.home = home;
|
|
171
143
|
this.#events = events;
|
|
172
|
-
mkdirSync(join(home, "packages"), { recursive: true, mode: 0o700 });
|
|
173
144
|
chmodSync(home, 0o700);
|
|
174
145
|
const file = join(home, "hub.db");
|
|
175
146
|
this.#db = new DatabaseSync(file);
|
|
@@ -246,17 +217,6 @@ export class HubState {
|
|
|
246
217
|
}
|
|
247
218
|
this.#audit(HUB, "bootstrap", { person: PERSON });
|
|
248
219
|
}
|
|
249
|
-
if (!this.#actor(STORAGE))
|
|
250
|
-
this.#db
|
|
251
|
-
.prepare("INSERT INTO actors (id, name, package, state, credential, created_at) VALUES (?, 'storage', 'builtin', 'running', NULL, ?)")
|
|
252
|
-
.run(STORAGE, now());
|
|
253
|
-
this.#declare(STORAGE, "export", "kv", describeInterface(kv), false);
|
|
254
|
-
this.#declare(STORAGE, "export", "admin", describeInterface(storageAdmin), false);
|
|
255
|
-
if (!this.#actor(NET))
|
|
256
|
-
this.#db
|
|
257
|
-
.prepare("INSERT INTO actors (id, name, package, state, credential, created_at) VALUES (?, 'net', 'builtin', 'running', NULL, ?)")
|
|
258
|
-
.run(NET, now());
|
|
259
|
-
this.#declare(NET, "export", "egress", describeInterface(httpEgress), false);
|
|
260
220
|
for (const [name, iface] of Object.entries(HUB_EXPORTS)) {
|
|
261
221
|
const described = describeInterface(iface);
|
|
262
222
|
this.#declare(HUB, "export", name, described, false);
|
|
@@ -323,17 +283,6 @@ export class HubState {
|
|
|
323
283
|
const entry = { seq: Number(lastInsertRowid), at, actor, kind, details };
|
|
324
284
|
this.#emit(() => this.#events.audited?.(entry));
|
|
325
285
|
}
|
|
326
|
-
/** Record something the Hub itself saw happen, such as the host sandbox's status changing. */
|
|
327
|
-
auditHub(kind, details, actor = HUB) {
|
|
328
|
-
this.#transaction(() => this.#audit(actor, kind, details));
|
|
329
|
-
}
|
|
330
|
-
/** A package Actor's process exited: kept as its `lastExit`, and counted when it is restarted. */
|
|
331
|
-
recordExit(actorId, exit, restarted) {
|
|
332
|
-
this.#db
|
|
333
|
-
.prepare("UPDATE actors SET last_exit = ?, restarts = restarts + ? WHERE id = ? AND package NOT IN ('external', 'builtin')")
|
|
334
|
-
.run(JSON.stringify(exit), restarted ? 1 : 0, actorId);
|
|
335
|
-
this.#emit(() => this.#events.actorChanged?.(actorId));
|
|
336
|
-
}
|
|
337
286
|
/** Record a call's metadata: who, through which binding, which method, and how it ended. */
|
|
338
287
|
auditCall(caller,
|
|
339
288
|
/** The binding the call went through; an unbound slot has only its consumer and slot. */
|
|
@@ -343,16 +292,13 @@ export class HubState {
|
|
|
343
292
|
// ── Actors ──────────────────────────────────────────────────────────────────
|
|
344
293
|
#actor(actorId) {
|
|
345
294
|
const row = this.#db
|
|
346
|
-
.prepare("SELECT id, name, package, state
|
|
295
|
+
.prepare("SELECT id, name, package, state FROM actors WHERE id = ?")
|
|
347
296
|
.get(actorId);
|
|
348
297
|
return (row && {
|
|
349
298
|
actorId: String(row.id),
|
|
350
299
|
name: String(row.name),
|
|
351
300
|
package: String(row.package),
|
|
352
301
|
state: row.state,
|
|
353
|
-
restarts: Number(row.restarts),
|
|
354
|
-
...(row.last_exit ? { lastExit: JSON.parse(String(row.last_exit)) } : {}),
|
|
355
|
-
...(row.installed_for ? { installedFor: String(row.installed_for) } : {}),
|
|
356
302
|
});
|
|
357
303
|
}
|
|
358
304
|
/** An Actor as a refusal names it: "Name (id)", or the id alone when there's no such Actor. */
|
|
@@ -366,12 +312,6 @@ export class HubState {
|
|
|
366
312
|
throw new Refused("unknown", `No Actor ${actorId}; hub.actors.list lists them.`);
|
|
367
313
|
return actor;
|
|
368
314
|
}
|
|
369
|
-
/** The host sandbox's status changed: every package Actor's record (its `host`) differs. */
|
|
370
|
-
hostChanged() {
|
|
371
|
-
for (const actor of this.actors())
|
|
372
|
-
if (actor.package !== "external" && actor.package !== "builtin")
|
|
373
|
-
this.#emit(() => this.#events.actorChanged?.(actor.actorId));
|
|
374
|
-
}
|
|
375
315
|
actors() {
|
|
376
316
|
const rows = this.#db.prepare("SELECT id FROM actors ORDER BY created_at, id").all();
|
|
377
317
|
return rows.map((row) => this.actor(String(row.id)));
|
|
@@ -386,42 +326,24 @@ export class HubState {
|
|
|
386
326
|
if (!this.holds(caller, hubExport))
|
|
387
327
|
throw new Refused("forbidden", `${this.named(caller)} isn't bound to hub.${hubExport}@1; a manager (the person) can bind it, or do this for it.`);
|
|
388
328
|
}
|
|
389
|
-
/** Create an Actor:
|
|
329
|
+
/** Create an Actor: a program that connects itself, with a credential that comes back once. */
|
|
390
330
|
createActor(caller, input) {
|
|
391
331
|
this.#require(caller, "actors");
|
|
392
|
-
if (input.for !== undefined) {
|
|
393
|
-
if (!this.isManager(caller))
|
|
394
|
-
throw new Refused("forbidden", `Only a manager says whom an Actor is made for; ${this.named(caller)} isn't one. Create it without for.`);
|
|
395
|
-
this.#open(input.for);
|
|
396
|
-
}
|
|
397
332
|
const actorId = `act_${randomBytes(8).toString("hex")}`;
|
|
398
|
-
const credential =
|
|
399
|
-
const insert = this.#db.prepare("INSERT INTO actors (id, name, package, state, credential, created_at
|
|
400
|
-
const madeFor = input.for ?? null;
|
|
333
|
+
const credential = randomBytes(32).toString("base64url");
|
|
334
|
+
const insert = this.#db.prepare("INSERT INTO actors (id, name, package, state, credential, created_at) VALUES (?, ?, 'external', 'created', ?, ?)");
|
|
401
335
|
this.#transaction(() => {
|
|
402
|
-
|
|
403
|
-
const { manifest } = this.package(input.package);
|
|
404
|
-
insert.run(actorId, input.name, input.package, null, now(), madeFor);
|
|
405
|
-
for (const { side, name, iface } of packageDeclarations(manifest))
|
|
406
|
-
this.#declare(actorId, side, name, iface, false);
|
|
407
|
-
// A package's own wiring is applied as it is installed.
|
|
408
|
-
for (const [slot, exported] of manifest.connections)
|
|
409
|
-
this.#insertBinding({ consumer: actorId, slot, provider: actorId, export: exported }, HUB);
|
|
410
|
-
}
|
|
411
|
-
else
|
|
412
|
-
insert.run(actorId, input.name, "external", digest(credential), now(), madeFor);
|
|
336
|
+
insert.run(actorId, input.name, digest(credential), now());
|
|
413
337
|
this.#audit(caller, "actor.create", {
|
|
414
338
|
actorId,
|
|
415
339
|
name: input.name,
|
|
416
|
-
package: input.package ?? "external",
|
|
417
|
-
...(input.for === undefined ? {} : { for: input.for }),
|
|
418
340
|
});
|
|
419
341
|
this.#emit(() => this.#events.actorChanged?.(actorId));
|
|
420
342
|
for (const initial of input.bindings ?? []) {
|
|
421
343
|
const wiring = { consumer: actorId, ...initial };
|
|
422
344
|
if (!this.sides(caller, wiring).export)
|
|
423
|
-
throw new Refused("forbidden", `${this.named(caller)} can't grant ${this.named(initial.provider)}'s ${initial.export}
|
|
424
|
-
if (!
|
|
345
|
+
throw new Refused("forbidden", `${this.named(caller)} can't grant ${this.named(initial.provider)}'s ${initial.export}: only a manager, the provider or an Actor bound to that export can.`);
|
|
346
|
+
if (!this.declaration(actorId, "slot", initial.slot)) {
|
|
425
347
|
if (!NAME.test(initial.slot))
|
|
426
348
|
throw new Refused("invalid", `${JSON.stringify(initial.slot)} isn't a valid slot name.`);
|
|
427
349
|
const offered = this.declaration(initial.provider, "export", initial.export);
|
|
@@ -432,7 +354,7 @@ export class HubState {
|
|
|
432
354
|
this.#insertBinding(wiring, caller);
|
|
433
355
|
}
|
|
434
356
|
});
|
|
435
|
-
return
|
|
357
|
+
return { actorId, credential };
|
|
436
358
|
}
|
|
437
359
|
/** Whether an external Actor's credential is right, compared in constant time. */
|
|
438
360
|
authenticate(actorId, credential) {
|
|
@@ -444,53 +366,18 @@ export class HubState {
|
|
|
444
366
|
// Both sides are SHA-256 digests, so the lengths always match.
|
|
445
367
|
return timingSafeEqual(Buffer.from(row.credential), digest(credential));
|
|
446
368
|
}
|
|
447
|
-
|
|
448
|
-
this.#require(caller, "actors");
|
|
449
|
-
const actor = this.actor(actorId);
|
|
450
|
-
if (actor.package === "external" || actor.package === "builtin")
|
|
451
|
-
throw new Refused("state", `${this.named(actorId)} isn't a package Actor, so the Hub doesn't run its process.`);
|
|
452
|
-
return actor;
|
|
453
|
-
}
|
|
454
|
-
/** Refused unless the caller may start the Actor and it can be started now; changes nothing. */
|
|
455
|
-
checkStart(caller, actorId) {
|
|
456
|
-
const actor = this.#packageActor(caller, actorId);
|
|
457
|
-
if (actor.state !== "created" && actor.state !== "stopped")
|
|
458
|
-
throw new Refused("state", `${this.named(actorId)} is ${actor.state === "running" ? "already running" : "closed and can't start again; make another"}.`);
|
|
459
|
-
}
|
|
460
|
-
/** Start a package Actor: recorded as running; the Hub spawns its process. */
|
|
461
|
-
start(caller, actorId) {
|
|
462
|
-
this.checkStart(caller, actorId);
|
|
463
|
-
this.#transaction(() => {
|
|
464
|
-
this.#db.prepare("UPDATE actors SET state = 'running' WHERE id = ?").run(actorId);
|
|
465
|
-
this.#audit(caller, "actor.start", { actorId });
|
|
466
|
-
this.#emit(() => this.#events.actorChanged?.(actorId));
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
stop(caller, actorId) {
|
|
470
|
-
const actor = this.#packageActor(caller, actorId);
|
|
471
|
-
if (actor.state !== "running")
|
|
472
|
-
throw new Refused("state", `${this.named(actorId)} is ${actor.state}, not running.`);
|
|
473
|
-
this.#transaction(() => {
|
|
474
|
-
this.#db.prepare("UPDATE actors SET state = 'stopped' WHERE id = ?").run(actorId);
|
|
475
|
-
this.#audit(caller, "actor.stop", { actorId });
|
|
476
|
-
this.#emit(() => this.#events.actorChanged?.(actorId));
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
/** Close an Actor for good: its bindings both ways, its declarations and its open requests go. */
|
|
369
|
+
/** Close an Actor for good: its bindings both ways and its declarations go. */
|
|
480
370
|
closeActor(caller, actorId) {
|
|
481
371
|
this.#require(caller, "actors");
|
|
482
372
|
const actor = this.actor(actorId);
|
|
483
|
-
if (actorId === HUB || actorId === PERSON
|
|
373
|
+
if (actorId === HUB || actorId === PERSON)
|
|
484
374
|
throw new Refused("forbidden", `${this.named(actorId)} is built in and can't be closed.`);
|
|
485
375
|
if (actor.state === "closed")
|
|
486
376
|
return;
|
|
487
377
|
this.#transaction(() => {
|
|
488
|
-
const pending = this.#pending("consumer = ? OR provider = ? OR requested_by = ?", actorId, actorId, actorId);
|
|
489
378
|
this.#db.prepare("UPDATE actors SET state = 'closed' WHERE id = ?").run(actorId);
|
|
490
379
|
this.#removeBindings("consumer = ? OR provider = ?", actorId, actorId);
|
|
491
380
|
this.#db.prepare("DELETE FROM declarations WHERE actor = ?").run(actorId);
|
|
492
|
-
for (const open of pending)
|
|
493
|
-
this.#decide(open, "closed");
|
|
494
381
|
this.#audit(caller, "actor.close", { actorId });
|
|
495
382
|
this.#emit(() => this.#events.exportsChanged?.(actorId));
|
|
496
383
|
this.#emit(() => this.#events.actorChanged?.(actorId));
|
|
@@ -518,160 +405,6 @@ export class HubState {
|
|
|
518
405
|
this.#audit(by, "binding.drop", { ...wiring, why: "its interface changed" });
|
|
519
406
|
return wirings;
|
|
520
407
|
}
|
|
521
|
-
/** Close the open requests to bind through one of an Actor's slots or exports. */
|
|
522
|
-
#closeRequestsThrough(actorId, side, name) {
|
|
523
|
-
const where = side === "slot" ? "consumer = ? AND slot = ?" : "provider = ? AND export = ?";
|
|
524
|
-
for (const open of this.#pending(where, actorId, name))
|
|
525
|
-
this.#decide(open, "closed");
|
|
526
|
-
}
|
|
527
|
-
/** Refused unless the caller may upgrade the Actor to the package now; changes nothing. */
|
|
528
|
-
checkUpgrade(caller, actorId, hash) {
|
|
529
|
-
this.#require(caller, "actors");
|
|
530
|
-
// An upgrade runs new code as an Actor that keeps its bindings and its store: whatever it was
|
|
531
|
-
// granted, the new code holds. Only a manager, who could grant all of it anyway, may.
|
|
532
|
-
if (!this.isManager(caller))
|
|
533
|
-
throw new Refused("forbidden", `Only a manager may upgrade an Actor, since the new package keeps its bindings and its store; ${this.named(caller)} isn't one.`);
|
|
534
|
-
const actor = this.actor(actorId);
|
|
535
|
-
if (actorId === HUB || actorId === PERSON || actorId === STORAGE || actorId === NET)
|
|
536
|
-
throw new Refused("forbidden", `${this.named(actorId)} is built in and can't be upgraded.`);
|
|
537
|
-
if (actor.package === "external")
|
|
538
|
-
throw new Refused("state", `${this.named(actorId)} isn't a package Actor; it declares what it takes when it connects.`);
|
|
539
|
-
if (actor.state === "closed")
|
|
540
|
-
throw new Refused("state", `${this.named(actorId)} is closed for good; make another.`);
|
|
541
|
-
packageDeclarations(this.package(hash).manifest);
|
|
542
|
-
return actor;
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Run another package as the same Actor: its id, its stores and every binding whose interfaces
|
|
546
|
-
* still match stay; a binding through a slot or export the new package drops or declares as
|
|
547
|
-
* another interface is dropped, audited and returned. The new package's own wiring is applied
|
|
548
|
-
* where its slot is unbound. The process isn't touched here: a running Actor is stopped first
|
|
549
|
-
* and started after, by whoever runs processes.
|
|
550
|
-
*/
|
|
551
|
-
upgrade(caller, actorId, hash) {
|
|
552
|
-
const actor = this.checkUpgrade(caller, actorId, hash);
|
|
553
|
-
if (actor.package === hash)
|
|
554
|
-
return [];
|
|
555
|
-
const { manifest } = this.package(hash);
|
|
556
|
-
const next = packageDeclarations(manifest);
|
|
557
|
-
const declared = (side, name) => next.find((n) => n.side === side && n.name === name)?.iface;
|
|
558
|
-
const dropped = [];
|
|
559
|
-
this.#transaction(() => {
|
|
560
|
-
// What it offered at run time was its process's, which this restarts: it goes, with the
|
|
561
|
-
// bindings to it, which are returned with the rest.
|
|
562
|
-
for (const row of this.#db
|
|
563
|
-
.prepare("SELECT name FROM declarations WHERE actor = ? AND side = 'export' AND dynamic = 1")
|
|
564
|
-
.all(actorId)) {
|
|
565
|
-
dropped.push(...this.#dropBindingsOf(actorId, "export", String(row.name), caller));
|
|
566
|
-
this.#drop(actorId, "export", String(row.name));
|
|
567
|
-
}
|
|
568
|
-
// A binding stays while its two ends declare the same interface: this Actor's end as the
|
|
569
|
-
// new package declares it, the other end as it is declared now.
|
|
570
|
-
const keeps = (b) => {
|
|
571
|
-
const slot = b.consumer === actorId
|
|
572
|
-
? declared("slot", b.slot)
|
|
573
|
-
: this.declaration(b.consumer, "slot", b.slot)?.iface;
|
|
574
|
-
const exported = b.provider === actorId
|
|
575
|
-
? declared("export", b.export)
|
|
576
|
-
: this.declaration(b.provider, "export", b.export)?.iface;
|
|
577
|
-
return !!slot && !!exported && compareInterfaces(slot, exported) === "same";
|
|
578
|
-
};
|
|
579
|
-
const old = this.#db
|
|
580
|
-
.prepare("SELECT side, name, iface FROM declarations WHERE actor = ? AND dynamic = 0")
|
|
581
|
-
.all(actorId).map((row) => ({
|
|
582
|
-
side: row.side,
|
|
583
|
-
name: String(row.name),
|
|
584
|
-
iface: JSON.parse(String(row.iface)),
|
|
585
|
-
}));
|
|
586
|
-
for (const was of old) {
|
|
587
|
-
dropped.push(...this.#dropBindingsOf(actorId, was.side, was.name, caller, keeps));
|
|
588
|
-
// A request through a name the package no longer declares as it was can't be made.
|
|
589
|
-
const fresh = declared(was.side, was.name);
|
|
590
|
-
if (!fresh || compareInterfaces(was.iface, fresh) !== "same")
|
|
591
|
-
this.#closeRequestsThrough(actorId, was.side, was.name);
|
|
592
|
-
}
|
|
593
|
-
this.#db.prepare("DELETE FROM declarations WHERE actor = ? AND dynamic = 0").run(actorId);
|
|
594
|
-
for (const { side, name, iface } of next)
|
|
595
|
-
this.#declare(actorId, side, name, iface, false);
|
|
596
|
-
this.#db.prepare("UPDATE actors SET package = ? WHERE id = ?").run(hash, actorId);
|
|
597
|
-
for (const [slot, exported] of manifest.connections)
|
|
598
|
-
if (!this.binding(actorId, slot))
|
|
599
|
-
this.#insertBinding({ consumer: actorId, slot, provider: actorId, export: exported }, HUB);
|
|
600
|
-
this.#audit(caller, "actor.upgrade", { actorId, from: actor.package, to: hash, dropped });
|
|
601
|
-
this.#emit(() => this.#events.exportsChanged?.(actorId));
|
|
602
|
-
this.#emit(() => this.#events.actorChanged?.(actorId));
|
|
603
|
-
});
|
|
604
|
-
return dropped;
|
|
605
|
-
}
|
|
606
|
-
// ── Packages ────────────────────────────────────────────────────────────────
|
|
607
|
-
/** Add a `.agentorg` archive from a path on this machine, verified; adding it again is a no-op. */
|
|
608
|
-
addPackage(caller, path) {
|
|
609
|
-
this.#require(caller, "packages");
|
|
610
|
-
let bytes;
|
|
611
|
-
try {
|
|
612
|
-
bytes = new Uint8Array(readFileSync(path));
|
|
613
|
-
}
|
|
614
|
-
catch {
|
|
615
|
-
throw new Refused("unknown", `No package file at ${path}.`);
|
|
616
|
-
}
|
|
617
|
-
let expected = /^(sha256-[0-9a-f]{64})\.agentorg$/.exec(basename(path))?.[1];
|
|
618
|
-
if (!expected)
|
|
619
|
-
try {
|
|
620
|
-
expected = treeHash(readTar(bytes));
|
|
621
|
-
}
|
|
622
|
-
catch (error) {
|
|
623
|
-
throw new Refused("invalid", error instanceof Error ? error.message : String(error));
|
|
624
|
-
}
|
|
625
|
-
const verified = verifyArchive(bytes, expected);
|
|
626
|
-
if (!verified.ok)
|
|
627
|
-
throw new Refused("invalid", verified.problems.join("\n"));
|
|
628
|
-
const { hash, manifest, files } = verified;
|
|
629
|
-
if (this.#db.prepare("SELECT 1 FROM packages WHERE hash = ?").get(hash))
|
|
630
|
-
return { hash, manifest };
|
|
631
|
-
const dir = join(this.home, "packages", hash);
|
|
632
|
-
for (const [file, data] of files) {
|
|
633
|
-
const target = resolve(dir, file);
|
|
634
|
-
// verifyArchive allows only agentorg.json and dist/<module>.js; this keeps it so.
|
|
635
|
-
if (relative(dir, target).startsWith(`..${sep}`))
|
|
636
|
-
throw new Refused("invalid", `Unsafe path ${file}.`);
|
|
637
|
-
mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
|
|
638
|
-
writeFileSync(target, data, { mode: 0o600 });
|
|
639
|
-
}
|
|
640
|
-
this.#transaction(() => {
|
|
641
|
-
this.#db
|
|
642
|
-
.prepare("INSERT INTO packages (hash, name, version, manifest, added_by, added_at) VALUES (?, ?, ?, ?, ?, ?)")
|
|
643
|
-
.run(hash, manifest.name, manifest.version, JSON.stringify(manifest), caller, now());
|
|
644
|
-
this.#audit(caller, "package.add", { hash, name: manifest.name, version: manifest.version });
|
|
645
|
-
});
|
|
646
|
-
return { hash, manifest };
|
|
647
|
-
}
|
|
648
|
-
packages() {
|
|
649
|
-
const rows = this.#db
|
|
650
|
-
.prepare("SELECT hash, name, version FROM packages ORDER BY added_at, hash")
|
|
651
|
-
.all();
|
|
652
|
-
return rows.map((row) => ({
|
|
653
|
-
hash: String(row.hash),
|
|
654
|
-
name: String(row.name),
|
|
655
|
-
version: String(row.version),
|
|
656
|
-
}));
|
|
657
|
-
}
|
|
658
|
-
package(hash) {
|
|
659
|
-
const row = this.#db.prepare("SELECT manifest FROM packages WHERE hash = ?").get(hash);
|
|
660
|
-
if (!row)
|
|
661
|
-
throw new Refused("unknown", `No package ${hash}.`);
|
|
662
|
-
return { hash, manifest: JSON.parse(String(row.manifest)) };
|
|
663
|
-
}
|
|
664
|
-
/** Remove a package no live Actor runs. Its files stay until a later clean-up. */
|
|
665
|
-
removePackage(caller, hash) {
|
|
666
|
-
this.#require(caller, "packages");
|
|
667
|
-
this.package(hash);
|
|
668
|
-
if (this.#db.prepare("SELECT 1 FROM actors WHERE package = ? AND state != 'closed'").get(hash))
|
|
669
|
-
throw new Refused("in_use", `An Actor runs ${hash}.`);
|
|
670
|
-
this.#transaction(() => {
|
|
671
|
-
this.#db.prepare("DELETE FROM packages WHERE hash = ?").run(hash);
|
|
672
|
-
this.#audit(caller, "package.remove", { hash });
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
408
|
// ── Declarations: what each Actor takes and offers ──────────────────────────
|
|
676
409
|
#declare(actor, side, name, iface, dynamic) {
|
|
677
410
|
this.#db
|
|
@@ -718,9 +451,7 @@ export class HubState {
|
|
|
718
451
|
* A name it no longer declares is dropped, with its bindings.
|
|
719
452
|
*/
|
|
720
453
|
declareExternal(actorId, declares) {
|
|
721
|
-
|
|
722
|
-
if (actor.package !== "external")
|
|
723
|
-
throw new Refused("forbidden", `${this.named(actorId)} declares through its package; an Actor of another package declares otherwise.`);
|
|
454
|
+
this.#open(actorId);
|
|
724
455
|
this.#transaction(() => {
|
|
725
456
|
for (const [side, members] of [
|
|
726
457
|
["slot", declares.slots],
|
|
@@ -732,15 +463,12 @@ export class HubState {
|
|
|
732
463
|
this.#checked(iface);
|
|
733
464
|
if (side === "slot") {
|
|
734
465
|
// The Actor's own slots are its to name: an exposure under the same name yields,
|
|
735
|
-
// with its binding
|
|
466
|
+
// with its binding.
|
|
736
467
|
if (this.declaration(actorId, side, name)?.dynamic)
|
|
737
468
|
this.#drop(actorId, side, name);
|
|
738
|
-
for (const open of this.#pending("consumer = ? AND slot = ? AND exposed = 1", actorId, name))
|
|
739
|
-
this.#decide(open, "closed");
|
|
740
469
|
}
|
|
741
470
|
// A binding holds while both ends declare the same interface: redeclared as another,
|
|
742
|
-
// the name's bindings whose other end doesn't declare it are dropped
|
|
743
|
-
// close.
|
|
471
|
+
// the name's bindings whose other end doesn't declare it are dropped.
|
|
744
472
|
const known = this.declaration(actorId, side, name);
|
|
745
473
|
if (known && compareInterfaces(known.iface, iface) !== "same") {
|
|
746
474
|
this.#dropBindingsOf(actorId, side, name, actorId, (b) => {
|
|
@@ -749,7 +477,6 @@ export class HubState {
|
|
|
749
477
|
: this.declaration(b.consumer, "slot", b.slot)?.iface;
|
|
750
478
|
return !!other && compareInterfaces(iface, other) === "same";
|
|
751
479
|
});
|
|
752
|
-
this.#closeRequestsThrough(actorId, side, name);
|
|
753
480
|
}
|
|
754
481
|
this.#declare(actorId, side, name, iface, false);
|
|
755
482
|
}
|
|
@@ -772,51 +499,11 @@ export class HubState {
|
|
|
772
499
|
.prepare("DELETE FROM declarations WHERE actor = ? AND side = ? AND name = ?")
|
|
773
500
|
.run(actor, side, name);
|
|
774
501
|
const where = side === "slot" ? "consumer = ? AND slot = ?" : "provider = ? AND export = ?";
|
|
775
|
-
const pending = this.#pending(where, actor, name);
|
|
776
502
|
this.#removeBindings(where, actor, name);
|
|
777
|
-
for (const open of pending)
|
|
778
|
-
this.#decide(open, "closed");
|
|
779
|
-
}
|
|
780
|
-
/** Offer an export at run time; it lasts until withdrawn or until the Actor disconnects. */
|
|
781
|
-
offer(actorId, name, iface) {
|
|
782
|
-
this.#open(actorId);
|
|
783
|
-
if (!MEMBER.test(name))
|
|
784
|
-
throw new Refused("invalid", `${JSON.stringify(name)} isn't a valid export name.`);
|
|
785
|
-
if (this.declaration(actorId, "export", name))
|
|
786
|
-
throw new Refused("conflict", `${this.named(actorId)} already offers ${name}; offer it under another name${this.declaration(actorId, "export", name)?.dynamic ? ", or withdraw it first" : ""}.`);
|
|
787
|
-
this.#checked(iface);
|
|
788
|
-
this.#transaction(() => {
|
|
789
|
-
this.#declare(actorId, "export", name, iface, true);
|
|
790
|
-
this.#audit(actorId, "export.offer", { export: name, interface: iface.id });
|
|
791
|
-
this.#emit(() => this.#events.exportsChanged?.(actorId));
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
/** Withdraw an export offered at run time: the bindings to it end. */
|
|
795
|
-
withdraw(actorId, name) {
|
|
796
|
-
const declared = this.declaration(actorId, "export", name);
|
|
797
|
-
if (!declared?.dynamic)
|
|
798
|
-
throw new Refused("unknown", `${this.named(actorId)} offers no ${name} at run time to withdraw; only exports offered at run time can be.`);
|
|
799
|
-
this.#transaction(() => {
|
|
800
|
-
this.#drop(actorId, "export", name);
|
|
801
|
-
this.#audit(actorId, "export.withdraw", { export: name });
|
|
802
|
-
this.#emit(() => this.#events.exportsChanged?.(actorId));
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
/** An Actor's connection ended: what it offered at run time goes, with the bindings to it. */
|
|
806
|
-
disconnected(actorId) {
|
|
807
|
-
this.#transaction(() => {
|
|
808
|
-
const dynamic = this.#db
|
|
809
|
-
.prepare("SELECT name FROM declarations WHERE actor = ? AND side = 'export' AND dynamic = 1")
|
|
810
|
-
.all(actorId);
|
|
811
|
-
for (const row of dynamic)
|
|
812
|
-
this.#drop(actorId, "export", String(row.name));
|
|
813
|
-
if (dynamic.length)
|
|
814
|
-
this.#emit(() => this.#events.exportsChanged?.(actorId));
|
|
815
|
-
});
|
|
816
503
|
}
|
|
817
504
|
exports(actor) {
|
|
818
505
|
const rows = this.#db
|
|
819
|
-
.prepare(`SELECT d.actor, d.name, d.iface
|
|
506
|
+
.prepare(`SELECT d.actor, d.name, d.iface FROM declarations d JOIN actors a ON a.id = d.actor
|
|
820
507
|
WHERE d.side = 'export' AND a.state != 'closed' ${actor ? "AND d.actor = ?" : ""}
|
|
821
508
|
ORDER BY d.actor, d.name`)
|
|
822
509
|
.all(...(actor ? [actor] : []));
|
|
@@ -827,7 +514,6 @@ export class HubState {
|
|
|
827
514
|
export: String(row.name),
|
|
828
515
|
interface: iface.id,
|
|
829
516
|
hash: iface.hash,
|
|
830
|
-
dynamic: !!row.dynamic,
|
|
831
517
|
};
|
|
832
518
|
});
|
|
833
519
|
}
|
|
@@ -877,21 +563,6 @@ export class HubState {
|
|
|
877
563
|
slot: manager || caller === wiring.consumer,
|
|
878
564
|
};
|
|
879
565
|
}
|
|
880
|
-
/** Who can give the side a request needs: exactly the Actors `sides` grants it to. */
|
|
881
|
-
approvers(request) {
|
|
882
|
-
const managers = this.#db
|
|
883
|
-
.prepare("SELECT DISTINCT consumer FROM bindings WHERE provider = ? AND export = 'bindings'")
|
|
884
|
-
.all(HUB).map((m) => String(m.consumer));
|
|
885
|
-
const own = request.needs === "slot"
|
|
886
|
-
? [request.consumer]
|
|
887
|
-
: [
|
|
888
|
-
request.provider,
|
|
889
|
-
...this.#db
|
|
890
|
-
.prepare("SELECT DISTINCT consumer FROM bindings WHERE provider = ? AND export = ?")
|
|
891
|
-
.all(request.provider, request.export).map((h) => String(h.consumer)),
|
|
892
|
-
];
|
|
893
|
-
return [...new Set([...own, ...managers])];
|
|
894
|
-
}
|
|
895
566
|
/** The two ends exist, are live, and take and offer the same interface. */
|
|
896
567
|
#ends(wiring) {
|
|
897
568
|
this.#open(wiring.consumer);
|
|
@@ -927,8 +598,6 @@ export class HubState {
|
|
|
927
598
|
return;
|
|
928
599
|
if (removed.some((b) => b.consumer === PERSON && b.provider === HUB && !b.exposed))
|
|
929
600
|
throw new Refused("forbidden", "The person's hub bindings stay, so someone can always manage the Hub.");
|
|
930
|
-
// Requests whose requester held its end through these bindings can't be approved any more.
|
|
931
|
-
const open = this.#pending("1 = 1");
|
|
932
601
|
this.#db.prepare(`DELETE FROM bindings WHERE ${where}`).run(...params);
|
|
933
602
|
// A slot made at run time goes with its binding.
|
|
934
603
|
const dropExposed = this.#db.prepare("DELETE FROM declarations WHERE actor = ? AND side = 'slot' AND name = ? AND dynamic = 1");
|
|
@@ -936,35 +605,13 @@ export class HubState {
|
|
|
936
605
|
if (binding.exposed)
|
|
937
606
|
dropExposed.run(binding.consumer, binding.slot);
|
|
938
607
|
this.#emit(() => this.#events.unbound?.(removed));
|
|
939
|
-
for (const pending of open)
|
|
940
|
-
if (!this.#standing(pending.request))
|
|
941
|
-
this.#decide(pending, "closed");
|
|
942
608
|
}
|
|
943
|
-
/**
|
|
944
|
-
* Bind a slot to an export. A caller with both ends binds. A caller with one end approves an
|
|
945
|
-
* open request for this binding from the other end, if its requester still holds that end;
|
|
946
|
-
* otherwise its call becomes a request to the other end's approvers. A caller with neither end
|
|
947
|
-
* is refused.
|
|
948
|
-
*/
|
|
609
|
+
/** Bind a slot to an export: the caller must hold both ends. */
|
|
949
610
|
bind(caller, wiring) {
|
|
950
611
|
const sides = this.sides(caller, wiring);
|
|
951
|
-
if (!sides.export &&
|
|
952
|
-
throw new Refused("forbidden", `${this.named(caller)}
|
|
953
|
-
|
|
954
|
-
const has = sides.export ? "export" : "slot";
|
|
955
|
-
if (!this.#asked(wiring, has))
|
|
956
|
-
return { bound: false, requestId: this.#ask(caller, wiring, "", other(has)).requestId };
|
|
957
|
-
}
|
|
958
|
-
this.#transaction(() => {
|
|
959
|
-
// Every open request for this slot is settled: the ones for this binding are approved, and
|
|
960
|
-
// the rest can't be made any more.
|
|
961
|
-
const pending = this.#pending("consumer = ? AND slot = ?", wiring.consumer, wiring.slot);
|
|
962
|
-
this.#insertBinding(wiring, caller);
|
|
963
|
-
for (const open of pending)
|
|
964
|
-
this.#decide(open, open.request.provider === wiring.provider && open.request.export === wiring.export
|
|
965
|
-
? "approved"
|
|
966
|
-
: "closed");
|
|
967
|
-
});
|
|
612
|
+
if (!(sides.export && sides.slot))
|
|
613
|
+
throw new Refused("forbidden", `${this.named(caller)} doesn't hold both ends of this binding: the export side is ${this.named(wiring.provider)}, the slot side ${this.named(wiring.consumer)}. A manager can bind it.`);
|
|
614
|
+
this.#transaction(() => this.#insertBinding(wiring, caller));
|
|
968
615
|
return { bound: true };
|
|
969
616
|
}
|
|
970
617
|
/** Unbind a slot: its consumer, its provider or a manager may. */
|
|
@@ -986,68 +633,44 @@ export class HubState {
|
|
|
986
633
|
}
|
|
987
634
|
/**
|
|
988
635
|
* Expose an export to one Actor: a slot made on `to` at run time, typed by the export's
|
|
989
|
-
* interface and bound to it, in one step. The
|
|
990
|
-
* consumer
|
|
991
|
-
* request to the other end's approvers, who approve by exposing the same; with neither, it is
|
|
992
|
-
* refused. The same export already exposed to `to` gives that slot back.
|
|
636
|
+
* interface and bound to it, in one step. The caller must hold both ends, with `to` as the
|
|
637
|
+
* consumer. The same export already exposed to `to` gives that slot back.
|
|
993
638
|
*/
|
|
994
639
|
expose(caller, input) {
|
|
995
640
|
const to = this.#open(input.to);
|
|
996
641
|
const provider = this.#open(input.provider);
|
|
997
|
-
// Authority first
|
|
642
|
+
// Authority first: a caller without it learns nothing more.
|
|
998
643
|
const ends = { consumer: to.actorId, provider: provider.actorId, export: input.export };
|
|
999
644
|
const sides = this.sides(caller, { ...ends, slot: "" });
|
|
1000
|
-
if (!sides.export &&
|
|
1001
|
-
throw new Refused("forbidden", `${this.named(caller)} can't expose ${input.export} of ${this.named(provider.actorId)} to ${this.named(to.actorId)}:
|
|
1002
|
-
if (
|
|
645
|
+
if (!(sides.export && sides.slot))
|
|
646
|
+
throw new Refused("forbidden", `${this.named(caller)} can't expose ${input.export} of ${this.named(provider.actorId)} to ${this.named(to.actorId)}: a manager can.`);
|
|
647
|
+
if (to.actorId === HUB)
|
|
1003
648
|
throw new Refused("forbidden", `${this.named(to.actorId)} is the Hub itself and takes no slots.`);
|
|
1004
649
|
const offered = this.declaration(provider.actorId, "export", input.export);
|
|
1005
650
|
if (!offered)
|
|
1006
651
|
throw new Refused("unknown", `${this.named(provider.actorId)} offers no ${input.export}.`);
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
652
|
+
const already = this.bindings(to.actorId).find((b) => b.consumer === to.actorId &&
|
|
653
|
+
b.exposed &&
|
|
654
|
+
b.provider === provider.actorId &&
|
|
655
|
+
b.export === input.export);
|
|
1011
656
|
if (already && (input.as === undefined || input.as === already.slot))
|
|
1012
657
|
return { exposed: true, slot: already.slot };
|
|
1013
|
-
const
|
|
1014
|
-
.prepare("SELECT * FROM requests WHERE status = 'open' AND exposed = 1 AND consumer = ?")
|
|
1015
|
-
.all(to.actorId).map(requestOf);
|
|
1016
|
-
// An open request's name for this export is taken again, so approving needs no `as`.
|
|
1017
|
-
const slot = input.as ??
|
|
1018
|
-
requested.find(same)?.slot ??
|
|
1019
|
-
this.#freeName(to.actorId, exposedName(provider.name, input.export), requested);
|
|
658
|
+
const slot = input.as ?? this.#freeName(to.actorId, exposedName(provider.name, input.export));
|
|
1020
659
|
if (!NAME.test(slot) || slot.length > EXPOSED_MAX)
|
|
1021
660
|
throw new Refused("invalid", `${JSON.stringify(slot)} isn't a slot name: one word of letters, digits and _, at most ${EXPOSED_MAX}.`);
|
|
1022
|
-
// A request reserves no name: the first exposure made under it has it, and the rest close.
|
|
1023
661
|
if (this.declaration(to.actorId, "slot", slot))
|
|
1024
662
|
throw new Refused("conflict", `${this.named(to.actorId)} already has a slot ${slot}; name another with as.`);
|
|
1025
663
|
const wiring = { ...ends, slot };
|
|
1026
|
-
if (!(sides.export && sides.slot)) {
|
|
1027
|
-
const has = sides.export ? "export" : "slot";
|
|
1028
|
-
if (!this.#asked(wiring, has)) {
|
|
1029
|
-
const mine = requested.filter((r) => r.requestedBy === caller && !(same(r) && r.slot === slot));
|
|
1030
|
-
if (mine.length >= MAX_OPEN_EXPOSURES)
|
|
1031
|
-
throw new Refused("limit", `${this.named(caller)} has ${MAX_OPEN_EXPOSURES} exposures to ${this.named(to.actorId)} waiting; wait for them to be decided, or withdraw one by exposing it.`);
|
|
1032
|
-
const request = this.#ask(caller, wiring, "", other(has), true);
|
|
1033
|
-
return { exposed: false, slot, requestId: request.requestId };
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
664
|
this.#transaction(() => {
|
|
1037
|
-
// Settled: this slot's requests (approved if for this export), and this export's requests
|
|
1038
|
-
// to `to` under other names, which can't be made any more.
|
|
1039
|
-
const pending = this.#pending("consumer = ? AND (slot = ? OR (exposed = 1 AND provider = ? AND export = ?))", wiring.consumer, wiring.slot, wiring.provider, wiring.export);
|
|
1040
665
|
this.#declare(to.actorId, "slot", slot, offered.iface, true);
|
|
1041
666
|
this.#insertBinding(wiring, caller);
|
|
1042
|
-
for (const open of pending)
|
|
1043
|
-
this.#decide(open, same(open.request) && open.request.slot === slot ? "approved" : "closed");
|
|
1044
667
|
this.#audit(caller, "expose", { ...wiring });
|
|
1045
668
|
});
|
|
1046
669
|
return { exposed: true, slot };
|
|
1047
670
|
}
|
|
1048
671
|
/** A name free on `to`: `base`, else `base_2`, `base_3`…, each within the longest name. */
|
|
1049
|
-
#freeName(to, base
|
|
1050
|
-
const taken = (name) => !!this.declaration(to, "slot", name)
|
|
672
|
+
#freeName(to, base) {
|
|
673
|
+
const taken = (name) => !!this.declaration(to, "slot", name);
|
|
1051
674
|
for (let n = 1;; n++) {
|
|
1052
675
|
const suffix = n === 1 ? "" : `_${n}`;
|
|
1053
676
|
const name = base.slice(0, EXPOSED_MAX - suffix.length) + suffix;
|
|
@@ -1062,123 +685,6 @@ export class HubState {
|
|
|
1062
685
|
throw new Refused("unknown", `${this.named(to)} has no slot ${slot} made by expose; unbind a declared slot instead.`);
|
|
1063
686
|
this.unbind(caller, to, slot);
|
|
1064
687
|
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Ask for a binding the caller holds one end of; it goes to the other end's approvers. Asking
|
|
1067
|
-
* again for the same wiring while a request is open returns that request.
|
|
1068
|
-
*/
|
|
1069
|
-
request(caller, wiring, reason) {
|
|
1070
|
-
const sides = this.sides(caller, wiring);
|
|
1071
|
-
if (!sides.export && !sides.slot)
|
|
1072
|
-
throw new Refused("forbidden", `${this.named(caller)} holds neither end of this binding: the export side is ${this.named(wiring.provider)}, an Actor bound to that export, or a manager; the slot side is ${this.named(wiring.consumer)} or a manager. Ask one of them.`);
|
|
1073
|
-
if (sides.export && sides.slot)
|
|
1074
|
-
throw new Refused("state", `${this.named(caller)} holds both ends: bind it instead.`);
|
|
1075
|
-
const has = sides.export ? "export" : "slot";
|
|
1076
|
-
if (this.#asked(wiring, has))
|
|
1077
|
-
throw new Refused("state", "The other end has asked already: bind to approve.");
|
|
1078
|
-
return this.#ask(caller, wiring, reason, other(has));
|
|
1079
|
-
}
|
|
1080
|
-
/** Whether the other end has an open request for this binding that `has` would complete. */
|
|
1081
|
-
#asked(wiring, has) {
|
|
1082
|
-
return this.#db
|
|
1083
|
-
.prepare("SELECT * FROM requests WHERE status = 'open' AND needs = ? AND consumer = ? AND slot = ? AND provider = ? AND export = ?")
|
|
1084
|
-
.all(has, wiring.consumer, wiring.slot, wiring.provider, wiring.export)
|
|
1085
|
-
.map(requestOf)
|
|
1086
|
-
.some((request) => this.#standing(request));
|
|
1087
|
-
}
|
|
1088
|
-
#ask(caller, wiring, reason, needs, exposed = false) {
|
|
1089
|
-
// An exposure's slot doesn't exist until it is approved; `expose` checked its ends.
|
|
1090
|
-
if (!exposed)
|
|
1091
|
-
this.#ends(wiring);
|
|
1092
|
-
if (this.binding(wiring.consumer, wiring.slot))
|
|
1093
|
-
throw new Refused("bound", `${this.named(wiring.consumer)}'s ${wiring.slot} is already bound; unbind it first.`);
|
|
1094
|
-
const open = this.#db
|
|
1095
|
-
.prepare("SELECT * FROM requests WHERE status = 'open' AND requested_by = ? AND consumer = ? AND slot = ? AND provider = ? AND export = ?")
|
|
1096
|
-
.get(caller, wiring.consumer, wiring.slot, wiring.provider, wiring.export);
|
|
1097
|
-
if (open)
|
|
1098
|
-
return requestOf(open);
|
|
1099
|
-
const request = {
|
|
1100
|
-
requestId: `req_${randomBytes(8).toString("hex")}`,
|
|
1101
|
-
...wiring,
|
|
1102
|
-
needs,
|
|
1103
|
-
reason,
|
|
1104
|
-
requestedBy: caller,
|
|
1105
|
-
at: now(),
|
|
1106
|
-
...(exposed ? { exposed: true } : {}),
|
|
1107
|
-
};
|
|
1108
|
-
this.#transaction(() => {
|
|
1109
|
-
this.#db
|
|
1110
|
-
.prepare("INSERT INTO requests (id, consumer, slot, provider, export, reason, requested_by, at, status, needs, exposed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'open', ?, ?)")
|
|
1111
|
-
.run(request.requestId, wiring.consumer, wiring.slot, wiring.provider, wiring.export, reason, caller, request.at, needs, exposed ? 1 : 0);
|
|
1112
|
-
this.#audit(caller, "request", {
|
|
1113
|
-
...wiring,
|
|
1114
|
-
needs,
|
|
1115
|
-
requestId: request.requestId,
|
|
1116
|
-
...(exposed ? { exposed: true } : {}),
|
|
1117
|
-
});
|
|
1118
|
-
const approvers = this.approvers(request).filter((a) => a !== caller);
|
|
1119
|
-
this.#emit(() => this.#events.requested?.(request, approvers));
|
|
1120
|
-
});
|
|
1121
|
-
return request;
|
|
1122
|
-
}
|
|
1123
|
-
/** The open requests the caller could approve. */
|
|
1124
|
-
requests(caller) {
|
|
1125
|
-
const rows = this.#db
|
|
1126
|
-
.prepare("SELECT * FROM requests WHERE status = 'open' ORDER BY at")
|
|
1127
|
-
.all();
|
|
1128
|
-
return rows
|
|
1129
|
-
.map(requestOf)
|
|
1130
|
-
.filter((request) => request.requestedBy !== caller &&
|
|
1131
|
-
this.#standing(request) &&
|
|
1132
|
-
this.approvers(request).includes(caller));
|
|
1133
|
-
}
|
|
1134
|
-
/** The caller's own requests with how each stands, newest first. */
|
|
1135
|
-
mine(caller, limit = 100) {
|
|
1136
|
-
const rows = this.#db
|
|
1137
|
-
.prepare("SELECT * FROM requests WHERE requested_by = ? ORDER BY at DESC, rowid DESC LIMIT ?")
|
|
1138
|
-
.all(caller, limit);
|
|
1139
|
-
return rows.map((row) => ({ ...requestOf(row), status: row.status }));
|
|
1140
|
-
}
|
|
1141
|
-
/** A request can still be approved only while its requester holds the end it brought. */
|
|
1142
|
-
#standing(request) {
|
|
1143
|
-
return this.sides(request.requestedBy, request)[other(request.needs)];
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Open requests matching a condition, each with the approvers it went to. Taken before bindings
|
|
1147
|
-
* change, so the decision reaches the same Actors as the request.
|
|
1148
|
-
*/
|
|
1149
|
-
#pending(where, ...params) {
|
|
1150
|
-
return this.#db
|
|
1151
|
-
.prepare(`SELECT * FROM requests WHERE status = 'open' AND (${where})`)
|
|
1152
|
-
.all(...params).map((row) => {
|
|
1153
|
-
const request = requestOf(row);
|
|
1154
|
-
return {
|
|
1155
|
-
request,
|
|
1156
|
-
approvers: this.approvers(request).filter((a) => a !== request.requestedBy),
|
|
1157
|
-
};
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
#decide({ request, approvers }, status) {
|
|
1161
|
-
// A request is decided once; a later path that finds it again leaves it be.
|
|
1162
|
-
const { changes } = this.#db
|
|
1163
|
-
.prepare("UPDATE requests SET status = ? WHERE id = ? AND status = 'open'")
|
|
1164
|
-
.run(status, request.requestId);
|
|
1165
|
-
if (!changes)
|
|
1166
|
-
return;
|
|
1167
|
-
// The requester hears too; events are at most once, so it can also read the outcome in `mine`.
|
|
1168
|
-
const to = [...new Set([...approvers, request.requestedBy])];
|
|
1169
|
-
this.#emit(() => this.#events.decided?.(request, status, to));
|
|
1170
|
-
}
|
|
1171
|
-
decline(caller, requestId) {
|
|
1172
|
-
const request = this.requests(caller).find((r) => r.requestId === requestId);
|
|
1173
|
-
if (!request)
|
|
1174
|
-
throw new Refused("unknown", `No open request ${requestId} for ${this.named(caller)} to decide; hub.grants.requests lists those it can.`);
|
|
1175
|
-
this.#transaction(() => {
|
|
1176
|
-
const [pending] = this.#pending("id = ?", requestId);
|
|
1177
|
-
if (pending)
|
|
1178
|
-
this.#decide(pending, "declined");
|
|
1179
|
-
this.#audit(caller, "decline", { requestId });
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
688
|
// ── Audit ───────────────────────────────────────────────────────────────────
|
|
1183
689
|
audit(since = 0, limit = 100) {
|
|
1184
690
|
const rows = this.#db
|