@nylorun/runtime 0.14.0-beta → 0.15.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -0
- package/README.md +75 -33
- package/dist/adapters/execution/restate.js +2 -2
- package/dist/adapters/replication/pgoutput.d.ts +5 -0
- package/dist/adapters/replication/pgoutput.js +4 -3
- package/dist/admin-openapi.json +184 -685
- package/dist/api/components.d.ts +308 -68
- package/dist/api/components.js +4 -5
- package/dist/api/gate/keys.d.ts +14 -0
- package/dist/api/gate/keys.js +59 -0
- package/dist/api/gate/routes.d.ts +34 -0
- package/dist/api/gate/routes.js +235 -0
- package/dist/api/http/define.js +0 -8
- package/dist/api/http/routes/access.js +25 -18
- package/dist/api/http/routes/tenant.d.ts +2 -1
- package/dist/api/http/routes/tenant.js +77 -5
- package/dist/api/http/routes/vaults.js +2 -2
- package/dist/api/openapi.js +4 -5
- package/dist/gates/contract.d.ts +51 -0
- package/dist/gates/contract.js +37 -0
- package/dist/gates/handler.d.ts +21 -0
- package/dist/gates/handler.js +55 -0
- package/dist/gates/http-client.d.ts +12 -0
- package/dist/gates/http-client.js +160 -0
- package/dist/gates/in-process.d.ts +32 -0
- package/dist/gates/in-process.js +42 -0
- package/dist/gates/inflight.d.ts +39 -0
- package/dist/gates/inflight.js +70 -0
- package/dist/gates/mcp-handler.d.ts +35 -0
- package/dist/gates/mcp-handler.js +142 -0
- package/dist/gates/meter.d.ts +24 -0
- package/dist/gates/meter.js +165 -0
- package/dist/gates/model-gate.d.ts +45 -0
- package/dist/gates/model-gate.js +1 -0
- package/dist/gates/tenant-vaults.d.ts +64 -0
- package/dist/gates/tenant-vaults.js +94 -0
- package/dist/gates/tool-calls.d.ts +51 -0
- package/dist/gates/tool-calls.js +64 -0
- package/dist/gates/tool-client.d.ts +12 -0
- package/dist/gates/tool-client.js +201 -0
- package/dist/gates/tool-contract.d.ts +116 -0
- package/dist/gates/tool-contract.js +64 -0
- package/dist/gates/tool-gate.d.ts +45 -0
- package/dist/gates/tool-gate.js +10 -0
- package/dist/host/admin-api.d.ts +7 -6
- package/dist/host/admin-api.js +9 -113
- package/dist/host/app.d.ts +1 -1
- package/dist/host/app.js +20 -24
- package/dist/host/config-for.d.ts +1 -1
- package/dist/host/config-for.js +2 -2
- package/dist/host/create-host.d.ts +5 -5
- package/dist/host/create-host.js +5 -15
- package/dist/host/execution.d.ts +20 -18
- package/dist/host/execution.js +2 -2
- package/dist/host/gates.d.ts +58 -0
- package/dist/host/gates.js +127 -0
- package/dist/host/http.d.ts +6 -1
- package/dist/host/http.js +17 -0
- package/dist/host/index.d.ts +1 -1
- package/dist/host/main.js +151 -71
- package/dist/host/stack-config.d.ts +71 -19
- package/dist/host/stack-config.js +192 -18
- package/dist/infra/database.d.ts +2 -2
- package/dist/infra/execution.d.ts +5 -5
- package/dist/infra/execution.js +8 -6
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/workers.d.ts +17 -17
- package/dist/infra/workers.js +9 -9
- package/dist/keys/client.d.ts +9 -0
- package/dist/keys/client.js +70 -0
- package/dist/keys/contract.d.ts +24 -0
- package/dist/keys/contract.js +12 -0
- package/dist/keys/keys.d.ts +41 -0
- package/dist/keys/keys.js +50 -0
- package/dist/mcp/connect.d.ts +43 -3
- package/dist/mcp/connect.js +12 -5
- package/dist/mcp/pool.d.ts +21 -3
- package/dist/mcp/pool.js +15 -2
- package/dist/openapi.json +887 -694
- package/dist/record/append.d.ts +51 -0
- package/dist/record/append.js +20 -0
- package/dist/{store/event.js → record/envelope.js} +1 -1
- package/dist/record/index.d.ts +11 -0
- package/dist/record/index.js +11 -0
- package/dist/store/postgres/connect.d.ts +15 -12
- package/dist/store/postgres/connect.js +24 -12
- package/dist/store/postgres/db.d.ts +16 -0
- package/dist/store/postgres/db.js +28 -0
- package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
- package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
- package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
- package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
- package/dist/store/postgres/migrate.d.ts +68 -0
- package/dist/store/postgres/migrate.js +183 -0
- package/dist/store/postgres/record-writer.d.ts +4 -0
- package/dist/store/postgres/record-writer.js +34 -0
- package/dist/store/postgres/record.d.ts +2 -8
- package/dist/store/postgres/record.js +50 -54
- package/dist/store/postgres/schema.d.ts +3096 -0
- package/dist/store/postgres/schema.js +363 -0
- package/dist/store/postgres/store.d.ts +2 -4
- package/dist/store/postgres/store.js +481 -567
- package/dist/store/postgres/tenant.d.ts +56 -0
- package/dist/store/postgres/tenant.js +154 -0
- package/dist/store/types.d.ts +43 -87
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +1 -1
- package/dist/streams/control.js +6 -2
- package/dist/streams/relay/types.d.ts +2 -1
- package/dist/streams/relay/types.js +2 -1
- package/dist/streams/types.d.ts +5 -0
- package/dist/tenant/advance.d.ts +7 -2
- package/dist/tenant/advance.js +95 -16
- package/dist/tenant/cause.d.ts +25 -0
- package/dist/tenant/cause.js +45 -0
- package/dist/tenant/commands.js +4 -2
- package/dist/tenant/context.d.ts +26 -3
- package/dist/tenant/delivery-token.js +17 -18
- package/dist/tenant/delivery.d.ts +1 -1
- package/dist/tenant/delivery.js +18 -13
- package/dist/tenant/effects.d.ts +15 -1
- package/dist/tenant/effects.js +93 -17
- package/dist/tenant/ephemeral.d.ts +16 -7
- package/dist/tenant/ephemeral.js +45 -111
- package/dist/tenant/history.d.ts +64 -0
- package/dist/tenant/history.js +120 -0
- package/dist/tenant/model-setting.d.ts +2 -2
- package/dist/tenant/module.d.ts +14 -28
- package/dist/tenant/module.js +131 -271
- package/dist/tenant/paths.d.ts +11 -16
- package/dist/tenant/paths.js +24 -68
- package/dist/tenant/pool.d.ts +1 -1
- package/dist/tenant/pool.js +1 -1
- package/dist/tenant/principals.d.ts +23 -11
- package/dist/tenant/principals.js +61 -49
- package/dist/tenant/runtime.d.ts +19 -1
- package/dist/tenant/runtime.js +40 -26
- package/dist/tenant/scheduler.d.ts +4 -2
- package/dist/tenant/scheduler.js +4 -4
- package/dist/tenant/session-streams.d.ts +2 -0
- package/dist/tenant/session-streams.js +8 -3
- package/dist/tenant/status.d.ts +3 -0
- package/dist/tenant/status.js +1 -1
- package/dist/tenant/store-pg.d.ts +9 -6
- package/dist/tenant/store-pg.js +60 -125
- package/dist/tenant/streams.d.ts +9 -25
- package/dist/tenant/streams.js +7 -22
- package/dist/tenant/tokens.js +16 -14
- package/dist/tenant/types.d.ts +41 -92
- package/dist/tenant/types.js +4 -15
- package/dist/tenant/worker.js +4 -4
- package/dist/vault/host-model.d.ts +31 -0
- package/dist/vault/host-model.js +57 -0
- package/dist/vault/service.d.ts +26 -15
- package/dist/vault/service.js +70 -109
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -5
- package/dist/store/memory.d.ts +0 -69
- package/dist/store/memory.js +0 -810
- package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
- package/dist/store/postgres/migrations/001_initial.js +0 -196
- package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
- package/dist/store/postgres/migrations/002_session_owner.js +0 -13
- package/dist/store/postgres/migrations/003_subject_tokens.d.ts +0 -8
- package/dist/store/postgres/migrations/003_subject_tokens.js +0 -42
- package/dist/store/postgres/migrations/004_publishable_keys.d.ts +0 -6
- package/dist/store/postgres/migrations/004_publishable_keys.js +0 -18
- package/dist/store/postgres/migrations/005_action_endpoints.d.ts +0 -6
- package/dist/store/postgres/migrations/005_action_endpoints.js +0 -32
- package/dist/store/postgres/migrations/006_drop_executors.d.ts +0 -11
- package/dist/store/postgres/migrations/006_drop_executors.js +0 -32
- package/dist/store/postgres/migrations/007_event_time.d.ts +0 -7
- package/dist/store/postgres/migrations/007_event_time.js +0 -14
- package/dist/store/postgres/migrations/008_durable_streams.d.ts +0 -13
- package/dist/store/postgres/migrations/008_durable_streams.js +0 -35
- package/dist/store/postgres/migrations/index.d.ts +0 -53
- package/dist/store/postgres/migrations/index.js +0 -96
- package/dist/store/postgres/migrations/shared/index.d.ts +0 -31
- package/dist/store/postgres/migrations/shared/index.js +0 -92
- package/dist/store/postgres/names.d.ts +0 -11
- package/dist/store/postgres/names.js +0 -42
- package/dist/store/postgres/tenants.d.ts +0 -90
- package/dist/store/postgres/tenants.js +0 -228
- package/dist/streams/relay/memory.d.ts +0 -69
- package/dist/streams/relay/memory.js +0 -176
- package/dist/tenant/envelope.d.ts +0 -7
- package/dist/tenant/envelope.js +0 -22
- package/dist/tenant/quarantine-error.d.ts +0 -6
- package/dist/tenant/quarantine-error.js +0 -9
- package/dist/tenant/quarantine.d.ts +0 -27
- package/dist/tenant/quarantine.js +0 -72
- package/dist/tenant/store-memory.d.ts +0 -16
- package/dist/tenant/store-memory.js +0 -111
- /package/dist/{store → record}/cursor.d.ts +0 -0
- /package/dist/{store → record}/cursor.js +0 -0
- /package/dist/{store/event.d.ts → record/envelope.d.ts} +0 -0
package/dist/store/memory.js
DELETED
|
@@ -1,810 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { MemoryRecord } from "../streams/relay/memory.js";
|
|
4
|
-
import { buildEvent } from "./event.js";
|
|
5
|
-
import { encodeCursor } from "./cursor.js";
|
|
6
|
-
import { OwnershipLostError } from "./ownership.js";
|
|
7
|
-
import { DOC_TABLES, } from "./types.js";
|
|
8
|
-
/** Schema version the fake reports; it has no migrations. */
|
|
9
|
-
export const MEMORY_SCHEMA_VERSION = 1;
|
|
10
|
-
function emptyState() {
|
|
11
|
-
return {
|
|
12
|
-
docs: Object.fromEntries(DOC_TABLES.map((table) => [table, new Map()])),
|
|
13
|
-
sessionMeta: new Map(),
|
|
14
|
-
heads: new Map(),
|
|
15
|
-
basin: { current: 0, retired: [] },
|
|
16
|
-
endpoints: new Map(),
|
|
17
|
-
principals: new Map(),
|
|
18
|
-
vaults: new Map(),
|
|
19
|
-
credentials: new Map(),
|
|
20
|
-
audit: [],
|
|
21
|
-
idempotency: new Map(),
|
|
22
|
-
settings: new Map(),
|
|
23
|
-
signingKeys: new Map(),
|
|
24
|
-
subjectEpochs: new Map(),
|
|
25
|
-
subjectUsage: new Map(),
|
|
26
|
-
publishableKeys: new Map(),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
const OWNERSHIP_KEYS = new Set(["owner", "epoch", "ownerExpiresAt"]);
|
|
30
|
-
const OPEN_SESSION = new Set(["running", "runnable"]);
|
|
31
|
-
const byId = (entries) => [...entries].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
32
|
-
const copy = (value) => structuredClone(value);
|
|
33
|
-
/**
|
|
34
|
-
* The data behind in-memory Session Stores: what a database is to its connections. Several
|
|
35
|
-
* `MemorySessionStore`s on one `MemoryStoreData` see each other's commits and serialize their
|
|
36
|
-
* transactions together, as several processes on one Tenant schema would; it outlives them,
|
|
37
|
-
* so a test can close a Tenant and open it again.
|
|
38
|
-
*/
|
|
39
|
-
export class MemoryStoreData {
|
|
40
|
-
/** @internal */ state = emptyState();
|
|
41
|
-
/** @internal */ queue = Promise.resolve();
|
|
42
|
-
/** The record of committed events, read back by the stream relay. */
|
|
43
|
-
record = new MemoryRecord();
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* In-memory `SessionStore` for unit tests. Not a supported profile.
|
|
47
|
-
*
|
|
48
|
-
* Transactions are serialized (one at a time), each works on a copy of the
|
|
49
|
-
* state and replaces it on commit, so a throw rolls back everything: documents,
|
|
50
|
-
* sequences, record rows and `afterCommit` callbacks. Commit listeners see
|
|
51
|
-
* only this store's commits, as with a Postgres connection.
|
|
52
|
-
*/
|
|
53
|
-
export class MemorySessionStore {
|
|
54
|
-
data;
|
|
55
|
-
tenantId;
|
|
56
|
-
active = new AsyncLocalStorage();
|
|
57
|
-
listeners = new Set();
|
|
58
|
-
now;
|
|
59
|
-
onError;
|
|
60
|
-
closed = false;
|
|
61
|
-
constructor(options,
|
|
62
|
-
/** Shared with other stores on the same data; a fresh, private one by default. */
|
|
63
|
-
data = new MemoryStoreData()) {
|
|
64
|
-
this.data = data;
|
|
65
|
-
this.tenantId = options.tenantId;
|
|
66
|
-
this.now = options.now ?? (() => new Date());
|
|
67
|
-
this.onError =
|
|
68
|
-
options.onError ??
|
|
69
|
-
((error) => queueMicrotask(() => {
|
|
70
|
-
throw error;
|
|
71
|
-
}));
|
|
72
|
-
}
|
|
73
|
-
async tx(fn) {
|
|
74
|
-
if (this.closed)
|
|
75
|
-
throw new Error("SessionStore is closed");
|
|
76
|
-
if (this.active.getStore() === this)
|
|
77
|
-
throw new Error("Nested SessionStore.tx is not allowed");
|
|
78
|
-
const release = await this.acquire();
|
|
79
|
-
let t;
|
|
80
|
-
let result;
|
|
81
|
-
try {
|
|
82
|
-
const working = copy(this.data.state);
|
|
83
|
-
t = new MemoryTx(working, this.tenantId, this.now);
|
|
84
|
-
try {
|
|
85
|
-
result = await this.active.run(this, () => fn(t));
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
t.closed = true;
|
|
89
|
-
}
|
|
90
|
-
this.data.state = working;
|
|
91
|
-
for (const change of t.recordChanges)
|
|
92
|
-
if (change.kind === "rows")
|
|
93
|
-
this.data.record.commit(change.rows);
|
|
94
|
-
else {
|
|
95
|
-
this.data.record.deleteRows(this.tenantId);
|
|
96
|
-
this.data.record.setGeneration(this.tenantId, change.generation);
|
|
97
|
-
}
|
|
98
|
-
if (t.events.length > 0) {
|
|
99
|
-
const commit = {
|
|
100
|
-
events: t.events,
|
|
101
|
-
generations: t.generations,
|
|
102
|
-
};
|
|
103
|
-
for (const listener of this.listeners) {
|
|
104
|
-
try {
|
|
105
|
-
listener(commit);
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
this.onError(error);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
|
-
release();
|
|
115
|
-
}
|
|
116
|
-
for (const callback of t.callbacks) {
|
|
117
|
-
try {
|
|
118
|
-
await callback();
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
this.onError(error);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return result;
|
|
125
|
-
}
|
|
126
|
-
onCommit(listener) {
|
|
127
|
-
this.listeners.add(listener);
|
|
128
|
-
return () => this.listeners.delete(listener);
|
|
129
|
-
}
|
|
130
|
-
record() {
|
|
131
|
-
return this.data.record;
|
|
132
|
-
}
|
|
133
|
-
async health() {
|
|
134
|
-
return {
|
|
135
|
-
ok: !this.closed,
|
|
136
|
-
schemaVersion: MEMORY_SCHEMA_VERSION,
|
|
137
|
-
expectedSchemaVersion: MEMORY_SCHEMA_VERSION,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
async close() {
|
|
141
|
-
this.closed = true;
|
|
142
|
-
await this.data.queue;
|
|
143
|
-
}
|
|
144
|
-
acquire() {
|
|
145
|
-
let release;
|
|
146
|
-
const next = new Promise((resolve) => (release = resolve));
|
|
147
|
-
const ready = this.data.queue.then(() => release);
|
|
148
|
-
this.data.queue = this.data.queue.then(() => next);
|
|
149
|
-
return ready;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
class MemoryTx {
|
|
153
|
-
s;
|
|
154
|
-
tenantId;
|
|
155
|
-
now;
|
|
156
|
-
closed = false;
|
|
157
|
-
events = [];
|
|
158
|
-
generations = [];
|
|
159
|
-
recordChanges = [];
|
|
160
|
-
callbacks = [];
|
|
161
|
-
constructor(s, tenantId, now) {
|
|
162
|
-
this.s = s;
|
|
163
|
-
this.tenantId = tenantId;
|
|
164
|
-
this.now = now;
|
|
165
|
-
}
|
|
166
|
-
check() {
|
|
167
|
-
if (this.closed)
|
|
168
|
-
throw new Error("Tx used after its transaction ended");
|
|
169
|
-
}
|
|
170
|
-
// --- documents -----------------------------------------------------------
|
|
171
|
-
async get(table, id) {
|
|
172
|
-
this.check();
|
|
173
|
-
const raw = this.s.docs[table].get(id);
|
|
174
|
-
if (raw === undefined)
|
|
175
|
-
return undefined;
|
|
176
|
-
return (table === "sessions" ? this.session(id) : JSON.parse(raw));
|
|
177
|
-
}
|
|
178
|
-
async put(table, id, body) {
|
|
179
|
-
this.check();
|
|
180
|
-
let value = body;
|
|
181
|
-
if (table === "sessions" && body && typeof body === "object") {
|
|
182
|
-
value = Object.fromEntries(Object.entries(body).filter(([key]) => !OWNERSHIP_KEYS.has(key)));
|
|
183
|
-
if (!this.s.sessionMeta.has(id))
|
|
184
|
-
this.s.sessionMeta.set(id, {
|
|
185
|
-
owner: null,
|
|
186
|
-
epoch: 0,
|
|
187
|
-
ownerExpiresAt: null,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
const json = JSON.stringify(value);
|
|
191
|
-
if (json === undefined)
|
|
192
|
-
throw new Error("Document body must be JSON");
|
|
193
|
-
this.s.docs[table].set(id, json);
|
|
194
|
-
}
|
|
195
|
-
async delete(table, id) {
|
|
196
|
-
this.check();
|
|
197
|
-
this.s.docs[table].delete(id);
|
|
198
|
-
// The session's log head stays: a session created again with this id continues its log.
|
|
199
|
-
if (table === "sessions")
|
|
200
|
-
this.s.sessionMeta.delete(id);
|
|
201
|
-
}
|
|
202
|
-
session(id) {
|
|
203
|
-
const raw = this.s.docs.sessions.get(id);
|
|
204
|
-
const meta = this.s.sessionMeta.get(id);
|
|
205
|
-
if (raw === undefined || !meta)
|
|
206
|
-
return undefined;
|
|
207
|
-
return {
|
|
208
|
-
...JSON.parse(raw),
|
|
209
|
-
owner: meta.owner,
|
|
210
|
-
epoch: meta.epoch,
|
|
211
|
-
ownerExpiresAt: meta.ownerExpiresAt,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
docs(table) {
|
|
215
|
-
return byId(this.s.docs[table]).map(([id, raw]) => [
|
|
216
|
-
id,
|
|
217
|
-
JSON.parse(raw),
|
|
218
|
-
]);
|
|
219
|
-
}
|
|
220
|
-
sessions() {
|
|
221
|
-
return byId(this.s.docs.sessions).map(([id]) => this.session(id));
|
|
222
|
-
}
|
|
223
|
-
// --- ordering ------------------------------------------------------------
|
|
224
|
-
async lockSession(id) {
|
|
225
|
-
this.check();
|
|
226
|
-
// Transactions are serialized, so the whole store is already locked.
|
|
227
|
-
return this.session(id);
|
|
228
|
-
}
|
|
229
|
-
async event(sessionId, turnId, type, payload) {
|
|
230
|
-
this.check();
|
|
231
|
-
const meta = this.s.sessionMeta.get(sessionId);
|
|
232
|
-
if (!meta)
|
|
233
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
234
|
-
let head = this.s.heads.get(sessionId);
|
|
235
|
-
if (!head)
|
|
236
|
-
this.s.heads.set(sessionId, (head = { head: 0, generation: this.s.basin.current }));
|
|
237
|
-
const event = buildEvent({
|
|
238
|
-
tenantId: this.tenantId,
|
|
239
|
-
sessionId,
|
|
240
|
-
turnId,
|
|
241
|
-
seq: head.head,
|
|
242
|
-
epoch: meta.epoch,
|
|
243
|
-
time: this.now(),
|
|
244
|
-
type,
|
|
245
|
-
payload,
|
|
246
|
-
});
|
|
247
|
-
const seq = head.head++;
|
|
248
|
-
this.recordChanges.push({
|
|
249
|
-
kind: "rows",
|
|
250
|
-
rows: [{ tenantId: this.tenantId, sessionId, seq, generation: head.generation, body: event }],
|
|
251
|
-
});
|
|
252
|
-
this.events.push(event);
|
|
253
|
-
this.generations.push(head.generation);
|
|
254
|
-
return copy(event);
|
|
255
|
-
}
|
|
256
|
-
afterCommit(fn) {
|
|
257
|
-
this.check();
|
|
258
|
-
this.callbacks.push(fn);
|
|
259
|
-
}
|
|
260
|
-
// --- ownership -----------------------------------------------------------
|
|
261
|
-
async takeOwnership(sessionId, claim) {
|
|
262
|
-
this.check();
|
|
263
|
-
const meta = this.s.sessionMeta.get(sessionId);
|
|
264
|
-
if (!meta)
|
|
265
|
-
return { status: "missing" };
|
|
266
|
-
if (meta.owner !== null &&
|
|
267
|
-
meta.ownerExpiresAt !== null &&
|
|
268
|
-
Date.parse(meta.ownerExpiresAt) > claim.now.getTime())
|
|
269
|
-
return {
|
|
270
|
-
status: "busy",
|
|
271
|
-
owner: meta.owner,
|
|
272
|
-
ownerExpiresAt: meta.ownerExpiresAt,
|
|
273
|
-
};
|
|
274
|
-
const previous = {
|
|
275
|
-
owner: meta.owner,
|
|
276
|
-
epoch: meta.epoch,
|
|
277
|
-
ownerExpiresAt: meta.ownerExpiresAt,
|
|
278
|
-
};
|
|
279
|
-
meta.epoch += 1;
|
|
280
|
-
meta.owner = claim.owner;
|
|
281
|
-
meta.ownerExpiresAt = new Date(claim.now.getTime() + claim.leaseMs).toISOString();
|
|
282
|
-
return {
|
|
283
|
-
status: "owned",
|
|
284
|
-
epoch: meta.epoch,
|
|
285
|
-
takeover: previous.owner !== null,
|
|
286
|
-
previous,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
async renewOwnership(sessionId, owner, epoch, until) {
|
|
290
|
-
this.check();
|
|
291
|
-
const meta = this.s.sessionMeta.get(sessionId);
|
|
292
|
-
if (!meta || meta.owner !== owner || meta.epoch !== epoch)
|
|
293
|
-
return false;
|
|
294
|
-
meta.ownerExpiresAt = until.toISOString();
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
async releaseOwnership(sessionId, owner, epoch) {
|
|
298
|
-
this.check();
|
|
299
|
-
const meta = this.s.sessionMeta.get(sessionId);
|
|
300
|
-
if (!meta || meta.owner !== owner || meta.epoch !== epoch)
|
|
301
|
-
return false;
|
|
302
|
-
meta.owner = null;
|
|
303
|
-
meta.ownerExpiresAt = null;
|
|
304
|
-
return true;
|
|
305
|
-
}
|
|
306
|
-
async assertEpoch(sessionId, epoch) {
|
|
307
|
-
this.check();
|
|
308
|
-
const session = this.session(sessionId);
|
|
309
|
-
if (!session)
|
|
310
|
-
throw new OwnershipLostError(sessionId, epoch, undefined);
|
|
311
|
-
if (session.epoch !== epoch)
|
|
312
|
-
throw new OwnershipLostError(sessionId, epoch, session.epoch);
|
|
313
|
-
return session;
|
|
314
|
-
}
|
|
315
|
-
// --- typed queries -------------------------------------------------------
|
|
316
|
-
async sessionsWithStatus(statuses) {
|
|
317
|
-
this.check();
|
|
318
|
-
return this.sessions().filter((s) => statuses.includes(s.status));
|
|
319
|
-
}
|
|
320
|
-
async orphanedSessions(now, limit) {
|
|
321
|
-
this.check();
|
|
322
|
-
const lease = (s) => s.owner === null || s.ownerExpiresAt === null
|
|
323
|
-
? -Infinity
|
|
324
|
-
: Date.parse(s.ownerExpiresAt);
|
|
325
|
-
return this.sessions()
|
|
326
|
-
.filter((s) => OPEN_SESSION.has(s.status) && lease(s) <= now.getTime())
|
|
327
|
-
.sort((a, b) => lease(a) - lease(b) || (a.id < b.id ? -1 : 1))
|
|
328
|
-
.slice(0, limit);
|
|
329
|
-
}
|
|
330
|
-
async listSessions(filter = {}) {
|
|
331
|
-
this.check();
|
|
332
|
-
return this.sessions().filter((s) => (filter.agentId === undefined || s.agentId === filter.agentId) &&
|
|
333
|
-
(filter.ownerUserId === undefined ||
|
|
334
|
-
s.ownerUserId === filter.ownerUserId));
|
|
335
|
-
}
|
|
336
|
-
async listDefinitions() {
|
|
337
|
-
this.check();
|
|
338
|
-
return this.docs("definitions").map(([, doc]) => doc);
|
|
339
|
-
}
|
|
340
|
-
async listSandboxes() {
|
|
341
|
-
this.check();
|
|
342
|
-
return this.docs("sandboxes").map(([, doc]) => doc);
|
|
343
|
-
}
|
|
344
|
-
actions() {
|
|
345
|
-
return this.docs("actions").map(([, doc]) => doc);
|
|
346
|
-
}
|
|
347
|
-
async pendingActions(agentId) {
|
|
348
|
-
this.check();
|
|
349
|
-
return this.actions().filter((a) => a.status === "pending" && a.agentId === agentId);
|
|
350
|
-
}
|
|
351
|
-
async deliveringCount(agentId) {
|
|
352
|
-
this.check();
|
|
353
|
-
return this.actions().filter((a) => a.status === "delivering" && a.agentId === agentId).length;
|
|
354
|
-
}
|
|
355
|
-
async pendingActionsWithEndpoint(limit) {
|
|
356
|
-
this.check();
|
|
357
|
-
return this.actions()
|
|
358
|
-
.filter((a) => a.status === "pending" && this.s.endpoints.has(a.agentId))
|
|
359
|
-
.slice(0, limit);
|
|
360
|
-
}
|
|
361
|
-
async expiredDeliveries(now, limit) {
|
|
362
|
-
this.check();
|
|
363
|
-
return this.actions()
|
|
364
|
-
.filter((a) => a.status === "delivering" &&
|
|
365
|
-
typeof a.deadlineAt === "string" &&
|
|
366
|
-
Date.parse(a.deadlineAt) <= now.getTime())
|
|
367
|
-
.sort((a, b) => Date.parse(a.deadlineAt) - Date.parse(b.deadlineAt) ||
|
|
368
|
-
(a.actionId < b.actionId ? -1 : 1))
|
|
369
|
-
.slice(0, limit);
|
|
370
|
-
}
|
|
371
|
-
async actionsForSession(sessionId, filter = {}) {
|
|
372
|
-
this.check();
|
|
373
|
-
return this.actions().filter((a) => a.sessionId === sessionId &&
|
|
374
|
-
(filter.turnId === undefined || a.turnId === filter.turnId) &&
|
|
375
|
-
(filter.statuses === undefined || filter.statuses.includes(a.status)));
|
|
376
|
-
}
|
|
377
|
-
async actionsWithStatus(statuses, filter = {}) {
|
|
378
|
-
this.check();
|
|
379
|
-
return this.actions().filter((a) => statuses.includes(a.status) &&
|
|
380
|
-
(filter.kinds === undefined || filter.kinds.includes(a.kind)));
|
|
381
|
-
}
|
|
382
|
-
effects() {
|
|
383
|
-
return this.docs("effects").map(([, doc]) => doc);
|
|
384
|
-
}
|
|
385
|
-
async invokingEffects(sessionId) {
|
|
386
|
-
return this.effectsForSession(sessionId, { statuses: ["invoking"] });
|
|
387
|
-
}
|
|
388
|
-
async effectsForSession(sessionId, filter = {}) {
|
|
389
|
-
this.check();
|
|
390
|
-
return this.effects().filter((e) => e.request?.sessionId === sessionId &&
|
|
391
|
-
(filter.turnId === undefined || e.request.turnId === filter.turnId) &&
|
|
392
|
-
(filter.statuses === undefined || filter.statuses.includes(e.status)));
|
|
393
|
-
}
|
|
394
|
-
async effectsForTurn(sessionId, turnId, statuses) {
|
|
395
|
-
return this.effectsForSession(sessionId, { turnId, statuses });
|
|
396
|
-
}
|
|
397
|
-
async effectsWithStatus(statuses, filter = {}) {
|
|
398
|
-
this.check();
|
|
399
|
-
return this.effects().filter((e) => statuses.includes(e.status) &&
|
|
400
|
-
(filter.kinds === undefined || filter.kinds.includes(e.request?.kind)));
|
|
401
|
-
}
|
|
402
|
-
async linkedSessions(workflowSessionId) {
|
|
403
|
-
this.check();
|
|
404
|
-
const out = [];
|
|
405
|
-
for (const [agentSessionId, link] of this.docs("links")) {
|
|
406
|
-
if (link.workflowSessionId !== workflowSessionId)
|
|
407
|
-
continue;
|
|
408
|
-
const session = this.session(agentSessionId);
|
|
409
|
-
if (session)
|
|
410
|
-
out.push({ agentSessionId, link, session });
|
|
411
|
-
}
|
|
412
|
-
return out;
|
|
413
|
-
}
|
|
414
|
-
async counts() {
|
|
415
|
-
this.check();
|
|
416
|
-
const sessions = this.sessions();
|
|
417
|
-
return {
|
|
418
|
-
sessions: sessions.length,
|
|
419
|
-
runningSessions: sessions.filter((s) => OPEN_SESSION.has(s.status))
|
|
420
|
-
.length,
|
|
421
|
-
pendingActions: this.actions().filter((a) => a.status === "pending" || a.status === "delivering").length,
|
|
422
|
-
uncertainEffects: this.effects().filter((e) => e.status === "uncertain")
|
|
423
|
-
.length,
|
|
424
|
-
sandboxes: this.s.docs.sandboxes.size,
|
|
425
|
-
definitions: this.s.docs.definitions.size,
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
// --- basin generations --------------------------------------------------
|
|
429
|
-
async basinGenerations() {
|
|
430
|
-
this.check();
|
|
431
|
-
return copy(this.s.basin);
|
|
432
|
-
}
|
|
433
|
-
async forgetRetiredGeneration(generation) {
|
|
434
|
-
this.check();
|
|
435
|
-
this.s.basin.retired = this.s.basin.retired.filter((g) => g !== generation);
|
|
436
|
-
}
|
|
437
|
-
// --- Action endpoints -----------------------------------------------------
|
|
438
|
-
async listEndpoints() {
|
|
439
|
-
this.check();
|
|
440
|
-
return byId(this.s.endpoints).map(([, row]) => copy(row));
|
|
441
|
-
}
|
|
442
|
-
async getEndpoint(agentId) {
|
|
443
|
-
this.check();
|
|
444
|
-
const row = this.s.endpoints.get(agentId);
|
|
445
|
-
return row && copy(row);
|
|
446
|
-
}
|
|
447
|
-
async putEndpoint(row) {
|
|
448
|
-
this.check();
|
|
449
|
-
const existing = this.s.endpoints.get(row.agentId);
|
|
450
|
-
const health = existing && existing.url === row.url ? healthOf(existing) : { consecutiveFailures: 0 };
|
|
451
|
-
this.s.endpoints.set(row.agentId, {
|
|
452
|
-
...copy(row),
|
|
453
|
-
...health,
|
|
454
|
-
createdAt: existing?.createdAt ?? row.updatedAt,
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
async deleteEndpoint(agentId) {
|
|
458
|
-
this.check();
|
|
459
|
-
this.s.endpoints.delete(agentId);
|
|
460
|
-
}
|
|
461
|
-
async recordEndpointHealth(agentId, update) {
|
|
462
|
-
this.check();
|
|
463
|
-
const row = this.s.endpoints.get(agentId);
|
|
464
|
-
if (row)
|
|
465
|
-
this.s.endpoints.set(agentId, withHealth(row, update));
|
|
466
|
-
}
|
|
467
|
-
// --- principals ----------------------------------------------------------
|
|
468
|
-
async insertPrincipal(row) {
|
|
469
|
-
this.check();
|
|
470
|
-
if (this.s.principals.has(row.id))
|
|
471
|
-
throw new Error("principals.id must be unique");
|
|
472
|
-
for (const other of this.s.principals.values())
|
|
473
|
-
if (other.tokenHash === row.tokenHash)
|
|
474
|
-
throw new Error("principals.token_hash must be unique");
|
|
475
|
-
this.s.principals.set(row.id, copy(row));
|
|
476
|
-
}
|
|
477
|
-
async principalByTokenHash(tokenHash) {
|
|
478
|
-
this.check();
|
|
479
|
-
for (const row of this.s.principals.values())
|
|
480
|
-
if (row.tokenHash === tokenHash)
|
|
481
|
-
return copy(row);
|
|
482
|
-
return undefined;
|
|
483
|
-
}
|
|
484
|
-
async principalById(id) {
|
|
485
|
-
this.check();
|
|
486
|
-
const row = this.s.principals.get(id);
|
|
487
|
-
return row && copy(row);
|
|
488
|
-
}
|
|
489
|
-
async applicationTokenHashes() {
|
|
490
|
-
this.check();
|
|
491
|
-
return byId(this.s.principals)
|
|
492
|
-
.filter(([, row]) => row.role === "application")
|
|
493
|
-
.map(([, row]) => row.tokenHash);
|
|
494
|
-
}
|
|
495
|
-
// --- vault ---------------------------------------------------------------
|
|
496
|
-
async insertVault(row) {
|
|
497
|
-
this.check();
|
|
498
|
-
if (this.s.vaults.has(row.id))
|
|
499
|
-
throw new Error("vaults.id must be unique");
|
|
500
|
-
if (row.scope === "host" &&
|
|
501
|
-
[...this.s.vaults.values()].some((v) => v.scope === "host"))
|
|
502
|
-
throw new Error("Only one host vault is allowed");
|
|
503
|
-
this.s.vaults.set(row.id, copy(row));
|
|
504
|
-
}
|
|
505
|
-
async getVault(id) {
|
|
506
|
-
this.check();
|
|
507
|
-
const row = this.s.vaults.get(id);
|
|
508
|
-
return row && copy(row);
|
|
509
|
-
}
|
|
510
|
-
async vaultsByOwner(ownerUserId) {
|
|
511
|
-
this.check();
|
|
512
|
-
return [...this.s.vaults.values()]
|
|
513
|
-
.filter((v) => v.ownerUserId === ownerUserId && v.scope === "user")
|
|
514
|
-
.sort(byCreated)
|
|
515
|
-
.map(copy);
|
|
516
|
-
}
|
|
517
|
-
async updateVaultMetadata(id, metadataJson) {
|
|
518
|
-
this.check();
|
|
519
|
-
const row = this.s.vaults.get(id);
|
|
520
|
-
if (row)
|
|
521
|
-
row.metadataJson = metadataJson;
|
|
522
|
-
}
|
|
523
|
-
async deleteVault(id) {
|
|
524
|
-
this.check();
|
|
525
|
-
for (const [credentialId, row] of this.s.credentials)
|
|
526
|
-
if (row.vaultId === id)
|
|
527
|
-
this.s.credentials.delete(credentialId);
|
|
528
|
-
this.s.vaults.delete(id);
|
|
529
|
-
}
|
|
530
|
-
async insertCredential(row) {
|
|
531
|
-
this.check();
|
|
532
|
-
if (this.s.credentials.has(row.id))
|
|
533
|
-
throw new Error("vault_credentials.id must be unique");
|
|
534
|
-
if (!this.s.vaults.has(row.vaultId))
|
|
535
|
-
throw new Error("vault_credentials.vault_id references a missing vault");
|
|
536
|
-
this.s.credentials.set(row.id, copy(row));
|
|
537
|
-
}
|
|
538
|
-
async getCredential(id) {
|
|
539
|
-
this.check();
|
|
540
|
-
const row = this.s.credentials.get(id);
|
|
541
|
-
return row && copy(row);
|
|
542
|
-
}
|
|
543
|
-
async credentialsForVault(vaultId, filter = {}) {
|
|
544
|
-
this.check();
|
|
545
|
-
return [...this.s.credentials.values()]
|
|
546
|
-
.filter((c) => c.vaultId === vaultId &&
|
|
547
|
-
(filter.type === undefined || c.type === filter.type))
|
|
548
|
-
.sort(byCreated)
|
|
549
|
-
.map(copy);
|
|
550
|
-
}
|
|
551
|
-
async updateCredential(vaultId, id, patch) {
|
|
552
|
-
this.check();
|
|
553
|
-
const row = this.s.credentials.get(id);
|
|
554
|
-
if (!row || row.vaultId !== vaultId)
|
|
555
|
-
return false;
|
|
556
|
-
const defined = Object.fromEntries(Object.entries(copy(patch)).filter(([, value]) => value !== undefined));
|
|
557
|
-
this.s.credentials.set(id, { ...row, ...defined });
|
|
558
|
-
return true;
|
|
559
|
-
}
|
|
560
|
-
async deleteCredential(vaultId, id) {
|
|
561
|
-
this.check();
|
|
562
|
-
const row = this.s.credentials.get(id);
|
|
563
|
-
if (!row || row.vaultId !== vaultId)
|
|
564
|
-
return false;
|
|
565
|
-
this.s.credentials.delete(id);
|
|
566
|
-
return true;
|
|
567
|
-
}
|
|
568
|
-
async countCredentials() {
|
|
569
|
-
this.check();
|
|
570
|
-
return this.s.credentials.size;
|
|
571
|
-
}
|
|
572
|
-
async insertVaultAudit(row) {
|
|
573
|
-
this.check();
|
|
574
|
-
this.s.audit.push(copy(row));
|
|
575
|
-
}
|
|
576
|
-
async vaultAudit(filter = {}) {
|
|
577
|
-
this.check();
|
|
578
|
-
return this.s.audit
|
|
579
|
-
.filter((r) => filter.vaultId === undefined || r.vaultId === filter.vaultId)
|
|
580
|
-
.slice(0, filter.limit ?? Infinity)
|
|
581
|
-
.map(copy);
|
|
582
|
-
}
|
|
583
|
-
async getVaultIdempotency(id) {
|
|
584
|
-
this.check();
|
|
585
|
-
const row = this.s.idempotency.get(id);
|
|
586
|
-
return row && copy(row);
|
|
587
|
-
}
|
|
588
|
-
async insertVaultIdempotency(row) {
|
|
589
|
-
this.check();
|
|
590
|
-
if (this.s.idempotency.has(row.id))
|
|
591
|
-
throw new Error("vault_idempotency.id must be unique");
|
|
592
|
-
this.s.idempotency.set(row.id, copy(row));
|
|
593
|
-
}
|
|
594
|
-
// --- subject tokens ------------------------------------------------------
|
|
595
|
-
/** Transactions are serialized already. */
|
|
596
|
-
async lockSigningKeys() {
|
|
597
|
-
this.check();
|
|
598
|
-
}
|
|
599
|
-
async insertSigningKey(row) {
|
|
600
|
-
this.check();
|
|
601
|
-
if (this.s.signingKeys.has(row.id))
|
|
602
|
-
throw new Error("signing_keys.id must be unique");
|
|
603
|
-
if (row.state !== "revoked")
|
|
604
|
-
for (const other of this.s.signingKeys.values())
|
|
605
|
-
if (other.state === row.state)
|
|
606
|
-
throw new Error(`signing_keys allows one ${row.state} key`);
|
|
607
|
-
this.s.signingKeys.set(row.id, copy(row));
|
|
608
|
-
}
|
|
609
|
-
async signingKey(id) {
|
|
610
|
-
this.check();
|
|
611
|
-
const row = this.s.signingKeys.get(id);
|
|
612
|
-
return row && copy(row);
|
|
613
|
-
}
|
|
614
|
-
async signingKeys(states) {
|
|
615
|
-
this.check();
|
|
616
|
-
return [...this.s.signingKeys.values()]
|
|
617
|
-
.filter((row) => states === undefined || states.includes(row.state))
|
|
618
|
-
.sort(byCreated)
|
|
619
|
-
.map(copy);
|
|
620
|
-
}
|
|
621
|
-
async setSigningKeyState(id, from, to, at) {
|
|
622
|
-
this.check();
|
|
623
|
-
const row = this.s.signingKeys.get(id);
|
|
624
|
-
if (!row || row.state !== from)
|
|
625
|
-
return false;
|
|
626
|
-
if (to !== "revoked")
|
|
627
|
-
for (const other of this.s.signingKeys.values())
|
|
628
|
-
if (other.id !== id && other.state === to)
|
|
629
|
-
throw new Error(`signing_keys allows one ${to} key`);
|
|
630
|
-
this.s.signingKeys.set(id, { ...row, ...signingKeyStamp(to, at), state: to });
|
|
631
|
-
return true;
|
|
632
|
-
}
|
|
633
|
-
async countSigningKeys() {
|
|
634
|
-
this.check();
|
|
635
|
-
return this.s.signingKeys.size;
|
|
636
|
-
}
|
|
637
|
-
async subjectEpoch(subject) {
|
|
638
|
-
this.check();
|
|
639
|
-
return this.s.subjectEpochs.get(subject) ?? 0;
|
|
640
|
-
}
|
|
641
|
-
async subjectEpochs(subjects) {
|
|
642
|
-
this.check();
|
|
643
|
-
const out = new Map();
|
|
644
|
-
for (const subject of subjects) {
|
|
645
|
-
const epoch = this.s.subjectEpochs.get(subject);
|
|
646
|
-
if (epoch !== undefined)
|
|
647
|
-
out.set(subject, epoch);
|
|
648
|
-
}
|
|
649
|
-
return out;
|
|
650
|
-
}
|
|
651
|
-
async bumpSubjectEpoch(subject, _at) {
|
|
652
|
-
this.check();
|
|
653
|
-
const epoch = (this.s.subjectEpochs.get(subject) ?? 0) + 1;
|
|
654
|
-
this.s.subjectEpochs.set(subject, epoch);
|
|
655
|
-
return epoch;
|
|
656
|
-
}
|
|
657
|
-
async lockSubjectUsage(initial) {
|
|
658
|
-
this.check();
|
|
659
|
-
// Transactions are serialized, so the row is as good as locked.
|
|
660
|
-
const row = this.s.subjectUsage.get(initial.subject);
|
|
661
|
-
if (row)
|
|
662
|
-
return copy(row);
|
|
663
|
-
this.s.subjectUsage.set(initial.subject, copy(initial));
|
|
664
|
-
return copy(initial);
|
|
665
|
-
}
|
|
666
|
-
async putSubjectUsage(row) {
|
|
667
|
-
this.check();
|
|
668
|
-
this.s.subjectUsage.set(row.subject, copy(row));
|
|
669
|
-
}
|
|
670
|
-
async countOwnerSessions(ownerUserId, statuses) {
|
|
671
|
-
this.check();
|
|
672
|
-
return this.sessions().filter((s) => s.ownerUserId === ownerUserId &&
|
|
673
|
-
statuses.includes(s.status ?? "")).length;
|
|
674
|
-
}
|
|
675
|
-
// --- publishable keys ----------------------------------------------------
|
|
676
|
-
async insertPublishableKey(row) {
|
|
677
|
-
this.check();
|
|
678
|
-
for (const other of this.s.publishableKeys.values())
|
|
679
|
-
if (other.id === row.id || other.key === row.key || other.name === row.name)
|
|
680
|
-
throw new Error("publishable_keys id, key and name must be unique");
|
|
681
|
-
this.s.publishableKeys.set(row.id, copy(row));
|
|
682
|
-
}
|
|
683
|
-
async publishableKeyByKey(key) {
|
|
684
|
-
this.check();
|
|
685
|
-
for (const row of this.s.publishableKeys.values())
|
|
686
|
-
if (row.key === key)
|
|
687
|
-
return copy(row);
|
|
688
|
-
return undefined;
|
|
689
|
-
}
|
|
690
|
-
async publishableKey(id) {
|
|
691
|
-
this.check();
|
|
692
|
-
const row = this.s.publishableKeys.get(id);
|
|
693
|
-
return row && copy(row);
|
|
694
|
-
}
|
|
695
|
-
async publishableKeys() {
|
|
696
|
-
this.check();
|
|
697
|
-
return [...this.s.publishableKeys.values()].sort(byCreated).map(copy);
|
|
698
|
-
}
|
|
699
|
-
async updatePublishableKey(id, patch) {
|
|
700
|
-
this.check();
|
|
701
|
-
const row = this.s.publishableKeys.get(id);
|
|
702
|
-
if (!row)
|
|
703
|
-
return false;
|
|
704
|
-
this.s.publishableKeys.set(id, { ...row, ...copy(patch) });
|
|
705
|
-
return true;
|
|
706
|
-
}
|
|
707
|
-
// --- settings ------------------------------------------------------------
|
|
708
|
-
async getSetting(key) {
|
|
709
|
-
this.check();
|
|
710
|
-
return this.s.settings.get(key);
|
|
711
|
-
}
|
|
712
|
-
async putSetting(key, value) {
|
|
713
|
-
this.check();
|
|
714
|
-
this.s.settings.set(key, value);
|
|
715
|
-
}
|
|
716
|
-
// --- reset ---------------------------------------------------------------
|
|
717
|
-
async reset(scope) {
|
|
718
|
-
this.check();
|
|
719
|
-
if (scope === "sessions" || scope === "all") {
|
|
720
|
-
for (const table of [
|
|
721
|
-
"sessions",
|
|
722
|
-
"commands",
|
|
723
|
-
"checkpoints",
|
|
724
|
-
"effects",
|
|
725
|
-
"actions",
|
|
726
|
-
"links",
|
|
727
|
-
])
|
|
728
|
-
this.s.docs[table].clear();
|
|
729
|
-
this.s.sessionMeta.clear();
|
|
730
|
-
this.s.heads.clear();
|
|
731
|
-
this.s.subjectUsage.clear();
|
|
732
|
-
this.s.basin = {
|
|
733
|
-
current: this.s.basin.current + 1,
|
|
734
|
-
retired: [...this.s.basin.retired, this.s.basin.current],
|
|
735
|
-
};
|
|
736
|
-
this.recordChanges.push({ kind: "reset", generation: this.s.basin.current });
|
|
737
|
-
}
|
|
738
|
-
if (scope === "sandboxes" || scope === "all")
|
|
739
|
-
this.s.docs.sandboxes.clear();
|
|
740
|
-
if (scope === "all") {
|
|
741
|
-
this.s.docs.definitions.clear();
|
|
742
|
-
this.s.endpoints.clear();
|
|
743
|
-
for (const vault of [...this.s.vaults.values()])
|
|
744
|
-
if (vault.scope !== "host")
|
|
745
|
-
await this.deleteVault(vault.id);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
/** The time column a signing key's new state stamps. */
|
|
750
|
-
export function signingKeyStamp(state, at) {
|
|
751
|
-
if (state === "current")
|
|
752
|
-
return { activatedAt: at };
|
|
753
|
-
if (state === "previous")
|
|
754
|
-
return { retiredAt: at };
|
|
755
|
-
if (state === "revoked")
|
|
756
|
-
return { revokedAt: at };
|
|
757
|
-
return {};
|
|
758
|
-
}
|
|
759
|
-
function byCreated(a, b) {
|
|
760
|
-
if (a.createdAt !== b.createdAt)
|
|
761
|
-
return a.createdAt < b.createdAt ? -1 : 1;
|
|
762
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
763
|
-
}
|
|
764
|
-
const HEALTH_KEYS = [
|
|
765
|
-
"lastDeliveryAt",
|
|
766
|
-
"lastSuccessAt",
|
|
767
|
-
"lastErrorCode",
|
|
768
|
-
"lastErrorMessage",
|
|
769
|
-
"consecutiveFailures",
|
|
770
|
-
"servedImplementationVersion",
|
|
771
|
-
"servedManifestHash",
|
|
772
|
-
];
|
|
773
|
-
/** The health fields of an endpoint row. */
|
|
774
|
-
function healthOf(row) {
|
|
775
|
-
const health = {};
|
|
776
|
-
for (const key of HEALTH_KEYS)
|
|
777
|
-
if (row[key] !== undefined)
|
|
778
|
-
health[key] = row[key];
|
|
779
|
-
return { ...health, consecutiveFailures: row.consecutiveFailures };
|
|
780
|
-
}
|
|
781
|
-
/** `row` after one health observation (the same rules as the Postgres store). */
|
|
782
|
-
function withHealth(row, update) {
|
|
783
|
-
switch (update.kind) {
|
|
784
|
-
case "success": {
|
|
785
|
-
const { lastErrorCode: _c, lastErrorMessage: _m, ...rest } = row;
|
|
786
|
-
return {
|
|
787
|
-
...rest,
|
|
788
|
-
lastDeliveryAt: update.at,
|
|
789
|
-
lastSuccessAt: update.at,
|
|
790
|
-
consecutiveFailures: 0,
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
case "failure":
|
|
794
|
-
return {
|
|
795
|
-
...row,
|
|
796
|
-
lastDeliveryAt: update.at,
|
|
797
|
-
lastErrorCode: update.code,
|
|
798
|
-
lastErrorMessage: update.message,
|
|
799
|
-
consecutiveFailures: row.consecutiveFailures + 1,
|
|
800
|
-
};
|
|
801
|
-
case "served": {
|
|
802
|
-
const { servedManifestHash: _h, ...rest } = row;
|
|
803
|
-
return {
|
|
804
|
-
...rest,
|
|
805
|
-
servedImplementationVersion: update.implementationVersion,
|
|
806
|
-
...(update.manifestHash === undefined ? {} : { servedManifestHash: update.manifestHash }),
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|