@unidocs/operator-cloudflare 0.1.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/dist/agent-session-store.d.ts +27 -0
- package/dist/agent-session-store.d.ts.map +1 -0
- package/dist/agent-session-store.js +86 -0
- package/dist/agent-session-store.js.map +1 -0
- package/dist/cas.d.ts +38 -0
- package/dist/cas.d.ts.map +1 -0
- package/dist/cas.js +126 -0
- package/dist/cas.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/session.d.ts +43 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +41 -0
- package/dist/session.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type SessionKey, type SessionStore, type WakeScheduler } from "@unidocs/agent-operator-core";
|
|
2
|
+
export interface SqlLike {
|
|
3
|
+
exec(query: string, ...bindings: (string | number | null)[]): {
|
|
4
|
+
toArray(): Record<string, unknown>[];
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The session of the one document this Durable Object is named after, in its
|
|
9
|
+
* own sqlite. Every read-modify-write runs inside `transaction`, and a lease
|
|
10
|
+
* is a (lease_id, lease_until) pair that `saveWithLease` must still match, so
|
|
11
|
+
* a round that outlived its lease cannot overwrite its successor (spec §5.7).
|
|
12
|
+
* Statements always call `toArray()`, so they run eagerly on every driver.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createSqlSessionStore(options: {
|
|
15
|
+
readonly sql: SqlLike;
|
|
16
|
+
readonly transaction: <T>(fn: () => T) => T;
|
|
17
|
+
readonly now?: () => number;
|
|
18
|
+
readonly id?: () => string;
|
|
19
|
+
}): SessionStore & {
|
|
20
|
+
currentKey(): SessionKey | null;
|
|
21
|
+
};
|
|
22
|
+
/** The DO alarm is the wake-up: one pending alarm, the earliest requested wins. */
|
|
23
|
+
export declare function createAlarmWakeScheduler(storage: {
|
|
24
|
+
getAlarm(): Promise<number | null>;
|
|
25
|
+
setAlarm(scheduledTime: number): Promise<void>;
|
|
26
|
+
}): WakeScheduler;
|
|
27
|
+
//# sourceMappingURL=agent-session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-store.d.ts","sourceRoot":"","sources":["../src/agent-session-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,UAAU,EAAqB,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE9I,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG;QAAE,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC;CACxG;AAWD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAC7C,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B,GAAG,YAAY,GAAG;IAAE,UAAU,IAAI,UAAU,GAAG,IAAI,CAAA;CAAE,CA8DrD;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAAE,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAAG,aAAa,CAOvJ"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { EMPTY_SESSION_STATE } from "@unidocs/agent-operator-core";
|
|
2
|
+
const SCHEMA = `CREATE TABLE IF NOT EXISTS agent_session (
|
|
3
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
4
|
+
tenant_id TEXT NOT NULL,
|
|
5
|
+
document_id TEXT NOT NULL,
|
|
6
|
+
state_json TEXT NOT NULL,
|
|
7
|
+
lease_id TEXT,
|
|
8
|
+
lease_until INTEGER
|
|
9
|
+
)`;
|
|
10
|
+
/**
|
|
11
|
+
* The session of the one document this Durable Object is named after, in its
|
|
12
|
+
* own sqlite. Every read-modify-write runs inside `transaction`, and a lease
|
|
13
|
+
* is a (lease_id, lease_until) pair that `saveWithLease` must still match, so
|
|
14
|
+
* a round that outlived its lease cannot overwrite its successor (spec §5.7).
|
|
15
|
+
* Statements always call `toArray()`, so they run eagerly on every driver.
|
|
16
|
+
*/
|
|
17
|
+
export function createSqlSessionStore(options) {
|
|
18
|
+
const { sql, transaction } = options;
|
|
19
|
+
const now = options.now ?? (() => Date.now());
|
|
20
|
+
const id = options.id ?? (() => crypto.randomUUID());
|
|
21
|
+
sql.exec(SCHEMA).toArray();
|
|
22
|
+
const row = () => sql.exec("SELECT tenant_id, document_id, state_json, lease_id, lease_until FROM agent_session WHERE id = 1").toArray()[0];
|
|
23
|
+
function ensure(key) {
|
|
24
|
+
const found = row();
|
|
25
|
+
if (!found) {
|
|
26
|
+
sql.exec("INSERT INTO agent_session (id, tenant_id, document_id, state_json) VALUES (1, ?, ?, ?)", key.tenantId, key.documentId, JSON.stringify(EMPTY_SESSION_STATE)).toArray();
|
|
27
|
+
return row();
|
|
28
|
+
}
|
|
29
|
+
if (found.tenant_id !== key.tenantId || found.document_id !== key.documentId)
|
|
30
|
+
throw new Error("This session belongs to another document");
|
|
31
|
+
return found;
|
|
32
|
+
}
|
|
33
|
+
const write = (state) => sql.exec("UPDATE agent_session SET state_json = ? WHERE id = 1", JSON.stringify(state)).toArray();
|
|
34
|
+
return {
|
|
35
|
+
currentKey() {
|
|
36
|
+
const found = row();
|
|
37
|
+
return found ? { tenantId: String(found.tenant_id), documentId: String(found.document_id) } : null;
|
|
38
|
+
},
|
|
39
|
+
async acquire(key, ttlSeconds) {
|
|
40
|
+
return transaction(() => {
|
|
41
|
+
ensure(key);
|
|
42
|
+
const at = now();
|
|
43
|
+
const leaseId = id();
|
|
44
|
+
const taken = sql.exec("UPDATE agent_session SET lease_id = ?, lease_until = ? WHERE id = 1 AND (lease_id IS NULL OR lease_until <= ?) RETURNING lease_id", leaseId, at + ttlSeconds * 1000, at).toArray();
|
|
45
|
+
return taken.length === 1 ? leaseId : null;
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
async load(key) {
|
|
49
|
+
const found = row();
|
|
50
|
+
if (!found)
|
|
51
|
+
return EMPTY_SESSION_STATE;
|
|
52
|
+
if (found.tenant_id !== key.tenantId || found.document_id !== key.documentId)
|
|
53
|
+
throw new Error("This session belongs to another document");
|
|
54
|
+
return JSON.parse(String(found.state_json));
|
|
55
|
+
},
|
|
56
|
+
async update(key, fn) {
|
|
57
|
+
transaction(() => {
|
|
58
|
+
const found = ensure(key);
|
|
59
|
+
write(fn(JSON.parse(String(found.state_json))));
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
async saveWithLease(key, leaseId, fn) {
|
|
63
|
+
return transaction(() => {
|
|
64
|
+
const found = ensure(key);
|
|
65
|
+
if (found.lease_id !== leaseId)
|
|
66
|
+
return false;
|
|
67
|
+
write(fn(JSON.parse(String(found.state_json))));
|
|
68
|
+
return true;
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
async release(_key, leaseId) {
|
|
72
|
+
sql.exec("UPDATE agent_session SET lease_id = NULL, lease_until = NULL WHERE id = 1 AND lease_id = ?", leaseId).toArray();
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** The DO alarm is the wake-up: one pending alarm, the earliest requested wins. */
|
|
77
|
+
export function createAlarmWakeScheduler(storage) {
|
|
78
|
+
return {
|
|
79
|
+
async wake(_key, at) {
|
|
80
|
+
const existing = await storage.getAlarm();
|
|
81
|
+
if (existing === null || at.getTime() < existing)
|
|
82
|
+
await storage.setAlarm(at.getTime());
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=agent-session-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-store.js","sourceRoot":"","sources":["../src/agent-session-store.ts"],"names":[],"mappings":"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"}
|
package/dist/cas.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type CasBlobClient } from "@unicas/tenant-blob-client";
|
|
2
|
+
import { type TenantCasClient } from "@unicas/tenant-client";
|
|
3
|
+
import type { SnapshotWriter } from "@unidocs/agent-operator-core";
|
|
4
|
+
import { type DocumentTypeContext, type MakeSBlob } from "@unidocs/protocol";
|
|
5
|
+
export interface OperatorCasEnv {
|
|
6
|
+
readonly OPERATOR_CAS_ORIGIN: string;
|
|
7
|
+
readonly OPERATOR_CAS_STACK_ID: string;
|
|
8
|
+
readonly OPERATOR_CAS_ISSUER: string;
|
|
9
|
+
readonly OPERATOR_CAS_AUDIENCE: string;
|
|
10
|
+
readonly OPERATOR_CAS_SIGNING_KID: string;
|
|
11
|
+
readonly OPERATOR_CAS_SIGNING_KEY: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function createOperatorStorage(env: Partial<OperatorCasEnv>, subject: string): {
|
|
14
|
+
snapshots: SnapshotWriter;
|
|
15
|
+
context(tenantId: string): DocumentTypeContext;
|
|
16
|
+
};
|
|
17
|
+
export interface CasClients {
|
|
18
|
+
readonly cas: Pick<TenantCasClient, "leaseNode">;
|
|
19
|
+
readonly blobs: Pick<CasBlobClient, "storeBlob" | "openBlob">;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `MakeSBlob` 的两种调用形态,其中按 hash 那一种是**认领**而不是写入。
|
|
23
|
+
*
|
|
24
|
+
* 调用方(`storePsdDoc` 之类的外部化步骤)手里已经有一个 hash —— 字节是上一步
|
|
25
|
+
* `serialize` 刚写进 CAS 的 —— 它要的只是「把这个 hash 变成一个 SBlob」。`produce` 是
|
|
26
|
+
* blob 不在时的回落,不是正常路径。
|
|
27
|
+
*
|
|
28
|
+
* 此前这里无条件调 `produce`,于是认领这条路根本走不通:PSD 的 produce 是一句
|
|
29
|
+
* 「was not stored during externalization」的抛错,所以任何带像素的 apply 都必然失败,
|
|
30
|
+
* 一个版本都提交不出来。一代(`doctype-server-common` 的 `#ensure`)一直是先探后产,
|
|
31
|
+
* 两条路径的差异就在这里。
|
|
32
|
+
*
|
|
33
|
+
* 只有 404 / 409 算「不在」。授权与传输失败必须原样抛:把 401 伪装成「内容没写进去」
|
|
34
|
+
* 会让排查方向整个反掉。
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMakeSBlob(open: () => Promise<CasClients>): MakeSBlob;
|
|
37
|
+
export declare function createSnapshotWriter(env: Partial<OperatorCasEnv>, subject: string): SnapshotWriter;
|
|
38
|
+
//# sourceMappingURL=cas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cas.d.ts","sourceRoot":"","sources":["../src/cas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAyC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACpG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAA+B,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAoB,MAAM,mBAAmB,CAAC;AAK5H,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;CAC3C;AAYD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAA;CAAE,CAoDlK;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC;CAC/D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,CAkB1E;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAElG"}
|
package/dist/cas.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createCasBlobClient } from "@unicas/tenant-blob-client";
|
|
2
|
+
import { CasClientError, createTenantCasClient } from "@unicas/tenant-client";
|
|
3
|
+
import { documentSnapshotContentType } from "@unidocs/protocol";
|
|
4
|
+
import { casReadPermission, casWritePermission, createPkcs8CapabilityIssuer } from "@unidocs/service-auth";
|
|
5
|
+
import { encodeSValue } from "@unidocs/svalue-codec";
|
|
6
|
+
import { createSBlob } from "@unidocs/svalue-codec/internal";
|
|
7
|
+
const CAS_BINDINGS = ["OPERATOR_CAS_ORIGIN", "OPERATOR_CAS_STACK_ID", "OPERATOR_CAS_ISSUER", "OPERATOR_CAS_AUDIENCE", "OPERATOR_CAS_SIGNING_KID", "OPERATOR_CAS_SIGNING_KEY"];
|
|
8
|
+
function stream(source) {
|
|
9
|
+
if ("data" in source)
|
|
10
|
+
return new ReadableStream({ start(controller) { controller.enqueue(source.data); controller.close(); } });
|
|
11
|
+
const iterator = source.body[Symbol.asyncIterator]();
|
|
12
|
+
return new ReadableStream({
|
|
13
|
+
async pull(controller) { const next = await iterator.next(); if (next.done)
|
|
14
|
+
controller.close();
|
|
15
|
+
else
|
|
16
|
+
controller.enqueue(next.value); },
|
|
17
|
+
async cancel() { await iterator.return?.(); },
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function createOperatorStorage(env, subject) {
|
|
21
|
+
let issuer;
|
|
22
|
+
// 两层都留着:`CasBlobClient` 管整份 blob 的读写,底下那个 `TenantCasClient` 提供
|
|
23
|
+
// `leaseNode` —— 「这个 hash 在不在 CAS 里」的存在性探针,`makeSBlob(hash, produce)`
|
|
24
|
+
// 靠它决定是认领还是产出。`createCasBlobClient` 不暴露它。
|
|
25
|
+
const clients = new Map();
|
|
26
|
+
async function open(tenantId) {
|
|
27
|
+
for (const name of CAS_BINDINGS)
|
|
28
|
+
if (!env[name])
|
|
29
|
+
throw new TypeError(`Operator CAS binding ${name} is missing`);
|
|
30
|
+
let pending = clients.get(tenantId);
|
|
31
|
+
if (!pending) {
|
|
32
|
+
const config = env;
|
|
33
|
+
pending = (async () => {
|
|
34
|
+
issuer ??= createPkcs8CapabilityIssuer({ issuer: config.OPERATOR_CAS_ISSUER, kid: config.OPERATOR_CAS_SIGNING_KID, privateKeyPkcs8: config.OPERATOR_CAS_SIGNING_KEY });
|
|
35
|
+
const resolved = await issuer.catch(error => { issuer = undefined; throw error; });
|
|
36
|
+
const cas = createTenantCasClient({
|
|
37
|
+
baseUrl: config.OPERATOR_CAS_ORIGIN, stackId: config.OPERATOR_CAS_STACK_ID, tenantId,
|
|
38
|
+
getToken: () => resolved.issue({ subject, audience: config.OPERATOR_CAS_AUDIENCE, tenantId, permissions: [casReadPermission(tenantId), casWritePermission(tenantId)] }),
|
|
39
|
+
});
|
|
40
|
+
return { cas, blobs: createCasBlobClient(cas) };
|
|
41
|
+
})();
|
|
42
|
+
clients.set(tenantId, pending);
|
|
43
|
+
pending.catch(() => clients.delete(tenantId));
|
|
44
|
+
}
|
|
45
|
+
return pending;
|
|
46
|
+
}
|
|
47
|
+
const client = async (tenantId) => (await open(tenantId)).blobs;
|
|
48
|
+
return {
|
|
49
|
+
snapshots: {
|
|
50
|
+
async write(tenantId, documentType, snapshot) {
|
|
51
|
+
const data = encodeSValue(snapshot);
|
|
52
|
+
const ref = await (await client(tenantId)).storeBlob(stream({ data, contentType: documentSnapshotContentType(documentType) }), { size: data.byteLength, contentType: documentSnapshotContentType(documentType) });
|
|
53
|
+
return { blobHash: ref.hash, size: ref.size, contentType: ref.contentType };
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
context(tenantId) {
|
|
57
|
+
return {
|
|
58
|
+
makeSBlob: createMakeSBlob(() => open(tenantId)),
|
|
59
|
+
async openSBlob(blob) {
|
|
60
|
+
const handle = await (await client(tenantId)).openBlob(blob.hash);
|
|
61
|
+
return {
|
|
62
|
+
size: handle.ref.size, contentType: handle.ref.contentType,
|
|
63
|
+
readBytes: range => handle.readBytes(range),
|
|
64
|
+
async *read(range) {
|
|
65
|
+
const reader = handle.read(range).getReader();
|
|
66
|
+
try {
|
|
67
|
+
for (;;) {
|
|
68
|
+
const { done, value } = await reader.read();
|
|
69
|
+
if (done)
|
|
70
|
+
return;
|
|
71
|
+
yield value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await reader.cancel().catch(() => { });
|
|
76
|
+
reader.releaseLock();
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `MakeSBlob` 的两种调用形态,其中按 hash 那一种是**认领**而不是写入。
|
|
87
|
+
*
|
|
88
|
+
* 调用方(`storePsdDoc` 之类的外部化步骤)手里已经有一个 hash —— 字节是上一步
|
|
89
|
+
* `serialize` 刚写进 CAS 的 —— 它要的只是「把这个 hash 变成一个 SBlob」。`produce` 是
|
|
90
|
+
* blob 不在时的回落,不是正常路径。
|
|
91
|
+
*
|
|
92
|
+
* 此前这里无条件调 `produce`,于是认领这条路根本走不通:PSD 的 produce 是一句
|
|
93
|
+
* 「was not stored during externalization」的抛错,所以任何带像素的 apply 都必然失败,
|
|
94
|
+
* 一个版本都提交不出来。一代(`doctype-server-common` 的 `#ensure`)一直是先探后产,
|
|
95
|
+
* 两条路径的差异就在这里。
|
|
96
|
+
*
|
|
97
|
+
* 只有 404 / 409 算「不在」。授权与传输失败必须原样抛:把 401 伪装成「内容没写进去」
|
|
98
|
+
* 会让排查方向整个反掉。
|
|
99
|
+
*/
|
|
100
|
+
export function createMakeSBlob(open) {
|
|
101
|
+
const store = async (source) => {
|
|
102
|
+
const { blobs } = await open();
|
|
103
|
+
const ref = await blobs.storeBlob(stream(source), { contentType: source.contentType, size: "data" in source ? source.data.byteLength : source.size });
|
|
104
|
+
return ref.hash;
|
|
105
|
+
};
|
|
106
|
+
return (async (input, load) => {
|
|
107
|
+
if (typeof input !== "string")
|
|
108
|
+
return createSBlob(await store(input));
|
|
109
|
+
try {
|
|
110
|
+
await (await open()).cas.leaseNode(input);
|
|
111
|
+
return createSBlob(input);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof CasClientError) || (error.status !== 404 && error.status !== 409))
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
const hash = await store(await load());
|
|
118
|
+
if (hash !== input)
|
|
119
|
+
throw new TypeError("SBlob content does not match its expected hash");
|
|
120
|
+
return createSBlob(hash);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export function createSnapshotWriter(env, subject) {
|
|
124
|
+
return createOperatorStorage(env, subject).snapshots;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=cas.js.map
|
package/dist/cas.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cas.js","sourceRoot":"","sources":["../src/cas.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DurableObject } from "cloudflare:workers";
|
|
2
|
+
import { type AgentDocumentKind, type SessionKey } from "@unidocs/agent-operator-core";
|
|
3
|
+
import type { DocumentTypeContext, LlmProvider } from "@unidocs/protocol";
|
|
4
|
+
import type { OperatorWebhookRequest } from "@unidocs/protocol-platform";
|
|
5
|
+
import { type OperatorCasEnv } from "./cas.js";
|
|
6
|
+
export interface AgentSessionEnv extends Partial<OperatorCasEnv> {
|
|
7
|
+
readonly PLATFORM_ORIGIN?: string;
|
|
8
|
+
readonly PLATFORM_SERVICE?: Fetcher;
|
|
9
|
+
readonly OPERATOR_AGENT_TOKEN?: string;
|
|
10
|
+
readonly LLM_API_KEY?: string;
|
|
11
|
+
readonly LLM_BASE_URL?: string;
|
|
12
|
+
readonly LLM_MODEL?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface OperatorAgentSession extends DurableObject<AgentSessionEnv> {
|
|
15
|
+
notify(key: SessionKey, event: OperatorWebhookRequest): Promise<void>;
|
|
16
|
+
alarm(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export type OperatorAgentSessionClass = new (ctx: DurableObjectState, env: AgentSessionEnv) => OperatorAgentSession;
|
|
19
|
+
export declare function createOperatorAgentSessionClass<TDoc, TQuery, TOp, TEnv extends AgentSessionEnv = AgentSessionEnv>(options: {
|
|
20
|
+
readonly subject: string;
|
|
21
|
+
/**
|
|
22
|
+
* 按 `SessionKey` 构造 kind,并且**也收一份 env**。
|
|
23
|
+
*
|
|
24
|
+
* 收 env 是因为不是所有 doctype 的 kind 都只需要一个 blob 端口:PSD 的工具表要按 env
|
|
25
|
+
* 决定装不装 `editPixels`(要一个图像编辑器)与 `setText`(要字体索引),两者都来自
|
|
26
|
+
* 绑定;字体索引还要按 `key.tenantId` 分租户,所以 key 也一并给出。markdown 与 canvas
|
|
27
|
+
* 用不到后两个参数,忽略即可。
|
|
28
|
+
*
|
|
29
|
+
* 在此之前只有 `provider` 收 env,于是这一层隐含地假设了「doctype 从环境里唯一要的
|
|
30
|
+
* 东西是模型」。PSD 是第一个不成立的类型。
|
|
31
|
+
*/
|
|
32
|
+
readonly kind: (context: DocumentTypeContext, env: TEnv, key: SessionKey) => AgentDocumentKind<TDoc, TQuery, TOp>;
|
|
33
|
+
readonly provider: (env: TEnv) => LlmProvider;
|
|
34
|
+
/**
|
|
35
|
+
* 这个 Operator 按哪个契约修订提交。缺省 0 —— 只有一个修订的类型不必关心。
|
|
36
|
+
*
|
|
37
|
+
* 必须与它 descriptor 里声明的那一档是**同一个数**:平台按
|
|
38
|
+
* `availableDocumentContractIdxs`(View manifest ∩ Operator descriptor)判定可用,
|
|
39
|
+
* 填一个不在其中的修订,整轮提交会被拒。
|
|
40
|
+
*/
|
|
41
|
+
readonly documentContractIdx?: number;
|
|
42
|
+
}): OperatorAgentSessionClass;
|
|
43
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAgD,KAAK,iBAAiB,EAAyB,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC5J,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEzE,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAEtE,MAAM,WAAW,eAAgB,SAAQ,OAAO,CAAC,cAAc,CAAC;IAC9D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AACD,MAAM,WAAW,oBAAqB,SAAQ,aAAa,CAAC,eAAe,CAAC;IAC1E,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AACD,MAAM,MAAM,yBAAyB,GAAG,KAAK,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,eAAe,KAAK,oBAAoB,CAAC;AAEpH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,SAAS,eAAe,GAAG,eAAe,EAAE,OAAO,EAAE;IAC1H,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,KAAK,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IAClH,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,WAAW,CAAC;IAC9C;;;;;;OAMG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC,GAAG,yBAAyB,CA+B5B"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DurableObject } from "cloudflare:workers";
|
|
2
|
+
import { createAgentSessionCore, createPlatformClient } from "@unidocs/agent-operator-core";
|
|
3
|
+
import { createAlarmWakeScheduler, createSqlSessionStore } from "./agent-session-store.js";
|
|
4
|
+
import { createOperatorStorage } from "./cas.js";
|
|
5
|
+
export function createOperatorAgentSessionClass(options) {
|
|
6
|
+
return class extends DurableObject {
|
|
7
|
+
#store;
|
|
8
|
+
#core;
|
|
9
|
+
constructor(ctx, env) {
|
|
10
|
+
super(ctx, env);
|
|
11
|
+
this.#store = createSqlSessionStore({ sql: ctx.storage.sql, transaction: fn => ctx.storage.transactionSync(fn) });
|
|
12
|
+
const storage = createOperatorStorage(env, options.subject);
|
|
13
|
+
const contexts = new Map();
|
|
14
|
+
const context = (key) => {
|
|
15
|
+
let value = contexts.get(key.tenantId);
|
|
16
|
+
if (!value) {
|
|
17
|
+
value = storage.context(key.tenantId);
|
|
18
|
+
contexts.set(key.tenantId, value);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
this.#core = createAgentSessionCore({
|
|
23
|
+
kind: key => options.kind(context(key), env, key),
|
|
24
|
+
store: this.#store, wake: createAlarmWakeScheduler(ctx.storage),
|
|
25
|
+
platform: createPlatformClient({
|
|
26
|
+
origin: env.PLATFORM_ORIGIN ?? "", token: env.OPERATOR_AGENT_TOKEN ?? "",
|
|
27
|
+
fetch: request => env.PLATFORM_SERVICE ? env.PLATFORM_SERVICE.fetch(request) : fetch(request),
|
|
28
|
+
}),
|
|
29
|
+
snapshots: storage.snapshots, provider: options.provider(env),
|
|
30
|
+
...(options.documentContractIdx === undefined ? {} : { documentContractIdx: options.documentContractIdx }),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async notify(key, event) { await this.#core.notify(key, event); }
|
|
34
|
+
async alarm() {
|
|
35
|
+
const key = this.#store.currentKey();
|
|
36
|
+
if (key)
|
|
37
|
+
await this.#core.runOnce(key);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAkE,MAAM,8BAA8B,CAAC;AAG5J,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,EAAE,qBAAqB,EAAuB,MAAM,UAAU,CAAC;AAgBtE,MAAM,UAAU,+BAA+B,CAAoE,OAuBlH;IACC,OAAO,KAAM,SAAQ,aAA8B;QACxC,MAAM,CAA2C;QACjD,KAAK,CAAmB;QACjC,YAAY,GAAuB,EAAE,GAAoB;YACvD,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAChB,IAAI,CAAC,MAAM,GAAG,qBAAqB,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAClH,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;YACxD,MAAM,OAAO,GAAG,CAAC,GAAe,EAAE,EAAE;gBAClC,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACvC,IAAI,CAAC,KAAK,EAAE,CAAC;oBAAC,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAAC,CAAC;gBACzF,OAAO,KAAK,CAAC;YACf,CAAC,CAAC;YACF,IAAI,CAAC,KAAK,GAAG,sBAAsB,CAAC;gBAClC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAW,EAAE,GAAG,CAAC;gBACzD,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,wBAAwB,CAAC,GAAG,CAAC,OAAO,CAAC;gBAC/D,QAAQ,EAAE,oBAAoB,CAAC;oBAC7B,MAAM,EAAE,GAAG,CAAC,eAAe,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,oBAAoB,IAAI,EAAE;oBACxE,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;iBAC9F,CAAC;gBACF,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAW,CAAC;gBACrE,GAAG,CAAC,OAAO,CAAC,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,EAAE,CAAC;aAC3G,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAe,EAAE,KAA6B,IAAmB,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACpH,KAAK,CAAC,KAAK;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACrC,IAAI,GAAG;gBAAE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unidocs/operator-cloudflare",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cloudflare runtime for the UniDocs Operator: Agent session Durable Object and operator CAS storage",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cas": {
|
|
14
|
+
"types": "./dist/cas.d.ts",
|
|
15
|
+
"import": "./dist/cas.js"
|
|
16
|
+
},
|
|
17
|
+
"./agent-session-store": {
|
|
18
|
+
"types": "./dist/agent-session-store.d.ts",
|
|
19
|
+
"import": "./dist/agent-session-store.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@cloudflare/workers-types": "^4.20250817.0",
|
|
27
|
+
"typescript": "^5.9.0",
|
|
28
|
+
"vitest": "^3.2.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@unicas/tenant-blob-client": "0.1.0",
|
|
32
|
+
"@unicas/tenant-client": "0.1.0",
|
|
33
|
+
"@unidocs/agent-operator-core": "0.1.0",
|
|
34
|
+
"@unidocs/protocol": "0.1.0",
|
|
35
|
+
"@unidocs/protocol-platform": "0.1.0",
|
|
36
|
+
"@unidocs/service-auth": "0.1.0",
|
|
37
|
+
"@unidocs/svalue-codec": "0.1.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"test": "vitest run --maxWorkers=2 --passWithNoTests",
|
|
42
|
+
"typecheck": "tsc -b",
|
|
43
|
+
"clean": "rimraf --glob dist \"*.tsbuildinfo\""
|
|
44
|
+
}
|
|
45
|
+
}
|