@byok-sdk/cloud 0.4.2 → 0.6.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 +19 -0
- package/dist/auth/device-assertion.d.ts +18 -0
- package/dist/cloud.d.ts +9 -6
- package/dist/errors.d.ts +2 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +315 -56
- package/dist/index.js.map +1 -1
- package/dist/stores/in-memory/device-directory.d.ts +2 -1
- package/dist/stores/in-memory/index.d.ts +2 -1
- package/dist/stores/in-memory/task-attempts.d.ts +11 -1
- package/dist/stores/in-memory/task-cancellations.d.ts +9 -0
- package/dist/stores/ports.d.ts +31 -3
- package/dist/tenant-stores.d.ts +8 -2
- package/dist/terminal-result.d.ts +7 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BOARD_STATUSES,
|
|
1
|
+
import { BOARD_STATUSES, CapabilityDeclarationSchema, NONCE_SIGNING_DOMAIN, hasCapability, isTenantId, tenantId, principalTenant, parseDeviceProofEnvelope, deviceProofSigningInput, contentHash, parseCapabilityDeclaration, ByokCoreError, tenantKey, PRESENCE_LEVELS, createInMemoryCoreStores, authenticateDeviceAssertion, assertCapability, isCoreConflictError, isCoreError, DEVICE_PROOF_HEADER, TRUTH_RECORD_KINDS, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS } from '@byok-sdk/core';
|
|
2
2
|
export { DEVICE_PROOF_HEADER, NONCE_SIGNING_DOMAIN, isTenantId, tenantId } from '@byok-sdk/core';
|
|
3
|
-
import { AgentEventOrUnknownSchema,
|
|
3
|
+
import { AgentEventOrUnknownSchema, DAEMON_TO_SERVER_TYPES, encodeEnvelope, decodeEnvelope, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_BOARD_PATH, BYOK_BOARD_CLAIM_ROUTE, BYOK_BOARD_UNCLAIM_ROUTE, BYOK_BOARD_STATUS_ROUTE, BYOK_BOARD_STREAM_PATH, BYOK_PRESENCE_PATH, BYOK_ACTIVITY_PATH, BYOK_RECORDS_PATH, BYOK_RECORD_ROUTE, BYOK_SKILL_PACKS_PATH, BYOK_SKILL_PACK_FILE_ROUTE, BYOK_BLOBS_PATH, BYOK_BLOB_FINALIZE_ROUTE, BYOK_BLOB_URL_ROUTE, BYOK_BLOB_CONTENT_ROUTE, createEnvelope, PairRequestSchema, ChallengeRequestSchema, TokenRequestSchema, MessagesSendRequestSchema, PresencePublishRequestSchema, CreateBlobRequestSchema, byokBlobContentPath } from '@byok-sdk/protocol';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
|
|
@@ -317,7 +317,9 @@ var CLOUD_ERROR_CODES = {
|
|
|
317
317
|
*/
|
|
318
318
|
terminal_receipt_unreadable: "terminal_receipt_unreadable",
|
|
319
319
|
/** A progress/activity batch exceeded the configured event or byte ceiling. */
|
|
320
|
-
activity_batch_too_large: "activity_batch_too_large"
|
|
320
|
+
activity_batch_too_large: "activity_batch_too_large",
|
|
321
|
+
/** Host control-plane task lookup is tenant-closed and found no task. */
|
|
322
|
+
task_not_found: "task_not_found"
|
|
321
323
|
};
|
|
322
324
|
var ByokCloudError = class extends Error {
|
|
323
325
|
code;
|
|
@@ -524,14 +526,19 @@ function tenantStoresFor(principal, root) {
|
|
|
524
526
|
devices: {
|
|
525
527
|
get: (deviceId) => cloud.devices.get(tenant, deviceId),
|
|
526
528
|
list: () => cloud.devices.list(tenant),
|
|
527
|
-
revoke: (deviceId) => cloud.devices.revoke(tenant, deviceId)
|
|
529
|
+
revoke: (deviceId) => cloud.devices.revoke(tenant, deviceId),
|
|
530
|
+
readiness: () => cloud.devices.readiness(tenant, core.presence)
|
|
528
531
|
},
|
|
529
532
|
tasks: {
|
|
530
533
|
open: (input) => cloud.tasks.open(tenant, input),
|
|
531
534
|
get: (taskId) => cloud.tasks.get(tenant, taskId),
|
|
535
|
+
getMany: (taskIds) => cloud.tasks.getMany(tenant, taskIds),
|
|
532
536
|
claim: (input) => cloud.tasks.claim(tenant, input),
|
|
533
537
|
recordStatus: (input) => cloud.tasks.recordStatus(tenant, input)
|
|
534
538
|
},
|
|
539
|
+
cancellations: {
|
|
540
|
+
request: (input) => cloud.cancellations.request(tenant, input)
|
|
541
|
+
},
|
|
535
542
|
dedup: {
|
|
536
543
|
checkAndRecord: (deviceId, envelopeId) => cloud.dedup.checkAndRecord(tenant, deviceId, envelopeId)
|
|
537
544
|
},
|
|
@@ -774,13 +781,30 @@ function eventsHandler(deps) {
|
|
|
774
781
|
}
|
|
775
782
|
const attempts = Math.max(1, Math.ceil(deps.longPollHoldMs / deps.longPollIntervalMs));
|
|
776
783
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
+
let scanCursor = cursor;
|
|
785
|
+
while (true) {
|
|
786
|
+
const page = await stores.mailbox.readAfter({
|
|
787
|
+
deviceId: device.deviceId,
|
|
788
|
+
afterSeq: scanCursor,
|
|
789
|
+
limit: deps.pageLimit
|
|
790
|
+
});
|
|
791
|
+
if (page.messages.length === 0) break;
|
|
792
|
+
const decoded = page.messages.map((message) => decodeEnvelope(message.body));
|
|
793
|
+
const offeredTaskIds = decoded.flatMap(
|
|
794
|
+
(event) => (event.type === "task.offer" || event.type === "task.offer_with_toolsets") && event.task_id !== void 0 ? [event.task_id] : []
|
|
795
|
+
);
|
|
796
|
+
const attemptsByTaskId = Object.fromEntries(
|
|
797
|
+
(await stores.tasks.getMany(offeredTaskIds)).map((attempt2) => [attempt2.taskId, attempt2])
|
|
798
|
+
);
|
|
799
|
+
const events = decoded.filter((event) => {
|
|
800
|
+
if (event.type !== "task.offer" && event.type !== "task.offer_with_toolsets") return true;
|
|
801
|
+
return event.task_id === void 0 || attemptsByTaskId[event.task_id]?.cancellation === void 0;
|
|
802
|
+
});
|
|
803
|
+
if (events.length === 0 && page.hasMore) {
|
|
804
|
+
scanCursor = page.nextSeq;
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
if (events.length === 0) break;
|
|
784
808
|
const response2 = {
|
|
785
809
|
events,
|
|
786
810
|
cursor: page.nextSeq,
|
|
@@ -999,12 +1023,20 @@ function approvalTimelineAppendInput(taskId, envelope) {
|
|
|
999
1023
|
}
|
|
1000
1024
|
}
|
|
1001
1025
|
async function recordTerminal(stores, taskId, envelope, status) {
|
|
1026
|
+
const attempt = await stores.tasks.get(taskId);
|
|
1002
1027
|
const { created } = await stores.receipts.record({
|
|
1003
1028
|
key: terminalReceiptKey(taskId),
|
|
1004
1029
|
body: encodeEnvelope(envelope)
|
|
1005
1030
|
});
|
|
1031
|
+
if (attempt?.cancellation !== void 0) {
|
|
1032
|
+
if (status === "cancelled") {
|
|
1033
|
+
await stores.tasks.recordStatus({ taskId, status: "cancelled" });
|
|
1034
|
+
}
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1006
1037
|
if (!created) return;
|
|
1007
|
-
await stores.tasks.recordStatus({ taskId, status });
|
|
1038
|
+
const recordedAttempt = await stores.tasks.recordStatus({ taskId, status });
|
|
1039
|
+
if (recordedAttempt?.status !== status || recordedAttempt.cancellation !== void 0) return;
|
|
1008
1040
|
await projectTerminalToReview(stores.board, taskId);
|
|
1009
1041
|
}
|
|
1010
1042
|
|
|
@@ -1271,11 +1303,7 @@ function boardFailure(c, caught) {
|
|
|
1271
1303
|
if (isCoreError(caught, "board_not_held")) return c.json({ error: caught.code }, 409);
|
|
1272
1304
|
throw caught;
|
|
1273
1305
|
}
|
|
1274
|
-
var PresenceBodySchema =
|
|
1275
|
-
level: z.enum(PRESENCE_LEVELS),
|
|
1276
|
-
detail: z.string().optional(),
|
|
1277
|
-
configuredToolsets: ConfiguredToolsetsSchema.optional()
|
|
1278
|
-
});
|
|
1306
|
+
var PresenceBodySchema = PresencePublishRequestSchema;
|
|
1279
1307
|
function presencePublishHandler(deps) {
|
|
1280
1308
|
return async (c) => {
|
|
1281
1309
|
const authenticated = await authenticateDevice(c, deps);
|
|
@@ -1292,6 +1320,15 @@ function presencePublishHandler(deps) {
|
|
|
1292
1320
|
level: parsed.data.level,
|
|
1293
1321
|
...parsed.data.detail === void 0 ? {} : { detail: parsed.data.detail },
|
|
1294
1322
|
...parsed.data.configuredToolsets === void 0 ? {} : { configuredToolsets: parsed.data.configuredToolsets },
|
|
1323
|
+
...parsed.data.clientVersion === void 0 ? {} : { clientVersion: parsed.data.clientVersion },
|
|
1324
|
+
...parsed.data.protocolVersions === void 0 ? {} : { protocolVersions: parsed.data.protocolVersions },
|
|
1325
|
+
...parsed.data.runtimes === void 0 ? {} : {
|
|
1326
|
+
runtimes: parsed.data.runtimes.map(({ id, version, authPresent }) => ({
|
|
1327
|
+
id,
|
|
1328
|
+
...version === void 0 ? {} : { version },
|
|
1329
|
+
...authPresent === void 0 ? {} : { authPresent }
|
|
1330
|
+
}))
|
|
1331
|
+
},
|
|
1295
1332
|
ttlMs: deps.ttlMs,
|
|
1296
1333
|
minimumIntervalMs: deps.minimumIntervalMs
|
|
1297
1334
|
}),
|
|
@@ -1792,6 +1829,7 @@ function projectTerminalResult(taskId, receipt) {
|
|
|
1792
1829
|
sessionRef: envelope.payload.sessionRef,
|
|
1793
1830
|
...envelope.payload.artifactRefs !== void 0 ? { artifactRefs: envelope.payload.artifactRefs } : {},
|
|
1794
1831
|
...envelope.payload.document !== void 0 ? { document: envelope.payload.document } : {},
|
|
1832
|
+
...envelope.payload.usage !== void 0 ? { usage: envelope.payload.usage } : {},
|
|
1795
1833
|
recordedAt: receipt.recordedAt
|
|
1796
1834
|
};
|
|
1797
1835
|
case "task.fail":
|
|
@@ -1800,6 +1838,7 @@ function projectTerminalResult(taskId, receipt) {
|
|
|
1800
1838
|
state: "failed",
|
|
1801
1839
|
reason: envelope.payload.reason,
|
|
1802
1840
|
...envelope.payload.retryable !== void 0 ? { retryable: envelope.payload.retryable } : {},
|
|
1841
|
+
...envelope.payload.usage !== void 0 ? { usage: envelope.payload.usage } : {},
|
|
1803
1842
|
recordedAt: receipt.recordedAt
|
|
1804
1843
|
};
|
|
1805
1844
|
case "task.cancelled":
|
|
@@ -1807,6 +1846,7 @@ function projectTerminalResult(taskId, receipt) {
|
|
|
1807
1846
|
taskId,
|
|
1808
1847
|
state: "cancelled",
|
|
1809
1848
|
...envelope.payload.reason !== void 0 ? { reason: envelope.payload.reason } : {},
|
|
1849
|
+
...envelope.payload.usage !== void 0 ? { usage: envelope.payload.usage } : {},
|
|
1810
1850
|
recordedAt: receipt.recordedAt
|
|
1811
1851
|
};
|
|
1812
1852
|
default:
|
|
@@ -2041,6 +2081,42 @@ function createByokCloud(options) {
|
|
|
2041
2081
|
(taskId, seq, messageId) => createEnvelope("task.offer_with_toolsets", input.payload, { id: messageId, taskId, seq })
|
|
2042
2082
|
);
|
|
2043
2083
|
},
|
|
2084
|
+
async cancelTask(tenant, taskId, reason) {
|
|
2085
|
+
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
2086
|
+
const proposedMessageId = options.crypto.randomUuid();
|
|
2087
|
+
const mutation = await stores.cancellations.request({
|
|
2088
|
+
taskId,
|
|
2089
|
+
proposedMessageId,
|
|
2090
|
+
...reason === void 0 ? {} : { reason },
|
|
2091
|
+
materialize: async (seq, messageId) => {
|
|
2092
|
+
const envelope = createEnvelope("task.cancel", reason === void 0 ? {} : { reason }, {
|
|
2093
|
+
id: messageId,
|
|
2094
|
+
taskId,
|
|
2095
|
+
seq
|
|
2096
|
+
});
|
|
2097
|
+
const body = encodeEnvelope(envelope);
|
|
2098
|
+
const bytes = new TextEncoder().encode(body);
|
|
2099
|
+
return {
|
|
2100
|
+
body,
|
|
2101
|
+
bodyHash: contentHash(await options.crypto.sha256(bytes)),
|
|
2102
|
+
byteSize: BigInt(bytes.length)
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
if (mutation === void 0) {
|
|
2107
|
+
throw new ByokCloudError("task_not_found", `Task ${taskId} was not found for this tenant.`);
|
|
2108
|
+
}
|
|
2109
|
+
if (mutation.message !== void 0) {
|
|
2110
|
+
const envelope = decodeEnvelope(mutation.message.body);
|
|
2111
|
+
if (envelope.seq !== mutation.message.seq || envelope.type !== "task.cancel") {
|
|
2112
|
+
throw new ByokCloudError(
|
|
2113
|
+
"mailbox_seq_mismatch",
|
|
2114
|
+
`Mailbox stored cancellation seq ${String(envelope.seq)} at row ${mutation.message.seq}.`
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
return mutation.attempt;
|
|
2119
|
+
},
|
|
2044
2120
|
readTaskAttempt(tenant, taskId) {
|
|
2045
2121
|
return tenantStoresFor(controlPlane(tenant), root).tasks.get(taskId);
|
|
2046
2122
|
},
|
|
@@ -2048,7 +2124,17 @@ function createByokCloud(options) {
|
|
|
2048
2124
|
return tenantStoresFor(controlPlane(tenant), root).receipts.get(terminalReceiptKey(taskId));
|
|
2049
2125
|
},
|
|
2050
2126
|
async readTaskResult(tenant, taskId) {
|
|
2051
|
-
const
|
|
2127
|
+
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
2128
|
+
const attempt = await stores.tasks.get(taskId);
|
|
2129
|
+
if (attempt?.cancellation !== void 0) {
|
|
2130
|
+
return {
|
|
2131
|
+
taskId,
|
|
2132
|
+
state: "cancelled",
|
|
2133
|
+
...attempt.cancellation.reason === void 0 ? {} : { reason: attempt.cancellation.reason },
|
|
2134
|
+
recordedAt: attempt.cancellation.requestedAt
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
const receipt = await stores.receipts.get(
|
|
2052
2138
|
terminalReceiptKey(taskId)
|
|
2053
2139
|
);
|
|
2054
2140
|
return receipt === void 0 ? void 0 : projectTerminalResult(taskId, receipt);
|
|
@@ -2079,6 +2165,9 @@ function createByokCloud(options) {
|
|
|
2079
2165
|
listPresence(tenant) {
|
|
2080
2166
|
return tenantStoresFor(controlPlane(tenant), root).presence.list();
|
|
2081
2167
|
},
|
|
2168
|
+
readTenantReadiness(tenant) {
|
|
2169
|
+
return tenantStoresFor(controlPlane(tenant), root).devices.readiness();
|
|
2170
|
+
},
|
|
2082
2171
|
readActivity(tenant, taskId) {
|
|
2083
2172
|
return tenantStoresFor(controlPlane(tenant), root).activity.read(taskId);
|
|
2084
2173
|
},
|
|
@@ -2307,6 +2396,52 @@ var InMemoryDeviceDirectory = class {
|
|
|
2307
2396
|
const prefix = tenantKey(tenant, "");
|
|
2308
2397
|
return [...this.#byTenant.entries()].filter(([key]) => key.startsWith(prefix)).map(([, record]) => record);
|
|
2309
2398
|
}
|
|
2399
|
+
async readiness(tenant, presence) {
|
|
2400
|
+
const devices = await this.list(tenant);
|
|
2401
|
+
const livePresence = await presence.list(tenant);
|
|
2402
|
+
const presenceByDevice = new Map(livePresence.map((hint) => [hint.deviceId, hint]));
|
|
2403
|
+
const activeDeviceIds = new Set(
|
|
2404
|
+
devices.filter((device) => !device.revoked).map((device) => device.deviceId)
|
|
2405
|
+
);
|
|
2406
|
+
const observedPresenceByLevel = Object.fromEntries(
|
|
2407
|
+
PRESENCE_LEVELS.map((level) => [level, 0])
|
|
2408
|
+
);
|
|
2409
|
+
for (const hint of livePresence) {
|
|
2410
|
+
if (!activeDeviceIds.has(hint.deviceId)) continue;
|
|
2411
|
+
observedPresenceByLevel[hint.level] += 1;
|
|
2412
|
+
}
|
|
2413
|
+
return {
|
|
2414
|
+
tenantId: tenant,
|
|
2415
|
+
activePairedDeviceCount: devices.filter((device) => !device.revoked).length,
|
|
2416
|
+
revokedDeviceCount: devices.filter((device) => device.revoked).length,
|
|
2417
|
+
observedPresenceCount: Object.values(observedPresenceByLevel).reduce(
|
|
2418
|
+
(total, count) => total + count,
|
|
2419
|
+
0
|
|
2420
|
+
),
|
|
2421
|
+
observedPresenceByLevel,
|
|
2422
|
+
devices: [...devices].sort((left, right) => left.deviceId.localeCompare(right.deviceId)).map((device) => {
|
|
2423
|
+
const hint = device.revoked ? void 0 : presenceByDevice.get(device.deviceId);
|
|
2424
|
+
return {
|
|
2425
|
+
deviceId: device.deviceId,
|
|
2426
|
+
productId: device.productId,
|
|
2427
|
+
deviceName: device.deviceName,
|
|
2428
|
+
revoked: device.revoked,
|
|
2429
|
+
...hint === void 0 ? {} : {
|
|
2430
|
+
presence: {
|
|
2431
|
+
level: hint.level,
|
|
2432
|
+
...hint.detail === void 0 ? {} : { detail: hint.detail },
|
|
2433
|
+
...hint.configuredToolsets === void 0 ? {} : { configuredToolsets: hint.configuredToolsets },
|
|
2434
|
+
...hint.clientVersion === void 0 ? {} : { clientVersion: hint.clientVersion },
|
|
2435
|
+
...hint.protocolVersions === void 0 ? {} : { protocolVersions: hint.protocolVersions },
|
|
2436
|
+
...hint.runtimes === void 0 ? {} : { runtimes: hint.runtimes },
|
|
2437
|
+
observedAt: hint.observedAt,
|
|
2438
|
+
expiresAt: hint.expiresAt
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
})
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2310
2445
|
async resolveByDeviceId(deviceId) {
|
|
2311
2446
|
const key = this.#byDeviceId.get(deviceId);
|
|
2312
2447
|
return key === void 0 ? void 0 : this.#byTenant.get(key);
|
|
@@ -2449,53 +2584,143 @@ var InMemoryProofRequestReceiptStore = class {
|
|
|
2449
2584
|
}
|
|
2450
2585
|
};
|
|
2451
2586
|
var InMemoryTaskAttemptStore = class {
|
|
2452
|
-
#
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
this.#clock = clock;
|
|
2587
|
+
#state;
|
|
2588
|
+
constructor(clock, state = new InMemoryTaskAttemptState(clock)) {
|
|
2589
|
+
this.#state = state;
|
|
2456
2590
|
}
|
|
2457
2591
|
async open(tenant, input) {
|
|
2458
2592
|
const key = tenantKey(tenant, input.taskId);
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2593
|
+
return this.#state.mutate(key, () => {
|
|
2594
|
+
const existing = this.#state.attempts.get(key);
|
|
2595
|
+
if (existing !== void 0) return existing;
|
|
2596
|
+
const attempt = {
|
|
2597
|
+
tenantId: tenant,
|
|
2598
|
+
taskId: input.taskId,
|
|
2599
|
+
deviceId: input.deviceId,
|
|
2600
|
+
status: "offered",
|
|
2601
|
+
updatedAt: this.#now()
|
|
2602
|
+
};
|
|
2603
|
+
this.#state.attempts.set(key, attempt);
|
|
2604
|
+
return attempt;
|
|
2605
|
+
});
|
|
2470
2606
|
}
|
|
2471
2607
|
async get(tenant, taskId) {
|
|
2472
|
-
return this.#attempts.get(tenantKey(tenant, taskId));
|
|
2608
|
+
return this.#state.attempts.get(tenantKey(tenant, taskId));
|
|
2609
|
+
}
|
|
2610
|
+
async getMany(tenant, taskIds) {
|
|
2611
|
+
return taskIds.flatMap((taskId) => {
|
|
2612
|
+
const attempt = this.#state.attempts.get(tenantKey(tenant, taskId));
|
|
2613
|
+
return attempt === void 0 ? [] : [attempt];
|
|
2614
|
+
});
|
|
2473
2615
|
}
|
|
2474
2616
|
async claim(tenant, input) {
|
|
2475
2617
|
const key = tenantKey(tenant, input.taskId);
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2618
|
+
return this.#state.mutate(key, () => {
|
|
2619
|
+
const existing = this.#state.attempts.get(key);
|
|
2620
|
+
if (existing === void 0) return void 0;
|
|
2621
|
+
if (existing.ownerDeviceId !== void 0 || existing.cancellation !== void 0 || existing.status !== "offered") return existing;
|
|
2622
|
+
const claimed = {
|
|
2623
|
+
...existing,
|
|
2624
|
+
ownerDeviceId: input.deviceId,
|
|
2625
|
+
status: "claimed",
|
|
2626
|
+
updatedAt: this.#now()
|
|
2627
|
+
};
|
|
2628
|
+
this.#state.attempts.set(key, claimed);
|
|
2629
|
+
return claimed;
|
|
2630
|
+
});
|
|
2487
2631
|
}
|
|
2488
2632
|
async recordStatus(tenant, input) {
|
|
2489
2633
|
const key = tenantKey(tenant, input.taskId);
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2634
|
+
return this.#state.mutate(key, () => {
|
|
2635
|
+
const existing = this.#state.attempts.get(key);
|
|
2636
|
+
if (existing === void 0) return void 0;
|
|
2637
|
+
if (existing.cancellation !== void 0) {
|
|
2638
|
+
if (input.status !== "cancelled" || existing.status === "cancelled") return existing;
|
|
2639
|
+
} else if (existing.status === "complete" || existing.status === "failed" || existing.status === "cancelled") {
|
|
2640
|
+
return existing;
|
|
2641
|
+
}
|
|
2642
|
+
const updated = { ...existing, status: input.status, updatedAt: this.#now() };
|
|
2643
|
+
this.#state.attempts.set(key, updated);
|
|
2644
|
+
return updated;
|
|
2645
|
+
});
|
|
2495
2646
|
}
|
|
2496
2647
|
#now() {
|
|
2648
|
+
return this.#state.now();
|
|
2649
|
+
}
|
|
2650
|
+
};
|
|
2651
|
+
var InMemoryTaskAttemptState = class {
|
|
2652
|
+
attempts = /* @__PURE__ */ new Map();
|
|
2653
|
+
#clock;
|
|
2654
|
+
#mutationTails = /* @__PURE__ */ new Map();
|
|
2655
|
+
constructor(clock) {
|
|
2656
|
+
this.#clock = clock;
|
|
2657
|
+
}
|
|
2658
|
+
now() {
|
|
2497
2659
|
return this.#clock.now().toISOString();
|
|
2498
2660
|
}
|
|
2661
|
+
/** Serialize every state-changing operation for one tenant/task key. */
|
|
2662
|
+
async mutate(key, operation) {
|
|
2663
|
+
const previous = this.#mutationTails.get(key) ?? Promise.resolve();
|
|
2664
|
+
const result = previous.then(operation);
|
|
2665
|
+
const tail = result.then(
|
|
2666
|
+
() => void 0,
|
|
2667
|
+
() => void 0
|
|
2668
|
+
);
|
|
2669
|
+
this.#mutationTails.set(key, tail);
|
|
2670
|
+
try {
|
|
2671
|
+
return await result;
|
|
2672
|
+
} finally {
|
|
2673
|
+
if (this.#mutationTails.get(key) === tail) this.#mutationTails.delete(key);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
};
|
|
2677
|
+
var InMemoryTaskCancellationStore = class {
|
|
2678
|
+
#state;
|
|
2679
|
+
#mailbox;
|
|
2680
|
+
#messageIds = /* @__PURE__ */ new Map();
|
|
2681
|
+
#inFlight = /* @__PURE__ */ new Map();
|
|
2682
|
+
constructor(state, mailbox) {
|
|
2683
|
+
this.#state = state;
|
|
2684
|
+
this.#mailbox = mailbox;
|
|
2685
|
+
}
|
|
2686
|
+
request(tenant, input) {
|
|
2687
|
+
const key = tenantKey(tenant, input.taskId);
|
|
2688
|
+
const running = this.#inFlight.get(key);
|
|
2689
|
+
if (running !== void 0) return running;
|
|
2690
|
+
const operation = this.#request(tenant, key, input).finally(() => {
|
|
2691
|
+
this.#inFlight.delete(key);
|
|
2692
|
+
});
|
|
2693
|
+
this.#inFlight.set(key, operation);
|
|
2694
|
+
return operation;
|
|
2695
|
+
}
|
|
2696
|
+
async #request(tenant, key, input) {
|
|
2697
|
+
return this.#state.mutate(key, async () => {
|
|
2698
|
+
const existing = this.#state.attempts.get(key);
|
|
2699
|
+
if (existing === void 0) return void 0;
|
|
2700
|
+
if (existing.cancellation === void 0 && (existing.status === "complete" || existing.status === "failed" || existing.status === "cancelled")) {
|
|
2701
|
+
return { attempt: existing };
|
|
2702
|
+
}
|
|
2703
|
+
const messageId = this.#messageIds.get(key) ?? input.proposedMessageId;
|
|
2704
|
+
const message = await this.#mailbox.append(tenant, {
|
|
2705
|
+
deviceId: existing.deviceId,
|
|
2706
|
+
messageId,
|
|
2707
|
+
materialize: (seq) => input.materialize(seq, messageId)
|
|
2708
|
+
});
|
|
2709
|
+
const requestedAt = this.#state.now();
|
|
2710
|
+
const attempt = existing.cancellation === void 0 ? {
|
|
2711
|
+
...existing,
|
|
2712
|
+
status: existing.ownerDeviceId === void 0 ? "cancelled" : "cancel_requested",
|
|
2713
|
+
cancellation: {
|
|
2714
|
+
requestedAt,
|
|
2715
|
+
...input.reason === void 0 ? {} : { reason: input.reason }
|
|
2716
|
+
},
|
|
2717
|
+
updatedAt: requestedAt
|
|
2718
|
+
} : existing;
|
|
2719
|
+
this.#state.attempts.set(key, attempt);
|
|
2720
|
+
this.#messageIds.set(key, messageId);
|
|
2721
|
+
return { attempt, message };
|
|
2722
|
+
});
|
|
2723
|
+
}
|
|
2499
2724
|
};
|
|
2500
2725
|
|
|
2501
2726
|
// src/stores/in-memory/activity.ts
|
|
@@ -2616,8 +2841,10 @@ var InMemoryApprovalTimelineStore = class {
|
|
|
2616
2841
|
};
|
|
2617
2842
|
|
|
2618
2843
|
// src/stores/in-memory/index.ts
|
|
2619
|
-
function createInMemoryCloudStores(clock, crypto, objects) {
|
|
2844
|
+
function createInMemoryCloudStores(clock, crypto, objects, mailbox) {
|
|
2620
2845
|
const blobs = createInMemoryBlobs(clock, crypto, objects);
|
|
2846
|
+
const taskState = new InMemoryTaskAttemptState(clock);
|
|
2847
|
+
const tasks = new InMemoryTaskAttemptStore(clock, taskState);
|
|
2621
2848
|
return {
|
|
2622
2849
|
stores: {
|
|
2623
2850
|
activity: new InMemoryActivityStore(clock),
|
|
@@ -2626,7 +2853,8 @@ function createInMemoryCloudStores(clock, crypto, objects) {
|
|
|
2626
2853
|
pairingCodes: new InMemoryPairingCodeStore(clock),
|
|
2627
2854
|
nonces: new InMemoryNonceStore(clock, crypto),
|
|
2628
2855
|
dedup: new InMemoryInboundDedupStore(),
|
|
2629
|
-
tasks
|
|
2856
|
+
tasks,
|
|
2857
|
+
cancellations: new InMemoryTaskCancellationStore(taskState, mailbox),
|
|
2630
2858
|
receipts: new InMemoryRequestReceiptStore(clock),
|
|
2631
2859
|
proofReceipts: new InMemoryProofRequestReceiptStore(clock),
|
|
2632
2860
|
blobs: blobs.blobs,
|
|
@@ -2645,7 +2873,12 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
2645
2873
|
const clock = options.clock ?? systemClock();
|
|
2646
2874
|
const crypto = options.crypto ?? createWebCrypto();
|
|
2647
2875
|
const core = createInMemoryCoreStores({ clock }).stores;
|
|
2648
|
-
const { stores, blobContentProxy } = createInMemoryCloudStores(
|
|
2876
|
+
const { stores, blobContentProxy } = createInMemoryCloudStores(
|
|
2877
|
+
clock,
|
|
2878
|
+
crypto,
|
|
2879
|
+
core.objects,
|
|
2880
|
+
core.mailbox
|
|
2881
|
+
);
|
|
2649
2882
|
const tokenSigner = options.tokenSigner ?? createHmacTokenSigner(globalThis.crypto.getRandomValues(new Uint8Array(TOKEN_SECRET_BYTES)), clock);
|
|
2650
2883
|
const cloud = createByokCloud({
|
|
2651
2884
|
core,
|
|
@@ -2686,6 +2919,28 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
2686
2919
|
});
|
|
2687
2920
|
return { cloud, core, stores, blobContentProxy, clock, crypto };
|
|
2688
2921
|
}
|
|
2922
|
+
function authenticateHostedDeviceAssertion(input, deps) {
|
|
2923
|
+
return authenticateDeviceAssertion(input, {
|
|
2924
|
+
verifier: {
|
|
2925
|
+
verify: ({ publicKey, signingInput, signature }) => deps.crypto.verifyEd25519(publicKey, signingInput, signature)
|
|
2926
|
+
},
|
|
2927
|
+
lookupDevice: async (deviceId) => {
|
|
2928
|
+
const row = await deps.devices.resolveByDeviceId(deviceId);
|
|
2929
|
+
if (row === void 0) return void 0;
|
|
2930
|
+
return {
|
|
2931
|
+
tenantId: row.tenantId,
|
|
2932
|
+
productId: row.productId,
|
|
2933
|
+
deviceId: row.deviceId,
|
|
2934
|
+
publicKeyJwkX: row.devicePublicKey,
|
|
2935
|
+
revoked: row.revoked
|
|
2936
|
+
};
|
|
2937
|
+
},
|
|
2938
|
+
replay: deps.replay,
|
|
2939
|
+
expected: deps.expected,
|
|
2940
|
+
now: deps.clock.now(),
|
|
2941
|
+
...deps.maxLifetimeMs === void 0 ? {} : { maxLifetimeMs: deps.maxLifetimeMs }
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2689
2944
|
var BoardFeedItemSchema = z.object({
|
|
2690
2945
|
tenantId: z.string(),
|
|
2691
2946
|
itemId: z.string(),
|
|
@@ -2825,6 +3080,7 @@ var TASK_ATTEMPT_STATUSES = [
|
|
|
2825
3080
|
"offered",
|
|
2826
3081
|
"claimed",
|
|
2827
3082
|
"running",
|
|
3083
|
+
"cancel_requested",
|
|
2828
3084
|
"complete",
|
|
2829
3085
|
"failed",
|
|
2830
3086
|
"cancelled"
|
|
@@ -2837,6 +3093,7 @@ var CLOUD_STORE_NAMES = [
|
|
|
2837
3093
|
"nonces",
|
|
2838
3094
|
"dedup",
|
|
2839
3095
|
"tasks",
|
|
3096
|
+
"cancellations",
|
|
2840
3097
|
"receipts",
|
|
2841
3098
|
"proofReceipts",
|
|
2842
3099
|
"blobs",
|
|
@@ -2847,11 +3104,12 @@ var CLOUD_STORE_NAMES = [
|
|
|
2847
3104
|
var CLOUD_PORT_METHODS = {
|
|
2848
3105
|
activity: ["append", "read"],
|
|
2849
3106
|
approvals: ["append", "read"],
|
|
2850
|
-
devices: ["register", "get", "revoke", "list", "resolveByDeviceId"],
|
|
3107
|
+
devices: ["register", "get", "revoke", "list", "readiness", "resolveByDeviceId"],
|
|
2851
3108
|
pairingCodes: ["issue", "redeem"],
|
|
2852
3109
|
nonces: ["issue", "validate", "markUsed"],
|
|
2853
3110
|
dedup: ["checkAndRecord"],
|
|
2854
|
-
tasks: ["open", "get", "claim", "recordStatus"],
|
|
3111
|
+
tasks: ["open", "get", "getMany", "claim", "recordStatus"],
|
|
3112
|
+
cancellations: ["request"],
|
|
2855
3113
|
receipts: ["record", "get"],
|
|
2856
3114
|
proofReceipts: ["record", "get"],
|
|
2857
3115
|
// Three methods, not six: the byte-proxy trio moved to `BlobContentProxy`,
|
|
@@ -2868,12 +3126,13 @@ var CLOUD_PORT_INTERFACES = {
|
|
|
2868
3126
|
nonces: "NonceStore",
|
|
2869
3127
|
dedup: "InboundDedupStore",
|
|
2870
3128
|
tasks: "TaskAttemptStore",
|
|
3129
|
+
cancellations: "TaskCancellationStore",
|
|
2871
3130
|
receipts: "RequestReceiptStore",
|
|
2872
3131
|
proofReceipts: "ProofRequestReceiptStore",
|
|
2873
3132
|
blobs: "CloudBlobStore",
|
|
2874
3133
|
rateLimiter: "InboundRateLimiter"
|
|
2875
3134
|
};
|
|
2876
3135
|
|
|
2877
|
-
export { ACCESS_TOKEN_TTL_SECONDS, APPROVAL_SUMMARY_MAX_BYTES, ActivityAppendRequestSchema, AllowAllRateLimiter, ApprovalObservationSchema, ApprovalTimelineEventSchema, BLOB_URL_TTL_MS, BoardFeedClient, BoardFeedRetryableError, BoardFeedStoppedError, ByokCloudError, CLOUD_CAPABILITIES, CLOUD_ERROR_CODES, CLOUD_PORT_INTERFACES, CLOUD_PORT_METHODS, CLOUD_STORE_NAMES, CapabilitiesResponseSchema, CloudRouteRegistry, DEDUP_RING_CAPACITY, DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_APPROVAL_TIMELINE_CAPACITY, DEFAULT_APPROVAL_TIMELINE_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_PAGE_LIMIT, DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS, DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS, DEFAULT_BOARD_STREAM_RECONCILIATION_INTERVAL_MS, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS, DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS, DEFAULT_EVENTS_PAGE_LIMIT, DEFAULT_LONG_POLL_HOLD_MS, DEFAULT_LONG_POLL_INTERVAL_MS, DEFAULT_MAX_BLOB_SIZE_BYTES, DEFAULT_MAX_TRUTH_REQUEST_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, DEFAULT_SKILL_PACK_PAGE_LIMIT, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID, InMemoryActivityStore, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, MAX_DEVICE_PROOF_CLOCK_SKEW_MS, MAX_DEVICE_PROOF_HEADER_BYTES, MAX_DEVICE_PROOF_MAX_LIFETIME_MS, NONCE_TTL_MS, PAIRING_CODE_TTL_MS, ROUTE_CLASSES, ROUTE_METHODS, TASK_ATTEMPT_STATUSES, TRUTH_BATCH_MAX_RECORDS, TRUTH_INLINE_CONTENT_TYPE, TRUTH_LABEL_MAX_LENGTH, TRUTH_MANIFEST_MAX_LIMIT, TRUTH_RECORD_CAPABILITY, TRUTH_RECORD_KEY_MAX_LENGTH, TRUTH_REQUEST_ID_MAX_LENGTH, TimelineEventSchema, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, activityCursor, approvalTimelineCursor, authenticateBearer, authenticateDeviceProof, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, parseApprovalObservations, parseTimelineEvents, projectTerminalResult, projectTimelineEvents, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, validateActivityAppend, validateApprovalTimelineAppend, verifyNonceSignature };
|
|
3136
|
+
export { ACCESS_TOKEN_TTL_SECONDS, APPROVAL_SUMMARY_MAX_BYTES, ActivityAppendRequestSchema, AllowAllRateLimiter, ApprovalObservationSchema, ApprovalTimelineEventSchema, BLOB_URL_TTL_MS, BoardFeedClient, BoardFeedRetryableError, BoardFeedStoppedError, ByokCloudError, CLOUD_CAPABILITIES, CLOUD_ERROR_CODES, CLOUD_PORT_INTERFACES, CLOUD_PORT_METHODS, CLOUD_STORE_NAMES, CapabilitiesResponseSchema, CloudRouteRegistry, DEDUP_RING_CAPACITY, DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_APPROVAL_TIMELINE_CAPACITY, DEFAULT_APPROVAL_TIMELINE_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_PAGE_LIMIT, DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS, DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS, DEFAULT_BOARD_STREAM_RECONCILIATION_INTERVAL_MS, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS, DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS, DEFAULT_EVENTS_PAGE_LIMIT, DEFAULT_LONG_POLL_HOLD_MS, DEFAULT_LONG_POLL_INTERVAL_MS, DEFAULT_MAX_BLOB_SIZE_BYTES, DEFAULT_MAX_TRUTH_REQUEST_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, DEFAULT_SKILL_PACK_PAGE_LIMIT, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID, InMemoryActivityStore, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, InMemoryTaskCancellationStore, MAX_DEVICE_PROOF_CLOCK_SKEW_MS, MAX_DEVICE_PROOF_HEADER_BYTES, MAX_DEVICE_PROOF_MAX_LIFETIME_MS, NONCE_TTL_MS, PAIRING_CODE_TTL_MS, ROUTE_CLASSES, ROUTE_METHODS, TASK_ATTEMPT_STATUSES, TRUTH_BATCH_MAX_RECORDS, TRUTH_INLINE_CONTENT_TYPE, TRUTH_LABEL_MAX_LENGTH, TRUTH_MANIFEST_MAX_LIMIT, TRUTH_RECORD_CAPABILITY, TRUTH_RECORD_KEY_MAX_LENGTH, TRUTH_REQUEST_ID_MAX_LENGTH, TimelineEventSchema, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, activityCursor, approvalTimelineCursor, authenticateBearer, authenticateDeviceProof, authenticateHostedDeviceAssertion, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, parseApprovalObservations, parseTimelineEvents, projectTerminalResult, projectTimelineEvents, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, validateActivityAppend, validateApprovalTimelineAppend, verifyNonceSignature };
|
|
2878
3137
|
//# sourceMappingURL=index.js.map
|
|
2879
3138
|
//# sourceMappingURL=index.js.map
|