@byok-sdk/server 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -22
- package/dist/index.js +16 -6
- package/dist/index.js.map +1 -1
- package/dist/stores/sqlite/index.d.ts +3 -2
- package/dist/types.d.ts +11 -4
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ live device advertises `toolset-selection` and its `configuredToolsets`
|
|
|
25
25
|
inventory contains every required ID. `machines.list()` projects that same
|
|
26
26
|
logical-ID-only inventory; MCP commands and credentials remain device-local.
|
|
27
27
|
|
|
28
|
-
MIT licensed. Node.js
|
|
28
|
+
MIT licensed. Node.js 24.15.0 or newer.
|
|
29
29
|
|
|
30
30
|
## SQLite enrollment, receipts and schema adoption
|
|
31
31
|
|
|
@@ -41,37 +41,47 @@ Unredeemed pairing codes, presence and other unmodified ports remain ephemeral.
|
|
|
41
41
|
Keep daemon OS credentials, server URL and token signer stable across restart.
|
|
42
42
|
This does not restore TaskHandle promises, subscriptions or provider processes.
|
|
43
43
|
|
|
44
|
-
This candidate writes schema
|
|
44
|
+
This candidate writes schema v4, including the write-once `claimedHarnessId`
|
|
45
|
+
stored atomically with task ownership. Stop **all** writers and take a consistent
|
|
45
46
|
SQLite backup (including WAL state or using SQLite's backup facility) before
|
|
46
|
-
explicit adoption. An already-open old writer is not stopped by the version
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
explicit adoption. An already-open old writer is not stopped by the version fence.
|
|
48
|
+
|
|
49
|
+
- `v1-to-v4` and `v2-to-v4` require no task, mailbox-message, agent-admission or
|
|
50
|
+
advanced cursor history: old in-memory receipts cannot be recovered from task
|
|
51
|
+
status or fabricated from a retry payload. A pristine poll cursor is allowed.
|
|
52
|
+
- `v3-to-v4` preserves receipts, unclaimed offers and claims with an identified
|
|
53
|
+
built-in runtime. It rejects any owned task with no `claimed_runtime`, including
|
|
54
|
+
terminal tasks: a lost custom-harness identity cannot be distinguished from an
|
|
55
|
+
identity-free legacy built-in claim. Neither the requested offer nor current
|
|
56
|
+
device inventory proves the actual claiming adapter. Existing rows receive a
|
|
57
|
+
null harness identity; migration never invents one.
|
|
58
|
+
|
|
59
|
+
Preserve rejected databases for separate reconciliation; do not delete history,
|
|
60
|
+
reset cursors or edit version markers to make adoption pass.
|
|
52
61
|
|
|
53
62
|
```ts
|
|
54
63
|
const server = createByokServer({
|
|
55
64
|
productId,
|
|
56
65
|
tokenSigner,
|
|
57
|
-
storage: { kind: 'sqlite', path, migration: '
|
|
66
|
+
storage: { kind: 'sqlite', path, migration: 'v3-to-v4' },
|
|
58
67
|
});
|
|
59
68
|
await server.close();
|
|
60
69
|
```
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
belongs to the next MINOR release; these
|
|
71
|
+
Remove `migration` from normal startup. Adoption adds the nullable harness column,
|
|
72
|
+
adds receipts for v1/v2 and an empty device directory for v1, and updates the marker
|
|
73
|
+
atomically; any failure rolls back. Existing v2/v3 devices and artifacts remain.
|
|
74
|
+
V1 enrollment was in-memory and needs explicit pairing. Unknown versions, missing
|
|
75
|
+
authorities and malformed receipt columns/primary key fail closed. Broader
|
|
76
|
+
validation of legacy table constraints is a separate tracked concern.
|
|
77
|
+
|
|
78
|
+
**Breaking storage boundary:** schema-v1/v2/v3 writers refuse v4. The public
|
|
79
|
+
`v1-to-v3` / `v2-to-v3` selectors are replaced by the corresponding target-v4
|
|
80
|
+
selectors, with no alias or silent reinterpretation: callers must update their
|
|
81
|
+
one-shot migration configuration after checking eligibility and backing up.
|
|
82
|
+
Restore of a backup requires a separate recovery procedure because it can lose
|
|
83
|
+
new state or restore old grants. This belongs to the next MINOR release; these
|
|
84
|
+
local changes are not a published upgrade.
|
|
75
85
|
|
|
76
86
|
|
|
77
87
|
## Persistent host request identity
|
package/dist/index.js
CHANGED
|
@@ -231,7 +231,9 @@ var TaskEventRelay = class {
|
|
|
231
231
|
}
|
|
232
232
|
/** The per-task feed backing `TaskHandle.events()`, replayed from the start of what is retained. */
|
|
233
233
|
events(taskId) {
|
|
234
|
-
|
|
234
|
+
const state = this.#tasks.get(taskId);
|
|
235
|
+
return state === void 0 ? { async *[Symbol.asyncIterator]() {
|
|
236
|
+
} } : state.queue.subscribe();
|
|
235
237
|
}
|
|
236
238
|
/** Settles when this task first reaches a terminal — the barrier `TaskHandle.result()` awaits. */
|
|
237
239
|
terminal(taskId) {
|
|
@@ -879,7 +881,7 @@ var DEFAULT_MAILBOX_READ_LIMIT = 50;
|
|
|
879
881
|
var DEFAULT_OBJECT_LIST_LIMIT = 100;
|
|
880
882
|
var DEFAULT_BLOB_URL_TTL_MS = 15 * 6e4;
|
|
881
883
|
var SIGNING_SECRET_BYTES = 32;
|
|
882
|
-
var SQLITE_SCHEMA_VERSION = "
|
|
884
|
+
var SQLITE_SCHEMA_VERSION = "4";
|
|
883
885
|
var RECEIPT_SCHEMA = `
|
|
884
886
|
CREATE TABLE request_receipt (
|
|
885
887
|
tenant_id TEXT NOT NULL,
|
|
@@ -923,6 +925,7 @@ CREATE TABLE IF NOT EXISTS task_attempt (
|
|
|
923
925
|
agent_ref_json TEXT,
|
|
924
926
|
owner_device_id TEXT,
|
|
925
927
|
claimed_runtime TEXT,
|
|
928
|
+
claimed_harness_id TEXT,
|
|
926
929
|
claimed_runtime_capabilities_json TEXT,
|
|
927
930
|
status TEXT NOT NULL,
|
|
928
931
|
terminal_cause TEXT,
|
|
@@ -985,8 +988,8 @@ var SqliteCoordinator = class {
|
|
|
985
988
|
const hasMetadata = this.db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'byok_sqlite_meta'").get();
|
|
986
989
|
if (hasMetadata !== void 0) {
|
|
987
990
|
const version = this.db.prepare("SELECT value FROM byok_sqlite_meta WHERE key = 'schema_version'").get();
|
|
988
|
-
if (version?.value !== SQLITE_SCHEMA_VERSION && !(version?.value === "1" && migration === "v1-to-
|
|
989
|
-
throw new Error(`Unsupported BYOK SQLite schema version ${JSON.stringify(version?.value)}; this build requires ${SQLITE_SCHEMA_VERSION}. Stop all writers, back up the database and explicitly select migration: 'v1-to-
|
|
991
|
+
if (version?.value !== SQLITE_SCHEMA_VERSION && !(version?.value === "1" && migration === "v1-to-v4" || version?.value === "2" && migration === "v2-to-v4" || version?.value === "3" && migration === "v3-to-v4")) {
|
|
992
|
+
throw new Error(`Unsupported BYOK SQLite schema version ${JSON.stringify(version?.value)}; this build requires ${SQLITE_SCHEMA_VERSION}. Stop all writers, back up the database and explicitly select migration: 'v1-to-v4' or 'v2-to-v4' for a receipt-free legacy database without task history, or 'v3-to-v4' for a database without ambiguous claimed identity. Target-v3 migration selectors are no longer supported.`);
|
|
990
993
|
}
|
|
991
994
|
for (const projection of [
|
|
992
995
|
"tenant_id, device_id, next_seq, delivered_seq, acked_seq, updated_at FROM mailbox_cursor",
|
|
@@ -999,7 +1002,7 @@ var SqliteCoordinator = class {
|
|
|
999
1002
|
]) {
|
|
1000
1003
|
this.db.prepare(`SELECT ${projection} LIMIT 0`);
|
|
1001
1004
|
}
|
|
1002
|
-
if (version?.value
|
|
1005
|
+
if (version?.value === "1" || version?.value === "2") {
|
|
1003
1006
|
for (const table of ["task_attempt", "mailbox_message", "agent_message_admission"]) {
|
|
1004
1007
|
if (this.db.prepare(`SELECT 1 FROM ${table} LIMIT 1`).get() !== void 0) {
|
|
1005
1008
|
throw new Error("Cannot migrate BYOK SQLite database with task history: historical receipts are unavailable; preserve this database for reconciliation");
|
|
@@ -1019,6 +1022,10 @@ var SqliteCoordinator = class {
|
|
|
1019
1022
|
this.db.exec(DEVICE_SCHEMA);
|
|
1020
1023
|
}
|
|
1021
1024
|
if (version?.value !== SQLITE_SCHEMA_VERSION) {
|
|
1025
|
+
if (this.db.prepare("SELECT 1 FROM task_attempt WHERE owner_device_id IS NOT NULL AND claimed_runtime IS NULL LIMIT 1").get() !== void 0) {
|
|
1026
|
+
throw new Error("Cannot migrate BYOK SQLite database with ambiguous claimed identity: historical harness identities are unavailable; preserve this database for reconciliation");
|
|
1027
|
+
}
|
|
1028
|
+
this.db.exec("ALTER TABLE task_attempt ADD COLUMN claimed_harness_id TEXT");
|
|
1022
1029
|
this.db.prepare("UPDATE byok_sqlite_meta SET value = ? WHERE key = 'schema_version'").run(SQLITE_SCHEMA_VERSION);
|
|
1023
1030
|
}
|
|
1024
1031
|
} else {
|
|
@@ -1029,6 +1036,7 @@ var SqliteCoordinator = class {
|
|
|
1029
1036
|
this.db.exec(DEVICE_SCHEMA);
|
|
1030
1037
|
this.db.prepare("INSERT INTO byok_sqlite_meta (key, value) VALUES ('schema_version', ?)").run(SQLITE_SCHEMA_VERSION);
|
|
1031
1038
|
}
|
|
1039
|
+
this.db.prepare("SELECT claimed_harness_id FROM task_attempt LIMIT 0");
|
|
1032
1040
|
this.db.prepare("SELECT tenant_id, device_id, product_id, machine_id, device_name, device_public_key, proof_key_id, proof_key_epoch, capabilities_json, harnesses_json FROM device_directory LIMIT 0");
|
|
1033
1041
|
const receiptColumns = this.db.prepare("PRAGMA table_info(request_receipt)").all();
|
|
1034
1042
|
const expectedColumns = ["tenant_id", "key", "body", "recorded_at"];
|
|
@@ -1086,6 +1094,7 @@ function taskRow(row) {
|
|
|
1086
1094
|
...row.agent_ref_json === null ? {} : { agentRef: JSON.parse(row.agent_ref_json) },
|
|
1087
1095
|
...row.owner_device_id === null ? {} : { ownerDeviceId: row.owner_device_id },
|
|
1088
1096
|
...row.claimed_runtime === null ? {} : { claimedRuntime: row.claimed_runtime },
|
|
1097
|
+
...row.claimed_harness_id === null ? {} : { claimedHarnessId: row.claimed_harness_id },
|
|
1089
1098
|
...row.claimed_runtime_capabilities_json === null ? {} : { claimedRuntimeCapabilities: JSON.parse(row.claimed_runtime_capabilities_json) },
|
|
1090
1099
|
status: row.status,
|
|
1091
1100
|
...row.terminal_cause === null ? {} : { terminalCause: row.terminal_cause },
|
|
@@ -1247,13 +1256,14 @@ var SqliteTaskAttemptStore = class {
|
|
|
1247
1256
|
claim(tenant, input) {
|
|
1248
1257
|
return this.coordinator.run((db) => {
|
|
1249
1258
|
db.prepare(
|
|
1250
|
-
`UPDATE task_attempt SET owner_device_id = ?, claimed_runtime = ?, claimed_runtime_capabilities_json = ?,
|
|
1259
|
+
`UPDATE task_attempt SET owner_device_id = ?, claimed_runtime = ?, claimed_harness_id = ?, claimed_runtime_capabilities_json = ?,
|
|
1251
1260
|
status = 'claimed', updated_at = ?
|
|
1252
1261
|
WHERE tenant_id = ? AND task_id = ? AND device_id = ? AND owner_device_id IS NULL
|
|
1253
1262
|
AND cancellation_requested_at IS NULL AND status = 'offered'`
|
|
1254
1263
|
).run(
|
|
1255
1264
|
input.deviceId,
|
|
1256
1265
|
input.runtime ?? null,
|
|
1266
|
+
input.harnessId ?? null,
|
|
1257
1267
|
input.capabilities === void 0 ? null : JSON.stringify(input.capabilities),
|
|
1258
1268
|
this.#now(),
|
|
1259
1269
|
tenant,
|