@tool-bridge/server 0.19.0 → 0.21.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/{airtable-23JBIM6X.js → airtable-DMJEEGJP.js} +19 -16
- package/dist/{alt_text_generator_ai-ADWBTCIH.js → alt_text_generator_ai-RELFQ5FV.js} +4 -4
- package/dist/{amap-CYQCYW2Z.js → amap-YSYEACKQ.js} +4 -4
- package/dist/{anthropic-5P62HDNR.js → anthropic-D5X5GSYE.js} +23 -25
- package/dist/{apify-54RZUAGW.js → apify-5NVFU3IZ.js} +20 -19
- package/dist/{brave_search-AHJ6FTD5.js → brave_search-X557FMGO.js} +16 -14
- package/dist/chunk-JEWFX6Z6.js +71 -0
- package/dist/{chunk-CLEZ6YQ6.js → chunk-MDY7K5CX.js} +1090 -606
- package/dist/{chunk-OKRZPQ3R.js → chunk-MZCIPUNQ.js} +1002 -242
- package/dist/{chunk-VLOIDU5U.js → chunk-Q74GLIDK.js} +3 -3
- package/dist/{chunk-B4P6YZFS.js → chunk-QR4LK3HF.js} +1 -1
- package/dist/{chunk-SWBNHIO4.js → chunk-ZTTN6AJG.js} +1 -1
- package/dist/{circleci-UM3NSQJ3.js → circleci-3P3QTUX2.js} +20 -24
- package/dist/{clerk-FE5LQ2CZ.js → clerk-GNMZQ2MY.js} +22 -21
- package/dist/{cloudflare_dns-KEZABKUT.js → cloudflare_dns-VWZH3ZTR.js} +21 -17
- package/dist/{cohere-4IPJADXP.js → cohere-C7QVIIMG.js} +19 -17
- package/dist/{confluence-56QEKNQI.js → confluence-52I37IUT.js} +16 -11
- package/dist/{convertapi-3QRM5K5T.js → convertapi-3TRJ3WEO.js} +3 -3
- package/dist/{currencyapi-L6G4OND5.js → currencyapi-LWEANWVI.js} +23 -26
- package/dist/{deepseek-LGC6HBFG.js → deepseek-5U24YG4P.js} +27 -17
- package/dist/{docker_hub-XDMK3BIU.js → docker_hub-CMISLRSE.js} +4 -4
- package/dist/{dropbox-4QARMFZU.js → dropbox-TW4QN6FT.js} +9 -8
- package/dist/{dub-C6JSJKOT.js → dub-EFXGI5UH.js} +22 -19
- package/dist/{e2b-6A6TVACJ.js → e2b-PHM63OVF.js} +20 -20
- package/dist/{exa-SIWWW6AA.js → exa-DYOQWURR.js} +22 -25
- package/dist/{fal_ai-3IJXOKSP.js → fal_ai-VJPMCNRH.js} +18 -17
- package/dist/{feishu-L4KDA4DC.js → feishu-TFIZXPIN.js} +2 -2
- package/dist/{feishu_custom_bot-SDM75H4P.js → feishu_custom_bot-ADSE23KU.js} +3 -3
- package/dist/{figma-6DD436DN.js → figma-4AK47AYY.js} +21 -23
- package/dist/{firehydrant-SYSGYB3R.js → firehydrant-RGPTW4EA.js} +15 -15
- package/dist/{fixer-FZG7ASTF.js → fixer-OAMF4DKP.js} +4 -4
- package/dist/{fly-HMRCZBUC.js → fly-YB4AQD4O.js} +20 -16
- package/dist/{front-FAFSD7I3.js → front-6GTVYKUM.js} +20 -23
- package/dist/{geocodio-4D3TWENM.js → geocodio-YRT4K25M.js} +4 -4
- package/dist/{ghost-OSO2FOZS.js → ghost-JCW6UIP2.js} +4 -4
- package/dist/{github-OR4I4RHE.js → github-CEGVRMNX.js} +28 -27
- package/dist/{gmail-5AOPJ63E.js → gmail-JCMJD55P.js} +17 -17
- package/dist/{googlecalendar-SOJIJUWH.js → googlecalendar-2EAW4MNM.js} +16 -14
- package/dist/{googledocs-QSKN5TLN.js → googledocs-YNKKBM6Q.js} +3 -3
- package/dist/{grafana-R3AIZM7M.js → grafana-353VSGXN.js} +24 -25
- package/dist/{graphhopper-4CR3QOAX.js → graphhopper-H3IVJNDS.js} +4 -4
- package/dist/index.d.ts +52 -36
- package/dist/index.js +3 -3
- package/dist/{ip2proxy-OBCG6X6J.js → ip2proxy-DBGRWH7S.js} +4 -4
- package/dist/{ipgeolocation_io-NHH3DTQW.js → ipgeolocation_io-5I2BDVMG.js} +4 -4
- package/dist/{ipqualityscore-7POMCBEC.js → ipqualityscore-ILV7TQCV.js} +4 -4
- package/dist/{jina_ai-YXVKROV4.js → jina_ai-L5BH455V.js} +20 -21
- package/dist/{jira-LADO3VJU.js → jira-IGTILUZA.js} +20 -16
- package/dist/{langsmith-XZ6UAQFU.js → langsmith-BYRX4AOF.js} +25 -25
- package/dist/{lemlist-P74GLNE7.js → lemlist-ZGNYTIQO.js} +23 -24
- package/dist/{linear-CXJ7D37A.js → linear-HLGOWISN.js} +3 -3
- package/dist/{logsnag-EVVHWFLK.js → logsnag-5RGTMTU2.js} +20 -22
- package/dist/main.js +102 -102
- package/dist/{mattermost-WG2MRGBD.js → mattermost-BONGXVC7.js} +21 -17
- package/dist/{mem0-HQWUKG6U.js → mem0-RUVNUPTE.js} +15 -15
- package/dist/{memos-SQMJIFOE.js → memos-TED4HI54.js} +36 -32
- package/dist/{metabase-E27G2DZI.js → metabase-ZHXHRHZK.js} +22 -18
- package/dist/{mistral_ai-MIL63NIT.js → mistral_ai-NGP5SHU7.js} +24 -21
- package/dist/{mother_duck-AIZVCBNR.js → mother_duck-TKVQM35M.js} +23 -19
- package/dist/{ngrok-XS4DVIQS.js → ngrok-BYCUJ7QV.js} +17 -22
- package/dist/{notes-JEOUSL2Y.js → notes-KXRNNYDW.js} +1 -1
- package/dist/{notion-U4EZVDW2.js → notion-2ILYKJOO.js} +20 -17
- package/dist/{open_exchange_rates-AZSQEYEJ.js → open_exchange_rates-JU4DC6HZ.js} +4 -4
- package/dist/{openai-HW3SYMTQ.js → openai-6SOT26ZK.js} +22 -16
- package/dist/{opengraph_io-QX74MAEL.js → opengraph_io-ZEFHV2OL.js} +4 -4
- package/dist/{openrouter-OXSKYL6N.js → openrouter-R4P7JL5L.js} +19 -14
- package/dist/{outline-CCQAPDSW.js → outline-VCTQ5V2X.js} +18 -20
- package/dist/{paddle-6KQPSSCK.js → paddle-AZOAKBJS.js} +17 -15
- package/dist/{perplexity-2TXO7IQE.js → perplexity-OMRLGVAT.js} +19 -23
- package/dist/{pinecone-YOO7NULI.js → pinecone-DI62W3ZX.js} +19 -24
- package/dist/{pipedrive-Q6RIFIFP.js → pipedrive-CW7HONYW.js} +22 -23
- package/dist/{polar-KUMGCES3.js → polar-5DRGTXNR.js} +19 -17
- package/dist/{posthog-VXQHBSMB.js → posthog-3DTHFCKE.js} +29 -24
- package/dist/{postmark-YLIPF52S.js → postmark-ME2V2XXI.js} +19 -17
- package/dist/{prerender-CRY7UJN5.js → prerender-5EAY7GDM.js} +4 -4
- package/dist/{pubmed-3Z4T5AM7.js → pubmed-GRQUBGVA.js} +3 -3
- package/dist/{railway-BP37IGFL.js → railway-FTM75TMX.js} +3 -3
- package/dist/{readwise-HTKICF4K.js → readwise-IJQGV6IQ.js} +16 -15
- package/dist/{render-WIVTUXJH.js → render-TMSPHRTY.js} +21 -20
- package/dist/{replicate-JJYPA43D.js → replicate-RJCMDB7H.js} +20 -21
- package/dist/{resend-TKHZVG7A.js → resend-YYN22NIL.js} +15 -11
- package/dist/{rootly-FRSZLG5U.js → rootly-7RXE4K7L.js} +19 -18
- package/dist/{scrapfly-M5WXGQWE.js → scrapfly-ZJJ6XM4Q.js} +4 -4
- package/dist/{scrapingbee-PMXNLLBK.js → scrapingbee-QNGEOD6D.js} +4 -4
- package/dist/{screenshot_fyi-HFW4TAOH.js → screenshot_fyi-SPZAABVD.js} +4 -4
- package/dist/{semantic_scholar-KXVH2VZQ.js → semantic_scholar-SDPVGDDI.js} +21 -20
- package/dist/{sentry-WWOT2LRX.js → sentry-SJ5NN4C4.js} +22 -19
- package/dist/{shodan-FY635H35.js → shodan-YMGOY7KW.js} +4 -4
- package/dist/{shopify-HQLBVSA5.js → shopify-SU5ABP54.js} +23 -19
- package/dist/{stripe-GJN2GOHB.js → stripe-JAC425KE.js} +3 -3
- package/dist/{supabase-BBNZGQU6.js → supabase-GQ2OFVIL.js} +18 -17
- package/dist/{tavily-DPENIBOG.js → tavily-Y7MDA5CI.js} +20 -20
- package/dist/{telegram-L4NONG6H.js → telegram-OIDT3ZLH.js} +4 -4
- package/dist/{telnyx-H67MAEZS.js → telnyx-4JGMC3K7.js} +19 -19
- package/dist/{todoist-G2BSP4DX.js → todoist-O7S2OUGX.js} +18 -17
- package/dist/{trello-7WMVFDG2.js → trello-3LDE4TAQ.js} +4 -4
- package/dist/{turso-ZUAESFC2.js → turso-MUQ44KCA.js} +23 -30
- package/dist/{twilio-EYFYIX3B.js → twilio-VYF3HUU6.js} +3 -3
- package/dist/{umami-AXAUDUVF.js → umami-SVQIK26J.js} +23 -28
- package/dist/{unsplash-UCQAWPLR.js → unsplash-333XYDCJ.js} +19 -18
- package/dist/{upstash_redis-ZPZ4ASU6.js → upstash_redis-IUM2HDLU.js} +17 -13
- package/dist/{uptimerobot-4QVKKUD5.js → uptimerobot-7ZRU2NJV.js} +3 -3
- package/dist/{v2ex-VGEFJTYC.js → v2ex-AVSJCQPS.js} +35 -18
- package/dist/{vercel-JME4TSCN.js → vercel-J7RGXNSA.js} +21 -18
- package/dist/{wolfram_alpha_api-TRGU2MDG.js → wolfram_alpha_api-6CRVUZOJ.js} +4 -4
- package/dist/{wordpress-WRUKLCZR.js → wordpress-THOJLPKA.js} +21 -17
- package/dist/{workos-ZDW4EHS4.js → workos-ENTGRWF2.js} +26 -27
- package/dist/{zhihu-4VI67CWU.js → zhihu-W2BXICTX.js} +21 -21
- package/package.json +3 -3
|
@@ -1328,16 +1328,16 @@ function cleanEnum(obj) {
|
|
|
1328
1328
|
}
|
|
1329
1329
|
function base64ToUint8Array(base644) {
|
|
1330
1330
|
const binaryString = atob(base644);
|
|
1331
|
-
const
|
|
1331
|
+
const bytes2 = new Uint8Array(binaryString.length);
|
|
1332
1332
|
for (let i = 0; i < binaryString.length; i++) {
|
|
1333
|
-
|
|
1333
|
+
bytes2[i] = binaryString.charCodeAt(i);
|
|
1334
1334
|
}
|
|
1335
|
-
return
|
|
1335
|
+
return bytes2;
|
|
1336
1336
|
}
|
|
1337
|
-
function uint8ArrayToBase64(
|
|
1337
|
+
function uint8ArrayToBase64(bytes2) {
|
|
1338
1338
|
let binaryString = "";
|
|
1339
|
-
for (let i = 0; i <
|
|
1340
|
-
binaryString += String.fromCharCode(
|
|
1339
|
+
for (let i = 0; i < bytes2.length; i++) {
|
|
1340
|
+
binaryString += String.fromCharCode(bytes2[i]);
|
|
1341
1341
|
}
|
|
1342
1342
|
return btoa(binaryString);
|
|
1343
1343
|
}
|
|
@@ -1346,22 +1346,22 @@ function base64urlToUint8Array(base64url3) {
|
|
|
1346
1346
|
const padding = "=".repeat((4 - base644.length % 4) % 4);
|
|
1347
1347
|
return base64ToUint8Array(base644 + padding);
|
|
1348
1348
|
}
|
|
1349
|
-
function uint8ArrayToBase64url(
|
|
1350
|
-
return uint8ArrayToBase64(
|
|
1349
|
+
function uint8ArrayToBase64url(bytes2) {
|
|
1350
|
+
return uint8ArrayToBase64(bytes2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1351
1351
|
}
|
|
1352
1352
|
function hexToUint8Array(hex3) {
|
|
1353
1353
|
const cleanHex = hex3.replace(/^0x/, "");
|
|
1354
1354
|
if (cleanHex.length % 2 !== 0) {
|
|
1355
1355
|
throw new Error("Invalid hex string length");
|
|
1356
1356
|
}
|
|
1357
|
-
const
|
|
1357
|
+
const bytes2 = new Uint8Array(cleanHex.length / 2);
|
|
1358
1358
|
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1359
|
-
|
|
1359
|
+
bytes2[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1360
1360
|
}
|
|
1361
|
-
return
|
|
1361
|
+
return bytes2;
|
|
1362
1362
|
}
|
|
1363
|
-
function uint8ArrayToHex(
|
|
1364
|
-
return Array.from(
|
|
1363
|
+
function uint8ArrayToHex(bytes2) {
|
|
1364
|
+
return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1365
1365
|
}
|
|
1366
1366
|
var Class = class {
|
|
1367
1367
|
constructor(..._args) {
|
|
@@ -14609,6 +14609,20 @@ function canonicalizePath(path) {
|
|
|
14609
14609
|
if (norm === "") return "";
|
|
14610
14610
|
return norm.split("/").map((seg) => canonicalizeSegment(seg)).join("/");
|
|
14611
14611
|
}
|
|
14612
|
+
function assertNoCollision(names, context) {
|
|
14613
|
+
const seen = /* @__PURE__ */ new Map();
|
|
14614
|
+
for (const name of names) {
|
|
14615
|
+
const lower = name.toLowerCase();
|
|
14616
|
+
const prev = seen.get(lower);
|
|
14617
|
+
if (prev !== void 0 && prev !== name) {
|
|
14618
|
+
throw new TBError(
|
|
14619
|
+
"invalid_argument",
|
|
14620
|
+
`${context}:\u6807\u8BC6\u7B26 '${prev}' \u4E0E '${name}' \u5C0F\u5199\u89C4\u8303\u5316\u540E\u51B2\u7A81`
|
|
14621
|
+
);
|
|
14622
|
+
}
|
|
14623
|
+
seen.set(lower, name);
|
|
14624
|
+
}
|
|
14625
|
+
}
|
|
14612
14626
|
function segments(path) {
|
|
14613
14627
|
const p = normalizePath(path);
|
|
14614
14628
|
return p === "" ? [] : p.split("/");
|
|
@@ -14682,10 +14696,10 @@ function normalizeAnnotationPath(path) {
|
|
|
14682
14696
|
return norm;
|
|
14683
14697
|
}
|
|
14684
14698
|
var AnnotationStore = class {
|
|
14699
|
+
store;
|
|
14685
14700
|
constructor(store) {
|
|
14686
14701
|
this.store = store;
|
|
14687
14702
|
}
|
|
14688
|
-
store;
|
|
14689
14703
|
keyOf(path) {
|
|
14690
14704
|
return KEY_ANNOTATION + path;
|
|
14691
14705
|
}
|
|
@@ -14823,7 +14837,7 @@ var SYSTEM_AUTO = "system:auto";
|
|
|
14823
14837
|
var RESERVED_ROOTS = ["system", "ui"];
|
|
14824
14838
|
|
|
14825
14839
|
// ../core/src/auth/registerPath.ts
|
|
14826
|
-
var
|
|
14840
|
+
var foldedSegments = (path) => segments(path).map((seg) => seg.toLowerCase());
|
|
14827
14841
|
function isUnderPrefix(target, prefix) {
|
|
14828
14842
|
if (prefix.length > target.length) return false;
|
|
14829
14843
|
return prefix.every((seg, i) => seg === target[i]);
|
|
@@ -14834,7 +14848,7 @@ var deny = (message) => ({
|
|
|
14834
14848
|
});
|
|
14835
14849
|
function checkRegisterPath(input) {
|
|
14836
14850
|
const { sk, targetPath, existing, reservedRoots = [] } = input;
|
|
14837
|
-
const target =
|
|
14851
|
+
const target = foldedSegments(targetPath);
|
|
14838
14852
|
if (target.some((seg) => seg.startsWith("~"))) {
|
|
14839
14853
|
return {
|
|
14840
14854
|
allow: false,
|
|
@@ -14842,12 +14856,12 @@ function checkRegisterPath(input) {
|
|
|
14842
14856
|
};
|
|
14843
14857
|
}
|
|
14844
14858
|
if (sk.registerPaths !== void 0) {
|
|
14845
|
-
const ok = sk.registerPaths.some((p) => isUnderPrefix(target,
|
|
14859
|
+
const ok = sk.registerPaths.some((p) => isUnderPrefix(target, foldedSegments(p)));
|
|
14846
14860
|
if (!ok) {
|
|
14847
14861
|
return deny(`path '${targetPath}' is outside declared registerPaths`);
|
|
14848
14862
|
}
|
|
14849
14863
|
} else {
|
|
14850
|
-
const roots = /* @__PURE__ */ new Set([...RESERVED_ROOTS, ...reservedRoots]);
|
|
14864
|
+
const roots = /* @__PURE__ */ new Set([...RESERVED_ROOTS, ...reservedRoots.map((r) => r.toLowerCase())]);
|
|
14851
14865
|
const head = target[0];
|
|
14852
14866
|
if (head !== void 0 && roots.has(head)) {
|
|
14853
14867
|
return deny(`cannot register under reserved root '${head}'`);
|
|
@@ -14893,25 +14907,29 @@ function assertSecretRefUse(scopes, config2) {
|
|
|
14893
14907
|
|
|
14894
14908
|
// ../core/src/encoding/base64url.ts
|
|
14895
14909
|
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
14896
|
-
function base64urlEncode(
|
|
14910
|
+
function base64urlEncode(bytes2) {
|
|
14897
14911
|
let out = "";
|
|
14898
|
-
for (let i = 0; i <
|
|
14899
|
-
const a =
|
|
14900
|
-
const b =
|
|
14901
|
-
const c =
|
|
14912
|
+
for (let i = 0; i < bytes2.length; i += 3) {
|
|
14913
|
+
const a = bytes2[i] ?? 0;
|
|
14914
|
+
const b = bytes2[i + 1] ?? 0;
|
|
14915
|
+
const c = bytes2[i + 2] ?? 0;
|
|
14902
14916
|
const n = a << 16 | b << 8 | c;
|
|
14903
14917
|
out += ALPHABET[n >>> 18 & 63];
|
|
14904
14918
|
out += ALPHABET[n >>> 12 & 63];
|
|
14905
|
-
if (i + 1 <
|
|
14906
|
-
if (i + 2 <
|
|
14919
|
+
if (i + 1 < bytes2.length) out += ALPHABET[n >>> 6 & 63];
|
|
14920
|
+
if (i + 2 < bytes2.length) out += ALPHABET[n & 63];
|
|
14907
14921
|
}
|
|
14908
14922
|
return out;
|
|
14909
14923
|
}
|
|
14924
|
+
function base64Encode(bytes2) {
|
|
14925
|
+
const url2 = base64urlEncode(bytes2).replaceAll("-", "+").replaceAll("_", "/");
|
|
14926
|
+
return url2 + "=".repeat((4 - url2.length % 4) % 4);
|
|
14927
|
+
}
|
|
14910
14928
|
function base64urlDecode(input) {
|
|
14911
14929
|
if (input.length % 4 === 1 || !/^[A-Za-z0-9_-]*$/.test(input)) {
|
|
14912
14930
|
throw new Error("base64urlDecode: malformed base64url input");
|
|
14913
14931
|
}
|
|
14914
|
-
const
|
|
14932
|
+
const bytes2 = [];
|
|
14915
14933
|
for (let i = 0; i < input.length; i += 4) {
|
|
14916
14934
|
const chunk = input.slice(i, i + 4);
|
|
14917
14935
|
const a = ALPHABET.indexOf(chunk[0] ?? "");
|
|
@@ -14919,13 +14937,19 @@ function base64urlDecode(input) {
|
|
|
14919
14937
|
const c = chunk.length >= 3 ? ALPHABET.indexOf(chunk[2] ?? "") : 0;
|
|
14920
14938
|
const d = chunk.length >= 4 ? ALPHABET.indexOf(chunk[3] ?? "") : 0;
|
|
14921
14939
|
const n = a << 18 | b << 12 | c << 6 | d;
|
|
14922
|
-
|
|
14923
|
-
if (chunk.length >= 3)
|
|
14924
|
-
if (chunk.length >= 4)
|
|
14940
|
+
bytes2.push(n >>> 16 & 255);
|
|
14941
|
+
if (chunk.length >= 3) bytes2.push(n >>> 8 & 255);
|
|
14942
|
+
if (chunk.length >= 4) bytes2.push(n & 255);
|
|
14925
14943
|
}
|
|
14926
|
-
return new Uint8Array(
|
|
14944
|
+
return new Uint8Array(bytes2);
|
|
14927
14945
|
}
|
|
14928
14946
|
|
|
14947
|
+
// ../core/src/webGlobals.ts
|
|
14948
|
+
var g = globalThis;
|
|
14949
|
+
var crypto = g.crypto;
|
|
14950
|
+
var TextDecoder2 = g.TextDecoder;
|
|
14951
|
+
var TextEncoder = g.TextEncoder;
|
|
14952
|
+
|
|
14929
14953
|
// ../core/src/omit.ts
|
|
14930
14954
|
function omit2(obj, ...keys) {
|
|
14931
14955
|
const rest = { ...obj };
|
|
@@ -14942,8 +14966,8 @@ async function sha256Hex(text) {
|
|
|
14942
14966
|
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
14943
14967
|
}
|
|
14944
14968
|
function generateSecret() {
|
|
14945
|
-
const
|
|
14946
|
-
return `${SECRET_PREFIX}${base64urlEncode(
|
|
14969
|
+
const bytes2 = crypto.getRandomValues(new Uint8Array(16));
|
|
14970
|
+
return `${SECRET_PREFIX}${base64urlEncode(bytes2)}`;
|
|
14947
14971
|
}
|
|
14948
14972
|
function normalizeExpiresAt(value) {
|
|
14949
14973
|
const parsed = ISO_TIMESTAMP.safeParse(value);
|
|
@@ -15000,10 +15024,10 @@ function clampLimit(limit) {
|
|
|
15000
15024
|
return Math.min(Math.max(1, limit), LIST_LIMIT_MAX);
|
|
15001
15025
|
}
|
|
15002
15026
|
var SKRegistryStore = class {
|
|
15027
|
+
store;
|
|
15003
15028
|
constructor(store) {
|
|
15004
15029
|
this.store = store;
|
|
15005
15030
|
}
|
|
15006
|
-
store;
|
|
15007
15031
|
async recordById(id) {
|
|
15008
15032
|
const hash2 = await this.store.get(KEY_SK_ID + id);
|
|
15009
15033
|
if (typeof hash2 !== "string") return null;
|
|
@@ -15014,10 +15038,17 @@ var SKRegistryStore = class {
|
|
|
15014
15038
|
const listOpts = { limit: clampLimit(opts?.limit) };
|
|
15015
15039
|
if (opts?.cursor !== void 0) listOpts.cursor = opts.cursor;
|
|
15016
15040
|
const page = await this.store.list(KEY_SK_ID, listOpts);
|
|
15041
|
+
const hashes = page.items.map(({ value }) => value).filter((value) => typeof value === "string");
|
|
15042
|
+
const records = /* @__PURE__ */ new Map();
|
|
15043
|
+
for (let i = 0; i < hashes.length; i += 100) {
|
|
15044
|
+
const chunk = await this.store.getMany(
|
|
15045
|
+
hashes.slice(i, i + 100).map((hash2) => KEY_SK_HASH + hash2)
|
|
15046
|
+
);
|
|
15047
|
+
for (const [key, value] of chunk) records.set(key, value);
|
|
15048
|
+
}
|
|
15017
15049
|
const items = [];
|
|
15018
|
-
for (const
|
|
15019
|
-
|
|
15020
|
-
const rec = await this.store.get(KEY_SK_HASH + value);
|
|
15050
|
+
for (const hash2 of hashes) {
|
|
15051
|
+
const rec = records.get(KEY_SK_HASH + hash2);
|
|
15021
15052
|
if (rec) items.push(projectKey(rec));
|
|
15022
15053
|
}
|
|
15023
15054
|
return page.cursor !== void 0 ? { items, cursor: page.cursor } : { items };
|
|
@@ -15115,6 +15146,7 @@ var OperationRegistry = class {
|
|
|
15115
15146
|
const schema = spec.inputSchema !== void 0 ? normalizeSchema(spec.inputSchema) : void 0;
|
|
15116
15147
|
const toolSpec = { name };
|
|
15117
15148
|
if (spec.description !== void 0) toolSpec.description = spec.description;
|
|
15149
|
+
if (spec.delivery !== void 0) toolSpec.delivery = spec.delivery;
|
|
15118
15150
|
if (spec.effect !== void 0) toolSpec.effect = spec.effect;
|
|
15119
15151
|
if (spec.confirm !== void 0) toolSpec.confirm = spec.confirm;
|
|
15120
15152
|
if (schema !== void 0) toolSpec.inputSchema = toJsonSchema(schema);
|
|
@@ -15187,7 +15219,7 @@ var HtbpCommandRegistry = class {
|
|
|
15187
15219
|
metadata = /* @__PURE__ */ new Map();
|
|
15188
15220
|
operations = new OperationRegistry();
|
|
15189
15221
|
register(name, spec, handler) {
|
|
15190
|
-
const { h, returns, scope, ...operationSpec } = spec;
|
|
15222
|
+
const { h, hostOnly, returns, scope, ...operationSpec } = spec;
|
|
15191
15223
|
this.operations.register(
|
|
15192
15224
|
name,
|
|
15193
15225
|
{ ...operationSpec, description: h },
|
|
@@ -15195,6 +15227,7 @@ var HtbpCommandRegistry = class {
|
|
|
15195
15227
|
);
|
|
15196
15228
|
this.metadata.set(name, {
|
|
15197
15229
|
scope,
|
|
15230
|
+
...hostOnly !== void 0 ? { hostOnly } : {},
|
|
15198
15231
|
...returns !== void 0 ? { returns } : {}
|
|
15199
15232
|
});
|
|
15200
15233
|
return this;
|
|
@@ -15208,6 +15241,10 @@ var HtbpCommandRegistry = class {
|
|
|
15208
15241
|
scopeFor(name) {
|
|
15209
15242
|
return this.metadata.get(name)?.scope;
|
|
15210
15243
|
}
|
|
15244
|
+
/** 该命令是否宿主专属扩展(见 {@link HtbpCommandSpec.hostOnly});未注册按 false。 */
|
|
15245
|
+
isHostOnly(name) {
|
|
15246
|
+
return this.metadata.get(name)?.hostOnly === true;
|
|
15247
|
+
}
|
|
15211
15248
|
commandSpecs(nodePath) {
|
|
15212
15249
|
return this.operations.list().map((operation) => {
|
|
15213
15250
|
const metadata = this.metadata.get(operation.name);
|
|
@@ -15248,13 +15285,13 @@ var HtbpCommandRegistry = class {
|
|
|
15248
15285
|
|
|
15249
15286
|
// ../core/src/builtin/commandRegistry.ts
|
|
15250
15287
|
var BuiltinCommandRegistry = class extends HtbpCommandRegistry {
|
|
15288
|
+
moduleName;
|
|
15289
|
+
moduleDescription;
|
|
15251
15290
|
constructor(moduleName, moduleDescription) {
|
|
15252
15291
|
super();
|
|
15253
15292
|
this.moduleName = moduleName;
|
|
15254
15293
|
this.moduleDescription = moduleDescription;
|
|
15255
15294
|
}
|
|
15256
|
-
moduleName;
|
|
15257
|
-
moduleDescription;
|
|
15258
15295
|
help(nodePath) {
|
|
15259
15296
|
return this.helpModel({
|
|
15260
15297
|
path: nodePath,
|
|
@@ -15461,18 +15498,7 @@ function encodeForm(entries) {
|
|
|
15461
15498
|
return [...entries].map(([key, value]) => `${encode3(key)}=${encode3(value)}`).join("&");
|
|
15462
15499
|
}
|
|
15463
15500
|
function base643(ascii) {
|
|
15464
|
-
|
|
15465
|
-
let out = "";
|
|
15466
|
-
for (let i = 0; i < ascii.length; i += 3) {
|
|
15467
|
-
const a = ascii.charCodeAt(i);
|
|
15468
|
-
const b = i + 1 < ascii.length ? ascii.charCodeAt(i + 1) : Number.NaN;
|
|
15469
|
-
const c = i + 2 < ascii.length ? ascii.charCodeAt(i + 2) : Number.NaN;
|
|
15470
|
-
out += table[a >> 2];
|
|
15471
|
-
out += table[(a & 3) << 4 | (Number.isNaN(b) ? 0 : b >> 4)];
|
|
15472
|
-
out += Number.isNaN(b) ? "=" : table[(b & 15) << 2 | (Number.isNaN(c) ? 0 : c >> 6)];
|
|
15473
|
-
out += Number.isNaN(c) ? "=" : table[c & 63];
|
|
15474
|
-
}
|
|
15475
|
-
return out;
|
|
15501
|
+
return base64Encode(Uint8Array.from(ascii, (char) => char.charCodeAt(0)));
|
|
15476
15502
|
}
|
|
15477
15503
|
function asRecord(value) {
|
|
15478
15504
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
@@ -15663,6 +15689,8 @@ var contextCommands = new HtbpCommandRegistry().register(
|
|
|
15663
15689
|
"create_upload",
|
|
15664
15690
|
{
|
|
15665
15691
|
h: "create a short-lived, path-scoped direct-upload grant; persist the returned uri, not url",
|
|
15692
|
+
// 宿主专属扩展:仅在底层具备直传签名能力时由宿主装配,不进基础动词表。
|
|
15693
|
+
hostOnly: true,
|
|
15666
15694
|
inputSchema: external_exports.strictObject({
|
|
15667
15695
|
path: external_exports.string().describe("target entry path inside the namespace"),
|
|
15668
15696
|
contentType: external_exports.string().describe("media type signed into the PUT request"),
|
|
@@ -15686,14 +15714,14 @@ function contextScopeForCmd(command) {
|
|
|
15686
15714
|
}
|
|
15687
15715
|
function parseContextCmdArgs(command, args) {
|
|
15688
15716
|
const normalized = command.toLowerCase();
|
|
15689
|
-
if (!contextCommands.has(normalized) || normalized
|
|
15717
|
+
if (!contextCommands.has(normalized) || contextCommands.isHostOnly(normalized)) {
|
|
15690
15718
|
throw new TBError("invalid_argument", `unknown cmd '${command}'`);
|
|
15691
15719
|
}
|
|
15692
15720
|
return contextCommands.parse(normalized, args);
|
|
15693
15721
|
}
|
|
15694
15722
|
async function dispatchContextCmd(provider, command, args) {
|
|
15695
15723
|
const normalized = command.toLowerCase();
|
|
15696
|
-
if (!contextCommands.has(normalized) || normalized
|
|
15724
|
+
if (!contextCommands.has(normalized) || contextCommands.isHostOnly(normalized)) {
|
|
15697
15725
|
throw new TBError("invalid_argument", `unknown cmd '${command}'`);
|
|
15698
15726
|
}
|
|
15699
15727
|
const method = normalized;
|
|
@@ -15715,7 +15743,7 @@ async function dispatchContextUploadCmd(args, upload) {
|
|
|
15715
15743
|
}
|
|
15716
15744
|
|
|
15717
15745
|
// ../core/src/context/capabilities.ts
|
|
15718
|
-
var CONTEXT_METHODS = contextCommands.names().filter((name) => name
|
|
15746
|
+
var CONTEXT_METHODS = contextCommands.names().filter((name) => !contextCommands.isHostOnly(name));
|
|
15719
15747
|
var CONTEXT_WRITE_METHODS = CONTEXT_METHODS.filter((method) => contextCommands.scopeFor(method) === "write");
|
|
15720
15748
|
function contextMethodsOf(provider) {
|
|
15721
15749
|
const methods = /* @__PURE__ */ new Set();
|
|
@@ -16502,10 +16530,10 @@ function normalizeAllowHost(raw) {
|
|
|
16502
16530
|
return host;
|
|
16503
16531
|
}
|
|
16504
16532
|
var RemoteAllowlistStore = class {
|
|
16533
|
+
store;
|
|
16505
16534
|
constructor(store) {
|
|
16506
16535
|
this.store = store;
|
|
16507
16536
|
}
|
|
16508
|
-
store;
|
|
16509
16537
|
/** 读全部运行时条目(缺省/脏值 → 空数组);按 host 升序稳定输出。 */
|
|
16510
16538
|
async list() {
|
|
16511
16539
|
const raw = await this.store.get(KEY_REMOTE_ALLOWLIST);
|
|
@@ -16617,6 +16645,657 @@ function createFederationModule(deps) {
|
|
|
16617
16645
|
return COMMANDS5.module(deps);
|
|
16618
16646
|
}
|
|
16619
16647
|
|
|
16648
|
+
// ../core/src/device/mailbox.ts
|
|
16649
|
+
var KEY_DEVICE_OPERATION = "deviceop:";
|
|
16650
|
+
var DEVICE_OPERATION_STATES = [
|
|
16651
|
+
"queued",
|
|
16652
|
+
"claimed",
|
|
16653
|
+
"succeeded",
|
|
16654
|
+
"rejected",
|
|
16655
|
+
"failed",
|
|
16656
|
+
"result_unknown",
|
|
16657
|
+
"cancelled",
|
|
16658
|
+
"expired"
|
|
16659
|
+
];
|
|
16660
|
+
var DEFAULT_TTL_SECONDS = 24 * 60 * 60;
|
|
16661
|
+
var MAX_TTL_SECONDS = 7 * 24 * 60 * 60;
|
|
16662
|
+
var DEFAULT_LEASE_SECONDS = 60;
|
|
16663
|
+
var DEFAULT_MAX_PAYLOAD_BYTES = 256 * 1024;
|
|
16664
|
+
var DEFAULT_MAX_PENDING_PER_DEVICE = 1e3;
|
|
16665
|
+
var DEFAULT_TERMINAL_RETENTION_SECONDS = 7 * 24 * 60 * 60;
|
|
16666
|
+
var MAX_CAS_ATTEMPTS = 8;
|
|
16667
|
+
var LIST_LIMIT_DEFAULT2 = 50;
|
|
16668
|
+
var LIST_LIMIT_MAX2 = 200;
|
|
16669
|
+
var HKDF_SALT = new TextEncoder().encode("tool-bridge:device-mailbox:v1");
|
|
16670
|
+
var HKDF_INFO = new TextEncoder().encode("aes-256-gcm:operation-payload-and-result:v1");
|
|
16671
|
+
function mailboxUnavailable(message) {
|
|
16672
|
+
return new TBError("unavailable", message, { retryable: false });
|
|
16673
|
+
}
|
|
16674
|
+
function finiteTimestamp(value) {
|
|
16675
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
16676
|
+
}
|
|
16677
|
+
function nonEmpty(value) {
|
|
16678
|
+
return typeof value === "string" && value !== "";
|
|
16679
|
+
}
|
|
16680
|
+
function positiveInteger(value, fallback, field) {
|
|
16681
|
+
const actual = value ?? fallback;
|
|
16682
|
+
if (!Number.isSafeInteger(actual) || actual < 1) {
|
|
16683
|
+
throw new TBError("invalid_argument", `${field} must be a positive integer`);
|
|
16684
|
+
}
|
|
16685
|
+
return actual;
|
|
16686
|
+
}
|
|
16687
|
+
function clampLimit3(value) {
|
|
16688
|
+
if (value === void 0) return LIST_LIMIT_DEFAULT2;
|
|
16689
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
16690
|
+
throw new TBError("invalid_argument", "limit must be a positive integer");
|
|
16691
|
+
}
|
|
16692
|
+
return Math.min(value, LIST_LIMIT_MAX2);
|
|
16693
|
+
}
|
|
16694
|
+
function isState(value) {
|
|
16695
|
+
return typeof value === "string" && DEVICE_OPERATION_STATES.includes(value);
|
|
16696
|
+
}
|
|
16697
|
+
function isTerminal(state) {
|
|
16698
|
+
return state !== "queued" && state !== "claimed";
|
|
16699
|
+
}
|
|
16700
|
+
function parseEncrypted(value) {
|
|
16701
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("invalid encrypted value");
|
|
16702
|
+
const raw = value;
|
|
16703
|
+
if (raw.v !== 1 || !nonEmpty(raw.iv) || !nonEmpty(raw.ciphertext)) {
|
|
16704
|
+
throw new Error("invalid encrypted value");
|
|
16705
|
+
}
|
|
16706
|
+
return { v: 1, iv: raw.iv, ciphertext: raw.ciphertext };
|
|
16707
|
+
}
|
|
16708
|
+
function parseRecord(value) {
|
|
16709
|
+
try {
|
|
16710
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error();
|
|
16711
|
+
const raw = value;
|
|
16712
|
+
if (raw.v !== 1 || !Number.isSafeInteger(raw.revision) || raw.revision < 1 || !nonEmpty(raw.operationId) || !nonEmpty(raw.deviceId) || !nonEmpty(raw.deviceKeyId) || !nonEmpty(raw.mountPath) || !nonEmpty(raw.targetPath) || !nonEmpty(raw.path) || !nonEmpty(raw.callerKeyId) || !nonEmpty(raw.callerOwner) || !nonEmpty(raw.traceId) || !finiteTimestamp(raw.createdAt) || !finiteTimestamp(raw.updatedAt) || !finiteTimestamp(raw.expiresAt) || !isState(raw.state) || !Number.isSafeInteger(raw.attempt) || raw.attempt < 0 || typeof raw.executionMayHaveOccurred !== "boolean") throw new Error();
|
|
16713
|
+
if (raw.leaseId === void 0 !== (raw.leaseUntil === void 0)) throw new Error();
|
|
16714
|
+
if (raw.leaseId !== void 0 && (!nonEmpty(raw.leaseId) || !finiteTimestamp(raw.leaseUntil))) {
|
|
16715
|
+
throw new Error();
|
|
16716
|
+
}
|
|
16717
|
+
if (raw.cancelRequestedAt !== void 0 && !finiteTimestamp(raw.cancelRequestedAt)) throw new Error();
|
|
16718
|
+
if (raw.terminalAt !== void 0 && !finiteTimestamp(raw.terminalAt)) throw new Error();
|
|
16719
|
+
if (isTerminal(raw.state) && raw.terminalAt === void 0) throw new Error();
|
|
16720
|
+
return {
|
|
16721
|
+
v: 1,
|
|
16722
|
+
revision: raw.revision,
|
|
16723
|
+
operationId: raw.operationId,
|
|
16724
|
+
deviceId: raw.deviceId,
|
|
16725
|
+
deviceKeyId: raw.deviceKeyId,
|
|
16726
|
+
mountPath: raw.mountPath,
|
|
16727
|
+
targetPath: raw.targetPath,
|
|
16728
|
+
path: raw.path,
|
|
16729
|
+
callerKeyId: raw.callerKeyId,
|
|
16730
|
+
callerOwner: raw.callerOwner,
|
|
16731
|
+
traceId: raw.traceId,
|
|
16732
|
+
createdAt: raw.createdAt,
|
|
16733
|
+
updatedAt: raw.updatedAt,
|
|
16734
|
+
expiresAt: raw.expiresAt,
|
|
16735
|
+
state: raw.state,
|
|
16736
|
+
attempt: raw.attempt,
|
|
16737
|
+
executionMayHaveOccurred: raw.executionMayHaveOccurred,
|
|
16738
|
+
payload: parseEncrypted(raw.payload),
|
|
16739
|
+
...raw.leaseId === void 0 ? {} : { leaseId: raw.leaseId, leaseUntil: raw.leaseUntil },
|
|
16740
|
+
...raw.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: raw.cancelRequestedAt },
|
|
16741
|
+
...raw.completedLeaseId === void 0 ? {} : { completedLeaseId: String(raw.completedLeaseId) },
|
|
16742
|
+
...raw.terminalAt === void 0 ? {} : { terminalAt: raw.terminalAt },
|
|
16743
|
+
...raw.terminalData === void 0 ? {} : { terminalData: parseEncrypted(raw.terminalData) },
|
|
16744
|
+
...raw.idempotencyFingerprint === void 0 ? {} : { idempotencyFingerprint: String(raw.idempotencyFingerprint) }
|
|
16745
|
+
};
|
|
16746
|
+
} catch {
|
|
16747
|
+
throw new TBError("internal", "device mailbox record is invalid", { retryable: false });
|
|
16748
|
+
}
|
|
16749
|
+
}
|
|
16750
|
+
function canonicalJson(value) {
|
|
16751
|
+
if (value === null) return "null";
|
|
16752
|
+
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
16753
|
+
if (typeof value === "number") {
|
|
16754
|
+
if (!Number.isFinite(value)) throw new TBError("invalid_argument", "mailbox data must be valid JSON");
|
|
16755
|
+
return JSON.stringify(value);
|
|
16756
|
+
}
|
|
16757
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
16758
|
+
if (typeof value !== "object") {
|
|
16759
|
+
throw new TBError("invalid_argument", "mailbox data must be valid JSON");
|
|
16760
|
+
}
|
|
16761
|
+
const object2 = value;
|
|
16762
|
+
const keys = Object.keys(object2).sort();
|
|
16763
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(object2[key])}`).join(",")}}`;
|
|
16764
|
+
}
|
|
16765
|
+
function safeError(value) {
|
|
16766
|
+
if (typeof value !== "object" || value === null || typeof value.code !== "string" || typeof value.message !== "string" || typeof value.retryable !== "boolean") throw new TBError("invalid_argument", "completion error is invalid");
|
|
16767
|
+
return { code: value.code, message: value.message, retryable: value.retryable };
|
|
16768
|
+
}
|
|
16769
|
+
function normalizeCompletion(value) {
|
|
16770
|
+
if (value.outcome === "succeeded") {
|
|
16771
|
+
canonicalJson(value.result);
|
|
16772
|
+
return { outcome: "succeeded", result: value.result };
|
|
16773
|
+
}
|
|
16774
|
+
if (value.outcome === "rejected" || value.outcome === "failed") {
|
|
16775
|
+
return { outcome: value.outcome, error: safeError(value.error) };
|
|
16776
|
+
}
|
|
16777
|
+
return {
|
|
16778
|
+
outcome: "result_unknown",
|
|
16779
|
+
...value.error === void 0 ? {} : { error: safeError(value.error) }
|
|
16780
|
+
};
|
|
16781
|
+
}
|
|
16782
|
+
function bytes(value) {
|
|
16783
|
+
return new TextEncoder().encode(value).byteLength;
|
|
16784
|
+
}
|
|
16785
|
+
function randomId(randomBytes) {
|
|
16786
|
+
const value = randomBytes(18);
|
|
16787
|
+
if (!(value instanceof Uint8Array) || value.length !== 18) {
|
|
16788
|
+
throw mailboxUnavailable("device mailbox randomness is unavailable");
|
|
16789
|
+
}
|
|
16790
|
+
return `dop_${base64urlEncode(value)}`;
|
|
16791
|
+
}
|
|
16792
|
+
async function digestBytes(value) {
|
|
16793
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)));
|
|
16794
|
+
}
|
|
16795
|
+
async function deterministicOperationId(binding) {
|
|
16796
|
+
return `dop_${base64urlEncode((await digestBytes(binding)).slice(0, 18))}`;
|
|
16797
|
+
}
|
|
16798
|
+
async function devicePrefix(deviceId) {
|
|
16799
|
+
return `${KEY_DEVICE_OPERATION}${await sha256Hex(deviceId)}:`;
|
|
16800
|
+
}
|
|
16801
|
+
async function stateKey(deviceId, operationId) {
|
|
16802
|
+
if (!/^dop_[A-Za-z0-9_-]{24}$/.test(operationId)) {
|
|
16803
|
+
throw new TBError("invalid_argument", "operationId is invalid");
|
|
16804
|
+
}
|
|
16805
|
+
return await devicePrefix(deviceId) + operationId;
|
|
16806
|
+
}
|
|
16807
|
+
var DeviceMailboxCipher = class {
|
|
16808
|
+
root;
|
|
16809
|
+
keyPromise;
|
|
16810
|
+
constructor(masterKey) {
|
|
16811
|
+
let decoded;
|
|
16812
|
+
try {
|
|
16813
|
+
decoded = base64urlDecode(masterKey);
|
|
16814
|
+
} catch {
|
|
16815
|
+
throw mailboxUnavailable("device mailbox encryption key is invalid");
|
|
16816
|
+
}
|
|
16817
|
+
if (decoded.length !== 32) {
|
|
16818
|
+
throw mailboxUnavailable("device mailbox encryption key is invalid");
|
|
16819
|
+
}
|
|
16820
|
+
this.root = decoded;
|
|
16821
|
+
}
|
|
16822
|
+
key() {
|
|
16823
|
+
this.keyPromise ??= (async () => {
|
|
16824
|
+
const rootKey = await crypto.subtle.importKey("raw", this.root, "HKDF", false, ["deriveKey"]);
|
|
16825
|
+
return await crypto.subtle.deriveKey(
|
|
16826
|
+
{ name: "HKDF", hash: "SHA-256", salt: HKDF_SALT, info: HKDF_INFO },
|
|
16827
|
+
rootKey,
|
|
16828
|
+
{ name: "AES-GCM", length: 256 },
|
|
16829
|
+
false,
|
|
16830
|
+
["encrypt", "decrypt"]
|
|
16831
|
+
);
|
|
16832
|
+
})();
|
|
16833
|
+
return this.keyPromise;
|
|
16834
|
+
}
|
|
16835
|
+
async encrypt(operationId, field, value) {
|
|
16836
|
+
const plaintext = canonicalJson(value);
|
|
16837
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
16838
|
+
const additionalData = new TextEncoder().encode(`tool-bridge:device-mailbox:v1:${operationId}:${field}`);
|
|
16839
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
16840
|
+
{ name: "AES-GCM", iv, additionalData },
|
|
16841
|
+
await this.key(),
|
|
16842
|
+
new TextEncoder().encode(plaintext)
|
|
16843
|
+
);
|
|
16844
|
+
return {
|
|
16845
|
+
v: 1,
|
|
16846
|
+
iv: base64urlEncode(iv),
|
|
16847
|
+
ciphertext: base64urlEncode(new Uint8Array(ciphertext))
|
|
16848
|
+
};
|
|
16849
|
+
}
|
|
16850
|
+
async decrypt(operationId, field, value) {
|
|
16851
|
+
try {
|
|
16852
|
+
const iv = base64urlDecode(value.iv);
|
|
16853
|
+
const ciphertext = base64urlDecode(value.ciphertext);
|
|
16854
|
+
if (iv.length !== 12) throw new Error("invalid iv");
|
|
16855
|
+
const additionalData = new TextEncoder().encode(`tool-bridge:device-mailbox:v1:${operationId}:${field}`);
|
|
16856
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
16857
|
+
{ name: "AES-GCM", iv, additionalData },
|
|
16858
|
+
await this.key(),
|
|
16859
|
+
ciphertext
|
|
16860
|
+
);
|
|
16861
|
+
return JSON.parse(new TextDecoder2().decode(plaintext));
|
|
16862
|
+
} catch {
|
|
16863
|
+
throw new TBError("internal", "device mailbox data cannot be decrypted", { retryable: false });
|
|
16864
|
+
}
|
|
16865
|
+
}
|
|
16866
|
+
};
|
|
16867
|
+
var DeviceMailboxService = class {
|
|
16868
|
+
cas;
|
|
16869
|
+
cipher;
|
|
16870
|
+
defaultTtlSeconds;
|
|
16871
|
+
leaseSeconds;
|
|
16872
|
+
maxPayloadBytes;
|
|
16873
|
+
maxPendingPerDevice;
|
|
16874
|
+
maxTtlSeconds;
|
|
16875
|
+
now;
|
|
16876
|
+
randomBytes;
|
|
16877
|
+
state;
|
|
16878
|
+
terminalRetentionSeconds;
|
|
16879
|
+
constructor(state, encryptionRoot, opts = {}) {
|
|
16880
|
+
if (state.compareAndSwap === void 0) {
|
|
16881
|
+
throw mailboxUnavailable("device mailbox requires StateStore.compareAndSwap");
|
|
16882
|
+
}
|
|
16883
|
+
if (encryptionRoot === void 0) {
|
|
16884
|
+
throw mailboxUnavailable("device mailbox requires TB_SECRET_ENCRYPTION_KEY");
|
|
16885
|
+
}
|
|
16886
|
+
this.cas = state.compareAndSwap.bind(state);
|
|
16887
|
+
this.state = state;
|
|
16888
|
+
this.cipher = new DeviceMailboxCipher(encryptionRoot);
|
|
16889
|
+
this.defaultTtlSeconds = positiveInteger(
|
|
16890
|
+
opts.defaultTtlSeconds,
|
|
16891
|
+
DEFAULT_TTL_SECONDS,
|
|
16892
|
+
"defaultTtlSeconds"
|
|
16893
|
+
);
|
|
16894
|
+
this.maxTtlSeconds = positiveInteger(opts.maxTtlSeconds, MAX_TTL_SECONDS, "maxTtlSeconds");
|
|
16895
|
+
if (this.defaultTtlSeconds > this.maxTtlSeconds) {
|
|
16896
|
+
throw new TBError("invalid_argument", "defaultTtlSeconds cannot exceed maxTtlSeconds");
|
|
16897
|
+
}
|
|
16898
|
+
this.leaseSeconds = positiveInteger(opts.leaseSeconds, DEFAULT_LEASE_SECONDS, "leaseSeconds");
|
|
16899
|
+
this.maxPayloadBytes = positiveInteger(
|
|
16900
|
+
opts.maxPayloadBytes,
|
|
16901
|
+
DEFAULT_MAX_PAYLOAD_BYTES,
|
|
16902
|
+
"maxPayloadBytes"
|
|
16903
|
+
);
|
|
16904
|
+
this.maxPendingPerDevice = positiveInteger(
|
|
16905
|
+
opts.maxPendingPerDevice,
|
|
16906
|
+
DEFAULT_MAX_PENDING_PER_DEVICE,
|
|
16907
|
+
"maxPendingPerDevice"
|
|
16908
|
+
);
|
|
16909
|
+
this.terminalRetentionSeconds = positiveInteger(
|
|
16910
|
+
opts.terminalRetentionSeconds,
|
|
16911
|
+
DEFAULT_TERMINAL_RETENTION_SECONDS,
|
|
16912
|
+
"terminalRetentionSeconds"
|
|
16913
|
+
);
|
|
16914
|
+
this.now = opts.now ?? (() => Date.now());
|
|
16915
|
+
this.randomBytes = opts.randomBytes ?? ((length) => crypto.getRandomValues(new Uint8Array(length)));
|
|
16916
|
+
}
|
|
16917
|
+
nowIso() {
|
|
16918
|
+
return new Date(this.now()).toISOString();
|
|
16919
|
+
}
|
|
16920
|
+
async read(deviceId, operationId) {
|
|
16921
|
+
const key = await stateKey(deviceId, operationId);
|
|
16922
|
+
const value = await this.state.get(key);
|
|
16923
|
+
if (value === null) throw TBError.notFound("device operation not found");
|
|
16924
|
+
const record2 = parseRecord(value);
|
|
16925
|
+
if (record2.deviceId !== deviceId || record2.operationId !== operationId) {
|
|
16926
|
+
throw new TBError("internal", "device mailbox record identity mismatch", { retryable: false });
|
|
16927
|
+
}
|
|
16928
|
+
return { key, record: record2 };
|
|
16929
|
+
}
|
|
16930
|
+
summary(record2) {
|
|
16931
|
+
return {
|
|
16932
|
+
operationId: record2.operationId,
|
|
16933
|
+
deviceId: record2.deviceId,
|
|
16934
|
+
mountPath: record2.mountPath,
|
|
16935
|
+
targetPath: record2.targetPath,
|
|
16936
|
+
caller: { keyId: record2.callerKeyId, owner: record2.callerOwner },
|
|
16937
|
+
traceId: record2.traceId,
|
|
16938
|
+
createdAt: record2.createdAt,
|
|
16939
|
+
updatedAt: record2.updatedAt,
|
|
16940
|
+
expiresAt: record2.expiresAt,
|
|
16941
|
+
state: record2.state,
|
|
16942
|
+
attempt: record2.attempt,
|
|
16943
|
+
executionMayHaveOccurred: record2.executionMayHaveOccurred,
|
|
16944
|
+
...record2.leaseUntil === void 0 ? {} : { leaseUntil: record2.leaseUntil },
|
|
16945
|
+
...record2.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: record2.cancelRequestedAt },
|
|
16946
|
+
...record2.terminalAt === void 0 ? {} : { terminalAt: record2.terminalAt }
|
|
16947
|
+
};
|
|
16948
|
+
}
|
|
16949
|
+
async detail(record2) {
|
|
16950
|
+
const summary = this.summary(record2);
|
|
16951
|
+
if (record2.terminalData === void 0) return summary;
|
|
16952
|
+
const terminal = await this.cipher.decrypt(
|
|
16953
|
+
record2.operationId,
|
|
16954
|
+
"terminal",
|
|
16955
|
+
record2.terminalData
|
|
16956
|
+
);
|
|
16957
|
+
if (record2.state === "succeeded") return { ...summary, result: terminal.result };
|
|
16958
|
+
const error51 = terminal.error;
|
|
16959
|
+
if (error51 === void 0) return summary;
|
|
16960
|
+
return { ...summary, error: safeError(error51) };
|
|
16961
|
+
}
|
|
16962
|
+
async expireRecord(key, record2) {
|
|
16963
|
+
let current = record2;
|
|
16964
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
16965
|
+
if (isTerminal(current.state) || Date.parse(current.expiresAt) > this.now()) return current;
|
|
16966
|
+
const now = this.nowIso();
|
|
16967
|
+
const next = {
|
|
16968
|
+
...current,
|
|
16969
|
+
revision: current.revision + 1,
|
|
16970
|
+
state: "expired",
|
|
16971
|
+
updatedAt: now,
|
|
16972
|
+
terminalAt: now,
|
|
16973
|
+
executionMayHaveOccurred: current.state === "claimed"
|
|
16974
|
+
};
|
|
16975
|
+
if (await this.cas(key, current.revision, next)) return next;
|
|
16976
|
+
const winner = await this.state.get(key);
|
|
16977
|
+
if (winner === null) throw TBError.notFound("device operation not found");
|
|
16978
|
+
current = parseRecord(winner);
|
|
16979
|
+
}
|
|
16980
|
+
throw new TBError("unavailable", "device operation expiry is busy", { retryable: true });
|
|
16981
|
+
}
|
|
16982
|
+
async enforcePendingCap(deviceId) {
|
|
16983
|
+
const prefix = await devicePrefix(deviceId);
|
|
16984
|
+
let cursor;
|
|
16985
|
+
let pending = 0;
|
|
16986
|
+
do {
|
|
16987
|
+
const page = await this.state.list(prefix, {
|
|
16988
|
+
limit: LIST_LIMIT_MAX2,
|
|
16989
|
+
...cursor === void 0 ? {} : { cursor }
|
|
16990
|
+
});
|
|
16991
|
+
for (const item of page.items) {
|
|
16992
|
+
let record2 = parseRecord(item.value);
|
|
16993
|
+
if (record2.deviceId !== deviceId) continue;
|
|
16994
|
+
record2 = await this.expireRecord(item.key, record2);
|
|
16995
|
+
if (!isTerminal(record2.state)) pending++;
|
|
16996
|
+
if (pending >= this.maxPendingPerDevice) {
|
|
16997
|
+
throw new TBError("rate_limited", "device mailbox pending limit reached", {
|
|
16998
|
+
retryable: true
|
|
16999
|
+
});
|
|
17000
|
+
}
|
|
17001
|
+
}
|
|
17002
|
+
cursor = page.cursor;
|
|
17003
|
+
} while (cursor !== void 0);
|
|
17004
|
+
}
|
|
17005
|
+
async enqueue(input) {
|
|
17006
|
+
const ttlSeconds = input.ttlSeconds ?? this.defaultTtlSeconds;
|
|
17007
|
+
if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds < 1 || ttlSeconds > this.maxTtlSeconds) {
|
|
17008
|
+
throw new TBError("invalid_argument", `ttlSeconds must be between 1 and ${this.maxTtlSeconds}`);
|
|
17009
|
+
}
|
|
17010
|
+
const payloadText = canonicalJson({ arguments: input.arguments });
|
|
17011
|
+
if (bytes(payloadText) > this.maxPayloadBytes) {
|
|
17012
|
+
throw new TBError("rate_limited", "device mailbox payload is too large", { retryable: false });
|
|
17013
|
+
}
|
|
17014
|
+
if (input.idempotencyKey !== void 0 && (input.idempotencyKey.length < 1 || input.idempotencyKey.length > 255 || /[\r\n\0]/.test(input.idempotencyKey))) throw new TBError("invalid_argument", "idempotency key is invalid");
|
|
17015
|
+
const binding = input.idempotencyKey === void 0 ? void 0 : canonicalJson({
|
|
17016
|
+
callerOwner: input.caller.owner,
|
|
17017
|
+
deviceId: input.deviceId,
|
|
17018
|
+
targetPath: input.targetPath,
|
|
17019
|
+
idempotencyKey: input.idempotencyKey
|
|
17020
|
+
});
|
|
17021
|
+
const operationId = binding === void 0 ? randomId(this.randomBytes) : await deterministicOperationId(binding);
|
|
17022
|
+
const fingerprint = binding === void 0 ? void 0 : await sha256Hex(canonicalJson({
|
|
17023
|
+
arguments: input.arguments,
|
|
17024
|
+
deviceKeyId: input.deviceKeyId,
|
|
17025
|
+
path: input.path,
|
|
17026
|
+
ttlSeconds
|
|
17027
|
+
}));
|
|
17028
|
+
const key = await stateKey(input.deviceId, operationId);
|
|
17029
|
+
if (binding !== void 0) {
|
|
17030
|
+
const existing2 = await this.state.get(key);
|
|
17031
|
+
if (existing2 !== null) {
|
|
17032
|
+
const record3 = parseRecord(existing2);
|
|
17033
|
+
if (record3.callerOwner !== input.caller.owner || record3.deviceId !== input.deviceId || record3.targetPath !== input.targetPath || record3.idempotencyFingerprint !== fingerprint) throw new TBError("conflict", "idempotency key is bound to another operation");
|
|
17034
|
+
return await this.detail(record3);
|
|
17035
|
+
}
|
|
17036
|
+
}
|
|
17037
|
+
await this.enforcePendingCap(input.deviceId);
|
|
17038
|
+
const now = this.nowIso();
|
|
17039
|
+
const record2 = {
|
|
17040
|
+
v: 1,
|
|
17041
|
+
revision: 1,
|
|
17042
|
+
operationId,
|
|
17043
|
+
deviceId: input.deviceId,
|
|
17044
|
+
deviceKeyId: input.deviceKeyId,
|
|
17045
|
+
mountPath: input.mountPath,
|
|
17046
|
+
targetPath: input.targetPath,
|
|
17047
|
+
path: input.path,
|
|
17048
|
+
callerKeyId: input.caller.keyId,
|
|
17049
|
+
callerOwner: input.caller.owner,
|
|
17050
|
+
traceId: input.traceId,
|
|
17051
|
+
createdAt: now,
|
|
17052
|
+
updatedAt: now,
|
|
17053
|
+
expiresAt: new Date(this.now() + ttlSeconds * 1e3).toISOString(),
|
|
17054
|
+
state: "queued",
|
|
17055
|
+
attempt: 0,
|
|
17056
|
+
executionMayHaveOccurred: false,
|
|
17057
|
+
payload: await this.cipher.encrypt(operationId, "payload", { arguments: input.arguments }),
|
|
17058
|
+
...fingerprint === void 0 ? {} : { idempotencyFingerprint: fingerprint }
|
|
17059
|
+
};
|
|
17060
|
+
if (await this.cas(key, null, record2)) return await this.detail(record2);
|
|
17061
|
+
if (binding === void 0) {
|
|
17062
|
+
throw new TBError("conflict", "device operation id collision", { retryable: true });
|
|
17063
|
+
}
|
|
17064
|
+
const winner = await this.state.get(key);
|
|
17065
|
+
if (winner === null) throw new TBError("conflict", "device operation enqueue conflicted");
|
|
17066
|
+
const existing = parseRecord(winner);
|
|
17067
|
+
if (existing.idempotencyFingerprint !== fingerprint) {
|
|
17068
|
+
throw new TBError("conflict", "idempotency key is bound to another operation");
|
|
17069
|
+
}
|
|
17070
|
+
return await this.detail(existing);
|
|
17071
|
+
}
|
|
17072
|
+
async get(deviceId, operationId) {
|
|
17073
|
+
const { key, record: record2 } = await this.read(deviceId, operationId);
|
|
17074
|
+
return await this.detail(await this.expireRecord(key, record2));
|
|
17075
|
+
}
|
|
17076
|
+
async list(input) {
|
|
17077
|
+
const limit = clampLimit3(input.limit);
|
|
17078
|
+
const states = input.states === void 0 ? void 0 : new Set(input.states);
|
|
17079
|
+
if (states !== void 0 && [...states].some((state) => !isState(state))) {
|
|
17080
|
+
throw new TBError("invalid_argument", "states contains an invalid operation state");
|
|
17081
|
+
}
|
|
17082
|
+
const page = await this.state.list(await devicePrefix(input.deviceId), {
|
|
17083
|
+
limit,
|
|
17084
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
17085
|
+
});
|
|
17086
|
+
const items = [];
|
|
17087
|
+
for (const item of page.items) {
|
|
17088
|
+
let record2 = parseRecord(item.value);
|
|
17089
|
+
if (record2.deviceId !== input.deviceId) continue;
|
|
17090
|
+
record2 = await this.expireRecord(item.key, record2);
|
|
17091
|
+
if (states !== void 0 && !states.has(record2.state)) continue;
|
|
17092
|
+
items.push(this.summary(record2));
|
|
17093
|
+
}
|
|
17094
|
+
return { items, ...page.cursor === void 0 ? {} : { cursor: page.cursor } };
|
|
17095
|
+
}
|
|
17096
|
+
async claim(input) {
|
|
17097
|
+
const serverNow = this.nowIso();
|
|
17098
|
+
const page = await this.state.list(await devicePrefix(input.deviceId), {
|
|
17099
|
+
limit: clampLimit3(input.limit),
|
|
17100
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
17101
|
+
});
|
|
17102
|
+
for (const item of page.items) {
|
|
17103
|
+
let record2 = parseRecord(item.value);
|
|
17104
|
+
if (record2.deviceId !== input.deviceId || record2.deviceKeyId !== input.deviceKeyId) continue;
|
|
17105
|
+
record2 = await this.expireRecord(item.key, record2);
|
|
17106
|
+
const reclaimable = record2.state === "claimed" && record2.leaseUntil !== void 0 && Date.parse(record2.leaseUntil) <= this.now();
|
|
17107
|
+
if (record2.state !== "queued" && !reclaimable) continue;
|
|
17108
|
+
await input.authorize?.({
|
|
17109
|
+
deviceId: record2.deviceId,
|
|
17110
|
+
deviceKeyId: record2.deviceKeyId,
|
|
17111
|
+
mountPath: record2.mountPath,
|
|
17112
|
+
targetPath: record2.targetPath
|
|
17113
|
+
});
|
|
17114
|
+
const leaseId = randomId(this.randomBytes);
|
|
17115
|
+
const leaseUntil = new Date(Math.min(
|
|
17116
|
+
this.now() + this.leaseSeconds * 1e3,
|
|
17117
|
+
Date.parse(record2.expiresAt)
|
|
17118
|
+
)).toISOString();
|
|
17119
|
+
const next = {
|
|
17120
|
+
...record2,
|
|
17121
|
+
revision: record2.revision + 1,
|
|
17122
|
+
state: "claimed",
|
|
17123
|
+
attempt: record2.attempt + 1,
|
|
17124
|
+
leaseId,
|
|
17125
|
+
leaseUntil,
|
|
17126
|
+
updatedAt: serverNow
|
|
17127
|
+
};
|
|
17128
|
+
if (!await this.cas(item.key, record2.revision, next)) continue;
|
|
17129
|
+
const payload = await this.cipher.decrypt(
|
|
17130
|
+
record2.operationId,
|
|
17131
|
+
"payload",
|
|
17132
|
+
record2.payload
|
|
17133
|
+
);
|
|
17134
|
+
if (typeof payload !== "object" || payload === null || typeof payload.arguments !== "object" || payload.arguments === null || Array.isArray(payload.arguments)) throw new TBError("internal", "device mailbox payload is invalid", { retryable: false });
|
|
17135
|
+
return {
|
|
17136
|
+
serverNow,
|
|
17137
|
+
...page.cursor === void 0 ? {} : { cursor: page.cursor },
|
|
17138
|
+
operation: {
|
|
17139
|
+
operationId: next.operationId,
|
|
17140
|
+
targetPath: next.targetPath,
|
|
17141
|
+
path: next.path,
|
|
17142
|
+
arguments: payload.arguments,
|
|
17143
|
+
caller: { keyId: next.callerKeyId, owner: next.callerOwner },
|
|
17144
|
+
traceId: next.traceId,
|
|
17145
|
+
createdAt: next.createdAt,
|
|
17146
|
+
expiresAt: next.expiresAt,
|
|
17147
|
+
attempt: next.attempt,
|
|
17148
|
+
leaseId,
|
|
17149
|
+
leaseUntil,
|
|
17150
|
+
...next.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: next.cancelRequestedAt }
|
|
17151
|
+
}
|
|
17152
|
+
};
|
|
17153
|
+
}
|
|
17154
|
+
return {
|
|
17155
|
+
serverNow,
|
|
17156
|
+
...page.cursor === void 0 ? {} : { cursor: page.cursor }
|
|
17157
|
+
};
|
|
17158
|
+
}
|
|
17159
|
+
async activeLease(input) {
|
|
17160
|
+
const current = await this.read(input.deviceId, input.operationId);
|
|
17161
|
+
const record2 = await this.expireRecord(current.key, current.record);
|
|
17162
|
+
if (record2.deviceKeyId !== input.deviceKeyId || record2.state !== "claimed" || record2.leaseId !== input.leaseId || record2.leaseUntil === void 0 || Date.parse(record2.leaseUntil) <= this.now()) throw new TBError("conflict", "device operation lease is no longer active");
|
|
17163
|
+
await input.authorize?.({
|
|
17164
|
+
deviceId: record2.deviceId,
|
|
17165
|
+
deviceKeyId: record2.deviceKeyId,
|
|
17166
|
+
mountPath: record2.mountPath,
|
|
17167
|
+
targetPath: record2.targetPath
|
|
17168
|
+
});
|
|
17169
|
+
return { key: current.key, record: record2 };
|
|
17170
|
+
}
|
|
17171
|
+
async renew(input) {
|
|
17172
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
17173
|
+
const { key, record: record2 } = await this.activeLease(input);
|
|
17174
|
+
const serverNow = this.nowIso();
|
|
17175
|
+
const leaseUntil = new Date(Math.min(
|
|
17176
|
+
this.now() + this.leaseSeconds * 1e3,
|
|
17177
|
+
Date.parse(record2.expiresAt)
|
|
17178
|
+
)).toISOString();
|
|
17179
|
+
const next = {
|
|
17180
|
+
...record2,
|
|
17181
|
+
revision: record2.revision + 1,
|
|
17182
|
+
leaseUntil,
|
|
17183
|
+
updatedAt: serverNow
|
|
17184
|
+
};
|
|
17185
|
+
if (await this.cas(key, record2.revision, next)) {
|
|
17186
|
+
return {
|
|
17187
|
+
serverNow,
|
|
17188
|
+
leaseUntil,
|
|
17189
|
+
...next.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: next.cancelRequestedAt }
|
|
17190
|
+
};
|
|
17191
|
+
}
|
|
17192
|
+
}
|
|
17193
|
+
throw new TBError("unavailable", "device operation lease is busy", { retryable: true });
|
|
17194
|
+
}
|
|
17195
|
+
async complete(input, completionInput) {
|
|
17196
|
+
const completion = normalizeCompletion(completionInput);
|
|
17197
|
+
const terminalData = completion.outcome === "succeeded" ? { result: completion.result } : completion.error === void 0 ? {} : { error: completion.error };
|
|
17198
|
+
if (bytes(canonicalJson(terminalData)) > this.maxPayloadBytes) {
|
|
17199
|
+
throw new TBError("rate_limited", "device mailbox terminal payload is too large", {
|
|
17200
|
+
retryable: false
|
|
17201
|
+
});
|
|
17202
|
+
}
|
|
17203
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
17204
|
+
const current = await this.read(input.deviceId, input.operationId);
|
|
17205
|
+
let record2 = await this.expireRecord(current.key, current.record);
|
|
17206
|
+
if (record2.deviceKeyId !== input.deviceKeyId) {
|
|
17207
|
+
throw new TBError("conflict", "device operation credential does not match");
|
|
17208
|
+
}
|
|
17209
|
+
await input.authorize?.({
|
|
17210
|
+
deviceId: record2.deviceId,
|
|
17211
|
+
deviceKeyId: record2.deviceKeyId,
|
|
17212
|
+
mountPath: record2.mountPath,
|
|
17213
|
+
targetPath: record2.targetPath
|
|
17214
|
+
});
|
|
17215
|
+
if (isTerminal(record2.state)) {
|
|
17216
|
+
if (record2.completedLeaseId !== input.leaseId || record2.state !== completion.outcome || record2.terminalData === void 0) throw new TBError("conflict", "device operation is already terminal");
|
|
17217
|
+
const existing = await this.cipher.decrypt(
|
|
17218
|
+
record2.operationId,
|
|
17219
|
+
"terminal",
|
|
17220
|
+
record2.terminalData
|
|
17221
|
+
);
|
|
17222
|
+
const requested = completion.outcome === "succeeded" ? { result: completion.result } : completion.error === void 0 ? {} : { error: completion.error };
|
|
17223
|
+
if (canonicalJson(existing) !== canonicalJson(requested)) {
|
|
17224
|
+
throw new TBError("conflict", "device operation completion differs from terminal result");
|
|
17225
|
+
}
|
|
17226
|
+
return await this.detail(record2);
|
|
17227
|
+
}
|
|
17228
|
+
const active = await this.activeLease(input);
|
|
17229
|
+
record2 = active.record;
|
|
17230
|
+
const now = this.nowIso();
|
|
17231
|
+
const next = {
|
|
17232
|
+
...record2,
|
|
17233
|
+
revision: record2.revision + 1,
|
|
17234
|
+
state: completion.outcome,
|
|
17235
|
+
terminalAt: now,
|
|
17236
|
+
terminalData: await this.cipher.encrypt(record2.operationId, "terminal", terminalData),
|
|
17237
|
+
completedLeaseId: input.leaseId,
|
|
17238
|
+
updatedAt: now
|
|
17239
|
+
};
|
|
17240
|
+
if (await this.cas(active.key, record2.revision, next)) return await this.detail(next);
|
|
17241
|
+
}
|
|
17242
|
+
throw new TBError("unavailable", "device operation completion is busy", { retryable: true });
|
|
17243
|
+
}
|
|
17244
|
+
async cancel(deviceId, operationId) {
|
|
17245
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
17246
|
+
const current = await this.read(deviceId, operationId);
|
|
17247
|
+
const record2 = await this.expireRecord(current.key, current.record);
|
|
17248
|
+
if (isTerminal(record2.state) || record2.cancelRequestedAt !== void 0) {
|
|
17249
|
+
return await this.detail(record2);
|
|
17250
|
+
}
|
|
17251
|
+
const now = this.nowIso();
|
|
17252
|
+
const next = record2.state === "queued" ? {
|
|
17253
|
+
...record2,
|
|
17254
|
+
revision: record2.revision + 1,
|
|
17255
|
+
state: "cancelled",
|
|
17256
|
+
cancelRequestedAt: now,
|
|
17257
|
+
terminalAt: now,
|
|
17258
|
+
updatedAt: now
|
|
17259
|
+
} : {
|
|
17260
|
+
...record2,
|
|
17261
|
+
revision: record2.revision + 1,
|
|
17262
|
+
cancelRequestedAt: now,
|
|
17263
|
+
updatedAt: now
|
|
17264
|
+
};
|
|
17265
|
+
if (await this.cas(current.key, record2.revision, next)) return await this.detail(next);
|
|
17266
|
+
}
|
|
17267
|
+
throw new TBError("unavailable", "device operation cancellation is busy", { retryable: true });
|
|
17268
|
+
}
|
|
17269
|
+
async cleanup(opts = {}) {
|
|
17270
|
+
const page = await this.state.list(KEY_DEVICE_OPERATION, {
|
|
17271
|
+
limit: clampLimit3(opts.limit),
|
|
17272
|
+
...opts.cursor === void 0 ? {} : { cursor: opts.cursor }
|
|
17273
|
+
});
|
|
17274
|
+
let deleted = 0;
|
|
17275
|
+
let expired = 0;
|
|
17276
|
+
for (const item of page.items) {
|
|
17277
|
+
let record2;
|
|
17278
|
+
try {
|
|
17279
|
+
record2 = parseRecord(item.value);
|
|
17280
|
+
} catch {
|
|
17281
|
+
continue;
|
|
17282
|
+
}
|
|
17283
|
+
if (!isTerminal(record2.state) && Date.parse(record2.expiresAt) <= this.now()) {
|
|
17284
|
+
const next = await this.expireRecord(item.key, record2);
|
|
17285
|
+
if (next.state === "expired") expired++;
|
|
17286
|
+
record2 = next;
|
|
17287
|
+
}
|
|
17288
|
+
if (isTerminal(record2.state) && record2.terminalAt !== void 0 && Date.parse(record2.terminalAt) + this.terminalRetentionSeconds * 1e3 <= this.now() && await this.cas(item.key, record2.revision, null)) deleted++;
|
|
17289
|
+
}
|
|
17290
|
+
return {
|
|
17291
|
+
scanned: page.items.length,
|
|
17292
|
+
expired,
|
|
17293
|
+
deleted,
|
|
17294
|
+
...page.cursor === void 0 ? {} : { cursor: page.cursor }
|
|
17295
|
+
};
|
|
17296
|
+
}
|
|
17297
|
+
};
|
|
17298
|
+
|
|
16620
17299
|
// ../core/src/protocol/errorWire.ts
|
|
16621
17300
|
var tbErrorCodeSchema = external_exports.enum(TB_ERROR_CODES);
|
|
16622
17301
|
var tbErrorBodySchema = external_exports.object({
|
|
@@ -16663,6 +17342,7 @@ var helpChildSchema = external_exports.object({
|
|
|
16663
17342
|
});
|
|
16664
17343
|
var helpCommandSchema = external_exports.object({
|
|
16665
17344
|
confirm: external_exports.boolean().optional(),
|
|
17345
|
+
delivery: external_exports.enum(["realtime", "mailbox", "both"]).optional(),
|
|
16666
17346
|
effect: external_exports.string().optional(),
|
|
16667
17347
|
h: external_exports.string().optional(),
|
|
16668
17348
|
inputSchema: external_exports.unknown().optional(),
|
|
@@ -16696,6 +17376,7 @@ var treeJsonSchema = external_exports.lazy(() => external_exports.object({
|
|
|
16696
17376
|
}));
|
|
16697
17377
|
var toolSpecSchema = external_exports.object({
|
|
16698
17378
|
confirm: external_exports.boolean().optional(),
|
|
17379
|
+
delivery: external_exports.enum(["realtime", "mailbox", "both"]).optional(),
|
|
16699
17380
|
description: external_exports.string().optional(),
|
|
16700
17381
|
effect: external_exports.string().optional(),
|
|
16701
17382
|
inputSchema: external_exports.unknown().optional(),
|
|
@@ -16733,6 +17414,12 @@ var toolSearchItemSchema = external_exports.strictObject({
|
|
|
16733
17414
|
source: toolSearchSourceSchema.optional(),
|
|
16734
17415
|
tool: toolSpecSchema
|
|
16735
17416
|
});
|
|
17417
|
+
function pageSchema(itemSchema) {
|
|
17418
|
+
return external_exports.object({
|
|
17419
|
+
cursor: external_exports.string().optional(),
|
|
17420
|
+
items: external_exports.array(itemSchema)
|
|
17421
|
+
});
|
|
17422
|
+
}
|
|
16736
17423
|
var toolSearchPageSchema = external_exports.strictObject({
|
|
16737
17424
|
cursor: external_exports.string().optional(),
|
|
16738
17425
|
items: external_exports.array(toolSearchItemSchema),
|
|
@@ -16749,15 +17436,118 @@ var toolSearchRequestSchema = external_exports.strictObject({
|
|
|
16749
17436
|
limit: external_exports.number().int().optional(),
|
|
16750
17437
|
matching: external_exports.enum(["best", "all"]).optional(),
|
|
16751
17438
|
minCoverage: external_exports.number().gt(0).max(1).optional(),
|
|
16752
|
-
mode: external_exports.enum(["keyword"
|
|
17439
|
+
mode: external_exports.enum(["keyword"]).optional(),
|
|
16753
17440
|
pathPrefix: external_exports.string().optional()
|
|
16754
17441
|
}).optional(),
|
|
16755
17442
|
query: external_exports.string().trim().min(1)
|
|
16756
17443
|
});
|
|
17444
|
+
var deviceOperationStateSchema = external_exports.enum(
|
|
17445
|
+
DEVICE_OPERATION_STATES
|
|
17446
|
+
);
|
|
17447
|
+
var deviceOperationCallerSchema = external_exports.object({
|
|
17448
|
+
keyId: external_exports.string().min(1),
|
|
17449
|
+
owner: external_exports.string().min(1)
|
|
17450
|
+
});
|
|
17451
|
+
var deviceOperationSummaryShape = {
|
|
17452
|
+
attempt: external_exports.number().int().nonnegative(),
|
|
17453
|
+
caller: deviceOperationCallerSchema,
|
|
17454
|
+
cancelRequestedAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17455
|
+
createdAt: external_exports.iso.datetime({ offset: true }),
|
|
17456
|
+
deviceId: external_exports.string().min(1),
|
|
17457
|
+
executionMayHaveOccurred: external_exports.boolean(),
|
|
17458
|
+
expiresAt: external_exports.iso.datetime({ offset: true }),
|
|
17459
|
+
leaseUntil: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17460
|
+
mountPath: external_exports.string().min(1),
|
|
17461
|
+
operationId: external_exports.string().min(1),
|
|
17462
|
+
state: deviceOperationStateSchema,
|
|
17463
|
+
targetPath: external_exports.string().min(1),
|
|
17464
|
+
terminalAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17465
|
+
traceId: external_exports.string().min(1),
|
|
17466
|
+
updatedAt: external_exports.iso.datetime({ offset: true })
|
|
17467
|
+
};
|
|
17468
|
+
var deviceOperationSummarySchema = external_exports.object(
|
|
17469
|
+
deviceOperationSummaryShape
|
|
17470
|
+
);
|
|
17471
|
+
var deviceOperationDetailSchema = external_exports.object({
|
|
17472
|
+
...deviceOperationSummaryShape,
|
|
17473
|
+
error: tbErrorBodySchema.optional(),
|
|
17474
|
+
result: external_exports.json().optional()
|
|
17475
|
+
});
|
|
17476
|
+
var deviceOperationListRequestSchema = external_exports.strictObject({
|
|
17477
|
+
deviceId: external_exports.string().min(1),
|
|
17478
|
+
opts: external_exports.strictObject({
|
|
17479
|
+
cursor: external_exports.string().min(1).optional(),
|
|
17480
|
+
limit: external_exports.number().int().positive().optional(),
|
|
17481
|
+
states: external_exports.array(deviceOperationStateSchema).min(1).optional()
|
|
17482
|
+
}).optional()
|
|
17483
|
+
});
|
|
17484
|
+
var deviceOperationListResponseSchema = pageSchema(deviceOperationSummarySchema);
|
|
17485
|
+
var deviceOperationIdentityRequestSchema = external_exports.strictObject({
|
|
17486
|
+
deviceId: external_exports.string().min(1),
|
|
17487
|
+
operationId: external_exports.string().min(1)
|
|
17488
|
+
});
|
|
17489
|
+
var deviceOperationClaimRequestSchema = external_exports.strictObject({
|
|
17490
|
+
cursor: external_exports.string().min(1).optional(),
|
|
17491
|
+
deviceId: external_exports.string().min(1),
|
|
17492
|
+
limit: external_exports.number().int().positive().optional()
|
|
17493
|
+
});
|
|
17494
|
+
var deviceOperationClaimSchema = external_exports.object({
|
|
17495
|
+
arguments: external_exports.record(external_exports.string(), external_exports.json()),
|
|
17496
|
+
attempt: external_exports.number().int().positive(),
|
|
17497
|
+
caller: deviceOperationCallerSchema,
|
|
17498
|
+
cancelRequestedAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17499
|
+
createdAt: external_exports.iso.datetime({ offset: true }),
|
|
17500
|
+
expiresAt: external_exports.iso.datetime({ offset: true }),
|
|
17501
|
+
leaseId: external_exports.string().min(1),
|
|
17502
|
+
leaseUntil: external_exports.iso.datetime({ offset: true }),
|
|
17503
|
+
operationId: external_exports.string().min(1),
|
|
17504
|
+
path: external_exports.string().min(1),
|
|
17505
|
+
targetPath: external_exports.string().min(1),
|
|
17506
|
+
traceId: external_exports.string().min(1)
|
|
17507
|
+
});
|
|
17508
|
+
var deviceOperationClaimResponseSchema = external_exports.object({
|
|
17509
|
+
cursor: external_exports.string().optional(),
|
|
17510
|
+
operation: deviceOperationClaimSchema.optional(),
|
|
17511
|
+
serverNow: external_exports.iso.datetime({ offset: true })
|
|
17512
|
+
});
|
|
17513
|
+
var deviceOperationLeaseShape = {
|
|
17514
|
+
deviceId: external_exports.string().min(1),
|
|
17515
|
+
leaseId: external_exports.string().min(1),
|
|
17516
|
+
operationId: external_exports.string().min(1)
|
|
17517
|
+
};
|
|
17518
|
+
var deviceOperationRenewRequestSchema = external_exports.strictObject(deviceOperationLeaseShape);
|
|
17519
|
+
var deviceOperationRenewResponseSchema = external_exports.object({
|
|
17520
|
+
cancelRequestedAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17521
|
+
leaseUntil: external_exports.iso.datetime({ offset: true }),
|
|
17522
|
+
serverNow: external_exports.iso.datetime({ offset: true })
|
|
17523
|
+
});
|
|
17524
|
+
var deviceOperationCompleteRequestSchema = external_exports.discriminatedUnion("outcome", [
|
|
17525
|
+
external_exports.strictObject({
|
|
17526
|
+
...deviceOperationLeaseShape,
|
|
17527
|
+
outcome: external_exports.literal("succeeded"),
|
|
17528
|
+
result: external_exports.json()
|
|
17529
|
+
}),
|
|
17530
|
+
external_exports.strictObject({
|
|
17531
|
+
...deviceOperationLeaseShape,
|
|
17532
|
+
error: tbErrorBodySchema,
|
|
17533
|
+
outcome: external_exports.literal("rejected")
|
|
17534
|
+
}),
|
|
17535
|
+
external_exports.strictObject({
|
|
17536
|
+
...deviceOperationLeaseShape,
|
|
17537
|
+
error: tbErrorBodySchema,
|
|
17538
|
+
outcome: external_exports.literal("failed")
|
|
17539
|
+
}),
|
|
17540
|
+
external_exports.strictObject({
|
|
17541
|
+
...deviceOperationLeaseShape,
|
|
17542
|
+
error: tbErrorBodySchema.optional(),
|
|
17543
|
+
outcome: external_exports.literal("result_unknown")
|
|
17544
|
+
})
|
|
17545
|
+
]);
|
|
16757
17546
|
var registryNodeSchema = external_exports.object({
|
|
16758
17547
|
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
16759
17548
|
createdAt: external_exports.string().optional(),
|
|
16760
17549
|
description: external_exports.string(),
|
|
17550
|
+
deviceId: external_exports.string().optional(),
|
|
16761
17551
|
kind: nodeKindSchema,
|
|
16762
17552
|
lastSeenAt: external_exports.string().optional(),
|
|
16763
17553
|
online: external_exports.boolean().optional(),
|
|
@@ -17106,17 +17896,7 @@ function createSkModule(store, now) {
|
|
|
17106
17896
|
|
|
17107
17897
|
// ../core/src/builtin/index.ts
|
|
17108
17898
|
async function countNodes(registry2) {
|
|
17109
|
-
|
|
17110
|
-
let cursor;
|
|
17111
|
-
do {
|
|
17112
|
-
const page = await registry2.list(void 0, {
|
|
17113
|
-
limit: LIST_LIMIT_MAX,
|
|
17114
|
-
...cursor !== void 0 ? { cursor } : {}
|
|
17115
|
-
});
|
|
17116
|
-
count += page.items.length;
|
|
17117
|
-
cursor = page.cursor;
|
|
17118
|
-
} while (cursor);
|
|
17119
|
-
return count;
|
|
17899
|
+
return (await registry2.subtree("")).length;
|
|
17120
17900
|
}
|
|
17121
17901
|
function createBuiltins(deps) {
|
|
17122
17902
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -17165,7 +17945,7 @@ function contextHelpModel(node, opts = {}) {
|
|
|
17165
17945
|
return contextCommands.helpModel(
|
|
17166
17946
|
{ path: node.path, kind: "context", description: node.description },
|
|
17167
17947
|
(command) => {
|
|
17168
|
-
if (command.name
|
|
17948
|
+
if (contextCommands.isHostOnly(command.name)) return false;
|
|
17169
17949
|
if (opts.methods !== void 0 && !opts.methods.has(command.name)) return false;
|
|
17170
17950
|
return opts.readOnly !== true || command.scope === "read";
|
|
17171
17951
|
}
|
|
@@ -17195,7 +17975,7 @@ async function readStreamBytes(stream) {
|
|
|
17195
17975
|
return out;
|
|
17196
17976
|
}
|
|
17197
17977
|
async function readStreamText(stream) {
|
|
17198
|
-
return new
|
|
17978
|
+
return new TextDecoder2().decode(await readStreamBytes(stream));
|
|
17199
17979
|
}
|
|
17200
17980
|
function ownedBytes(view) {
|
|
17201
17981
|
return view.buffer instanceof ArrayBuffer ? new Uint8Array(view.buffer, view.byteOffset, view.byteLength) : new Uint8Array(view);
|
|
@@ -17206,7 +17986,7 @@ async function objectBodyToBytes(body) {
|
|
|
17206
17986
|
if (body instanceof ArrayBuffer) return new Uint8Array(body);
|
|
17207
17987
|
return readStreamBytes(body);
|
|
17208
17988
|
}
|
|
17209
|
-
function bytesToObjectStream(
|
|
17989
|
+
function bytesToObjectStream(bytes2) {
|
|
17210
17990
|
return {
|
|
17211
17991
|
getReader() {
|
|
17212
17992
|
let done = false;
|
|
@@ -17214,7 +17994,7 @@ function bytesToObjectStream(bytes) {
|
|
|
17214
17994
|
async read() {
|
|
17215
17995
|
if (done) return { done: true };
|
|
17216
17996
|
done = true;
|
|
17217
|
-
return { done: false, value:
|
|
17997
|
+
return { done: false, value: bytes2 };
|
|
17218
17998
|
},
|
|
17219
17999
|
releaseLock() {
|
|
17220
18000
|
},
|
|
@@ -17232,6 +18012,14 @@ function bytesToObjectStream(bytes) {
|
|
|
17232
18012
|
var DEFAULT_STORE_NAME = "default";
|
|
17233
18013
|
var DEFAULT_STORE_DRIVER_KEY_ROOT = "__tool_bridge_internal__/store/v1";
|
|
17234
18014
|
|
|
18015
|
+
// ../core/src/context/contentType.ts
|
|
18016
|
+
function normalizeContentType(value, message) {
|
|
18017
|
+
if (typeof value !== "string" || value.trim() === "" || value.length > 255 || !value.includes("/") || /[\r\n\0]/.test(value)) {
|
|
18018
|
+
throw new TBError("invalid_argument", message);
|
|
18019
|
+
}
|
|
18020
|
+
return value.trim().toLowerCase();
|
|
18021
|
+
}
|
|
18022
|
+
|
|
17235
18023
|
// ../core/src/context/path.ts
|
|
17236
18024
|
function hasControlChar(s) {
|
|
17237
18025
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -17314,9 +18102,10 @@ async function createObjectContextUploadGrant(store, opts, input) {
|
|
|
17314
18102
|
if (unknownKeys.length > 0) {
|
|
17315
18103
|
throw new TBError("invalid_argument", `create_upload \u542B\u672A\u77E5\u5B57\u6BB5:${unknownKeys.join(",")}`);
|
|
17316
18104
|
}
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
18105
|
+
const contentType = normalizeContentType(
|
|
18106
|
+
input?.contentType,
|
|
18107
|
+
"create_upload \u9700\u8981\u5408\u6CD5\u7684 'contentType'"
|
|
18108
|
+
);
|
|
17320
18109
|
const nsPath = normalizePath(opts.nsPath);
|
|
17321
18110
|
const entryPath = normalizeEntryPath(input.path);
|
|
17322
18111
|
const key = `${keyPrefixBare === "" ? "" : `${keyPrefixBare}/`}${entryPath}`;
|
|
@@ -17325,7 +18114,6 @@ async function createObjectContextUploadGrant(store, opts, input) {
|
|
|
17325
18114
|
throw new TBError("unavailable", "\u5BF9\u8C61\u5B58\u50A8\u672A\u914D\u7F6E\u76F4\u4F20\u7B7E\u540D\u80FD\u529B", { retryable: false });
|
|
17326
18115
|
}
|
|
17327
18116
|
const ttlSec = assertPresignTtlSec(opts.uploadGrantTtlSec ?? PRESIGN_TTL_SEC_DEFAULT);
|
|
17328
|
-
const contentType = input.contentType.trim();
|
|
17329
18117
|
const expiresAt = new Date(Date.now() + ttlSec * 1e3).toISOString();
|
|
17330
18118
|
const signed = await store.presignPut(key, ttlSec, {
|
|
17331
18119
|
contentType,
|
|
@@ -17339,7 +18127,7 @@ async function createObjectContextUploadGrant(store, opts, input) {
|
|
|
17339
18127
|
expiresAt
|
|
17340
18128
|
};
|
|
17341
18129
|
}
|
|
17342
|
-
function
|
|
18130
|
+
function clampLimit4(limit) {
|
|
17343
18131
|
if (limit === void 0) return LIST_LIMIT_DEFAULT;
|
|
17344
18132
|
if (!Number.isInteger(limit) || limit < 1) {
|
|
17345
18133
|
throw new TBError("invalid_argument", `limit \u975E\u6CD5:${limit}`);
|
|
@@ -17413,7 +18201,7 @@ function createObjectContextProvider(store, opts) {
|
|
|
17413
18201
|
return {
|
|
17414
18202
|
async list(path, listOpts) {
|
|
17415
18203
|
rejectFilter(listOpts);
|
|
17416
|
-
const limit =
|
|
18204
|
+
const limit = clampLimit4(listOpts?.limit);
|
|
17417
18205
|
const rel = path === "" ? "" : `${normalizeEntryPath(path)}/`;
|
|
17418
18206
|
const full = keyPrefix + rel;
|
|
17419
18207
|
assertContextKeyAllowed(full.slice(0, -1), path, false);
|
|
@@ -17523,7 +18311,7 @@ function createObjectContextProvider(store, opts) {
|
|
|
17523
18311
|
throw new TBError("invalid_argument", "query \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17524
18312
|
}
|
|
17525
18313
|
rejectFilter(searchOpts);
|
|
17526
|
-
const limit =
|
|
18314
|
+
const limit = clampLimit4(searchOpts?.limit);
|
|
17527
18315
|
const q = query.toLowerCase();
|
|
17528
18316
|
const after = searchOpts?.cursor;
|
|
17529
18317
|
const items = [];
|
|
@@ -17580,6 +18368,7 @@ function isContextExpired(createdAt, ttlSec, now) {
|
|
|
17580
18368
|
// ../core/src/device/frames.ts
|
|
17581
18369
|
var deviceNodeCmdSchema = external_exports.object({
|
|
17582
18370
|
name: external_exports.string().min(1),
|
|
18371
|
+
delivery: external_exports.enum(["realtime", "mailbox", "both"]).optional(),
|
|
17583
18372
|
description: external_exports.string().optional(),
|
|
17584
18373
|
inputSchema: external_exports.unknown().optional(),
|
|
17585
18374
|
outputSchema: external_exports.unknown().optional(),
|
|
@@ -17762,18 +18551,18 @@ function derivePresence(input) {
|
|
|
17762
18551
|
var DEVICE_CALL_TIMEOUT_MS = 6e4;
|
|
17763
18552
|
var DEVICE_REJECT_CLOSE_CODE = 1008;
|
|
17764
18553
|
var DeviceGatewaySession = class {
|
|
17765
|
-
constructor(io, opts) {
|
|
17766
|
-
this.io = io;
|
|
17767
|
-
this.opts = opts;
|
|
17768
|
-
this.timeoutMs = opts.timeoutMs ?? DEVICE_CALL_TIMEOUT_MS;
|
|
17769
|
-
}
|
|
17770
|
-
io;
|
|
17771
|
-
opts;
|
|
17772
18554
|
phase_ = "awaiting-hello";
|
|
17773
18555
|
helloSeen = false;
|
|
17774
18556
|
results = /* @__PURE__ */ new Map();
|
|
17775
18557
|
pending = /* @__PURE__ */ new Map();
|
|
17776
18558
|
timeoutMs;
|
|
18559
|
+
io;
|
|
18560
|
+
opts;
|
|
18561
|
+
constructor(io, opts) {
|
|
18562
|
+
this.io = io;
|
|
18563
|
+
this.opts = opts;
|
|
18564
|
+
this.timeoutMs = opts.timeoutMs ?? DEVICE_CALL_TIMEOUT_MS;
|
|
18565
|
+
}
|
|
17777
18566
|
get phase() {
|
|
17778
18567
|
return this.phase_;
|
|
17779
18568
|
}
|
|
@@ -17846,7 +18635,7 @@ var DeviceGatewaySession = class {
|
|
|
17846
18635
|
call(req, done) {
|
|
17847
18636
|
const cached2 = this.results.get(req.id);
|
|
17848
18637
|
if (cached2 !== void 0) {
|
|
17849
|
-
done(cached2);
|
|
18638
|
+
done(cached2, "completed");
|
|
17850
18639
|
return;
|
|
17851
18640
|
}
|
|
17852
18641
|
const inflight = this.pending.get(req.id);
|
|
@@ -17855,19 +18644,28 @@ var DeviceGatewaySession = class {
|
|
|
17855
18644
|
return;
|
|
17856
18645
|
}
|
|
17857
18646
|
if (this.phase_ !== "ready") {
|
|
17858
|
-
done({ ok: false, error: TBError.deviceOffline().toJSON() });
|
|
18647
|
+
done({ ok: false, error: TBError.deviceOffline().toJSON() }, "not_dispatched");
|
|
17859
18648
|
return;
|
|
17860
18649
|
}
|
|
17861
18650
|
const cancelTimer = this.opts.setTimer(() => this.onCallTimeout(req.id), this.timeoutMs);
|
|
17862
18651
|
this.pending.set(req.id, { waiters: [done], cancelTimer });
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
18652
|
+
try {
|
|
18653
|
+
this.io.send({
|
|
18654
|
+
type: "call",
|
|
18655
|
+
id: req.id,
|
|
18656
|
+
path: req.path,
|
|
18657
|
+
arguments: req.arguments,
|
|
18658
|
+
// undefined 经 JSON.stringify 丢弃:老网关不带 context 时帧形状不变。
|
|
18659
|
+
...req.context !== void 0 ? { context: req.context } : {}
|
|
18660
|
+
});
|
|
18661
|
+
} catch {
|
|
18662
|
+
this.pending.delete(req.id);
|
|
18663
|
+
cancelTimer();
|
|
18664
|
+
done({
|
|
18665
|
+
ok: false,
|
|
18666
|
+
error: new TBError("unavailable", "\u8BBE\u5907\u8C03\u7528\u53D1\u9001\u5931\u8D25", { retryable: true }).toJSON()
|
|
18667
|
+
}, "unknown");
|
|
18668
|
+
}
|
|
17871
18669
|
}
|
|
17872
18670
|
/** 幂等表回放种子(DO 从 ctx.storage 恢复时用);已有同 id 结果则忽略。 */
|
|
17873
18671
|
seedResult(id, result) {
|
|
@@ -17887,7 +18685,7 @@ var DeviceGatewaySession = class {
|
|
|
17887
18685
|
};
|
|
17888
18686
|
for (const entry of entries) {
|
|
17889
18687
|
entry.cancelTimer();
|
|
17890
|
-
for (const waiter of entry.waiters) waiter(result);
|
|
18688
|
+
for (const waiter of entry.waiters) waiter(result, "unknown");
|
|
17891
18689
|
}
|
|
17892
18690
|
}
|
|
17893
18691
|
protocolReject(message) {
|
|
@@ -17901,7 +18699,7 @@ var DeviceGatewaySession = class {
|
|
|
17901
18699
|
if (entry === void 0) return;
|
|
17902
18700
|
this.pending.delete(id);
|
|
17903
18701
|
entry.cancelTimer();
|
|
17904
|
-
for (const waiter of entry.waiters) waiter(result);
|
|
18702
|
+
for (const waiter of entry.waiters) waiter(result, "completed");
|
|
17905
18703
|
}
|
|
17906
18704
|
onCallTimeout(id) {
|
|
17907
18705
|
const entry = this.pending.get(id);
|
|
@@ -17914,7 +18712,7 @@ var DeviceGatewaySession = class {
|
|
|
17914
18712
|
retryable: true
|
|
17915
18713
|
}).toJSON()
|
|
17916
18714
|
};
|
|
17917
|
-
for (const waiter of entry.waiters) waiter(result);
|
|
18715
|
+
for (const waiter of entry.waiters) waiter(result, "unknown");
|
|
17918
18716
|
}
|
|
17919
18717
|
};
|
|
17920
18718
|
|
|
@@ -17988,10 +18786,10 @@ function toView(entry) {
|
|
|
17988
18786
|
}
|
|
17989
18787
|
var ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
17990
18788
|
function randomFeedbackId() {
|
|
17991
|
-
const
|
|
17992
|
-
crypto.getRandomValues(
|
|
18789
|
+
const bytes2 = new Uint8Array(6);
|
|
18790
|
+
crypto.getRandomValues(bytes2);
|
|
17993
18791
|
let out = "fb_";
|
|
17994
|
-
for (const b of
|
|
18792
|
+
for (const b of bytes2) out += ID_ALPHABET[b % ID_ALPHABET.length];
|
|
17995
18793
|
return out;
|
|
17996
18794
|
}
|
|
17997
18795
|
function requireShort(value, field, max) {
|
|
@@ -18136,6 +18934,7 @@ function renderHelpDsl(model) {
|
|
|
18136
18934
|
if (cmd.outputSchema !== void 0) lines.push(` result ${JSON.stringify(cmd.outputSchema)}`);
|
|
18137
18935
|
if (cmd.returns !== void 0) lines.push(...attrLines("returns", cmd.returns));
|
|
18138
18936
|
lines.push(` scope ${cmd.scope}`);
|
|
18937
|
+
if (cmd.delivery !== void 0) lines.push(` delivery ${cmd.delivery}`);
|
|
18139
18938
|
if (cmd.effect !== void 0) lines.push(` effect ${cmd.effect}`);
|
|
18140
18939
|
if (cmd.confirm) lines.push(" confirm");
|
|
18141
18940
|
}
|
|
@@ -18168,6 +18967,7 @@ function renderHelpJson(model) {
|
|
|
18168
18967
|
if (cmd.inputSchema !== void 0) out.inputSchema = cmd.inputSchema;
|
|
18169
18968
|
if (cmd.outputSchema !== void 0) out.outputSchema = cmd.outputSchema;
|
|
18170
18969
|
if (cmd.returns !== void 0) out.returns = cmd.returns;
|
|
18970
|
+
if (cmd.delivery !== void 0) out.delivery = cmd.delivery;
|
|
18171
18971
|
if (cmd.effect !== void 0) out.effect = cmd.effect;
|
|
18172
18972
|
if (cmd.confirm) out.confirm = cmd.confirm;
|
|
18173
18973
|
return out;
|
|
@@ -18207,6 +19007,7 @@ function cmdSection(cmd, index) {
|
|
|
18207
19007
|
}
|
|
18208
19008
|
lines.push(`- Invoke: \`POST ${cmd.path}\` with body \`{...arguments}\``);
|
|
18209
19009
|
lines.push(`- Required scope: \`${cmd.scope}\``);
|
|
19010
|
+
if (cmd.delivery !== void 0) lines.push(`- Delivery: \`${cmd.delivery}\``);
|
|
18210
19011
|
if (cmd.effect !== void 0) {
|
|
18211
19012
|
lines.push(
|
|
18212
19013
|
`- Effect: \`${cmd.effect}\`${cmd.confirm ? " \u2014 **ask the user to confirm before calling**" : ""}`
|
|
@@ -18424,7 +19225,7 @@ var KEY_STORE_IDEMPOTENCY = "store:idempotency:";
|
|
|
18424
19225
|
var UPLOAD_TTL_SEC_DEFAULT = 15 * 60;
|
|
18425
19226
|
var SHARE_TTL_SEC_DEFAULT = 15 * 60;
|
|
18426
19227
|
var MAX_OBJECT_BYTES_DEFAULT = 256 * 1024 * 1024;
|
|
18427
|
-
var
|
|
19228
|
+
var MAX_CAS_ATTEMPTS2 = 32;
|
|
18428
19229
|
var CAPABILITY_TTL_SEC_MAX = 7 * 24 * 60 * 60;
|
|
18429
19230
|
var TOKEN_PREFIX = {
|
|
18430
19231
|
call: "tbc",
|
|
@@ -18574,12 +19375,6 @@ function normalizeTimestamp(value, field) {
|
|
|
18574
19375
|
throw error51;
|
|
18575
19376
|
}
|
|
18576
19377
|
}
|
|
18577
|
-
function normalizeContentType(value) {
|
|
18578
|
-
if (typeof value !== "string" || value.trim() === "" || value.length > 255 || !value.includes("/") || /[\r\n\0]/.test(value)) {
|
|
18579
|
-
throw new TBError("invalid_argument", "contentType \u4E0D\u5408\u6CD5");
|
|
18580
|
-
}
|
|
18581
|
-
return value.trim().toLowerCase();
|
|
18582
|
-
}
|
|
18583
19378
|
function normalizeChecksum(value) {
|
|
18584
19379
|
if (value === void 0) return void 0;
|
|
18585
19380
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -18596,7 +19391,7 @@ function normalizeChecksum(value) {
|
|
|
18596
19391
|
return { algorithm: "sha256", value: digest };
|
|
18597
19392
|
}
|
|
18598
19393
|
function normalizeUploadInput(input) {
|
|
18599
|
-
const contentType = normalizeContentType(input?.contentType);
|
|
19394
|
+
const contentType = normalizeContentType(input?.contentType, "contentType \u4E0D\u5408\u6CD5");
|
|
18600
19395
|
let filename;
|
|
18601
19396
|
if (input.filename !== void 0) {
|
|
18602
19397
|
if (typeof input.filename !== "string" || input.filename.trim() === "" || input.filename.length > 1024 || /[\r\n\0]/.test(input.filename)) throw new TBError("invalid_argument", "filename \u4E0D\u5408\u6CD5");
|
|
@@ -18645,7 +19440,7 @@ function normalizeContentTypePattern(value) {
|
|
|
18645
19440
|
const prefix = value.slice(0, -1);
|
|
18646
19441
|
if (prefix.length > 1 && !/[\r\n\0]/.test(value)) return value.toLowerCase();
|
|
18647
19442
|
}
|
|
18648
|
-
return normalizeContentType(value);
|
|
19443
|
+
return normalizeContentType(value, "contentType \u4E0D\u5408\u6CD5");
|
|
18649
19444
|
}
|
|
18650
19445
|
function descriptorOf(object2) {
|
|
18651
19446
|
if (object2.status !== "ready" || object2.readyAt === void 0 || object2.size === void 0) throw stateCorrupt("ready object");
|
|
@@ -18667,6 +19462,15 @@ function descriptorOf(object2) {
|
|
|
18667
19462
|
};
|
|
18668
19463
|
}
|
|
18669
19464
|
var StoreService = class {
|
|
19465
|
+
maxObjectBytes;
|
|
19466
|
+
now;
|
|
19467
|
+
relayMaxBytes;
|
|
19468
|
+
shareTtlSec;
|
|
19469
|
+
tokenSecret;
|
|
19470
|
+
uploadTtlSec;
|
|
19471
|
+
cas;
|
|
19472
|
+
state;
|
|
19473
|
+
objects;
|
|
18670
19474
|
constructor(state, objects, opts) {
|
|
18671
19475
|
this.state = state;
|
|
18672
19476
|
this.objects = objects;
|
|
@@ -18698,15 +19502,6 @@ var StoreService = class {
|
|
|
18698
19502
|
"shareTtlSec"
|
|
18699
19503
|
);
|
|
18700
19504
|
}
|
|
18701
|
-
state;
|
|
18702
|
-
objects;
|
|
18703
|
-
maxObjectBytes;
|
|
18704
|
-
now;
|
|
18705
|
-
relayMaxBytes;
|
|
18706
|
-
shareTtlSec;
|
|
18707
|
-
tokenSecret;
|
|
18708
|
-
uploadTtlSec;
|
|
18709
|
-
cas;
|
|
18710
19505
|
/** 所有已存在记录的 CAS 共用 revision 期望,并拒绝错误的版本跃迁。 */
|
|
18711
19506
|
async compareRecord(key, current, next) {
|
|
18712
19507
|
if (next !== null && next.revision !== current.revision + 1)
|
|
@@ -18774,7 +19569,7 @@ var StoreService = class {
|
|
|
18774
19569
|
}
|
|
18775
19570
|
async revokeCallUploadCapability(token) {
|
|
18776
19571
|
const id = this.tokenId(token, "call");
|
|
18777
|
-
for (let attempt = 0; attempt <
|
|
19572
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
18778
19573
|
const raw = await this.state.get(`${KEY_STORE_CALL_CAPABILITY}${id}`);
|
|
18779
19574
|
if (raw === null) throw new TBError("permission_denied", "call upload capability \u65E0\u6548");
|
|
18780
19575
|
const capability = parseCallCapability(raw);
|
|
@@ -18988,7 +19783,7 @@ var StoreService = class {
|
|
|
18988
19783
|
}
|
|
18989
19784
|
async delete(uri, actor) {
|
|
18990
19785
|
const { objectId } = parseStoreUri(uri);
|
|
18991
|
-
for (let attempt = 0; attempt <
|
|
19786
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
18992
19787
|
const object2 = await this.requireObject(objectId);
|
|
18993
19788
|
if (!actor.admin && object2.owner !== actor.owner) {
|
|
18994
19789
|
throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
|
|
@@ -19052,7 +19847,7 @@ var StoreService = class {
|
|
|
19052
19847
|
}
|
|
19053
19848
|
async revokeShare(shareId, actor) {
|
|
19054
19849
|
const owner = requireOwner(actor);
|
|
19055
|
-
for (let attempt = 0; attempt <
|
|
19850
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19056
19851
|
const raw = await this.state.get(`${KEY_STORE_SHARE}${shareId}`);
|
|
19057
19852
|
if (raw === null) throw new TBError("not_found", "share grant \u4E0D\u5B58\u5728");
|
|
19058
19853
|
const grant = parseShare(raw);
|
|
@@ -19376,7 +20171,7 @@ var StoreService = class {
|
|
|
19376
20171
|
await this.compareRecord(resolved.key, resolved.binding, null);
|
|
19377
20172
|
}
|
|
19378
20173
|
async reserveCallCapability(token, objectId, input, transportMaxBytes) {
|
|
19379
|
-
for (let attempt = 0; attempt <
|
|
20174
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19380
20175
|
const capability = await this.verifyCallUploadCapability(token);
|
|
19381
20176
|
const previous = capability.reservations.find((r) => r.objectId === objectId);
|
|
19382
20177
|
if (previous !== void 0) {
|
|
@@ -19498,7 +20293,7 @@ var StoreService = class {
|
|
|
19498
20293
|
}
|
|
19499
20294
|
/** driver DELETE 成功后持久化证据;后续 cleanup 只收 tombstone,不再重复外部 DELETE。 */
|
|
19500
20295
|
async markBytesDeleted(objectId, expectedDriverKey) {
|
|
19501
|
-
for (let attempt = 0; attempt <
|
|
20296
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19502
20297
|
const raw = await this.state.get(`${KEY_STORE_OBJECT}${objectId}`);
|
|
19503
20298
|
if (raw === null) return;
|
|
19504
20299
|
const object2 = parseObject(raw);
|
|
@@ -19598,7 +20393,7 @@ var StoreService = class {
|
|
|
19598
20393
|
}
|
|
19599
20394
|
async markReady(sessionInput, meta3) {
|
|
19600
20395
|
const actualChecksum = sessionInput.expectedChecksum !== void 0 && meta3.metadata?.["checksum-sha256"]?.toLowerCase() === sessionInput.expectedChecksum.value ? sessionInput.expectedChecksum : void 0;
|
|
19601
|
-
for (let attempt = 0; attempt <
|
|
20396
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19602
20397
|
const object2 = await this.requireObject(sessionInput.objectId);
|
|
19603
20398
|
if (object2.status === "ready") {
|
|
19604
20399
|
await this.completeSessionBestEffort(await this.requireSession(sessionInput.id));
|
|
@@ -19628,7 +20423,7 @@ var StoreService = class {
|
|
|
19628
20423
|
throw new TBError("conflict", "\u5BF9\u8C61\u5E76\u53D1\u72B6\u6001\u51B2\u7A81:pending");
|
|
19629
20424
|
}
|
|
19630
20425
|
async completeSessionBestEffort(sessionInput) {
|
|
19631
|
-
for (let attempt = 0; attempt <
|
|
20426
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19632
20427
|
const session = await this.requireSession(sessionInput.id);
|
|
19633
20428
|
if (session.status === "completed" || session.status !== "created") return;
|
|
19634
20429
|
const completedAt = this.nowIso();
|
|
@@ -19804,8 +20599,8 @@ function canonicalCatalogJson(value) {
|
|
|
19804
20599
|
return JSON.stringify(normalize(value));
|
|
19805
20600
|
}
|
|
19806
20601
|
async function catalogDigest(value) {
|
|
19807
|
-
const
|
|
19808
|
-
const hash2 = await crypto.subtle.digest("SHA-256",
|
|
20602
|
+
const bytes2 = new TextEncoder().encode(canonicalCatalogJson(value));
|
|
20603
|
+
const hash2 = await crypto.subtle.digest("SHA-256", bytes2);
|
|
19809
20604
|
return [...new Uint8Array(hash2)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
19810
20605
|
}
|
|
19811
20606
|
async function catalogSetDigest(catalog) {
|
|
@@ -19974,7 +20769,7 @@ function encodeCallContext(ctx) {
|
|
|
19974
20769
|
function decodeCallContext(header) {
|
|
19975
20770
|
let text;
|
|
19976
20771
|
try {
|
|
19977
|
-
text = new
|
|
20772
|
+
text = new TextDecoder2().decode(base64urlDecode(header));
|
|
19978
20773
|
} catch {
|
|
19979
20774
|
throw new TBError("invalid_argument", `${HEADER_TB_CONTEXT} \u975E\u6CD5 base64url`);
|
|
19980
20775
|
}
|
|
@@ -19999,11 +20794,11 @@ var pluginCallSchema = external_exports.object({
|
|
|
19999
20794
|
arguments: external_exports.record(external_exports.string(), external_exports.unknown())
|
|
20000
20795
|
});
|
|
20001
20796
|
function assertPluginPayloadSize(payload) {
|
|
20002
|
-
const
|
|
20003
|
-
if (
|
|
20797
|
+
const bytes2 = new TextEncoder().encode(payload).length;
|
|
20798
|
+
if (bytes2 > PLUGIN_PAYLOAD_MAX_BYTES) {
|
|
20004
20799
|
throw new TBError(
|
|
20005
20800
|
"invalid_argument",
|
|
20006
|
-
`payload ${
|
|
20801
|
+
`payload ${bytes2} \u5B57\u8282\u8D85\u8FC7\u4E0A\u9650 ${PLUGIN_PAYLOAD_MAX_BYTES}(\u2264 1 MiB;\u66F4\u5927\u5185\u5BB9\u7ECF $ref)`
|
|
20007
20802
|
);
|
|
20008
20803
|
}
|
|
20009
20804
|
}
|
|
@@ -20181,9 +20976,9 @@ function searchUnitAllowsPath(unit) {
|
|
|
20181
20976
|
return !/^%[A-Za-z0-9]{1,2}%$/u.test(unit.pattern);
|
|
20182
20977
|
}
|
|
20183
20978
|
function stableDigest(value) {
|
|
20184
|
-
const
|
|
20979
|
+
const bytes2 = new TextEncoder().encode(JSON.stringify(value));
|
|
20185
20980
|
let hash2 = 0xcbf29ce484222325n;
|
|
20186
|
-
for (const byte of
|
|
20981
|
+
for (const byte of bytes2) {
|
|
20187
20982
|
hash2 ^= BigInt(byte);
|
|
20188
20983
|
hash2 = BigInt.asUintN(64, hash2 * 0x100000001b3n);
|
|
20189
20984
|
}
|
|
@@ -20193,12 +20988,12 @@ function truncateUtf8(value, maxBytes) {
|
|
|
20193
20988
|
const encoder = new TextEncoder();
|
|
20194
20989
|
if (encoder.encode(value).length <= maxBytes) return value;
|
|
20195
20990
|
let result = "";
|
|
20196
|
-
let
|
|
20991
|
+
let bytes2 = 0;
|
|
20197
20992
|
for (const char of value) {
|
|
20198
20993
|
const size = encoder.encode(char).length;
|
|
20199
|
-
if (
|
|
20994
|
+
if (bytes2 + size > maxBytes) break;
|
|
20200
20995
|
result += char;
|
|
20201
|
-
|
|
20996
|
+
bytes2 += size;
|
|
20202
20997
|
}
|
|
20203
20998
|
return result;
|
|
20204
20999
|
}
|
|
@@ -20558,7 +21353,7 @@ async function decodeToolSearchCursor(cursor, query, mode, revision, secret, opt
|
|
|
20558
21353
|
await cursorCryptoKey(secret),
|
|
20559
21354
|
sealed.slice(12)
|
|
20560
21355
|
);
|
|
20561
|
-
value = JSON.parse(new
|
|
21356
|
+
value = JSON.parse(new TextDecoder2("utf-8", { fatal: true }).decode(new Uint8Array(plaintext)));
|
|
20562
21357
|
} catch (error51) {
|
|
20563
21358
|
if (error51 instanceof TBError) throw error51;
|
|
20564
21359
|
throw new TBError("invalid_argument", "\u641C\u7D22 cursor \u683C\u5F0F\u975E\u6CD5");
|
|
@@ -20626,36 +21421,45 @@ var TOOL_SEARCH_INSERT_SQL = `
|
|
|
20626
21421
|
INSERT INTO tb_search_tools_v5 (path, name, description, effect, feedback)
|
|
20627
21422
|
VALUES (?, ?, ?, ?, ?)
|
|
20628
21423
|
`;
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20632
|
-
|
|
21424
|
+
function toolSearchFixedStatements(syntax) {
|
|
21425
|
+
const p = (index) => syntax.placeholder(index);
|
|
21426
|
+
const int2 = syntax.castInt ?? ((expr) => expr);
|
|
21427
|
+
return {
|
|
21428
|
+
bumpRevision: "UPDATE tb_search_meta_v5 SET revision = revision + 1 WHERE singleton = 1",
|
|
21429
|
+
completeRebuild: "UPDATE tb_search_meta_v5 SET seeded = 1, revision = revision + 1 WHERE singleton = 1",
|
|
21430
|
+
deleteAllSnapshots: "DELETE FROM tb_search_snapshots_v5",
|
|
21431
|
+
deleteAllTools: "DELETE FROM tb_search_tools_v5",
|
|
21432
|
+
deleteSnapshot: `DELETE FROM tb_search_snapshots_v5 WHERE path = ${p(1)}`,
|
|
21433
|
+
deleteSnapshotPrefix: `
|
|
21434
|
+
DELETE FROM tb_search_snapshots_v5
|
|
21435
|
+
WHERE path = ${p(1)} OR substr(path, 1, length(${p(2)}) + 1) = ${p(3)} || '/'
|
|
21436
|
+
`,
|
|
21437
|
+
deleteTools: `DELETE FROM tb_search_tools_v5 WHERE path = ${p(1)}`,
|
|
21438
|
+
deleteToolsPrefix: `
|
|
21439
|
+
DELETE FROM tb_search_tools_v5
|
|
21440
|
+
WHERE path = ${p(1)} OR substr(path, 1, length(${p(2)}) + 1) = ${p(3)} || '/'
|
|
21441
|
+
`,
|
|
21442
|
+
insertSnapshot: `INSERT INTO tb_search_snapshots_v5(path, digest) VALUES (${p(1)}, ${p(2)})`,
|
|
21443
|
+
meta: `SELECT ${int2("revision")} AS revision, ${int2("seeded")} AS seeded, cursor_secret FROM tb_search_meta_v5 WHERE singleton = 1`,
|
|
21444
|
+
// replace 的前置探测:一次拿到本 path 的 digest、是否残留 source 行、已用节点数。
|
|
21445
|
+
pathState: `
|
|
20633
21446
|
SELECT snapshots.digest,
|
|
20634
|
-
EXISTS(SELECT 1 FROM tb_search_tools_v5 WHERE path =
|
|
20635
|
-
(SELECT COUNT(*) FROM tb_search_snapshots_v5) AS path_count
|
|
21447
|
+
${int2(`EXISTS(SELECT 1 FROM tb_search_tools_v5 WHERE path = ${p(1)})`)} AS has_tools,
|
|
21448
|
+
${int2("(SELECT COUNT(*) FROM tb_search_snapshots_v5)")} AS path_count
|
|
20636
21449
|
FROM (SELECT 1) AS singleton
|
|
20637
|
-
LEFT JOIN tb_search_snapshots_v5 AS snapshots ON snapshots.path =
|
|
20638
|
-
|
|
20639
|
-
|
|
20640
|
-
|
|
21450
|
+
LEFT JOIN tb_search_snapshots_v5 AS snapshots ON snapshots.path = ${p(2)}
|
|
21451
|
+
`,
|
|
21452
|
+
present: `SELECT 1 AS present FROM tb_search_tools_v5 WHERE path = ${p(1)} LIMIT 1`,
|
|
21453
|
+
presentPrefix: `
|
|
20641
21454
|
SELECT 1 AS present FROM tb_search_tools_v5
|
|
20642
|
-
WHERE path =
|
|
21455
|
+
WHERE path = ${p(1)} OR substr(path, 1, length(${p(2)}) + 1) = ${p(3)} || '/'
|
|
20643
21456
|
LIMIT 1
|
|
20644
|
-
|
|
20645
|
-
|
|
20646
|
-
|
|
20647
|
-
|
|
20648
|
-
|
|
20649
|
-
|
|
20650
|
-
`;
|
|
20651
|
-
var DELETE_SNAPSHOT_PREFIX_SQL = `
|
|
20652
|
-
DELETE FROM tb_search_snapshots_v5
|
|
20653
|
-
WHERE path = ? OR substr(path, 1, length(?) + 1) = ? || '/'
|
|
20654
|
-
`;
|
|
20655
|
-
var DELETE_ALL_TOOLS_SQL = "DELETE FROM tb_search_tools_v5";
|
|
20656
|
-
var DELETE_ALL_SNAPSHOTS_SQL = "DELETE FROM tb_search_snapshots_v5";
|
|
20657
|
-
var BUMP_REVISION_SQL = "UPDATE tb_search_meta_v5 SET revision = revision + 1 WHERE singleton = 1";
|
|
20658
|
-
var COMPLETE_REBUILD_SQL = "UPDATE tb_search_meta_v5 SET seeded = 1, revision = revision + 1 WHERE singleton = 1";
|
|
21457
|
+
`,
|
|
21458
|
+
snapshotDigests: "SELECT path, digest FROM tb_search_snapshots_v5 ORDER BY path"
|
|
21459
|
+
};
|
|
21460
|
+
}
|
|
21461
|
+
var SQLITE_FIXED_STATEMENTS = toolSearchFixedStatements({ placeholder: () => "?" });
|
|
21462
|
+
var TOOL_SEARCH_INSERT_SNAPSHOT_SQL = SQLITE_FIXED_STATEMENTS.insertSnapshot;
|
|
20659
21463
|
function toolSearchEffectSql(effect) {
|
|
20660
21464
|
switch (effect) {
|
|
20661
21465
|
case "read":
|
|
@@ -20762,31 +21566,16 @@ var sqliteSearchDialect = {
|
|
|
20762
21566
|
);
|
|
20763
21567
|
},
|
|
20764
21568
|
schemaStatements: TOOL_SEARCH_SCHEMA_STATEMENTS,
|
|
20765
|
-
statements:
|
|
20766
|
-
bumpRevision: BUMP_REVISION_SQL,
|
|
20767
|
-
completeRebuild: COMPLETE_REBUILD_SQL,
|
|
20768
|
-
deleteAllSnapshots: DELETE_ALL_SNAPSHOTS_SQL,
|
|
20769
|
-
deleteAllTools: DELETE_ALL_TOOLS_SQL,
|
|
20770
|
-
deleteSnapshot: DELETE_SNAPSHOT_SQL,
|
|
20771
|
-
deleteSnapshotPrefix: DELETE_SNAPSHOT_PREFIX_SQL,
|
|
20772
|
-
deleteTools: DELETE_TOOLS_SQL,
|
|
20773
|
-
deleteToolsPrefix: DELETE_TOOLS_PREFIX_SQL,
|
|
20774
|
-
insertSnapshot: TOOL_SEARCH_INSERT_SNAPSHOT_SQL,
|
|
20775
|
-
meta: META_SQL,
|
|
20776
|
-
pathState: PATH_STATE_SQL,
|
|
20777
|
-
present: PRESENT_SQL,
|
|
20778
|
-
presentPrefix: PRESENT_PREFIX_SQL,
|
|
20779
|
-
snapshotDigests: SNAPSHOT_DIGESTS_SQL
|
|
20780
|
-
}
|
|
21569
|
+
statements: SQLITE_FIXED_STATEMENTS
|
|
20781
21570
|
};
|
|
20782
21571
|
var SqlSearchIndex = class {
|
|
21572
|
+
capabilities = ["search"];
|
|
21573
|
+
dialect;
|
|
21574
|
+
driver;
|
|
20783
21575
|
constructor(driver, dialect = sqliteSearchDialect) {
|
|
20784
21576
|
this.driver = driver;
|
|
20785
21577
|
this.dialect = dialect;
|
|
20786
21578
|
}
|
|
20787
|
-
driver;
|
|
20788
|
-
capabilities = ["search"];
|
|
20789
|
-
dialect;
|
|
20790
21579
|
/** 容量 trigger 的 ABORT 在各驱动里错误形状不同,统一按标记串归一。 */
|
|
20791
21580
|
async writeOrCapacity(statements) {
|
|
20792
21581
|
try {
|
|
@@ -21006,7 +21795,11 @@ var PG_SEARCH_SCHEMA_STATEMENTS = [
|
|
|
21006
21795
|
FOR EACH ROW EXECUTE FUNCTION tb_search_snapshots_v5_capacity()`
|
|
21007
21796
|
];
|
|
21008
21797
|
var PG_SEARCH_WRITE_LOCK_KEY = 2069799624;
|
|
21009
|
-
var
|
|
21798
|
+
var PG_FIXED_STATEMENTS = toolSearchFixedStatements({
|
|
21799
|
+
castInt: (expr) => `${expr}::int`,
|
|
21800
|
+
placeholder: (index) => `$${index}`
|
|
21801
|
+
});
|
|
21802
|
+
var PG_SEARCH_INSERT_SNAPSHOT_SQL = PG_FIXED_STATEMENTS.insertSnapshot;
|
|
21010
21803
|
function pgBulkInsertSql(table, columns, rows) {
|
|
21011
21804
|
const tuples = [];
|
|
21012
21805
|
for (let r = 0; r < rows; r++) {
|
|
@@ -21026,38 +21819,6 @@ var PG_SEARCH_TOOLS_COLUMNS = [
|
|
|
21026
21819
|
var PG_SEARCH_SNAPSHOTS_TABLE = "tb_search_snapshots_v5";
|
|
21027
21820
|
var PG_SEARCH_SNAPSHOTS_COLUMNS = ["path", "digest"];
|
|
21028
21821
|
var PG_SEARCH_INSERT_ROWS_MAX = 1e3;
|
|
21029
|
-
var META_SQL2 = `
|
|
21030
|
-
SELECT revision::int AS revision, seeded::int AS seeded, cursor_secret
|
|
21031
|
-
FROM tb_search_meta_v5 WHERE singleton = 1
|
|
21032
|
-
`;
|
|
21033
|
-
var SNAPSHOT_DIGESTS_SQL2 = "SELECT path, digest FROM tb_search_snapshots_v5 ORDER BY path";
|
|
21034
|
-
var PATH_STATE_SQL2 = `
|
|
21035
|
-
SELECT snapshots.digest,
|
|
21036
|
-
EXISTS(SELECT 1 FROM tb_search_tools_v5 WHERE path = $1)::int AS has_tools,
|
|
21037
|
-
(SELECT COUNT(*) FROM tb_search_snapshots_v5)::int AS path_count
|
|
21038
|
-
FROM (SELECT 1) AS singleton
|
|
21039
|
-
LEFT JOIN tb_search_snapshots_v5 AS snapshots ON snapshots.path = $2
|
|
21040
|
-
`;
|
|
21041
|
-
var PRESENT_SQL2 = "SELECT 1 AS present FROM tb_search_tools_v5 WHERE path = $1 LIMIT 1";
|
|
21042
|
-
var PRESENT_PREFIX_SQL2 = `
|
|
21043
|
-
SELECT 1 AS present FROM tb_search_tools_v5
|
|
21044
|
-
WHERE path = $1 OR substr(path, 1, length($2) + 1) = $3 || '/'
|
|
21045
|
-
LIMIT 1
|
|
21046
|
-
`;
|
|
21047
|
-
var DELETE_TOOLS_SQL2 = "DELETE FROM tb_search_tools_v5 WHERE path = $1";
|
|
21048
|
-
var DELETE_SNAPSHOT_SQL2 = "DELETE FROM tb_search_snapshots_v5 WHERE path = $1";
|
|
21049
|
-
var DELETE_TOOLS_PREFIX_SQL2 = `
|
|
21050
|
-
DELETE FROM tb_search_tools_v5
|
|
21051
|
-
WHERE path = $1 OR substr(path, 1, length($2) + 1) = $3 || '/'
|
|
21052
|
-
`;
|
|
21053
|
-
var DELETE_SNAPSHOT_PREFIX_SQL2 = `
|
|
21054
|
-
DELETE FROM tb_search_snapshots_v5
|
|
21055
|
-
WHERE path = $1 OR substr(path, 1, length($2) + 1) = $3 || '/'
|
|
21056
|
-
`;
|
|
21057
|
-
var DELETE_ALL_TOOLS_SQL2 = "DELETE FROM tb_search_tools_v5";
|
|
21058
|
-
var DELETE_ALL_SNAPSHOTS_SQL2 = "DELETE FROM tb_search_snapshots_v5";
|
|
21059
|
-
var BUMP_REVISION_SQL2 = "UPDATE tb_search_meta_v5 SET revision = revision + 1 WHERE singleton = 1";
|
|
21060
|
-
var COMPLETE_REBUILD_SQL2 = "UPDATE tb_search_meta_v5 SET seeded = 1, revision = revision + 1 WHERE singleton = 1";
|
|
21061
21822
|
var pgSearchDialect = {
|
|
21062
21823
|
candidateStatement: (query, limit, offset, rawConstraints) => {
|
|
21063
21824
|
const constraints = normalizeToolSearchOptions(rawConstraints);
|
|
@@ -21075,22 +21836,7 @@ var pgSearchDialect = {
|
|
|
21075
21836
|
);
|
|
21076
21837
|
},
|
|
21077
21838
|
schemaStatements: PG_SEARCH_SCHEMA_STATEMENTS,
|
|
21078
|
-
statements:
|
|
21079
|
-
bumpRevision: BUMP_REVISION_SQL2,
|
|
21080
|
-
completeRebuild: COMPLETE_REBUILD_SQL2,
|
|
21081
|
-
deleteAllSnapshots: DELETE_ALL_SNAPSHOTS_SQL2,
|
|
21082
|
-
deleteAllTools: DELETE_ALL_TOOLS_SQL2,
|
|
21083
|
-
deleteSnapshot: DELETE_SNAPSHOT_SQL2,
|
|
21084
|
-
deleteSnapshotPrefix: DELETE_SNAPSHOT_PREFIX_SQL2,
|
|
21085
|
-
deleteTools: DELETE_TOOLS_SQL2,
|
|
21086
|
-
deleteToolsPrefix: DELETE_TOOLS_PREFIX_SQL2,
|
|
21087
|
-
insertSnapshot: PG_SEARCH_INSERT_SNAPSHOT_SQL,
|
|
21088
|
-
meta: META_SQL2,
|
|
21089
|
-
pathState: PATH_STATE_SQL2,
|
|
21090
|
-
present: PRESENT_SQL2,
|
|
21091
|
-
presentPrefix: PRESENT_PREFIX_SQL2,
|
|
21092
|
-
snapshotDigests: SNAPSHOT_DIGESTS_SQL2
|
|
21093
|
-
}
|
|
21839
|
+
statements: PG_FIXED_STATEMENTS
|
|
21094
21840
|
};
|
|
21095
21841
|
|
|
21096
21842
|
// ../core/src/secret/secretStore.ts
|
|
@@ -21222,7 +21968,7 @@ var SecretStoreImpl = class _SecretStoreImpl {
|
|
|
21222
21968
|
await this.key(),
|
|
21223
21969
|
ciphertext
|
|
21224
21970
|
);
|
|
21225
|
-
return new
|
|
21971
|
+
return new TextDecoder2().decode(plaintext);
|
|
21226
21972
|
}
|
|
21227
21973
|
};
|
|
21228
21974
|
|
|
@@ -21387,7 +22133,7 @@ function guessContentType(path) {
|
|
|
21387
22133
|
if (dot < 0) return "text/plain";
|
|
21388
22134
|
return CONTENT_TYPE_BY_EXT[path.slice(dot + 1).toLowerCase()] ?? "text/plain";
|
|
21389
22135
|
}
|
|
21390
|
-
function
|
|
22136
|
+
function clampLimit5(limit) {
|
|
21391
22137
|
if (limit === void 0) return LIST_LIMIT_DEFAULT;
|
|
21392
22138
|
if (!Number.isInteger(limit) || limit < 1) {
|
|
21393
22139
|
throw new TBError("invalid_argument", `limit \u975E\u6CD5:${limit}`);
|
|
@@ -21442,7 +22188,7 @@ function createSkillhubProvider(store, opts) {
|
|
|
21442
22188
|
};
|
|
21443
22189
|
return {
|
|
21444
22190
|
async list(listOpts) {
|
|
21445
|
-
const limit =
|
|
22191
|
+
const limit = clampLimit5(listOpts?.limit);
|
|
21446
22192
|
const res = await store.list(keyPrefix, {
|
|
21447
22193
|
delimiter: "/",
|
|
21448
22194
|
cursor: listOpts?.cursor,
|
|
@@ -21502,7 +22248,7 @@ function createSkillhubProvider(store, opts) {
|
|
|
21502
22248
|
if (typeof query !== "string" || query === "") {
|
|
21503
22249
|
throw new TBError("invalid_argument", "query \u4E0D\u80FD\u4E3A\u7A7A");
|
|
21504
22250
|
}
|
|
21505
|
-
const limit =
|
|
22251
|
+
const limit = clampLimit5(searchOpts?.limit);
|
|
21506
22252
|
const q = query.toLowerCase();
|
|
21507
22253
|
const after = searchOpts?.cursor;
|
|
21508
22254
|
const items = [];
|
|
@@ -21677,6 +22423,7 @@ function toolToCmd(nodePath, tool, index) {
|
|
|
21677
22423
|
if (tool.description !== void 0) {
|
|
21678
22424
|
cmd.h = index ? summarizeOneLine(tool.description) : tool.description;
|
|
21679
22425
|
}
|
|
22426
|
+
if (tool.delivery !== void 0) cmd.delivery = tool.delivery;
|
|
21680
22427
|
if (!index && tool.inputSchema !== void 0) cmd.inputSchema = tool.inputSchema;
|
|
21681
22428
|
if (!index && tool.outputSchema !== void 0) cmd.outputSchema = tool.outputSchema;
|
|
21682
22429
|
if (tool.effect !== void 0) cmd.effect = tool.effect;
|
|
@@ -21726,7 +22473,7 @@ function rewriteRemotePath(nodePath, requestPath, baseUrl) {
|
|
|
21726
22473
|
return rel === "" ? base : `${base}/${rel}`;
|
|
21727
22474
|
}
|
|
21728
22475
|
function hostOf(url2) {
|
|
21729
|
-
const m = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/([
|
|
22476
|
+
const m = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/([^/?#\\]*)/.exec(url2);
|
|
21730
22477
|
if (m === null) return void 0;
|
|
21731
22478
|
let authority = m[1] ?? "";
|
|
21732
22479
|
const at = authority.lastIndexOf("@");
|
|
@@ -21808,7 +22555,7 @@ function resolveUpstreamTool(v, source, virtualName2) {
|
|
|
21808
22555
|
}
|
|
21809
22556
|
|
|
21810
22557
|
// ../core/src/tree/registry.ts
|
|
21811
|
-
function
|
|
22558
|
+
function clampLimit6(limit) {
|
|
21812
22559
|
if (limit === void 0 || limit < 1) return LIST_LIMIT_DEFAULT;
|
|
21813
22560
|
return limit > LIST_LIMIT_MAX ? LIST_LIMIT_MAX : limit;
|
|
21814
22561
|
}
|
|
@@ -21825,10 +22572,10 @@ function byPath(a, b) {
|
|
|
21825
22572
|
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
21826
22573
|
}
|
|
21827
22574
|
var NodeRegistryStore = class {
|
|
22575
|
+
store;
|
|
21828
22576
|
constructor(store) {
|
|
21829
22577
|
this.store = store;
|
|
21830
22578
|
}
|
|
21831
|
-
store;
|
|
21832
22579
|
keyOf(path) {
|
|
21833
22580
|
return KEY_NODE + path;
|
|
21834
22581
|
}
|
|
@@ -21909,7 +22656,7 @@ var NodeRegistryStore = class {
|
|
|
21909
22656
|
*/
|
|
21910
22657
|
async list(prefix, opts) {
|
|
21911
22658
|
const normPrefix = prefix === void 0 ? "" : canonicalizePath(prefix);
|
|
21912
|
-
const limit =
|
|
22659
|
+
const limit = clampLimit6(opts?.limit);
|
|
21913
22660
|
const all = (await this.scanAll()).filter((n) => isPrefixOf(normPrefix, n.path)).sort(byPath);
|
|
21914
22661
|
const cursor = opts?.cursor;
|
|
21915
22662
|
const start = cursor ? all.findIndex((n) => n.path === cursor) + 1 : 0;
|
|
@@ -21998,6 +22745,7 @@ var NodeRegistryStore = class {
|
|
|
21998
22745
|
description: node.description,
|
|
21999
22746
|
...node.config !== void 0 ? { config: node.config } : {},
|
|
22000
22747
|
...node.virtualize !== void 0 ? { virtualize: node.virtualize } : {},
|
|
22748
|
+
...opts.deviceId !== void 0 ? { deviceId: opts.deviceId } : {},
|
|
22001
22749
|
...opts.online !== void 0 ? { online: opts.online } : {},
|
|
22002
22750
|
...opts.lastSeenAt !== void 0 ? { lastSeenAt: opts.lastSeenAt } : {},
|
|
22003
22751
|
registeredBy,
|
|
@@ -22218,8 +22966,8 @@ function createPlugin(opts = {}) {
|
|
|
22218
22966
|
const b64 = raw.replaceAll("-", "+").replaceAll("_", "/");
|
|
22219
22967
|
const padded = b64.padEnd(b64.length + (4 - b64.length % 4) % 4, "=");
|
|
22220
22968
|
try {
|
|
22221
|
-
const
|
|
22222
|
-
return new TextDecoder().decode(
|
|
22969
|
+
const bytes2 = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
22970
|
+
return new TextDecoder().decode(bytes2);
|
|
22223
22971
|
} catch {
|
|
22224
22972
|
throw new TBError("invalid_argument", `${HEADER_TB_UPSTREAM_AUTH} is not valid base64url`);
|
|
22225
22973
|
}
|
|
@@ -22481,6 +23229,7 @@ export {
|
|
|
22481
23229
|
TBError,
|
|
22482
23230
|
isTBError,
|
|
22483
23231
|
normalizePath,
|
|
23232
|
+
assertNoCollision,
|
|
22484
23233
|
validatePath,
|
|
22485
23234
|
KEY_SK_HASH,
|
|
22486
23235
|
KEY_SK_ID,
|
|
@@ -22545,6 +23294,7 @@ export {
|
|
|
22545
23294
|
pluginTokenSecretName,
|
|
22546
23295
|
createStoreModule,
|
|
22547
23296
|
RemoteAllowlistStore,
|
|
23297
|
+
DeviceMailboxService,
|
|
22548
23298
|
tbErrorBodySchema,
|
|
22549
23299
|
nodeInputSchema,
|
|
22550
23300
|
helpJsonSchema,
|
|
@@ -22554,6 +23304,16 @@ export {
|
|
|
22554
23304
|
toolSearchSourceResultSchema,
|
|
22555
23305
|
toolSearchPageSchema,
|
|
22556
23306
|
toolSearchRequestSchema,
|
|
23307
|
+
deviceOperationSummarySchema,
|
|
23308
|
+
deviceOperationDetailSchema,
|
|
23309
|
+
deviceOperationListRequestSchema,
|
|
23310
|
+
deviceOperationListResponseSchema,
|
|
23311
|
+
deviceOperationIdentityRequestSchema,
|
|
23312
|
+
deviceOperationClaimRequestSchema,
|
|
23313
|
+
deviceOperationClaimResponseSchema,
|
|
23314
|
+
deviceOperationRenewRequestSchema,
|
|
23315
|
+
deviceOperationRenewResponseSchema,
|
|
23316
|
+
deviceOperationCompleteRequestSchema,
|
|
22557
23317
|
registryNodeSchema,
|
|
22558
23318
|
feedbackViewSchema,
|
|
22559
23319
|
feedbackDetailSchema,
|