@tool-bridge/server 0.17.0 → 0.20.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-NOSOMOVB.js → airtable-ZNWHIJMO.js} +4 -4
- package/dist/{alt_text_generator_ai-BHIJ4LW5.js → alt_text_generator_ai-X7RMDDM5.js} +4 -4
- package/dist/{amap-QRLP4TWZ.js → amap-3VER75VH.js} +4 -4
- package/dist/{anthropic-IEOI36EV.js → anthropic-N7NTB3UC.js} +4 -4
- package/dist/{apify-3CCYTQVK.js → apify-JB5P5AJI.js} +4 -4
- package/dist/{brave_search-PZNYDQG7.js → brave_search-USL5OOBS.js} +4 -4
- package/dist/{chunk-U5UHT7CX.js → chunk-3WL6EXYX.js} +1 -1
- package/dist/{chunk-YASUMV2K.js → chunk-6ANLYNFB.js} +1 -1
- package/dist/{chunk-WW2IMPQK.js → chunk-6X55ALDX.js} +3 -3
- package/dist/{chunk-TLNUIPFZ.js → chunk-RVUERWUZ.js} +2698 -247
- package/dist/{chunk-RVWZN3CI.js → chunk-WIGQCP6O.js} +1306 -207
- package/dist/{circleci-VSJ4O5FC.js → circleci-LBGB25LP.js} +4 -4
- package/dist/{clerk-CC2JZBZI.js → clerk-6RX37CNZ.js} +4 -4
- package/dist/{cloudflare_dns-PJEBSEYQ.js → cloudflare_dns-NQGW7FHV.js} +4 -4
- package/dist/{cohere-KBACYSPF.js → cohere-XPC2NAEW.js} +4 -4
- package/dist/{confluence-2PX3O7G5.js → confluence-ZUG2LT5A.js} +4 -4
- package/dist/{convertapi-BVOTD2PJ.js → convertapi-ZLXKWV3L.js} +3 -3
- package/dist/{currencyapi-6ZQ2VNQ7.js → currencyapi-T3A3HSOD.js} +4 -4
- package/dist/{deepseek-MB3WVVTB.js → deepseek-NJMOD23E.js} +4 -4
- package/dist/{docker_hub-7CLQL2IJ.js → docker_hub-D2MQCSJ7.js} +4 -4
- package/dist/{dropbox-VFYDVUNK.js → dropbox-MI6U3SOT.js} +4 -4
- package/dist/{dub-QN3EETJY.js → dub-P2BYBYV4.js} +4 -4
- package/dist/{e2b-NKC3Y776.js → e2b-BGKIAE54.js} +4 -4
- package/dist/{exa-KKZ6F25K.js → exa-EUY7VV4C.js} +4 -4
- package/dist/{fal_ai-25LYJD2O.js → fal_ai-AAOG5FYJ.js} +4 -4
- package/dist/{feishu-NRT55B3Q.js → feishu-AN5IJYOW.js} +2 -2
- package/dist/{feishu_custom_bot-3W3XXUG4.js → feishu_custom_bot-BWGMNVXF.js} +3 -3
- package/dist/{figma-4FLP35TC.js → figma-SJSWYCGD.js} +4 -4
- package/dist/{firehydrant-BLXPLZ3X.js → firehydrant-MOXK2PST.js} +4 -4
- package/dist/{fixer-4OR62TGE.js → fixer-ISIDUDZU.js} +4 -4
- package/dist/{fly-MYCKOZZK.js → fly-U4KPSGBF.js} +4 -4
- package/dist/{front-3DFYVMF3.js → front-ZHM2YX2E.js} +4 -4
- package/dist/{geocodio-CXMD6VKA.js → geocodio-SVCCAXAV.js} +4 -4
- package/dist/{ghost-4UUE737W.js → ghost-INTR7FIY.js} +4 -4
- package/dist/{github-WFS2ON2N.js → github-T275VVWR.js} +4 -4
- package/dist/{gmail-KW7FXA7A.js → gmail-KSSPOE67.js} +4 -4
- package/dist/{googlecalendar-AMH5JQMC.js → googlecalendar-JB5RW27L.js} +4 -4
- package/dist/{googledocs-R5VA6CKD.js → googledocs-G522PUGW.js} +3 -3
- package/dist/{grafana-IT6DWIVB.js → grafana-44ZPIV5P.js} +4 -4
- package/dist/{graphhopper-TYFDJZWC.js → graphhopper-2MORXLOW.js} +4 -4
- package/dist/index.d.ts +74 -14
- package/dist/index.js +3 -3
- package/dist/{ip2proxy-GJ4HXVXO.js → ip2proxy-R4K4KEMI.js} +4 -4
- package/dist/{ipgeolocation_io-OAGR6PRF.js → ipgeolocation_io-H4J55OGI.js} +4 -4
- package/dist/{ipqualityscore-BCKAMDGL.js → ipqualityscore-XGID4OHA.js} +4 -4
- package/dist/{jina_ai-N3NXRVIZ.js → jina_ai-7ZVDOR3Z.js} +4 -4
- package/dist/{jira-CHLBKWLO.js → jira-CTIYZTT3.js} +4 -4
- package/dist/{langsmith-OIKOTPY3.js → langsmith-ISP567FS.js} +4 -4
- package/dist/{lemlist-VAKYPKS3.js → lemlist-OGKK272F.js} +4 -4
- package/dist/{linear-Q6QTPJ5R.js → linear-6JX4E3JI.js} +3 -3
- package/dist/{logsnag-SQOQBTES.js → logsnag-BVB36ZCL.js} +4 -4
- package/dist/main.js +102 -102
- package/dist/{mattermost-G2KBEICM.js → mattermost-UECVPZGY.js} +4 -4
- package/dist/{mem0-D2FPLYDX.js → mem0-EUA7S2OO.js} +4 -4
- package/dist/{memos-76PTWV5V.js → memos-BYSBJRWZ.js} +4 -4
- package/dist/{metabase-TWSIDVBM.js → metabase-4JXZH3YG.js} +4 -4
- package/dist/{mistral_ai-TP7TQUEJ.js → mistral_ai-2C5BTBKW.js} +4 -4
- package/dist/{mother_duck-UXY5THYU.js → mother_duck-ZGNNNR3X.js} +4 -4
- package/dist/{ngrok-RZW2ISJK.js → ngrok-NTPGMRXI.js} +4 -4
- package/dist/{notes-B2I6FWK2.js → notes-KGYXX6N6.js} +1 -1
- package/dist/{notion-QOKRUE6M.js → notion-EX3X2G7G.js} +4 -4
- package/dist/{open_exchange_rates-ODLFWEJ7.js → open_exchange_rates-2V2LIGPS.js} +4 -4
- package/dist/{openai-ZPBPPPGT.js → openai-KQFQPK7Z.js} +4 -4
- package/dist/{opengraph_io-EOI5UJKT.js → opengraph_io-B63SEYJY.js} +4 -4
- package/dist/{openrouter-BUWLF3BG.js → openrouter-62A4H62Q.js} +4 -4
- package/dist/{outline-NXS3QRXO.js → outline-QC33XLCK.js} +4 -4
- package/dist/{paddle-T4MSKQ76.js → paddle-HDZG35Y7.js} +4 -4
- package/dist/{perplexity-WW5N2LW7.js → perplexity-M7LOMJWA.js} +4 -4
- package/dist/{pinecone-RY5AZPQM.js → pinecone-Y7YCJRZM.js} +4 -4
- package/dist/{pipedrive-OHYQNZTU.js → pipedrive-FLJA62UX.js} +4 -4
- package/dist/{polar-XZIAHXS5.js → polar-AE55Q425.js} +4 -4
- package/dist/{posthog-DTPTWORY.js → posthog-IAY5D3YW.js} +4 -4
- package/dist/{postmark-F4UCQKC3.js → postmark-NMKAXDXZ.js} +4 -4
- package/dist/{prerender-P3Y2MLIC.js → prerender-CDJM7I7N.js} +4 -4
- package/dist/{pubmed-RHU3GXFB.js → pubmed-N437ZJOR.js} +3 -3
- package/dist/{railway-VB6SRLI6.js → railway-NBWYXCUM.js} +3 -3
- package/dist/{readwise-DIMSKJZR.js → readwise-QRO3UC3R.js} +4 -4
- package/dist/{render-XCCDCEEK.js → render-NEW7SROG.js} +4 -4
- package/dist/{replicate-IUEOQ4JC.js → replicate-73YQLRTY.js} +4 -4
- package/dist/{resend-N5MJZH5E.js → resend-R5BAZFHZ.js} +4 -4
- package/dist/{rootly-25T4NBTU.js → rootly-5BLALPCS.js} +4 -4
- package/dist/{scrapfly-PK4XFXUA.js → scrapfly-EHFLNNUZ.js} +4 -4
- package/dist/{scrapingbee-ITPBMWQU.js → scrapingbee-UXLYS72S.js} +4 -4
- package/dist/{screenshot_fyi-5U6ZYPKR.js → screenshot_fyi-KC3NLTJ6.js} +4 -4
- package/dist/{semantic_scholar-BJGCI2MU.js → semantic_scholar-SA5OIUWA.js} +4 -4
- package/dist/{sentry-63IC263P.js → sentry-BAE3XLJN.js} +4 -4
- package/dist/{shodan-2WBIFHFN.js → shodan-FJR6MKKW.js} +4 -4
- package/dist/{shopify-XLLJRAGM.js → shopify-7OA6SLN5.js} +4 -4
- package/dist/{stripe-WLVCY3UN.js → stripe-MUWDZGGF.js} +3 -3
- package/dist/{supabase-BKNZ35ER.js → supabase-SWWGNWYR.js} +4 -4
- package/dist/{tavily-YFSDEUTL.js → tavily-XQNJ5EDI.js} +4 -4
- package/dist/{telegram-FC35W74G.js → telegram-RR6JPZOG.js} +4 -4
- package/dist/{telnyx-MI32XZUG.js → telnyx-7PKXOWSO.js} +4 -4
- package/dist/{todoist-4ZFJKZC6.js → todoist-63X5IUJE.js} +4 -4
- package/dist/{trello-UOGFNMK4.js → trello-MOG63DAI.js} +4 -4
- package/dist/{turso-YTSTW3ZS.js → turso-ENWTSHDL.js} +4 -4
- package/dist/{twilio-XAJDFUQH.js → twilio-FDXUN7VX.js} +3 -3
- package/dist/{umami-ZVIUBPAI.js → umami-TM7PROWF.js} +4 -4
- package/dist/{unsplash-IU3PLDNS.js → unsplash-NWXLKSOW.js} +4 -4
- package/dist/{upstash_redis-3EYJW7SY.js → upstash_redis-CUTQ5L2N.js} +4 -4
- package/dist/{uptimerobot-PJG2ASRU.js → uptimerobot-NNO32M3I.js} +3 -3
- package/dist/{v2ex-KFQWWGFC.js → v2ex-24KMSWS7.js} +4 -4
- package/dist/{vercel-I2ZDOQRL.js → vercel-B45LPF6I.js} +4 -4
- package/dist/{wolfram_alpha_api-BNXOJMB6.js → wolfram_alpha_api-DKJWVR6G.js} +4 -4
- package/dist/{wordpress-36D7BSMP.js → wordpress-XYFSDZXY.js} +4 -4
- package/dist/{workos-6X4JJRRC.js → workos-BEM7EPHM.js} +4 -4
- package/dist/{zhihu-CIXKCHIU.js → zhihu-CFHZ4NC7.js} +4 -4
- 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) {
|
|
@@ -14873,6 +14873,10 @@ function secretRefsInConfig(config2) {
|
|
|
14873
14873
|
const refs = [];
|
|
14874
14874
|
const oauthMcp = c.kind === "mcp" && c.auth === "oauth";
|
|
14875
14875
|
if (!oauthMcp && typeof c.authRef === "string" && c.authRef.length > 0) refs.push(c.authRef);
|
|
14876
|
+
if (oauthMcp && c.oauthClient !== null && typeof c.oauthClient === "object") {
|
|
14877
|
+
const secretRef = c.oauthClient.clientSecretRef;
|
|
14878
|
+
if (typeof secretRef === "string" && secretRef.length > 0) refs.push(secretRef);
|
|
14879
|
+
}
|
|
14876
14880
|
if (typeof c.skRef === "string" && c.skRef.length > 0) refs.push(c.skRef);
|
|
14877
14881
|
return refs;
|
|
14878
14882
|
}
|
|
@@ -14889,17 +14893,17 @@ function assertSecretRefUse(scopes, config2) {
|
|
|
14889
14893
|
|
|
14890
14894
|
// ../core/src/encoding/base64url.ts
|
|
14891
14895
|
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
14892
|
-
function base64urlEncode(
|
|
14896
|
+
function base64urlEncode(bytes2) {
|
|
14893
14897
|
let out = "";
|
|
14894
|
-
for (let i = 0; i <
|
|
14895
|
-
const a =
|
|
14896
|
-
const b =
|
|
14897
|
-
const c =
|
|
14898
|
+
for (let i = 0; i < bytes2.length; i += 3) {
|
|
14899
|
+
const a = bytes2[i] ?? 0;
|
|
14900
|
+
const b = bytes2[i + 1] ?? 0;
|
|
14901
|
+
const c = bytes2[i + 2] ?? 0;
|
|
14898
14902
|
const n = a << 16 | b << 8 | c;
|
|
14899
14903
|
out += ALPHABET[n >>> 18 & 63];
|
|
14900
14904
|
out += ALPHABET[n >>> 12 & 63];
|
|
14901
|
-
if (i + 1 <
|
|
14902
|
-
if (i + 2 <
|
|
14905
|
+
if (i + 1 < bytes2.length) out += ALPHABET[n >>> 6 & 63];
|
|
14906
|
+
if (i + 2 < bytes2.length) out += ALPHABET[n & 63];
|
|
14903
14907
|
}
|
|
14904
14908
|
return out;
|
|
14905
14909
|
}
|
|
@@ -14907,7 +14911,7 @@ function base64urlDecode(input) {
|
|
|
14907
14911
|
if (input.length % 4 === 1 || !/^[A-Za-z0-9_-]*$/.test(input)) {
|
|
14908
14912
|
throw new Error("base64urlDecode: malformed base64url input");
|
|
14909
14913
|
}
|
|
14910
|
-
const
|
|
14914
|
+
const bytes2 = [];
|
|
14911
14915
|
for (let i = 0; i < input.length; i += 4) {
|
|
14912
14916
|
const chunk = input.slice(i, i + 4);
|
|
14913
14917
|
const a = ALPHABET.indexOf(chunk[0] ?? "");
|
|
@@ -14915,11 +14919,11 @@ function base64urlDecode(input) {
|
|
|
14915
14919
|
const c = chunk.length >= 3 ? ALPHABET.indexOf(chunk[2] ?? "") : 0;
|
|
14916
14920
|
const d = chunk.length >= 4 ? ALPHABET.indexOf(chunk[3] ?? "") : 0;
|
|
14917
14921
|
const n = a << 18 | b << 12 | c << 6 | d;
|
|
14918
|
-
|
|
14919
|
-
if (chunk.length >= 3)
|
|
14920
|
-
if (chunk.length >= 4)
|
|
14922
|
+
bytes2.push(n >>> 16 & 255);
|
|
14923
|
+
if (chunk.length >= 3) bytes2.push(n >>> 8 & 255);
|
|
14924
|
+
if (chunk.length >= 4) bytes2.push(n & 255);
|
|
14921
14925
|
}
|
|
14922
|
-
return new Uint8Array(
|
|
14926
|
+
return new Uint8Array(bytes2);
|
|
14923
14927
|
}
|
|
14924
14928
|
|
|
14925
14929
|
// ../core/src/omit.ts
|
|
@@ -14938,8 +14942,8 @@ async function sha256Hex(text) {
|
|
|
14938
14942
|
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
14939
14943
|
}
|
|
14940
14944
|
function generateSecret() {
|
|
14941
|
-
const
|
|
14942
|
-
return `${SECRET_PREFIX}${base64urlEncode(
|
|
14945
|
+
const bytes2 = crypto.getRandomValues(new Uint8Array(16));
|
|
14946
|
+
return `${SECRET_PREFIX}${base64urlEncode(bytes2)}`;
|
|
14943
14947
|
}
|
|
14944
14948
|
function normalizeExpiresAt(value) {
|
|
14945
14949
|
const parsed = ISO_TIMESTAMP.safeParse(value);
|
|
@@ -14996,10 +15000,10 @@ function clampLimit(limit) {
|
|
|
14996
15000
|
return Math.min(Math.max(1, limit), LIST_LIMIT_MAX);
|
|
14997
15001
|
}
|
|
14998
15002
|
var SKRegistryStore = class {
|
|
15003
|
+
store;
|
|
14999
15004
|
constructor(store) {
|
|
15000
15005
|
this.store = store;
|
|
15001
15006
|
}
|
|
15002
|
-
store;
|
|
15003
15007
|
async recordById(id) {
|
|
15004
15008
|
const hash2 = await this.store.get(KEY_SK_ID + id);
|
|
15005
15009
|
if (typeof hash2 !== "string") return null;
|
|
@@ -15111,6 +15115,7 @@ var OperationRegistry = class {
|
|
|
15111
15115
|
const schema = spec.inputSchema !== void 0 ? normalizeSchema(spec.inputSchema) : void 0;
|
|
15112
15116
|
const toolSpec = { name };
|
|
15113
15117
|
if (spec.description !== void 0) toolSpec.description = spec.description;
|
|
15118
|
+
if (spec.delivery !== void 0) toolSpec.delivery = spec.delivery;
|
|
15114
15119
|
if (spec.effect !== void 0) toolSpec.effect = spec.effect;
|
|
15115
15120
|
if (spec.confirm !== void 0) toolSpec.confirm = spec.confirm;
|
|
15116
15121
|
if (schema !== void 0) toolSpec.inputSchema = toJsonSchema(schema);
|
|
@@ -15589,7 +15594,8 @@ var contextCommands = new HtbpCommandRegistry().register(
|
|
|
15589
15594
|
opts: HTBP_LIST_OPTIONS_SCHEMA.optional()
|
|
15590
15595
|
}),
|
|
15591
15596
|
returns: "Page<ContextEntryMeta>",
|
|
15592
|
-
scope: "read"
|
|
15597
|
+
scope: "read",
|
|
15598
|
+
effect: "read"
|
|
15593
15599
|
},
|
|
15594
15600
|
({ opts, path }, { provider }) => provider.list(path ?? "", opts)
|
|
15595
15601
|
).register(
|
|
@@ -15600,7 +15606,8 @@ var contextCommands = new HtbpCommandRegistry().register(
|
|
|
15600
15606
|
path: external_exports.string().describe("entry path inside the namespace")
|
|
15601
15607
|
}),
|
|
15602
15608
|
returns: "ContextEntry",
|
|
15603
|
-
scope: "read"
|
|
15609
|
+
scope: "read",
|
|
15610
|
+
effect: "read"
|
|
15604
15611
|
},
|
|
15605
15612
|
({ path }, { provider }) => provider.get(path)
|
|
15606
15613
|
).register(
|
|
@@ -15612,7 +15619,8 @@ var contextCommands = new HtbpCommandRegistry().register(
|
|
|
15612
15619
|
entry: entrySchema
|
|
15613
15620
|
}),
|
|
15614
15621
|
returns: "ContextEntryMeta",
|
|
15615
|
-
scope: "write"
|
|
15622
|
+
scope: "write",
|
|
15623
|
+
effect: "write"
|
|
15616
15624
|
},
|
|
15617
15625
|
({ entry, path }, { provider }) => provider.write(path, entry)
|
|
15618
15626
|
).register(
|
|
@@ -15624,7 +15632,8 @@ var contextCommands = new HtbpCommandRegistry().register(
|
|
|
15624
15632
|
patch: patchSchema
|
|
15625
15633
|
}),
|
|
15626
15634
|
returns: "ContextEntryMeta",
|
|
15627
|
-
scope: "write"
|
|
15635
|
+
scope: "write",
|
|
15636
|
+
effect: "write"
|
|
15628
15637
|
},
|
|
15629
15638
|
({ patch, path }, { provider }) => provider.update(path, patch)
|
|
15630
15639
|
).register(
|
|
@@ -15647,7 +15656,8 @@ var contextCommands = new HtbpCommandRegistry().register(
|
|
|
15647
15656
|
opts: searchOptionsSchema.optional()
|
|
15648
15657
|
}),
|
|
15649
15658
|
returns: "Page<ContextEntryMeta>",
|
|
15650
|
-
scope: "read"
|
|
15659
|
+
scope: "read",
|
|
15660
|
+
effect: "read"
|
|
15651
15661
|
},
|
|
15652
15662
|
({ opts, query }, { provider }) => provider.search(query, opts)
|
|
15653
15663
|
).register(
|
|
@@ -16608,6 +16618,661 @@ function createFederationModule(deps) {
|
|
|
16608
16618
|
return COMMANDS5.module(deps);
|
|
16609
16619
|
}
|
|
16610
16620
|
|
|
16621
|
+
// ../core/src/device/mailbox.ts
|
|
16622
|
+
var KEY_DEVICE_OPERATION = "deviceop:";
|
|
16623
|
+
var DEVICE_OPERATION_STATES = [
|
|
16624
|
+
"queued",
|
|
16625
|
+
"claimed",
|
|
16626
|
+
"succeeded",
|
|
16627
|
+
"rejected",
|
|
16628
|
+
"failed",
|
|
16629
|
+
"result_unknown",
|
|
16630
|
+
"cancelled",
|
|
16631
|
+
"expired"
|
|
16632
|
+
];
|
|
16633
|
+
var DEFAULT_TTL_SECONDS = 24 * 60 * 60;
|
|
16634
|
+
var MAX_TTL_SECONDS = 7 * 24 * 60 * 60;
|
|
16635
|
+
var DEFAULT_LEASE_SECONDS = 60;
|
|
16636
|
+
var DEFAULT_MAX_PAYLOAD_BYTES = 256 * 1024;
|
|
16637
|
+
var DEFAULT_MAX_PENDING_PER_DEVICE = 1e3;
|
|
16638
|
+
var DEFAULT_TERMINAL_RETENTION_SECONDS = 7 * 24 * 60 * 60;
|
|
16639
|
+
var MAX_CAS_ATTEMPTS = 8;
|
|
16640
|
+
var LIST_LIMIT_DEFAULT2 = 50;
|
|
16641
|
+
var LIST_LIMIT_MAX2 = 200;
|
|
16642
|
+
var HKDF_SALT = new TextEncoder().encode("tool-bridge:device-mailbox:v1");
|
|
16643
|
+
var HKDF_INFO = new TextEncoder().encode("aes-256-gcm:operation-payload-and-result:v1");
|
|
16644
|
+
function mailboxUnavailable(message) {
|
|
16645
|
+
return new TBError("unavailable", message, { retryable: false });
|
|
16646
|
+
}
|
|
16647
|
+
function finiteTimestamp(value) {
|
|
16648
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
16649
|
+
}
|
|
16650
|
+
function nonEmpty(value) {
|
|
16651
|
+
return typeof value === "string" && value !== "";
|
|
16652
|
+
}
|
|
16653
|
+
function positiveInteger(value, fallback, field) {
|
|
16654
|
+
const actual = value ?? fallback;
|
|
16655
|
+
if (!Number.isSafeInteger(actual) || actual < 1) {
|
|
16656
|
+
throw new TBError("invalid_argument", `${field} must be a positive integer`);
|
|
16657
|
+
}
|
|
16658
|
+
return actual;
|
|
16659
|
+
}
|
|
16660
|
+
function clampLimit3(value) {
|
|
16661
|
+
if (value === void 0) return LIST_LIMIT_DEFAULT2;
|
|
16662
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
16663
|
+
throw new TBError("invalid_argument", "limit must be a positive integer");
|
|
16664
|
+
}
|
|
16665
|
+
return Math.min(value, LIST_LIMIT_MAX2);
|
|
16666
|
+
}
|
|
16667
|
+
function isState(value) {
|
|
16668
|
+
return typeof value === "string" && DEVICE_OPERATION_STATES.includes(value);
|
|
16669
|
+
}
|
|
16670
|
+
function isTerminal(state) {
|
|
16671
|
+
return state !== "queued" && state !== "claimed";
|
|
16672
|
+
}
|
|
16673
|
+
function parseEncrypted(value) {
|
|
16674
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("invalid encrypted value");
|
|
16675
|
+
const raw = value;
|
|
16676
|
+
if (raw.v !== 1 || !nonEmpty(raw.iv) || !nonEmpty(raw.ciphertext)) {
|
|
16677
|
+
throw new Error("invalid encrypted value");
|
|
16678
|
+
}
|
|
16679
|
+
return { v: 1, iv: raw.iv, ciphertext: raw.ciphertext };
|
|
16680
|
+
}
|
|
16681
|
+
function parseRecord(value) {
|
|
16682
|
+
try {
|
|
16683
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error();
|
|
16684
|
+
const raw = value;
|
|
16685
|
+
if (raw.v !== 1 || !Number.isSafeInteger(raw.revision) || raw.revision < 1 || !nonEmpty(raw.operationId) || raw.commandId !== 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();
|
|
16686
|
+
if (raw.leaseId === void 0 !== (raw.leaseUntil === void 0)) throw new Error();
|
|
16687
|
+
if (raw.leaseId !== void 0 && (!nonEmpty(raw.leaseId) || !finiteTimestamp(raw.leaseUntil))) {
|
|
16688
|
+
throw new Error();
|
|
16689
|
+
}
|
|
16690
|
+
if (raw.cancelRequestedAt !== void 0 && !finiteTimestamp(raw.cancelRequestedAt)) throw new Error();
|
|
16691
|
+
if (raw.terminalAt !== void 0 && !finiteTimestamp(raw.terminalAt)) throw new Error();
|
|
16692
|
+
if (isTerminal(raw.state) && raw.terminalAt === void 0) throw new Error();
|
|
16693
|
+
return {
|
|
16694
|
+
v: 1,
|
|
16695
|
+
revision: raw.revision,
|
|
16696
|
+
operationId: raw.operationId,
|
|
16697
|
+
commandId: raw.commandId,
|
|
16698
|
+
deviceId: raw.deviceId,
|
|
16699
|
+
deviceKeyId: raw.deviceKeyId,
|
|
16700
|
+
mountPath: raw.mountPath,
|
|
16701
|
+
targetPath: raw.targetPath,
|
|
16702
|
+
path: raw.path,
|
|
16703
|
+
callerKeyId: raw.callerKeyId,
|
|
16704
|
+
callerOwner: raw.callerOwner,
|
|
16705
|
+
traceId: raw.traceId,
|
|
16706
|
+
createdAt: raw.createdAt,
|
|
16707
|
+
updatedAt: raw.updatedAt,
|
|
16708
|
+
expiresAt: raw.expiresAt,
|
|
16709
|
+
state: raw.state,
|
|
16710
|
+
attempt: raw.attempt,
|
|
16711
|
+
executionMayHaveOccurred: raw.executionMayHaveOccurred,
|
|
16712
|
+
payload: parseEncrypted(raw.payload),
|
|
16713
|
+
...raw.leaseId === void 0 ? {} : { leaseId: raw.leaseId, leaseUntil: raw.leaseUntil },
|
|
16714
|
+
...raw.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: raw.cancelRequestedAt },
|
|
16715
|
+
...raw.completedLeaseId === void 0 ? {} : { completedLeaseId: String(raw.completedLeaseId) },
|
|
16716
|
+
...raw.terminalAt === void 0 ? {} : { terminalAt: raw.terminalAt },
|
|
16717
|
+
...raw.terminalData === void 0 ? {} : { terminalData: parseEncrypted(raw.terminalData) },
|
|
16718
|
+
...raw.idempotencyFingerprint === void 0 ? {} : { idempotencyFingerprint: String(raw.idempotencyFingerprint) }
|
|
16719
|
+
};
|
|
16720
|
+
} catch {
|
|
16721
|
+
throw new TBError("internal", "device mailbox record is invalid", { retryable: false });
|
|
16722
|
+
}
|
|
16723
|
+
}
|
|
16724
|
+
function canonicalJson(value) {
|
|
16725
|
+
if (value === null) return "null";
|
|
16726
|
+
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
16727
|
+
if (typeof value === "number") {
|
|
16728
|
+
if (!Number.isFinite(value)) throw new TBError("invalid_argument", "mailbox data must be valid JSON");
|
|
16729
|
+
return JSON.stringify(value);
|
|
16730
|
+
}
|
|
16731
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
16732
|
+
if (typeof value !== "object") {
|
|
16733
|
+
throw new TBError("invalid_argument", "mailbox data must be valid JSON");
|
|
16734
|
+
}
|
|
16735
|
+
const object2 = value;
|
|
16736
|
+
const keys = Object.keys(object2).sort();
|
|
16737
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(object2[key])}`).join(",")}}`;
|
|
16738
|
+
}
|
|
16739
|
+
function safeError(value) {
|
|
16740
|
+
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");
|
|
16741
|
+
return { code: value.code, message: value.message, retryable: value.retryable };
|
|
16742
|
+
}
|
|
16743
|
+
function normalizeCompletion(value) {
|
|
16744
|
+
if (value.outcome === "succeeded") {
|
|
16745
|
+
canonicalJson(value.result);
|
|
16746
|
+
return { outcome: "succeeded", result: value.result };
|
|
16747
|
+
}
|
|
16748
|
+
if (value.outcome === "rejected" || value.outcome === "failed") {
|
|
16749
|
+
return { outcome: value.outcome, error: safeError(value.error) };
|
|
16750
|
+
}
|
|
16751
|
+
return {
|
|
16752
|
+
outcome: "result_unknown",
|
|
16753
|
+
...value.error === void 0 ? {} : { error: safeError(value.error) }
|
|
16754
|
+
};
|
|
16755
|
+
}
|
|
16756
|
+
function bytes(value) {
|
|
16757
|
+
return new TextEncoder().encode(value).byteLength;
|
|
16758
|
+
}
|
|
16759
|
+
function randomId(randomBytes) {
|
|
16760
|
+
const value = randomBytes(18);
|
|
16761
|
+
if (!(value instanceof Uint8Array) || value.length !== 18) {
|
|
16762
|
+
throw mailboxUnavailable("device mailbox randomness is unavailable");
|
|
16763
|
+
}
|
|
16764
|
+
return `dop_${base64urlEncode(value)}`;
|
|
16765
|
+
}
|
|
16766
|
+
async function digestBytes(value) {
|
|
16767
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)));
|
|
16768
|
+
}
|
|
16769
|
+
async function deterministicOperationId(binding) {
|
|
16770
|
+
return `dop_${base64urlEncode((await digestBytes(binding)).slice(0, 18))}`;
|
|
16771
|
+
}
|
|
16772
|
+
async function devicePrefix(deviceId) {
|
|
16773
|
+
return `${KEY_DEVICE_OPERATION}${await sha256Hex(deviceId)}:`;
|
|
16774
|
+
}
|
|
16775
|
+
async function stateKey(deviceId, operationId) {
|
|
16776
|
+
if (!/^dop_[A-Za-z0-9_-]{24}$/.test(operationId)) {
|
|
16777
|
+
throw new TBError("invalid_argument", "operationId is invalid");
|
|
16778
|
+
}
|
|
16779
|
+
return await devicePrefix(deviceId) + operationId;
|
|
16780
|
+
}
|
|
16781
|
+
var DeviceMailboxCipher = class {
|
|
16782
|
+
root;
|
|
16783
|
+
keyPromise;
|
|
16784
|
+
constructor(masterKey) {
|
|
16785
|
+
let decoded;
|
|
16786
|
+
try {
|
|
16787
|
+
decoded = base64urlDecode(masterKey);
|
|
16788
|
+
} catch {
|
|
16789
|
+
throw mailboxUnavailable("device mailbox encryption key is invalid");
|
|
16790
|
+
}
|
|
16791
|
+
if (decoded.length !== 32) {
|
|
16792
|
+
throw mailboxUnavailable("device mailbox encryption key is invalid");
|
|
16793
|
+
}
|
|
16794
|
+
this.root = decoded;
|
|
16795
|
+
}
|
|
16796
|
+
key() {
|
|
16797
|
+
this.keyPromise ??= (async () => {
|
|
16798
|
+
const rootKey = await crypto.subtle.importKey("raw", this.root, "HKDF", false, ["deriveKey"]);
|
|
16799
|
+
return await crypto.subtle.deriveKey(
|
|
16800
|
+
{ name: "HKDF", hash: "SHA-256", salt: HKDF_SALT, info: HKDF_INFO },
|
|
16801
|
+
rootKey,
|
|
16802
|
+
{ name: "AES-GCM", length: 256 },
|
|
16803
|
+
false,
|
|
16804
|
+
["encrypt", "decrypt"]
|
|
16805
|
+
);
|
|
16806
|
+
})();
|
|
16807
|
+
return this.keyPromise;
|
|
16808
|
+
}
|
|
16809
|
+
async encrypt(operationId, field, value) {
|
|
16810
|
+
const plaintext = canonicalJson(value);
|
|
16811
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
16812
|
+
const additionalData = new TextEncoder().encode(`tool-bridge:device-mailbox:v1:${operationId}:${field}`);
|
|
16813
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
16814
|
+
{ name: "AES-GCM", iv, additionalData },
|
|
16815
|
+
await this.key(),
|
|
16816
|
+
new TextEncoder().encode(plaintext)
|
|
16817
|
+
);
|
|
16818
|
+
return {
|
|
16819
|
+
v: 1,
|
|
16820
|
+
iv: base64urlEncode(iv),
|
|
16821
|
+
ciphertext: base64urlEncode(new Uint8Array(ciphertext))
|
|
16822
|
+
};
|
|
16823
|
+
}
|
|
16824
|
+
async decrypt(operationId, field, value) {
|
|
16825
|
+
try {
|
|
16826
|
+
const iv = base64urlDecode(value.iv);
|
|
16827
|
+
const ciphertext = base64urlDecode(value.ciphertext);
|
|
16828
|
+
if (iv.length !== 12) throw new Error("invalid iv");
|
|
16829
|
+
const additionalData = new TextEncoder().encode(`tool-bridge:device-mailbox:v1:${operationId}:${field}`);
|
|
16830
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
16831
|
+
{ name: "AES-GCM", iv, additionalData },
|
|
16832
|
+
await this.key(),
|
|
16833
|
+
ciphertext
|
|
16834
|
+
);
|
|
16835
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
16836
|
+
} catch {
|
|
16837
|
+
throw new TBError("internal", "device mailbox data cannot be decrypted", { retryable: false });
|
|
16838
|
+
}
|
|
16839
|
+
}
|
|
16840
|
+
};
|
|
16841
|
+
var DeviceMailboxService = class {
|
|
16842
|
+
cas;
|
|
16843
|
+
cipher;
|
|
16844
|
+
defaultTtlSeconds;
|
|
16845
|
+
leaseSeconds;
|
|
16846
|
+
maxPayloadBytes;
|
|
16847
|
+
maxPendingPerDevice;
|
|
16848
|
+
maxTtlSeconds;
|
|
16849
|
+
now;
|
|
16850
|
+
randomBytes;
|
|
16851
|
+
state;
|
|
16852
|
+
terminalRetentionSeconds;
|
|
16853
|
+
constructor(state, encryptionRoot, opts = {}) {
|
|
16854
|
+
if (state.compareAndSwap === void 0) {
|
|
16855
|
+
throw mailboxUnavailable("device mailbox requires StateStore.compareAndSwap");
|
|
16856
|
+
}
|
|
16857
|
+
if (encryptionRoot === void 0) {
|
|
16858
|
+
throw mailboxUnavailable("device mailbox requires TB_SECRET_ENCRYPTION_KEY");
|
|
16859
|
+
}
|
|
16860
|
+
this.cas = state.compareAndSwap.bind(state);
|
|
16861
|
+
this.state = state;
|
|
16862
|
+
this.cipher = new DeviceMailboxCipher(encryptionRoot);
|
|
16863
|
+
this.defaultTtlSeconds = positiveInteger(
|
|
16864
|
+
opts.defaultTtlSeconds,
|
|
16865
|
+
DEFAULT_TTL_SECONDS,
|
|
16866
|
+
"defaultTtlSeconds"
|
|
16867
|
+
);
|
|
16868
|
+
this.maxTtlSeconds = positiveInteger(opts.maxTtlSeconds, MAX_TTL_SECONDS, "maxTtlSeconds");
|
|
16869
|
+
if (this.defaultTtlSeconds > this.maxTtlSeconds) {
|
|
16870
|
+
throw new TBError("invalid_argument", "defaultTtlSeconds cannot exceed maxTtlSeconds");
|
|
16871
|
+
}
|
|
16872
|
+
this.leaseSeconds = positiveInteger(opts.leaseSeconds, DEFAULT_LEASE_SECONDS, "leaseSeconds");
|
|
16873
|
+
this.maxPayloadBytes = positiveInteger(
|
|
16874
|
+
opts.maxPayloadBytes,
|
|
16875
|
+
DEFAULT_MAX_PAYLOAD_BYTES,
|
|
16876
|
+
"maxPayloadBytes"
|
|
16877
|
+
);
|
|
16878
|
+
this.maxPendingPerDevice = positiveInteger(
|
|
16879
|
+
opts.maxPendingPerDevice,
|
|
16880
|
+
DEFAULT_MAX_PENDING_PER_DEVICE,
|
|
16881
|
+
"maxPendingPerDevice"
|
|
16882
|
+
);
|
|
16883
|
+
this.terminalRetentionSeconds = positiveInteger(
|
|
16884
|
+
opts.terminalRetentionSeconds,
|
|
16885
|
+
DEFAULT_TERMINAL_RETENTION_SECONDS,
|
|
16886
|
+
"terminalRetentionSeconds"
|
|
16887
|
+
);
|
|
16888
|
+
this.now = opts.now ?? (() => Date.now());
|
|
16889
|
+
this.randomBytes = opts.randomBytes ?? ((length) => crypto.getRandomValues(new Uint8Array(length)));
|
|
16890
|
+
}
|
|
16891
|
+
nowIso() {
|
|
16892
|
+
return new Date(this.now()).toISOString();
|
|
16893
|
+
}
|
|
16894
|
+
async read(deviceId, operationId) {
|
|
16895
|
+
const key = await stateKey(deviceId, operationId);
|
|
16896
|
+
const value = await this.state.get(key);
|
|
16897
|
+
if (value === null) throw TBError.notFound("device operation not found");
|
|
16898
|
+
const record2 = parseRecord(value);
|
|
16899
|
+
if (record2.deviceId !== deviceId || record2.operationId !== operationId) {
|
|
16900
|
+
throw new TBError("internal", "device mailbox record identity mismatch", { retryable: false });
|
|
16901
|
+
}
|
|
16902
|
+
return { key, record: record2 };
|
|
16903
|
+
}
|
|
16904
|
+
summary(record2) {
|
|
16905
|
+
return {
|
|
16906
|
+
operationId: record2.operationId,
|
|
16907
|
+
commandId: record2.commandId,
|
|
16908
|
+
deviceId: record2.deviceId,
|
|
16909
|
+
mountPath: record2.mountPath,
|
|
16910
|
+
targetPath: record2.targetPath,
|
|
16911
|
+
caller: { keyId: record2.callerKeyId, owner: record2.callerOwner },
|
|
16912
|
+
traceId: record2.traceId,
|
|
16913
|
+
createdAt: record2.createdAt,
|
|
16914
|
+
updatedAt: record2.updatedAt,
|
|
16915
|
+
expiresAt: record2.expiresAt,
|
|
16916
|
+
state: record2.state,
|
|
16917
|
+
attempt: record2.attempt,
|
|
16918
|
+
executionMayHaveOccurred: record2.executionMayHaveOccurred,
|
|
16919
|
+
...record2.leaseUntil === void 0 ? {} : { leaseUntil: record2.leaseUntil },
|
|
16920
|
+
...record2.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: record2.cancelRequestedAt },
|
|
16921
|
+
...record2.terminalAt === void 0 ? {} : { terminalAt: record2.terminalAt }
|
|
16922
|
+
};
|
|
16923
|
+
}
|
|
16924
|
+
async detail(record2) {
|
|
16925
|
+
const summary = this.summary(record2);
|
|
16926
|
+
if (record2.terminalData === void 0) return summary;
|
|
16927
|
+
const terminal = await this.cipher.decrypt(
|
|
16928
|
+
record2.operationId,
|
|
16929
|
+
"terminal",
|
|
16930
|
+
record2.terminalData
|
|
16931
|
+
);
|
|
16932
|
+
if (record2.state === "succeeded") return { ...summary, result: terminal.result };
|
|
16933
|
+
const error51 = terminal.error;
|
|
16934
|
+
if (error51 === void 0) return summary;
|
|
16935
|
+
return { ...summary, error: safeError(error51) };
|
|
16936
|
+
}
|
|
16937
|
+
async expireRecord(key, record2) {
|
|
16938
|
+
let current = record2;
|
|
16939
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
16940
|
+
if (isTerminal(current.state) || Date.parse(current.expiresAt) > this.now()) return current;
|
|
16941
|
+
const now = this.nowIso();
|
|
16942
|
+
const next = {
|
|
16943
|
+
...current,
|
|
16944
|
+
revision: current.revision + 1,
|
|
16945
|
+
state: "expired",
|
|
16946
|
+
updatedAt: now,
|
|
16947
|
+
terminalAt: now,
|
|
16948
|
+
executionMayHaveOccurred: current.state === "claimed"
|
|
16949
|
+
};
|
|
16950
|
+
if (await this.cas(key, current.revision, next)) return next;
|
|
16951
|
+
const winner = await this.state.get(key);
|
|
16952
|
+
if (winner === null) throw TBError.notFound("device operation not found");
|
|
16953
|
+
current = parseRecord(winner);
|
|
16954
|
+
}
|
|
16955
|
+
throw new TBError("unavailable", "device operation expiry is busy", { retryable: true });
|
|
16956
|
+
}
|
|
16957
|
+
async enforcePendingCap(deviceId) {
|
|
16958
|
+
const prefix = await devicePrefix(deviceId);
|
|
16959
|
+
let cursor;
|
|
16960
|
+
let pending = 0;
|
|
16961
|
+
do {
|
|
16962
|
+
const page = await this.state.list(prefix, {
|
|
16963
|
+
limit: LIST_LIMIT_MAX2,
|
|
16964
|
+
...cursor === void 0 ? {} : { cursor }
|
|
16965
|
+
});
|
|
16966
|
+
for (const item of page.items) {
|
|
16967
|
+
let record2 = parseRecord(item.value);
|
|
16968
|
+
if (record2.deviceId !== deviceId) continue;
|
|
16969
|
+
record2 = await this.expireRecord(item.key, record2);
|
|
16970
|
+
if (!isTerminal(record2.state)) pending++;
|
|
16971
|
+
if (pending >= this.maxPendingPerDevice) {
|
|
16972
|
+
throw new TBError("rate_limited", "device mailbox pending limit reached", {
|
|
16973
|
+
retryable: true
|
|
16974
|
+
});
|
|
16975
|
+
}
|
|
16976
|
+
}
|
|
16977
|
+
cursor = page.cursor;
|
|
16978
|
+
} while (cursor !== void 0);
|
|
16979
|
+
}
|
|
16980
|
+
async enqueue(input) {
|
|
16981
|
+
const ttlSeconds = input.ttlSeconds ?? this.defaultTtlSeconds;
|
|
16982
|
+
if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds < 1 || ttlSeconds > this.maxTtlSeconds) {
|
|
16983
|
+
throw new TBError("invalid_argument", `ttlSeconds must be between 1 and ${this.maxTtlSeconds}`);
|
|
16984
|
+
}
|
|
16985
|
+
const payloadText = canonicalJson({ arguments: input.arguments });
|
|
16986
|
+
if (bytes(payloadText) > this.maxPayloadBytes) {
|
|
16987
|
+
throw new TBError("rate_limited", "device mailbox payload is too large", { retryable: false });
|
|
16988
|
+
}
|
|
16989
|
+
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");
|
|
16990
|
+
const binding = input.idempotencyKey === void 0 ? void 0 : canonicalJson({
|
|
16991
|
+
callerOwner: input.caller.owner,
|
|
16992
|
+
deviceId: input.deviceId,
|
|
16993
|
+
targetPath: input.targetPath,
|
|
16994
|
+
idempotencyKey: input.idempotencyKey
|
|
16995
|
+
});
|
|
16996
|
+
const operationId = binding === void 0 ? randomId(this.randomBytes) : await deterministicOperationId(binding);
|
|
16997
|
+
const fingerprint = binding === void 0 ? void 0 : await sha256Hex(canonicalJson({
|
|
16998
|
+
arguments: input.arguments,
|
|
16999
|
+
deviceKeyId: input.deviceKeyId,
|
|
17000
|
+
path: input.path,
|
|
17001
|
+
ttlSeconds
|
|
17002
|
+
}));
|
|
17003
|
+
const key = await stateKey(input.deviceId, operationId);
|
|
17004
|
+
if (binding !== void 0) {
|
|
17005
|
+
const existing2 = await this.state.get(key);
|
|
17006
|
+
if (existing2 !== null) {
|
|
17007
|
+
const record3 = parseRecord(existing2);
|
|
17008
|
+
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");
|
|
17009
|
+
return await this.detail(record3);
|
|
17010
|
+
}
|
|
17011
|
+
}
|
|
17012
|
+
await this.enforcePendingCap(input.deviceId);
|
|
17013
|
+
const now = this.nowIso();
|
|
17014
|
+
const record2 = {
|
|
17015
|
+
v: 1,
|
|
17016
|
+
revision: 1,
|
|
17017
|
+
operationId,
|
|
17018
|
+
commandId: operationId,
|
|
17019
|
+
deviceId: input.deviceId,
|
|
17020
|
+
deviceKeyId: input.deviceKeyId,
|
|
17021
|
+
mountPath: input.mountPath,
|
|
17022
|
+
targetPath: input.targetPath,
|
|
17023
|
+
path: input.path,
|
|
17024
|
+
callerKeyId: input.caller.keyId,
|
|
17025
|
+
callerOwner: input.caller.owner,
|
|
17026
|
+
traceId: input.traceId,
|
|
17027
|
+
createdAt: now,
|
|
17028
|
+
updatedAt: now,
|
|
17029
|
+
expiresAt: new Date(this.now() + ttlSeconds * 1e3).toISOString(),
|
|
17030
|
+
state: "queued",
|
|
17031
|
+
attempt: 0,
|
|
17032
|
+
executionMayHaveOccurred: false,
|
|
17033
|
+
payload: await this.cipher.encrypt(operationId, "payload", { arguments: input.arguments }),
|
|
17034
|
+
...fingerprint === void 0 ? {} : { idempotencyFingerprint: fingerprint }
|
|
17035
|
+
};
|
|
17036
|
+
if (await this.cas(key, null, record2)) return await this.detail(record2);
|
|
17037
|
+
if (binding === void 0) {
|
|
17038
|
+
throw new TBError("conflict", "device operation id collision", { retryable: true });
|
|
17039
|
+
}
|
|
17040
|
+
const winner = await this.state.get(key);
|
|
17041
|
+
if (winner === null) throw new TBError("conflict", "device operation enqueue conflicted");
|
|
17042
|
+
const existing = parseRecord(winner);
|
|
17043
|
+
if (existing.idempotencyFingerprint !== fingerprint) {
|
|
17044
|
+
throw new TBError("conflict", "idempotency key is bound to another operation");
|
|
17045
|
+
}
|
|
17046
|
+
return await this.detail(existing);
|
|
17047
|
+
}
|
|
17048
|
+
async get(deviceId, operationId) {
|
|
17049
|
+
const { key, record: record2 } = await this.read(deviceId, operationId);
|
|
17050
|
+
return await this.detail(await this.expireRecord(key, record2));
|
|
17051
|
+
}
|
|
17052
|
+
async list(input) {
|
|
17053
|
+
const limit = clampLimit3(input.limit);
|
|
17054
|
+
const states = input.states === void 0 ? void 0 : new Set(input.states);
|
|
17055
|
+
if (states !== void 0 && [...states].some((state) => !isState(state))) {
|
|
17056
|
+
throw new TBError("invalid_argument", "states contains an invalid operation state");
|
|
17057
|
+
}
|
|
17058
|
+
const page = await this.state.list(await devicePrefix(input.deviceId), {
|
|
17059
|
+
limit,
|
|
17060
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
17061
|
+
});
|
|
17062
|
+
const items = [];
|
|
17063
|
+
for (const item of page.items) {
|
|
17064
|
+
let record2 = parseRecord(item.value);
|
|
17065
|
+
if (record2.deviceId !== input.deviceId) continue;
|
|
17066
|
+
record2 = await this.expireRecord(item.key, record2);
|
|
17067
|
+
if (states !== void 0 && !states.has(record2.state)) continue;
|
|
17068
|
+
items.push(this.summary(record2));
|
|
17069
|
+
}
|
|
17070
|
+
return { items, ...page.cursor === void 0 ? {} : { cursor: page.cursor } };
|
|
17071
|
+
}
|
|
17072
|
+
async claim(input) {
|
|
17073
|
+
const serverNow = this.nowIso();
|
|
17074
|
+
const page = await this.state.list(await devicePrefix(input.deviceId), {
|
|
17075
|
+
limit: clampLimit3(input.limit),
|
|
17076
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
17077
|
+
});
|
|
17078
|
+
for (const item of page.items) {
|
|
17079
|
+
let record2 = parseRecord(item.value);
|
|
17080
|
+
if (record2.deviceId !== input.deviceId || record2.deviceKeyId !== input.deviceKeyId) continue;
|
|
17081
|
+
record2 = await this.expireRecord(item.key, record2);
|
|
17082
|
+
const reclaimable = record2.state === "claimed" && record2.leaseUntil !== void 0 && Date.parse(record2.leaseUntil) <= this.now();
|
|
17083
|
+
if (record2.state !== "queued" && !reclaimable) continue;
|
|
17084
|
+
await input.authorize?.({
|
|
17085
|
+
deviceId: record2.deviceId,
|
|
17086
|
+
deviceKeyId: record2.deviceKeyId,
|
|
17087
|
+
mountPath: record2.mountPath,
|
|
17088
|
+
targetPath: record2.targetPath
|
|
17089
|
+
});
|
|
17090
|
+
const leaseId = randomId(this.randomBytes);
|
|
17091
|
+
const leaseUntil = new Date(Math.min(
|
|
17092
|
+
this.now() + this.leaseSeconds * 1e3,
|
|
17093
|
+
Date.parse(record2.expiresAt)
|
|
17094
|
+
)).toISOString();
|
|
17095
|
+
const next = {
|
|
17096
|
+
...record2,
|
|
17097
|
+
revision: record2.revision + 1,
|
|
17098
|
+
state: "claimed",
|
|
17099
|
+
attempt: record2.attempt + 1,
|
|
17100
|
+
leaseId,
|
|
17101
|
+
leaseUntil,
|
|
17102
|
+
updatedAt: serverNow
|
|
17103
|
+
};
|
|
17104
|
+
if (!await this.cas(item.key, record2.revision, next)) continue;
|
|
17105
|
+
const payload = await this.cipher.decrypt(
|
|
17106
|
+
record2.operationId,
|
|
17107
|
+
"payload",
|
|
17108
|
+
record2.payload
|
|
17109
|
+
);
|
|
17110
|
+
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 });
|
|
17111
|
+
return {
|
|
17112
|
+
serverNow,
|
|
17113
|
+
...page.cursor === void 0 ? {} : { cursor: page.cursor },
|
|
17114
|
+
operation: {
|
|
17115
|
+
operationId: next.operationId,
|
|
17116
|
+
commandId: next.commandId,
|
|
17117
|
+
targetPath: next.targetPath,
|
|
17118
|
+
path: next.path,
|
|
17119
|
+
arguments: payload.arguments,
|
|
17120
|
+
caller: { keyId: next.callerKeyId, owner: next.callerOwner },
|
|
17121
|
+
traceId: next.traceId,
|
|
17122
|
+
createdAt: next.createdAt,
|
|
17123
|
+
expiresAt: next.expiresAt,
|
|
17124
|
+
attempt: next.attempt,
|
|
17125
|
+
leaseId,
|
|
17126
|
+
leaseUntil,
|
|
17127
|
+
...next.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: next.cancelRequestedAt }
|
|
17128
|
+
}
|
|
17129
|
+
};
|
|
17130
|
+
}
|
|
17131
|
+
return {
|
|
17132
|
+
serverNow,
|
|
17133
|
+
...page.cursor === void 0 ? {} : { cursor: page.cursor }
|
|
17134
|
+
};
|
|
17135
|
+
}
|
|
17136
|
+
async activeLease(input) {
|
|
17137
|
+
const current = await this.read(input.deviceId, input.operationId);
|
|
17138
|
+
const record2 = await this.expireRecord(current.key, current.record);
|
|
17139
|
+
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");
|
|
17140
|
+
await input.authorize?.({
|
|
17141
|
+
deviceId: record2.deviceId,
|
|
17142
|
+
deviceKeyId: record2.deviceKeyId,
|
|
17143
|
+
mountPath: record2.mountPath,
|
|
17144
|
+
targetPath: record2.targetPath
|
|
17145
|
+
});
|
|
17146
|
+
return { key: current.key, record: record2 };
|
|
17147
|
+
}
|
|
17148
|
+
async renew(input) {
|
|
17149
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
17150
|
+
const { key, record: record2 } = await this.activeLease(input);
|
|
17151
|
+
const serverNow = this.nowIso();
|
|
17152
|
+
const leaseUntil = new Date(Math.min(
|
|
17153
|
+
this.now() + this.leaseSeconds * 1e3,
|
|
17154
|
+
Date.parse(record2.expiresAt)
|
|
17155
|
+
)).toISOString();
|
|
17156
|
+
const next = {
|
|
17157
|
+
...record2,
|
|
17158
|
+
revision: record2.revision + 1,
|
|
17159
|
+
leaseUntil,
|
|
17160
|
+
updatedAt: serverNow
|
|
17161
|
+
};
|
|
17162
|
+
if (await this.cas(key, record2.revision, next)) {
|
|
17163
|
+
return {
|
|
17164
|
+
serverNow,
|
|
17165
|
+
leaseUntil,
|
|
17166
|
+
...next.cancelRequestedAt === void 0 ? {} : { cancelRequestedAt: next.cancelRequestedAt }
|
|
17167
|
+
};
|
|
17168
|
+
}
|
|
17169
|
+
}
|
|
17170
|
+
throw new TBError("unavailable", "device operation lease is busy", { retryable: true });
|
|
17171
|
+
}
|
|
17172
|
+
async complete(input, completionInput) {
|
|
17173
|
+
const completion = normalizeCompletion(completionInput);
|
|
17174
|
+
const terminalData = completion.outcome === "succeeded" ? { result: completion.result } : completion.error === void 0 ? {} : { error: completion.error };
|
|
17175
|
+
if (bytes(canonicalJson(terminalData)) > this.maxPayloadBytes) {
|
|
17176
|
+
throw new TBError("rate_limited", "device mailbox terminal payload is too large", {
|
|
17177
|
+
retryable: false
|
|
17178
|
+
});
|
|
17179
|
+
}
|
|
17180
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
17181
|
+
const current = await this.read(input.deviceId, input.operationId);
|
|
17182
|
+
let record2 = await this.expireRecord(current.key, current.record);
|
|
17183
|
+
if (record2.deviceKeyId !== input.deviceKeyId) {
|
|
17184
|
+
throw new TBError("conflict", "device operation credential does not match");
|
|
17185
|
+
}
|
|
17186
|
+
await input.authorize?.({
|
|
17187
|
+
deviceId: record2.deviceId,
|
|
17188
|
+
deviceKeyId: record2.deviceKeyId,
|
|
17189
|
+
mountPath: record2.mountPath,
|
|
17190
|
+
targetPath: record2.targetPath
|
|
17191
|
+
});
|
|
17192
|
+
if (isTerminal(record2.state)) {
|
|
17193
|
+
if (record2.completedLeaseId !== input.leaseId || record2.state !== completion.outcome || record2.terminalData === void 0) throw new TBError("conflict", "device operation is already terminal");
|
|
17194
|
+
const existing = await this.cipher.decrypt(
|
|
17195
|
+
record2.operationId,
|
|
17196
|
+
"terminal",
|
|
17197
|
+
record2.terminalData
|
|
17198
|
+
);
|
|
17199
|
+
const requested = completion.outcome === "succeeded" ? { result: completion.result } : completion.error === void 0 ? {} : { error: completion.error };
|
|
17200
|
+
if (canonicalJson(existing) !== canonicalJson(requested)) {
|
|
17201
|
+
throw new TBError("conflict", "device operation completion differs from terminal result");
|
|
17202
|
+
}
|
|
17203
|
+
return await this.detail(record2);
|
|
17204
|
+
}
|
|
17205
|
+
const active = await this.activeLease(input);
|
|
17206
|
+
record2 = active.record;
|
|
17207
|
+
const now = this.nowIso();
|
|
17208
|
+
const next = {
|
|
17209
|
+
...record2,
|
|
17210
|
+
revision: record2.revision + 1,
|
|
17211
|
+
state: completion.outcome,
|
|
17212
|
+
terminalAt: now,
|
|
17213
|
+
terminalData: await this.cipher.encrypt(record2.operationId, "terminal", terminalData),
|
|
17214
|
+
completedLeaseId: input.leaseId,
|
|
17215
|
+
updatedAt: now
|
|
17216
|
+
};
|
|
17217
|
+
if (await this.cas(active.key, record2.revision, next)) return await this.detail(next);
|
|
17218
|
+
}
|
|
17219
|
+
throw new TBError("unavailable", "device operation completion is busy", { retryable: true });
|
|
17220
|
+
}
|
|
17221
|
+
async cancel(deviceId, operationId) {
|
|
17222
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
|
|
17223
|
+
const current = await this.read(deviceId, operationId);
|
|
17224
|
+
const record2 = await this.expireRecord(current.key, current.record);
|
|
17225
|
+
if (isTerminal(record2.state) || record2.cancelRequestedAt !== void 0) {
|
|
17226
|
+
return await this.detail(record2);
|
|
17227
|
+
}
|
|
17228
|
+
const now = this.nowIso();
|
|
17229
|
+
const next = record2.state === "queued" ? {
|
|
17230
|
+
...record2,
|
|
17231
|
+
revision: record2.revision + 1,
|
|
17232
|
+
state: "cancelled",
|
|
17233
|
+
cancelRequestedAt: now,
|
|
17234
|
+
terminalAt: now,
|
|
17235
|
+
updatedAt: now
|
|
17236
|
+
} : {
|
|
17237
|
+
...record2,
|
|
17238
|
+
revision: record2.revision + 1,
|
|
17239
|
+
cancelRequestedAt: now,
|
|
17240
|
+
updatedAt: now
|
|
17241
|
+
};
|
|
17242
|
+
if (await this.cas(current.key, record2.revision, next)) return await this.detail(next);
|
|
17243
|
+
}
|
|
17244
|
+
throw new TBError("unavailable", "device operation cancellation is busy", { retryable: true });
|
|
17245
|
+
}
|
|
17246
|
+
async cleanup(opts = {}) {
|
|
17247
|
+
const page = await this.state.list(KEY_DEVICE_OPERATION, {
|
|
17248
|
+
limit: clampLimit3(opts.limit),
|
|
17249
|
+
...opts.cursor === void 0 ? {} : { cursor: opts.cursor }
|
|
17250
|
+
});
|
|
17251
|
+
let deleted = 0;
|
|
17252
|
+
let expired = 0;
|
|
17253
|
+
for (const item of page.items) {
|
|
17254
|
+
let record2;
|
|
17255
|
+
try {
|
|
17256
|
+
record2 = parseRecord(item.value);
|
|
17257
|
+
} catch {
|
|
17258
|
+
continue;
|
|
17259
|
+
}
|
|
17260
|
+
if (!isTerminal(record2.state) && Date.parse(record2.expiresAt) <= this.now()) {
|
|
17261
|
+
const next = await this.expireRecord(item.key, record2);
|
|
17262
|
+
if (next.state === "expired") expired++;
|
|
17263
|
+
record2 = next;
|
|
17264
|
+
}
|
|
17265
|
+
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++;
|
|
17266
|
+
}
|
|
17267
|
+
return {
|
|
17268
|
+
scanned: page.items.length,
|
|
17269
|
+
expired,
|
|
17270
|
+
deleted,
|
|
17271
|
+
...page.cursor === void 0 ? {} : { cursor: page.cursor }
|
|
17272
|
+
};
|
|
17273
|
+
}
|
|
17274
|
+
};
|
|
17275
|
+
|
|
16611
17276
|
// ../core/src/protocol/errorWire.ts
|
|
16612
17277
|
var tbErrorCodeSchema = external_exports.enum(TB_ERROR_CODES);
|
|
16613
17278
|
var tbErrorBodySchema = external_exports.object({
|
|
@@ -16620,7 +17285,7 @@ var tbErrorBodySchema = external_exports.object({
|
|
|
16620
17285
|
var actionSchema = external_exports.enum(ACTIONS);
|
|
16621
17286
|
var nodeKindSchema = external_exports.enum(NODE_KINDS);
|
|
16622
17287
|
var nodeInputConfigSchema = external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe(
|
|
16623
|
-
"kind-specific config, e.g. mcp: { url, auth?, authRef? } / http: { endpoint, tools } / context: { provider, bucket, \u2026 } / remote: { baseUrl, skRef }; credentials go by
|
|
17288
|
+
"kind-specific config, e.g. mcp: { url, auth?, authRef?, oauthClient?: { clientId, clientSecretRef? } } / http: { endpoint, tools } / context: { provider, bucket, \u2026 } / remote: { baseUrl, skRef }; credentials go by SecretStore reference name, never inline"
|
|
16624
17289
|
);
|
|
16625
17290
|
var nodeInputVirtualizeSchema = external_exports.strictObject({
|
|
16626
17291
|
prefix: external_exports.string().optional(),
|
|
@@ -16654,6 +17319,7 @@ var helpChildSchema = external_exports.object({
|
|
|
16654
17319
|
});
|
|
16655
17320
|
var helpCommandSchema = external_exports.object({
|
|
16656
17321
|
confirm: external_exports.boolean().optional(),
|
|
17322
|
+
delivery: external_exports.enum(["realtime", "mailbox", "both"]).optional(),
|
|
16657
17323
|
effect: external_exports.string().optional(),
|
|
16658
17324
|
h: external_exports.string().optional(),
|
|
16659
17325
|
inputSchema: external_exports.unknown().optional(),
|
|
@@ -16687,14 +17353,42 @@ var treeJsonSchema = external_exports.lazy(() => external_exports.object({
|
|
|
16687
17353
|
}));
|
|
16688
17354
|
var toolSpecSchema = external_exports.object({
|
|
16689
17355
|
confirm: external_exports.boolean().optional(),
|
|
17356
|
+
delivery: external_exports.enum(["realtime", "mailbox", "both"]).optional(),
|
|
16690
17357
|
description: external_exports.string().optional(),
|
|
16691
17358
|
effect: external_exports.string().optional(),
|
|
16692
17359
|
inputSchema: external_exports.unknown().optional(),
|
|
16693
17360
|
name: external_exports.string(),
|
|
16694
17361
|
outputSchema: external_exports.unknown().optional()
|
|
16695
17362
|
});
|
|
16696
|
-
var
|
|
17363
|
+
var toolSearchRelevanceSchema = external_exports.strictObject({
|
|
17364
|
+
coverage: external_exports.number().min(0).max(1),
|
|
17365
|
+
matchedTermCount: external_exports.number().int().nonnegative(),
|
|
17366
|
+
rankingVersion: external_exports.literal("keyword-v2"),
|
|
17367
|
+
totalTermCount: external_exports.number().int().positive()
|
|
17368
|
+
}).refine(
|
|
17369
|
+
(value) => value.matchedTermCount <= value.totalTermCount && value.coverage === value.matchedTermCount / value.totalTermCount,
|
|
17370
|
+
{ message: "coverage must equal matchedTermCount / totalTermCount" }
|
|
17371
|
+
);
|
|
17372
|
+
var toolSearchSourceSchema = external_exports.strictObject({
|
|
17373
|
+
path: external_exports.string()
|
|
17374
|
+
});
|
|
17375
|
+
var toolSearchSourceResultSchema = external_exports.strictObject({
|
|
16697
17376
|
path: external_exports.string(),
|
|
17377
|
+
status: external_exports.enum([
|
|
17378
|
+
"ok",
|
|
17379
|
+
"unsupported",
|
|
17380
|
+
"timed_out",
|
|
17381
|
+
"unavailable",
|
|
17382
|
+
"cycle",
|
|
17383
|
+
"hop_limit",
|
|
17384
|
+
"budget_exhausted",
|
|
17385
|
+
"invalid_response"
|
|
17386
|
+
])
|
|
17387
|
+
});
|
|
17388
|
+
var toolSearchItemSchema = external_exports.strictObject({
|
|
17389
|
+
path: external_exports.string(),
|
|
17390
|
+
relevance: toolSearchRelevanceSchema,
|
|
17391
|
+
source: toolSearchSourceSchema.optional(),
|
|
16698
17392
|
tool: toolSpecSchema
|
|
16699
17393
|
});
|
|
16700
17394
|
function pageSchema(itemSchema) {
|
|
@@ -16703,19 +17397,136 @@ function pageSchema(itemSchema) {
|
|
|
16703
17397
|
items: external_exports.array(itemSchema)
|
|
16704
17398
|
});
|
|
16705
17399
|
}
|
|
16706
|
-
var toolSearchPageSchema =
|
|
17400
|
+
var toolSearchPageSchema = external_exports.strictObject({
|
|
17401
|
+
cursor: external_exports.string().optional(),
|
|
17402
|
+
items: external_exports.array(toolSearchItemSchema),
|
|
17403
|
+
partial: external_exports.boolean().optional(),
|
|
17404
|
+
snapshot: external_exports.string().optional(),
|
|
17405
|
+
sources: external_exports.array(toolSearchSourceResultSchema).optional()
|
|
17406
|
+
});
|
|
16707
17407
|
var toolSearchRequestSchema = external_exports.strictObject({
|
|
16708
17408
|
opts: external_exports.strictObject({
|
|
16709
17409
|
cursor: external_exports.string().optional(),
|
|
17410
|
+
detail: external_exports.enum(["compact", "full"]).optional(),
|
|
17411
|
+
effects: external_exports.array(external_exports.enum(["read", "write", "destructive", "unknown"])).min(1).optional(),
|
|
17412
|
+
federation: external_exports.enum(["local", "recursive"]).optional(),
|
|
16710
17413
|
limit: external_exports.number().int().optional(),
|
|
16711
|
-
|
|
17414
|
+
matching: external_exports.enum(["best", "all"]).optional(),
|
|
17415
|
+
minCoverage: external_exports.number().gt(0).max(1).optional(),
|
|
17416
|
+
mode: external_exports.enum(["keyword", "semantic"]).optional(),
|
|
17417
|
+
pathPrefix: external_exports.string().optional()
|
|
16712
17418
|
}).optional(),
|
|
16713
17419
|
query: external_exports.string().trim().min(1)
|
|
16714
17420
|
});
|
|
17421
|
+
var deviceOperationStateSchema = external_exports.enum(
|
|
17422
|
+
DEVICE_OPERATION_STATES
|
|
17423
|
+
);
|
|
17424
|
+
var deviceOperationCallerSchema = external_exports.object({
|
|
17425
|
+
keyId: external_exports.string().min(1),
|
|
17426
|
+
owner: external_exports.string().min(1)
|
|
17427
|
+
});
|
|
17428
|
+
var deviceOperationSummaryShape = {
|
|
17429
|
+
attempt: external_exports.number().int().nonnegative(),
|
|
17430
|
+
caller: deviceOperationCallerSchema,
|
|
17431
|
+
cancelRequestedAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17432
|
+
commandId: external_exports.string().min(1),
|
|
17433
|
+
createdAt: external_exports.iso.datetime({ offset: true }),
|
|
17434
|
+
deviceId: external_exports.string().min(1),
|
|
17435
|
+
executionMayHaveOccurred: external_exports.boolean(),
|
|
17436
|
+
expiresAt: external_exports.iso.datetime({ offset: true }),
|
|
17437
|
+
leaseUntil: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17438
|
+
mountPath: external_exports.string().min(1),
|
|
17439
|
+
operationId: external_exports.string().min(1),
|
|
17440
|
+
state: deviceOperationStateSchema,
|
|
17441
|
+
targetPath: external_exports.string().min(1),
|
|
17442
|
+
terminalAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17443
|
+
traceId: external_exports.string().min(1),
|
|
17444
|
+
updatedAt: external_exports.iso.datetime({ offset: true })
|
|
17445
|
+
};
|
|
17446
|
+
var deviceOperationSummarySchema = external_exports.object(
|
|
17447
|
+
deviceOperationSummaryShape
|
|
17448
|
+
);
|
|
17449
|
+
var deviceOperationDetailSchema = external_exports.object({
|
|
17450
|
+
...deviceOperationSummaryShape,
|
|
17451
|
+
error: tbErrorBodySchema.optional(),
|
|
17452
|
+
result: external_exports.json().optional()
|
|
17453
|
+
});
|
|
17454
|
+
var deviceOperationListRequestSchema = external_exports.strictObject({
|
|
17455
|
+
deviceId: external_exports.string().min(1),
|
|
17456
|
+
opts: external_exports.strictObject({
|
|
17457
|
+
cursor: external_exports.string().min(1).optional(),
|
|
17458
|
+
limit: external_exports.number().int().positive().optional(),
|
|
17459
|
+
states: external_exports.array(deviceOperationStateSchema).min(1).optional()
|
|
17460
|
+
}).optional()
|
|
17461
|
+
});
|
|
17462
|
+
var deviceOperationListResponseSchema = pageSchema(deviceOperationSummarySchema);
|
|
17463
|
+
var deviceOperationIdentityRequestSchema = external_exports.strictObject({
|
|
17464
|
+
deviceId: external_exports.string().min(1),
|
|
17465
|
+
operationId: external_exports.string().min(1)
|
|
17466
|
+
});
|
|
17467
|
+
var deviceOperationClaimRequestSchema = external_exports.strictObject({
|
|
17468
|
+
cursor: external_exports.string().min(1).optional(),
|
|
17469
|
+
deviceId: external_exports.string().min(1),
|
|
17470
|
+
limit: external_exports.number().int().positive().optional()
|
|
17471
|
+
});
|
|
17472
|
+
var deviceOperationClaimSchema = external_exports.object({
|
|
17473
|
+
arguments: external_exports.record(external_exports.string(), external_exports.json()),
|
|
17474
|
+
attempt: external_exports.number().int().positive(),
|
|
17475
|
+
caller: deviceOperationCallerSchema,
|
|
17476
|
+
cancelRequestedAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17477
|
+
commandId: external_exports.string().min(1),
|
|
17478
|
+
createdAt: external_exports.iso.datetime({ offset: true }),
|
|
17479
|
+
expiresAt: external_exports.iso.datetime({ offset: true }),
|
|
17480
|
+
leaseId: external_exports.string().min(1),
|
|
17481
|
+
leaseUntil: external_exports.iso.datetime({ offset: true }),
|
|
17482
|
+
operationId: external_exports.string().min(1),
|
|
17483
|
+
path: external_exports.string().min(1),
|
|
17484
|
+
targetPath: external_exports.string().min(1),
|
|
17485
|
+
traceId: external_exports.string().min(1)
|
|
17486
|
+
});
|
|
17487
|
+
var deviceOperationClaimResponseSchema = external_exports.object({
|
|
17488
|
+
cursor: external_exports.string().optional(),
|
|
17489
|
+
operation: deviceOperationClaimSchema.optional(),
|
|
17490
|
+
serverNow: external_exports.iso.datetime({ offset: true })
|
|
17491
|
+
});
|
|
17492
|
+
var deviceOperationLeaseShape = {
|
|
17493
|
+
deviceId: external_exports.string().min(1),
|
|
17494
|
+
leaseId: external_exports.string().min(1),
|
|
17495
|
+
operationId: external_exports.string().min(1)
|
|
17496
|
+
};
|
|
17497
|
+
var deviceOperationRenewRequestSchema = external_exports.strictObject(deviceOperationLeaseShape);
|
|
17498
|
+
var deviceOperationRenewResponseSchema = external_exports.object({
|
|
17499
|
+
cancelRequestedAt: external_exports.iso.datetime({ offset: true }).optional(),
|
|
17500
|
+
leaseUntil: external_exports.iso.datetime({ offset: true }),
|
|
17501
|
+
serverNow: external_exports.iso.datetime({ offset: true })
|
|
17502
|
+
});
|
|
17503
|
+
var deviceOperationCompleteRequestSchema = external_exports.discriminatedUnion("outcome", [
|
|
17504
|
+
external_exports.strictObject({
|
|
17505
|
+
...deviceOperationLeaseShape,
|
|
17506
|
+
outcome: external_exports.literal("succeeded"),
|
|
17507
|
+
result: external_exports.json()
|
|
17508
|
+
}),
|
|
17509
|
+
external_exports.strictObject({
|
|
17510
|
+
...deviceOperationLeaseShape,
|
|
17511
|
+
error: tbErrorBodySchema,
|
|
17512
|
+
outcome: external_exports.literal("rejected")
|
|
17513
|
+
}),
|
|
17514
|
+
external_exports.strictObject({
|
|
17515
|
+
...deviceOperationLeaseShape,
|
|
17516
|
+
error: tbErrorBodySchema,
|
|
17517
|
+
outcome: external_exports.literal("failed")
|
|
17518
|
+
}),
|
|
17519
|
+
external_exports.strictObject({
|
|
17520
|
+
...deviceOperationLeaseShape,
|
|
17521
|
+
error: tbErrorBodySchema.optional(),
|
|
17522
|
+
outcome: external_exports.literal("result_unknown")
|
|
17523
|
+
})
|
|
17524
|
+
]);
|
|
16715
17525
|
var registryNodeSchema = external_exports.object({
|
|
16716
17526
|
config: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
16717
17527
|
createdAt: external_exports.string().optional(),
|
|
16718
17528
|
description: external_exports.string(),
|
|
17529
|
+
deviceId: external_exports.string().optional(),
|
|
16719
17530
|
kind: nodeKindSchema,
|
|
16720
17531
|
lastSeenAt: external_exports.string().optional(),
|
|
16721
17532
|
online: external_exports.boolean().optional(),
|
|
@@ -17164,7 +17975,7 @@ async function objectBodyToBytes(body) {
|
|
|
17164
17975
|
if (body instanceof ArrayBuffer) return new Uint8Array(body);
|
|
17165
17976
|
return readStreamBytes(body);
|
|
17166
17977
|
}
|
|
17167
|
-
function bytesToObjectStream(
|
|
17978
|
+
function bytesToObjectStream(bytes2) {
|
|
17168
17979
|
return {
|
|
17169
17980
|
getReader() {
|
|
17170
17981
|
let done = false;
|
|
@@ -17172,7 +17983,7 @@ function bytesToObjectStream(bytes) {
|
|
|
17172
17983
|
async read() {
|
|
17173
17984
|
if (done) return { done: true };
|
|
17174
17985
|
done = true;
|
|
17175
|
-
return { done: false, value:
|
|
17986
|
+
return { done: false, value: bytes2 };
|
|
17176
17987
|
},
|
|
17177
17988
|
releaseLock() {
|
|
17178
17989
|
},
|
|
@@ -17297,7 +18108,7 @@ async function createObjectContextUploadGrant(store, opts, input) {
|
|
|
17297
18108
|
expiresAt
|
|
17298
18109
|
};
|
|
17299
18110
|
}
|
|
17300
|
-
function
|
|
18111
|
+
function clampLimit4(limit) {
|
|
17301
18112
|
if (limit === void 0) return LIST_LIMIT_DEFAULT;
|
|
17302
18113
|
if (!Number.isInteger(limit) || limit < 1) {
|
|
17303
18114
|
throw new TBError("invalid_argument", `limit \u975E\u6CD5:${limit}`);
|
|
@@ -17371,7 +18182,7 @@ function createObjectContextProvider(store, opts) {
|
|
|
17371
18182
|
return {
|
|
17372
18183
|
async list(path, listOpts) {
|
|
17373
18184
|
rejectFilter(listOpts);
|
|
17374
|
-
const limit =
|
|
18185
|
+
const limit = clampLimit4(listOpts?.limit);
|
|
17375
18186
|
const rel = path === "" ? "" : `${normalizeEntryPath(path)}/`;
|
|
17376
18187
|
const full = keyPrefix + rel;
|
|
17377
18188
|
assertContextKeyAllowed(full.slice(0, -1), path, false);
|
|
@@ -17481,7 +18292,7 @@ function createObjectContextProvider(store, opts) {
|
|
|
17481
18292
|
throw new TBError("invalid_argument", "query \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17482
18293
|
}
|
|
17483
18294
|
rejectFilter(searchOpts);
|
|
17484
|
-
const limit =
|
|
18295
|
+
const limit = clampLimit4(searchOpts?.limit);
|
|
17485
18296
|
const q = query.toLowerCase();
|
|
17486
18297
|
const after = searchOpts?.cursor;
|
|
17487
18298
|
const items = [];
|
|
@@ -17538,6 +18349,7 @@ function isContextExpired(createdAt, ttlSec, now) {
|
|
|
17538
18349
|
// ../core/src/device/frames.ts
|
|
17539
18350
|
var deviceNodeCmdSchema = external_exports.object({
|
|
17540
18351
|
name: external_exports.string().min(1),
|
|
18352
|
+
delivery: external_exports.enum(["realtime", "mailbox", "both"]).optional(),
|
|
17541
18353
|
description: external_exports.string().optional(),
|
|
17542
18354
|
inputSchema: external_exports.unknown().optional(),
|
|
17543
18355
|
outputSchema: external_exports.unknown().optional(),
|
|
@@ -17669,7 +18481,7 @@ function deviceShellHelpModel(nodePath, shell = {}) {
|
|
|
17669
18481
|
timeoutMs: { type: "number", description: "kill the command after this many ms" }
|
|
17670
18482
|
}
|
|
17671
18483
|
},
|
|
17672
|
-
returns: "{ stdout, stderr, exitCode }",
|
|
18484
|
+
returns: "{ stdout, stderr, exitCode, startedAt, completedAt, outcome, signal?, stdoutTruncated, stderrTruncated }",
|
|
17673
18485
|
scope: "call",
|
|
17674
18486
|
effect: "destructive",
|
|
17675
18487
|
confirm: true
|
|
@@ -17804,7 +18616,7 @@ var DeviceGatewaySession = class {
|
|
|
17804
18616
|
call(req, done) {
|
|
17805
18617
|
const cached2 = this.results.get(req.id);
|
|
17806
18618
|
if (cached2 !== void 0) {
|
|
17807
|
-
done(cached2);
|
|
18619
|
+
done(cached2, "completed");
|
|
17808
18620
|
return;
|
|
17809
18621
|
}
|
|
17810
18622
|
const inflight = this.pending.get(req.id);
|
|
@@ -17813,19 +18625,28 @@ var DeviceGatewaySession = class {
|
|
|
17813
18625
|
return;
|
|
17814
18626
|
}
|
|
17815
18627
|
if (this.phase_ !== "ready") {
|
|
17816
|
-
done({ ok: false, error: TBError.deviceOffline().toJSON() });
|
|
18628
|
+
done({ ok: false, error: TBError.deviceOffline().toJSON() }, "not_dispatched");
|
|
17817
18629
|
return;
|
|
17818
18630
|
}
|
|
17819
18631
|
const cancelTimer = this.opts.setTimer(() => this.onCallTimeout(req.id), this.timeoutMs);
|
|
17820
18632
|
this.pending.set(req.id, { waiters: [done], cancelTimer });
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
17825
|
-
|
|
17826
|
-
|
|
17827
|
-
|
|
17828
|
-
|
|
18633
|
+
try {
|
|
18634
|
+
this.io.send({
|
|
18635
|
+
type: "call",
|
|
18636
|
+
id: req.id,
|
|
18637
|
+
path: req.path,
|
|
18638
|
+
arguments: req.arguments,
|
|
18639
|
+
// undefined 经 JSON.stringify 丢弃:老网关不带 context 时帧形状不变。
|
|
18640
|
+
...req.context !== void 0 ? { context: req.context } : {}
|
|
18641
|
+
});
|
|
18642
|
+
} catch {
|
|
18643
|
+
this.pending.delete(req.id);
|
|
18644
|
+
cancelTimer();
|
|
18645
|
+
done({
|
|
18646
|
+
ok: false,
|
|
18647
|
+
error: new TBError("unavailable", "\u8BBE\u5907\u8C03\u7528\u53D1\u9001\u5931\u8D25", { retryable: true }).toJSON()
|
|
18648
|
+
}, "unknown");
|
|
18649
|
+
}
|
|
17829
18650
|
}
|
|
17830
18651
|
/** 幂等表回放种子(DO 从 ctx.storage 恢复时用);已有同 id 结果则忽略。 */
|
|
17831
18652
|
seedResult(id, result) {
|
|
@@ -17845,7 +18666,7 @@ var DeviceGatewaySession = class {
|
|
|
17845
18666
|
};
|
|
17846
18667
|
for (const entry of entries) {
|
|
17847
18668
|
entry.cancelTimer();
|
|
17848
|
-
for (const waiter of entry.waiters) waiter(result);
|
|
18669
|
+
for (const waiter of entry.waiters) waiter(result, "unknown");
|
|
17849
18670
|
}
|
|
17850
18671
|
}
|
|
17851
18672
|
protocolReject(message) {
|
|
@@ -17859,7 +18680,7 @@ var DeviceGatewaySession = class {
|
|
|
17859
18680
|
if (entry === void 0) return;
|
|
17860
18681
|
this.pending.delete(id);
|
|
17861
18682
|
entry.cancelTimer();
|
|
17862
|
-
for (const waiter of entry.waiters) waiter(result);
|
|
18683
|
+
for (const waiter of entry.waiters) waiter(result, "completed");
|
|
17863
18684
|
}
|
|
17864
18685
|
onCallTimeout(id) {
|
|
17865
18686
|
const entry = this.pending.get(id);
|
|
@@ -17872,7 +18693,7 @@ var DeviceGatewaySession = class {
|
|
|
17872
18693
|
retryable: true
|
|
17873
18694
|
}).toJSON()
|
|
17874
18695
|
};
|
|
17875
|
-
for (const waiter of entry.waiters) waiter(result);
|
|
18696
|
+
for (const waiter of entry.waiters) waiter(result, "unknown");
|
|
17876
18697
|
}
|
|
17877
18698
|
};
|
|
17878
18699
|
|
|
@@ -17946,10 +18767,10 @@ function toView(entry) {
|
|
|
17946
18767
|
}
|
|
17947
18768
|
var ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
17948
18769
|
function randomFeedbackId() {
|
|
17949
|
-
const
|
|
17950
|
-
crypto.getRandomValues(
|
|
18770
|
+
const bytes2 = new Uint8Array(6);
|
|
18771
|
+
crypto.getRandomValues(bytes2);
|
|
17951
18772
|
let out = "fb_";
|
|
17952
|
-
for (const b of
|
|
18773
|
+
for (const b of bytes2) out += ID_ALPHABET[b % ID_ALPHABET.length];
|
|
17953
18774
|
return out;
|
|
17954
18775
|
}
|
|
17955
18776
|
function requireShort(value, field, max) {
|
|
@@ -18094,6 +18915,7 @@ function renderHelpDsl(model) {
|
|
|
18094
18915
|
if (cmd.outputSchema !== void 0) lines.push(` result ${JSON.stringify(cmd.outputSchema)}`);
|
|
18095
18916
|
if (cmd.returns !== void 0) lines.push(...attrLines("returns", cmd.returns));
|
|
18096
18917
|
lines.push(` scope ${cmd.scope}`);
|
|
18918
|
+
if (cmd.delivery !== void 0) lines.push(` delivery ${cmd.delivery}`);
|
|
18097
18919
|
if (cmd.effect !== void 0) lines.push(` effect ${cmd.effect}`);
|
|
18098
18920
|
if (cmd.confirm) lines.push(" confirm");
|
|
18099
18921
|
}
|
|
@@ -18126,6 +18948,7 @@ function renderHelpJson(model) {
|
|
|
18126
18948
|
if (cmd.inputSchema !== void 0) out.inputSchema = cmd.inputSchema;
|
|
18127
18949
|
if (cmd.outputSchema !== void 0) out.outputSchema = cmd.outputSchema;
|
|
18128
18950
|
if (cmd.returns !== void 0) out.returns = cmd.returns;
|
|
18951
|
+
if (cmd.delivery !== void 0) out.delivery = cmd.delivery;
|
|
18129
18952
|
if (cmd.effect !== void 0) out.effect = cmd.effect;
|
|
18130
18953
|
if (cmd.confirm) out.confirm = cmd.confirm;
|
|
18131
18954
|
return out;
|
|
@@ -18165,6 +18988,7 @@ function cmdSection(cmd, index) {
|
|
|
18165
18988
|
}
|
|
18166
18989
|
lines.push(`- Invoke: \`POST ${cmd.path}\` with body \`{...arguments}\``);
|
|
18167
18990
|
lines.push(`- Required scope: \`${cmd.scope}\``);
|
|
18991
|
+
if (cmd.delivery !== void 0) lines.push(`- Delivery: \`${cmd.delivery}\``);
|
|
18168
18992
|
if (cmd.effect !== void 0) {
|
|
18169
18993
|
lines.push(
|
|
18170
18994
|
`- Effect: \`${cmd.effect}\`${cmd.confirm ? " \u2014 **ask the user to confirm before calling**" : ""}`
|
|
@@ -18382,7 +19206,7 @@ var KEY_STORE_IDEMPOTENCY = "store:idempotency:";
|
|
|
18382
19206
|
var UPLOAD_TTL_SEC_DEFAULT = 15 * 60;
|
|
18383
19207
|
var SHARE_TTL_SEC_DEFAULT = 15 * 60;
|
|
18384
19208
|
var MAX_OBJECT_BYTES_DEFAULT = 256 * 1024 * 1024;
|
|
18385
|
-
var
|
|
19209
|
+
var MAX_CAS_ATTEMPTS2 = 32;
|
|
18386
19210
|
var CAPABILITY_TTL_SEC_MAX = 7 * 24 * 60 * 60;
|
|
18387
19211
|
var TOKEN_PREFIX = {
|
|
18388
19212
|
call: "tbc",
|
|
@@ -18732,7 +19556,7 @@ var StoreService = class {
|
|
|
18732
19556
|
}
|
|
18733
19557
|
async revokeCallUploadCapability(token) {
|
|
18734
19558
|
const id = this.tokenId(token, "call");
|
|
18735
|
-
for (let attempt = 0; attempt <
|
|
19559
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
18736
19560
|
const raw = await this.state.get(`${KEY_STORE_CALL_CAPABILITY}${id}`);
|
|
18737
19561
|
if (raw === null) throw new TBError("permission_denied", "call upload capability \u65E0\u6548");
|
|
18738
19562
|
const capability = parseCallCapability(raw);
|
|
@@ -18946,7 +19770,7 @@ var StoreService = class {
|
|
|
18946
19770
|
}
|
|
18947
19771
|
async delete(uri, actor) {
|
|
18948
19772
|
const { objectId } = parseStoreUri(uri);
|
|
18949
|
-
for (let attempt = 0; attempt <
|
|
19773
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
18950
19774
|
const object2 = await this.requireObject(objectId);
|
|
18951
19775
|
if (!actor.admin && object2.owner !== actor.owner) {
|
|
18952
19776
|
throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
|
|
@@ -19010,7 +19834,7 @@ var StoreService = class {
|
|
|
19010
19834
|
}
|
|
19011
19835
|
async revokeShare(shareId, actor) {
|
|
19012
19836
|
const owner = requireOwner(actor);
|
|
19013
|
-
for (let attempt = 0; attempt <
|
|
19837
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19014
19838
|
const raw = await this.state.get(`${KEY_STORE_SHARE}${shareId}`);
|
|
19015
19839
|
if (raw === null) throw new TBError("not_found", "share grant \u4E0D\u5B58\u5728");
|
|
19016
19840
|
const grant = parseShare(raw);
|
|
@@ -19334,7 +20158,7 @@ var StoreService = class {
|
|
|
19334
20158
|
await this.compareRecord(resolved.key, resolved.binding, null);
|
|
19335
20159
|
}
|
|
19336
20160
|
async reserveCallCapability(token, objectId, input, transportMaxBytes) {
|
|
19337
|
-
for (let attempt = 0; attempt <
|
|
20161
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19338
20162
|
const capability = await this.verifyCallUploadCapability(token);
|
|
19339
20163
|
const previous = capability.reservations.find((r) => r.objectId === objectId);
|
|
19340
20164
|
if (previous !== void 0) {
|
|
@@ -19456,7 +20280,7 @@ var StoreService = class {
|
|
|
19456
20280
|
}
|
|
19457
20281
|
/** driver DELETE 成功后持久化证据;后续 cleanup 只收 tombstone,不再重复外部 DELETE。 */
|
|
19458
20282
|
async markBytesDeleted(objectId, expectedDriverKey) {
|
|
19459
|
-
for (let attempt = 0; attempt <
|
|
20283
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19460
20284
|
const raw = await this.state.get(`${KEY_STORE_OBJECT}${objectId}`);
|
|
19461
20285
|
if (raw === null) return;
|
|
19462
20286
|
const object2 = parseObject(raw);
|
|
@@ -19556,7 +20380,7 @@ var StoreService = class {
|
|
|
19556
20380
|
}
|
|
19557
20381
|
async markReady(sessionInput, meta3) {
|
|
19558
20382
|
const actualChecksum = sessionInput.expectedChecksum !== void 0 && meta3.metadata?.["checksum-sha256"]?.toLowerCase() === sessionInput.expectedChecksum.value ? sessionInput.expectedChecksum : void 0;
|
|
19559
|
-
for (let attempt = 0; attempt <
|
|
20383
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19560
20384
|
const object2 = await this.requireObject(sessionInput.objectId);
|
|
19561
20385
|
if (object2.status === "ready") {
|
|
19562
20386
|
await this.completeSessionBestEffort(await this.requireSession(sessionInput.id));
|
|
@@ -19586,7 +20410,7 @@ var StoreService = class {
|
|
|
19586
20410
|
throw new TBError("conflict", "\u5BF9\u8C61\u5E76\u53D1\u72B6\u6001\u51B2\u7A81:pending");
|
|
19587
20411
|
}
|
|
19588
20412
|
async completeSessionBestEffort(sessionInput) {
|
|
19589
|
-
for (let attempt = 0; attempt <
|
|
20413
|
+
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS2; attempt++) {
|
|
19590
20414
|
const session = await this.requireSession(sessionInput.id);
|
|
19591
20415
|
if (session.status === "completed" || session.status !== "created") return;
|
|
19592
20416
|
const completedAt = this.nowIso();
|
|
@@ -19762,8 +20586,8 @@ function canonicalCatalogJson(value) {
|
|
|
19762
20586
|
return JSON.stringify(normalize(value));
|
|
19763
20587
|
}
|
|
19764
20588
|
async function catalogDigest(value) {
|
|
19765
|
-
const
|
|
19766
|
-
const hash2 = await crypto.subtle.digest("SHA-256",
|
|
20589
|
+
const bytes2 = new TextEncoder().encode(canonicalCatalogJson(value));
|
|
20590
|
+
const hash2 = await crypto.subtle.digest("SHA-256", bytes2);
|
|
19767
20591
|
return [...new Uint8Array(hash2)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
19768
20592
|
}
|
|
19769
20593
|
async function catalogSetDigest(catalog) {
|
|
@@ -19957,11 +20781,11 @@ var pluginCallSchema = external_exports.object({
|
|
|
19957
20781
|
arguments: external_exports.record(external_exports.string(), external_exports.unknown())
|
|
19958
20782
|
});
|
|
19959
20783
|
function assertPluginPayloadSize(payload) {
|
|
19960
|
-
const
|
|
19961
|
-
if (
|
|
20784
|
+
const bytes2 = new TextEncoder().encode(payload).length;
|
|
20785
|
+
if (bytes2 > PLUGIN_PAYLOAD_MAX_BYTES) {
|
|
19962
20786
|
throw new TBError(
|
|
19963
20787
|
"invalid_argument",
|
|
19964
|
-
`payload ${
|
|
20788
|
+
`payload ${bytes2} \u5B57\u8282\u8D85\u8FC7\u4E0A\u9650 ${PLUGIN_PAYLOAD_MAX_BYTES}(\u2264 1 MiB;\u66F4\u5927\u5185\u5BB9\u7ECF $ref)`
|
|
19965
20789
|
);
|
|
19966
20790
|
}
|
|
19967
20791
|
}
|
|
@@ -20000,6 +20824,13 @@ var positiveEnvKeys = [
|
|
|
20000
20824
|
"TB_MAX_HOPS",
|
|
20001
20825
|
"TB_REF_THRESHOLD_BYTES",
|
|
20002
20826
|
"TB_REF_TTL_SEC",
|
|
20827
|
+
"TB_SEARCH_FEDERATION_CONCURRENCY",
|
|
20828
|
+
"TB_SEARCH_FEDERATION_DEADLINE_MS",
|
|
20829
|
+
"TB_SEARCH_FEDERATION_MAX_RESPONSE_BYTES",
|
|
20830
|
+
"TB_SEARCH_FEDERATION_MAX_SOURCES",
|
|
20831
|
+
"TB_SEARCH_FEDERATION_MIN_CHILD_WORK_MS",
|
|
20832
|
+
"TB_SEARCH_FEDERATION_RETURN_RESERVE_MS",
|
|
20833
|
+
"TB_SEARCH_FEDERATION_SESSION_TTL_SEC",
|
|
20003
20834
|
"TB_STORE_CALL_MAX_BYTES",
|
|
20004
20835
|
"TB_STORE_CALL_MAX_OBJECT_BYTES",
|
|
20005
20836
|
"TB_STORE_CALL_MAX_OBJECTS",
|
|
@@ -20047,6 +20878,15 @@ function parseRuntimeEnv(env) {
|
|
|
20047
20878
|
remote: {
|
|
20048
20879
|
allowInsecure: allowInsecureHttp,
|
|
20049
20880
|
allowlist,
|
|
20881
|
+
federatedSearch: {
|
|
20882
|
+
...parsed.TB_SEARCH_FEDERATION_CONCURRENCY === void 0 ? {} : { maxConcurrency: parsed.TB_SEARCH_FEDERATION_CONCURRENCY },
|
|
20883
|
+
...parsed.TB_SEARCH_FEDERATION_DEADLINE_MS === void 0 ? {} : { totalDeadlineMs: parsed.TB_SEARCH_FEDERATION_DEADLINE_MS },
|
|
20884
|
+
...parsed.TB_SEARCH_FEDERATION_MAX_RESPONSE_BYTES === void 0 ? {} : { maxResponseBodyBytes: parsed.TB_SEARCH_FEDERATION_MAX_RESPONSE_BYTES },
|
|
20885
|
+
...parsed.TB_SEARCH_FEDERATION_MAX_SOURCES === void 0 ? {} : { maxSources: parsed.TB_SEARCH_FEDERATION_MAX_SOURCES },
|
|
20886
|
+
...parsed.TB_SEARCH_FEDERATION_MIN_CHILD_WORK_MS === void 0 ? {} : { minChildWorkMs: parsed.TB_SEARCH_FEDERATION_MIN_CHILD_WORK_MS },
|
|
20887
|
+
...parsed.TB_SEARCH_FEDERATION_RETURN_RESERVE_MS === void 0 ? {} : { perHopReturnReserveMs: parsed.TB_SEARCH_FEDERATION_RETURN_RESERVE_MS },
|
|
20888
|
+
...parsed.TB_SEARCH_FEDERATION_SESSION_TTL_SEC === void 0 ? {} : { sessionTtlMs: parsed.TB_SEARCH_FEDERATION_SESSION_TTL_SEC * 1e3 }
|
|
20889
|
+
},
|
|
20050
20890
|
maxHops: parsed.TB_MAX_HOPS ?? DEFAULT_MAX_HOPS,
|
|
20051
20891
|
...parsed.TB_INSTANCE_ID ? { instanceId: parsed.TB_INSTANCE_ID } : {}
|
|
20052
20892
|
}
|
|
@@ -20095,26 +20935,37 @@ var TOOL_SEARCH_SNAPSHOT_CHUNKS_MAX = Math.ceil(
|
|
|
20095
20935
|
var TOOL_SEARCH_REBUILD_CHUNKS_MAX = TOOL_SEARCH_SOURCE_CHUNKS_MAX + TOOL_SEARCH_SNAPSHOT_CHUNKS_MAX;
|
|
20096
20936
|
var TOOL_SEARCH_TERM_LIMIT = 32;
|
|
20097
20937
|
var TOOL_SEARCH_UNIT_LIMIT = 98;
|
|
20938
|
+
var TOOL_SEARCH_LIMIT_DEFAULT = 10;
|
|
20098
20939
|
var TOOL_SEARCH_LIKE_PATTERN_BYTES_MAX = 50;
|
|
20099
20940
|
var TOOL_SEARCH_QUERY_MAX = 1024;
|
|
20941
|
+
var TOOL_SEARCH_RANKING_VERSION = "keyword-v2";
|
|
20100
20942
|
var TOOL_SEARCH_CURSOR_OFFSET_MAX = 1e6;
|
|
20943
|
+
var TOOL_SEARCH_EFFECTS = [
|
|
20944
|
+
"read",
|
|
20945
|
+
"write",
|
|
20946
|
+
"destructive",
|
|
20947
|
+
"unknown"
|
|
20948
|
+
];
|
|
20101
20949
|
function assertKeywordToolSearchMode(opts) {
|
|
20102
20950
|
if (opts?.mode !== void 0 && opts.mode !== "keyword") {
|
|
20103
20951
|
throw new TBError("invalid_argument", `SearchIndex \u4E0D\u652F\u6301 mode '${String(opts.mode)}'`);
|
|
20104
20952
|
}
|
|
20105
20953
|
}
|
|
20106
20954
|
function normalizeToolSearchLimit(limit) {
|
|
20107
|
-
if (limit === void 0) return
|
|
20955
|
+
if (limit === void 0) return TOOL_SEARCH_LIMIT_DEFAULT;
|
|
20108
20956
|
if (typeof limit !== "number" || !Number.isFinite(limit) || !Number.isInteger(limit)) {
|
|
20109
20957
|
throw new TBError("invalid_argument", "opts.limit \u5FC5\u987B\u662F\u6574\u6570");
|
|
20110
20958
|
}
|
|
20111
|
-
if (limit < 1) return
|
|
20959
|
+
if (limit < 1) return TOOL_SEARCH_LIMIT_DEFAULT;
|
|
20112
20960
|
return Math.min(limit, LIST_LIMIT_MAX);
|
|
20113
20961
|
}
|
|
20962
|
+
function searchUnitAllowsPath(unit) {
|
|
20963
|
+
return !/^%[A-Za-z0-9]{1,2}%$/u.test(unit.pattern);
|
|
20964
|
+
}
|
|
20114
20965
|
function stableDigest(value) {
|
|
20115
|
-
const
|
|
20966
|
+
const bytes2 = new TextEncoder().encode(JSON.stringify(value));
|
|
20116
20967
|
let hash2 = 0xcbf29ce484222325n;
|
|
20117
|
-
for (const byte of
|
|
20968
|
+
for (const byte of bytes2) {
|
|
20118
20969
|
hash2 ^= BigInt(byte);
|
|
20119
20970
|
hash2 = BigInt.asUintN(64, hash2 * 0x100000001b3n);
|
|
20120
20971
|
}
|
|
@@ -20124,12 +20975,12 @@ function truncateUtf8(value, maxBytes) {
|
|
|
20124
20975
|
const encoder = new TextEncoder();
|
|
20125
20976
|
if (encoder.encode(value).length <= maxBytes) return value;
|
|
20126
20977
|
let result = "";
|
|
20127
|
-
let
|
|
20978
|
+
let bytes2 = 0;
|
|
20128
20979
|
for (const char of value) {
|
|
20129
20980
|
const size = encoder.encode(char).length;
|
|
20130
|
-
if (
|
|
20981
|
+
if (bytes2 + size > maxBytes) break;
|
|
20131
20982
|
result += char;
|
|
20132
|
-
|
|
20983
|
+
bytes2 += size;
|
|
20133
20984
|
}
|
|
20134
20985
|
return result;
|
|
20135
20986
|
}
|
|
@@ -20154,6 +21005,7 @@ function serializedRecord(path, tool, feedback) {
|
|
|
20154
21005
|
}
|
|
20155
21006
|
const record2 = {
|
|
20156
21007
|
description: truncateUtf8(tool.description ?? "", TOOL_SEARCH_DESCRIPTION_BYTES_MAX),
|
|
21008
|
+
effect: TOOL_SEARCH_EFFECTS.includes(tool.effect) ? tool.effect : "unknown",
|
|
20157
21009
|
feedback,
|
|
20158
21010
|
name: tool.name,
|
|
20159
21011
|
path,
|
|
@@ -20164,17 +21016,70 @@ function serializedRecord(path, tool, feedback) {
|
|
|
20164
21016
|
function jsonPayload(record2) {
|
|
20165
21017
|
return {
|
|
20166
21018
|
description: record2.description,
|
|
21019
|
+
effect: record2.effect,
|
|
20167
21020
|
feedback: record2.feedback,
|
|
20168
21021
|
name: record2.name,
|
|
20169
21022
|
path: record2.path
|
|
20170
21023
|
};
|
|
20171
21024
|
}
|
|
20172
21025
|
function normalizeToolSearchPath(path) {
|
|
20173
|
-
const canonical =
|
|
21026
|
+
const canonical = canonicalizePath(path);
|
|
20174
21027
|
const pathError = validatePath(canonical);
|
|
20175
21028
|
if (pathError !== null) throw pathError;
|
|
20176
21029
|
return canonical;
|
|
20177
21030
|
}
|
|
21031
|
+
function normalizeToolSearchOptions(opts) {
|
|
21032
|
+
let effects;
|
|
21033
|
+
if (opts?.effects !== void 0) {
|
|
21034
|
+
if (!Array.isArray(opts.effects) || opts.effects.length === 0) {
|
|
21035
|
+
throw new TBError("invalid_argument", "opts.effects \u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4");
|
|
21036
|
+
}
|
|
21037
|
+
const selected = /* @__PURE__ */ new Set();
|
|
21038
|
+
for (const effect of opts.effects) {
|
|
21039
|
+
if (!TOOL_SEARCH_EFFECTS.includes(effect)) {
|
|
21040
|
+
throw new TBError("invalid_argument", `opts.effects \u542B\u975E\u6CD5 effect '${String(effect)}'`);
|
|
21041
|
+
}
|
|
21042
|
+
selected.add(effect);
|
|
21043
|
+
}
|
|
21044
|
+
effects = TOOL_SEARCH_EFFECTS.filter((effect) => selected.has(effect));
|
|
21045
|
+
}
|
|
21046
|
+
const matching = opts?.matching ?? "best";
|
|
21047
|
+
if (matching !== "best" && matching !== "all") {
|
|
21048
|
+
throw new TBError("invalid_argument", `opts.matching '${String(matching)}' \u975E\u6CD5`);
|
|
21049
|
+
}
|
|
21050
|
+
let minCoverage = opts?.minCoverage;
|
|
21051
|
+
if (minCoverage !== void 0 && (typeof minCoverage !== "number" || !Number.isFinite(minCoverage) || minCoverage <= 0 || minCoverage > 1)) {
|
|
21052
|
+
throw new TBError("invalid_argument", "opts.minCoverage \u5FC5\u987B\u5927\u4E8E 0 \u4E14\u4E0D\u8D85\u8FC7 1");
|
|
21053
|
+
}
|
|
21054
|
+
if (matching === "all") {
|
|
21055
|
+
if (minCoverage !== void 0 && minCoverage !== 1) {
|
|
21056
|
+
throw new TBError("invalid_argument", "opts.matching='all' \u53EA\u5141\u8BB8 minCoverage=1");
|
|
21057
|
+
}
|
|
21058
|
+
minCoverage = 1;
|
|
21059
|
+
}
|
|
21060
|
+
let pathPrefix;
|
|
21061
|
+
if (opts?.pathPrefix !== void 0) {
|
|
21062
|
+
if (typeof opts.pathPrefix !== "string") {
|
|
21063
|
+
throw new TBError("invalid_argument", "opts.pathPrefix \u5FC5\u987B\u662F\u8DEF\u5F84\u5B57\u7B26\u4E32");
|
|
21064
|
+
}
|
|
21065
|
+
pathPrefix = normalizeToolSearchPath(opts.pathPrefix);
|
|
21066
|
+
}
|
|
21067
|
+
return {
|
|
21068
|
+
...effects === void 0 ? {} : { effects },
|
|
21069
|
+
matching,
|
|
21070
|
+
...minCoverage === void 0 ? {} : { minCoverage },
|
|
21071
|
+
...pathPrefix === void 0 ? {} : { pathPrefix }
|
|
21072
|
+
};
|
|
21073
|
+
}
|
|
21074
|
+
function toolSearchOptionsFingerprint(opts) {
|
|
21075
|
+
const normalized = normalizeToolSearchOptions(opts);
|
|
21076
|
+
return stableDigest([
|
|
21077
|
+
normalized.effects ?? null,
|
|
21078
|
+
normalized.matching,
|
|
21079
|
+
normalized.minCoverage ?? null,
|
|
21080
|
+
normalized.pathPrefix ?? null
|
|
21081
|
+
]);
|
|
21082
|
+
}
|
|
20178
21083
|
function serializeToolSearchSnapshot(path, tools, feedback = "") {
|
|
20179
21084
|
const canonical = normalizeToolSearchPath(path);
|
|
20180
21085
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -20230,6 +21135,7 @@ function toolSearchSnapshotDigest(records) {
|
|
|
20230
21135
|
record2.path,
|
|
20231
21136
|
record2.name,
|
|
20232
21137
|
record2.description,
|
|
21138
|
+
record2.effect,
|
|
20233
21139
|
record2.feedback,
|
|
20234
21140
|
record2.toolDigest
|
|
20235
21141
|
]));
|
|
@@ -20279,54 +21185,93 @@ function scriptRuns(term) {
|
|
|
20279
21185
|
}
|
|
20280
21186
|
return runs;
|
|
20281
21187
|
}
|
|
20282
|
-
function addSearchUnit(units, value, tier) {
|
|
21188
|
+
function addSearchUnit(units, value, tier, logicalTermId) {
|
|
20283
21189
|
const pattern = likePattern(value);
|
|
20284
21190
|
if (new TextEncoder().encode(pattern).length > TOOL_SEARCH_LIKE_PATTERN_BYTES_MAX) return false;
|
|
20285
|
-
const
|
|
21191
|
+
const key = `${logicalTermId}\0${pattern}`;
|
|
21192
|
+
const existing = units.get(key);
|
|
20286
21193
|
if (existing === void 0 || tier > existing.tier) {
|
|
20287
|
-
units.set(
|
|
21194
|
+
units.set(key, { logicalTermId, pattern, tier });
|
|
20288
21195
|
}
|
|
20289
21196
|
return true;
|
|
20290
21197
|
}
|
|
20291
|
-
function addChunkedSearchUnits(units, value, tier) {
|
|
21198
|
+
function addChunkedSearchUnits(units, value, tier, logicalTermId) {
|
|
20292
21199
|
let chunk = "";
|
|
20293
21200
|
for (const codePoint of value) {
|
|
20294
21201
|
if (likePatternBytes(chunk + codePoint) <= TOOL_SEARCH_LIKE_PATTERN_BYTES_MAX) {
|
|
20295
21202
|
chunk += codePoint;
|
|
20296
21203
|
continue;
|
|
20297
21204
|
}
|
|
20298
|
-
if (chunk.length > 0) addSearchUnit(units, chunk, tier);
|
|
21205
|
+
if (chunk.length > 0) addSearchUnit(units, chunk, tier, logicalTermId);
|
|
20299
21206
|
chunk = codePoint;
|
|
20300
21207
|
}
|
|
20301
|
-
if (chunk.length > 0) addSearchUnit(units, chunk, tier);
|
|
21208
|
+
if (chunk.length > 0) addSearchUnit(units, chunk, tier, logicalTermId);
|
|
20302
21209
|
}
|
|
20303
|
-
function
|
|
20304
|
-
|
|
20305
|
-
|
|
21210
|
+
function prepareToolSearchQuery(query, unitLimit = TOOL_SEARCH_UNIT_LIMIT) {
|
|
21211
|
+
if (!Number.isInteger(unitLimit) || unitLimit < 1 || unitLimit > TOOL_SEARCH_UNIT_LIMIT) {
|
|
21212
|
+
throw new TBError("invalid_argument", "\u5DE5\u5177\u641C\u7D22 unit limit \u975E\u6CD5");
|
|
21213
|
+
}
|
|
21214
|
+
const rawTerms = normalizeToolSearchQuery(query).split(/\s+/u);
|
|
21215
|
+
if (rawTerms.length > TOOL_SEARCH_TERM_LIMIT) {
|
|
20306
21216
|
throw new TBError("invalid_argument", `\u641C\u7D22 query \u6700\u591A ${TOOL_SEARCH_TERM_LIMIT} \u4E2A terms`);
|
|
20307
21217
|
}
|
|
21218
|
+
const seenTerms = /* @__PURE__ */ new Set();
|
|
21219
|
+
const terms = rawTerms.filter((term) => {
|
|
21220
|
+
const key = term.replace(/[A-Z]/g, (char) => char.toLowerCase());
|
|
21221
|
+
if (seenTerms.has(key)) return false;
|
|
21222
|
+
seenTerms.add(key);
|
|
21223
|
+
return true;
|
|
21224
|
+
});
|
|
21225
|
+
if (terms.length > unitLimit) {
|
|
21226
|
+
throw new TBError("invalid_argument", "\u5DE5\u5177\u641C\u7D22 unit limit \u65E0\u6CD5\u8986\u76D6\u5168\u90E8 logical terms");
|
|
21227
|
+
}
|
|
20308
21228
|
const units = /* @__PURE__ */ new Map();
|
|
20309
|
-
for (const term of terms) {
|
|
20310
|
-
const wholeAdded = addSearchUnit(units, term, 4);
|
|
21229
|
+
for (const [logicalTermId, term] of terms.entries()) {
|
|
21230
|
+
const wholeAdded = addSearchUnit(units, term, 4, logicalTermId);
|
|
20311
21231
|
const runs = scriptRuns(term);
|
|
20312
21232
|
if (runs.length > 1) {
|
|
20313
21233
|
for (const run of runs) {
|
|
20314
21234
|
const value = run.codePoints.join("");
|
|
20315
|
-
if (run.cjk) addSearchUnit(units, value, 2);
|
|
20316
|
-
else addChunkedSearchUnits(units, value, 2);
|
|
21235
|
+
if (run.cjk) addSearchUnit(units, value, 2, logicalTermId);
|
|
21236
|
+
else addChunkedSearchUnits(units, value, 2, logicalTermId);
|
|
20317
21237
|
}
|
|
20318
21238
|
} else if (!wholeAdded && runs[0]?.cjk === false) {
|
|
20319
|
-
addChunkedSearchUnits(units, term, 2);
|
|
21239
|
+
addChunkedSearchUnits(units, term, 2, logicalTermId);
|
|
20320
21240
|
}
|
|
20321
21241
|
for (const run of runs) {
|
|
20322
21242
|
if (!run.cjk) continue;
|
|
20323
21243
|
for (let index = 0; index < run.codePoints.length - 1; index += 1) {
|
|
20324
|
-
addSearchUnit(
|
|
21244
|
+
addSearchUnit(
|
|
21245
|
+
units,
|
|
21246
|
+
run.codePoints.slice(index, index + 2).join(""),
|
|
21247
|
+
2,
|
|
21248
|
+
logicalTermId
|
|
21249
|
+
);
|
|
21250
|
+
}
|
|
21251
|
+
for (const codePoint of run.codePoints) {
|
|
21252
|
+
addSearchUnit(units, codePoint, 1, logicalTermId);
|
|
20325
21253
|
}
|
|
20326
|
-
for (const codePoint of run.codePoints) addSearchUnit(units, codePoint, 1);
|
|
20327
21254
|
}
|
|
20328
21255
|
}
|
|
20329
|
-
|
|
21256
|
+
const ordered = [...units.values()].sort((left, right) => right.tier - left.tier);
|
|
21257
|
+
if (ordered.length <= unitLimit) {
|
|
21258
|
+
return { totalTermCount: terms.length, units: ordered };
|
|
21259
|
+
}
|
|
21260
|
+
const selected = /* @__PURE__ */ new Set();
|
|
21261
|
+
const represented = /* @__PURE__ */ new Set();
|
|
21262
|
+
for (const unit of ordered) {
|
|
21263
|
+
if (represented.has(unit.logicalTermId)) continue;
|
|
21264
|
+
represented.add(unit.logicalTermId);
|
|
21265
|
+
selected.add(unit);
|
|
21266
|
+
}
|
|
21267
|
+
for (const unit of ordered) {
|
|
21268
|
+
if (selected.size >= unitLimit) break;
|
|
21269
|
+
selected.add(unit);
|
|
21270
|
+
}
|
|
21271
|
+
return {
|
|
21272
|
+
totalTermCount: terms.length,
|
|
21273
|
+
units: ordered.filter((unit) => selected.has(unit))
|
|
21274
|
+
};
|
|
20330
21275
|
}
|
|
20331
21276
|
function cursorBase64UrlDecode(value) {
|
|
20332
21277
|
if (value.length === 0 || value.length > 4096) {
|
|
@@ -20353,13 +21298,15 @@ async function cursorCryptoKey(secret) {
|
|
|
20353
21298
|
["encrypt", "decrypt"]
|
|
20354
21299
|
);
|
|
20355
21300
|
}
|
|
20356
|
-
async function
|
|
21301
|
+
async function sealToolSearchCursor(query, mode, revision, offset, secret, optionsFingerprint) {
|
|
20357
21302
|
const payload = {
|
|
21303
|
+
f: optionsFingerprint,
|
|
20358
21304
|
h: stableDigest(normalizeToolSearchQuery(query)),
|
|
21305
|
+
k: TOOL_SEARCH_RANKING_VERSION,
|
|
20359
21306
|
m: mode,
|
|
20360
21307
|
o: offset,
|
|
20361
21308
|
r: revision,
|
|
20362
|
-
v:
|
|
21309
|
+
v: 2
|
|
20363
21310
|
};
|
|
20364
21311
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
20365
21312
|
const ciphertext = new Uint8Array(await crypto.subtle.encrypt(
|
|
@@ -20372,7 +21319,17 @@ async function encodeToolSearchCursor(query, mode, revision, offset, secret) {
|
|
|
20372
21319
|
sealed.set(ciphertext, iv.length);
|
|
20373
21320
|
return base64urlEncode(sealed);
|
|
20374
21321
|
}
|
|
20375
|
-
async function
|
|
21322
|
+
async function encodeToolSearchCursor(query, mode, revision, offset, secret, opts) {
|
|
21323
|
+
return await sealToolSearchCursor(
|
|
21324
|
+
query,
|
|
21325
|
+
mode,
|
|
21326
|
+
revision,
|
|
21327
|
+
offset,
|
|
21328
|
+
secret,
|
|
21329
|
+
toolSearchOptionsFingerprint(opts)
|
|
21330
|
+
);
|
|
21331
|
+
}
|
|
21332
|
+
async function decodeToolSearchCursor(cursor, query, mode, revision, secret, opts) {
|
|
20376
21333
|
if (cursor === void 0) return 0;
|
|
20377
21334
|
let value;
|
|
20378
21335
|
try {
|
|
@@ -20393,115 +21350,199 @@ async function decodeToolSearchCursor(cursor, query, mode, revision, secret) {
|
|
|
20393
21350
|
}
|
|
20394
21351
|
const payload = value;
|
|
20395
21352
|
const keys = Object.keys(payload).sort().join(",");
|
|
20396
|
-
if (keys !== "h,m,o,r,v" || payload.v !==
|
|
21353
|
+
if (keys !== "f,h,k,m,o,r,v" || payload.v !== 2 || payload.k !== TOOL_SEARCH_RANKING_VERSION || payload.f !== toolSearchOptionsFingerprint(opts) || payload.h !== stableDigest(normalizeToolSearchQuery(query)) || payload.m !== mode || payload.r !== revision || !Number.isSafeInteger(payload.o) || (payload.o ?? -1) < 0 || (payload.o ?? 0) > TOOL_SEARCH_CURSOR_OFFSET_MAX) {
|
|
20397
21354
|
throw new TBError("invalid_argument", "\u641C\u7D22 cursor \u5DF2\u5931\u6548\u6216\u4E0E\u5F53\u524D\u67E5\u8BE2\u4E0D\u5339\u914D");
|
|
20398
21355
|
}
|
|
20399
21356
|
return payload.o ?? 0;
|
|
20400
21357
|
}
|
|
21358
|
+
async function encodeToolSearchCursorForCandidate(query, mode, candidate, secret) {
|
|
21359
|
+
const fingerprint = candidate.searchOptionsFingerprint ?? toolSearchOptionsFingerprint();
|
|
21360
|
+
if (!/^[a-f0-9]{16}$/.test(fingerprint)) {
|
|
21361
|
+
throw new TBError("invalid_argument", "\u5DE5\u5177\u641C\u7D22\u5019\u9009\u7684 options fingerprint \u975E\u6CD5");
|
|
21362
|
+
}
|
|
21363
|
+
return await sealToolSearchCursor(
|
|
21364
|
+
query,
|
|
21365
|
+
mode,
|
|
21366
|
+
candidate.revision,
|
|
21367
|
+
candidate.resumeOffset,
|
|
21368
|
+
secret,
|
|
21369
|
+
fingerprint
|
|
21370
|
+
);
|
|
21371
|
+
}
|
|
20401
21372
|
|
|
20402
21373
|
// ../core/src/search/sqlSearchIndex.ts
|
|
20403
21374
|
var TOOL_SEARCH_SCHEMA_STATEMENTS = [
|
|
20404
|
-
`CREATE TABLE IF NOT EXISTS
|
|
21375
|
+
`CREATE TABLE IF NOT EXISTS tb_search_tools_v5 (
|
|
20405
21376
|
id INTEGER PRIMARY KEY,
|
|
20406
21377
|
path TEXT NOT NULL,
|
|
20407
21378
|
name TEXT NOT NULL,
|
|
20408
21379
|
description TEXT NOT NULL DEFAULT '',
|
|
21380
|
+
effect TEXT NOT NULL CHECK (effect IN ('read', 'write', 'destructive', 'unknown')),
|
|
20409
21381
|
feedback TEXT NOT NULL DEFAULT '',
|
|
20410
21382
|
UNIQUE(path, name)
|
|
20411
21383
|
)`,
|
|
20412
|
-
`CREATE TABLE IF NOT EXISTS
|
|
21384
|
+
`CREATE TABLE IF NOT EXISTS tb_search_meta_v5 (
|
|
20413
21385
|
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
20414
21386
|
revision INTEGER NOT NULL DEFAULT 0,
|
|
20415
21387
|
seeded INTEGER NOT NULL DEFAULT 0,
|
|
20416
21388
|
cursor_secret TEXT NOT NULL
|
|
20417
21389
|
)`,
|
|
20418
|
-
`INSERT OR IGNORE INTO
|
|
21390
|
+
`INSERT OR IGNORE INTO tb_search_meta_v5(
|
|
20419
21391
|
singleton, revision, seeded, cursor_secret
|
|
20420
21392
|
) VALUES (1, 0, 0, lower(hex(randomblob(32))))`,
|
|
20421
|
-
`CREATE TABLE IF NOT EXISTS
|
|
21393
|
+
`CREATE TABLE IF NOT EXISTS tb_search_snapshots_v5 (
|
|
20422
21394
|
path TEXT PRIMARY KEY,
|
|
20423
21395
|
digest TEXT NOT NULL
|
|
20424
21396
|
)`,
|
|
20425
|
-
`CREATE TRIGGER IF NOT EXISTS
|
|
20426
|
-
BEFORE INSERT ON
|
|
21397
|
+
`CREATE TRIGGER IF NOT EXISTS tb_search_snapshots_v5_capacity
|
|
21398
|
+
BEFORE INSERT ON tb_search_snapshots_v5
|
|
20427
21399
|
WHEN NOT EXISTS (
|
|
20428
|
-
SELECT 1 FROM
|
|
20429
|
-
) AND (SELECT COUNT(*) FROM
|
|
21400
|
+
SELECT 1 FROM tb_search_snapshots_v5 WHERE path = new.path
|
|
21401
|
+
) AND (SELECT COUNT(*) FROM tb_search_snapshots_v5) >= ${TOOL_SEARCH_AUDIT_NODE_LIMIT}
|
|
20430
21402
|
BEGIN
|
|
20431
21403
|
SELECT RAISE(ABORT, 'tb_search_path_capacity');
|
|
20432
21404
|
END`
|
|
20433
21405
|
];
|
|
20434
21406
|
var TOOL_SEARCH_CAPACITY_MARKER = "tb_search_path_capacity";
|
|
20435
21407
|
var TOOL_SEARCH_INSERT_SQL = `
|
|
20436
|
-
INSERT INTO
|
|
20437
|
-
VALUES (?, ?, ?, ?)
|
|
21408
|
+
INSERT INTO tb_search_tools_v5 (path, name, description, effect, feedback)
|
|
21409
|
+
VALUES (?, ?, ?, ?, ?)
|
|
20438
21410
|
`;
|
|
20439
|
-
var TOOL_SEARCH_INSERT_SNAPSHOT_SQL = "INSERT INTO
|
|
20440
|
-
var META_SQL = "SELECT revision, seeded, cursor_secret FROM
|
|
20441
|
-
var SNAPSHOT_DIGESTS_SQL = "SELECT path, digest FROM
|
|
21411
|
+
var TOOL_SEARCH_INSERT_SNAPSHOT_SQL = "INSERT INTO tb_search_snapshots_v5(path, digest) VALUES (?, ?)";
|
|
21412
|
+
var META_SQL = "SELECT revision, seeded, cursor_secret FROM tb_search_meta_v5 WHERE singleton = 1";
|
|
21413
|
+
var SNAPSHOT_DIGESTS_SQL = "SELECT path, digest FROM tb_search_snapshots_v5 ORDER BY path";
|
|
20442
21414
|
var PATH_STATE_SQL = `
|
|
20443
21415
|
SELECT snapshots.digest,
|
|
20444
|
-
EXISTS(SELECT 1 FROM
|
|
20445
|
-
(SELECT COUNT(*) FROM
|
|
21416
|
+
EXISTS(SELECT 1 FROM tb_search_tools_v5 WHERE path = ?) AS has_tools,
|
|
21417
|
+
(SELECT COUNT(*) FROM tb_search_snapshots_v5) AS path_count
|
|
20446
21418
|
FROM (SELECT 1) AS singleton
|
|
20447
|
-
LEFT JOIN
|
|
21419
|
+
LEFT JOIN tb_search_snapshots_v5 AS snapshots ON snapshots.path = ?
|
|
20448
21420
|
`;
|
|
20449
|
-
var PRESENT_SQL = "SELECT 1 AS present FROM
|
|
21421
|
+
var PRESENT_SQL = "SELECT 1 AS present FROM tb_search_tools_v5 WHERE path = ? LIMIT 1";
|
|
20450
21422
|
var PRESENT_PREFIX_SQL = `
|
|
20451
|
-
SELECT 1 AS present FROM
|
|
21423
|
+
SELECT 1 AS present FROM tb_search_tools_v5
|
|
20452
21424
|
WHERE path = ? OR substr(path, 1, length(?) + 1) = ? || '/'
|
|
20453
21425
|
LIMIT 1
|
|
20454
21426
|
`;
|
|
20455
|
-
var DELETE_TOOLS_SQL = "DELETE FROM
|
|
20456
|
-
var DELETE_SNAPSHOT_SQL = "DELETE FROM
|
|
21427
|
+
var DELETE_TOOLS_SQL = "DELETE FROM tb_search_tools_v5 WHERE path = ?";
|
|
21428
|
+
var DELETE_SNAPSHOT_SQL = "DELETE FROM tb_search_snapshots_v5 WHERE path = ?";
|
|
20457
21429
|
var DELETE_TOOLS_PREFIX_SQL = `
|
|
20458
|
-
DELETE FROM
|
|
21430
|
+
DELETE FROM tb_search_tools_v5
|
|
20459
21431
|
WHERE path = ? OR substr(path, 1, length(?) + 1) = ? || '/'
|
|
20460
21432
|
`;
|
|
20461
21433
|
var DELETE_SNAPSHOT_PREFIX_SQL = `
|
|
20462
|
-
DELETE FROM
|
|
21434
|
+
DELETE FROM tb_search_snapshots_v5
|
|
20463
21435
|
WHERE path = ? OR substr(path, 1, length(?) + 1) = ? || '/'
|
|
20464
21436
|
`;
|
|
20465
|
-
var DELETE_ALL_TOOLS_SQL = "DELETE FROM
|
|
20466
|
-
var DELETE_ALL_SNAPSHOTS_SQL = "DELETE FROM
|
|
20467
|
-
var BUMP_REVISION_SQL = "UPDATE
|
|
20468
|
-
var COMPLETE_REBUILD_SQL = "UPDATE
|
|
20469
|
-
function
|
|
20470
|
-
|
|
20471
|
-
|
|
20472
|
-
|
|
21437
|
+
var DELETE_ALL_TOOLS_SQL = "DELETE FROM tb_search_tools_v5";
|
|
21438
|
+
var DELETE_ALL_SNAPSHOTS_SQL = "DELETE FROM tb_search_snapshots_v5";
|
|
21439
|
+
var BUMP_REVISION_SQL = "UPDATE tb_search_meta_v5 SET revision = revision + 1 WHERE singleton = 1";
|
|
21440
|
+
var COMPLETE_REBUILD_SQL = "UPDATE tb_search_meta_v5 SET seeded = 1, revision = revision + 1 WHERE singleton = 1";
|
|
21441
|
+
function toolSearchEffectSql(effect) {
|
|
21442
|
+
switch (effect) {
|
|
21443
|
+
case "read":
|
|
21444
|
+
return "'read'";
|
|
21445
|
+
case "write":
|
|
21446
|
+
return "'write'";
|
|
21447
|
+
case "destructive":
|
|
21448
|
+
return "'destructive'";
|
|
21449
|
+
case "unknown":
|
|
21450
|
+
return "'unknown'";
|
|
21451
|
+
default:
|
|
21452
|
+
throw new TBError("invalid_argument", `\u5DE5\u5177\u641C\u7D22 effect '${String(effect)}' \u975E\u6CD5`);
|
|
21453
|
+
}
|
|
21454
|
+
}
|
|
21455
|
+
function toolSearchCandidateStatement(units, totalTermCount, limit, offset, constraints, syntax) {
|
|
21456
|
+
if (!Number.isInteger(totalTermCount) || totalTermCount < 1 || units.some((unit) => !Number.isInteger(unit.logicalTermId) || unit.logicalTermId < 0 || unit.logicalTermId >= totalTermCount)) {
|
|
21457
|
+
throw new TBError("invalid_argument", "\u5DE5\u5177\u641C\u7D22 logical term \u8BA1\u5212\u975E\u6CD5");
|
|
21458
|
+
}
|
|
21459
|
+
const values = units.map((unit, index) => `(${unit.logicalTermId}, ${syntax.placeholder(index + 1)}, ${unit.tier}, ${searchUnitAllowsPath(unit) ? 1 : 0})`).join(", ");
|
|
21460
|
+
const hasPrefix = constraints.pathPrefix !== void 0;
|
|
21461
|
+
const prefixParam = hasPrefix ? syntax.placeholder(units.length + 1) : void 0;
|
|
21462
|
+
const trailingStart = units.length + (hasPrefix ? 2 : 1);
|
|
21463
|
+
const limitParam = syntax.placeholder(trailingStart);
|
|
21464
|
+
const offsetParam = syntax.placeholder(trailingStart + 1);
|
|
21465
|
+
const requiredMatchedTerms = constraints.minCoverage === void 0 ? 1 : Math.ceil(constraints.minCoverage * totalTermCount);
|
|
20473
21466
|
const like = syntax.likeOperator;
|
|
21467
|
+
const effectLiterals = constraints.effects?.map(toolSearchEffectSql);
|
|
21468
|
+
const prefixCte = hasPrefix ? `, prefix_filter(value) AS (VALUES (${prefixParam}))` : "";
|
|
21469
|
+
const filterPredicates = [
|
|
21470
|
+
...hasPrefix ? [
|
|
21471
|
+
`(
|
|
21472
|
+
tools.path = prefix_filter.value
|
|
21473
|
+
OR substr(tools.path, 1, length(prefix_filter.value) + 1)
|
|
21474
|
+
= prefix_filter.value || '/'
|
|
21475
|
+
)`
|
|
21476
|
+
] : [],
|
|
21477
|
+
...effectLiterals === void 0 ? [] : [`tools.effect IN (${effectLiterals.join(", ")})`]
|
|
21478
|
+
];
|
|
21479
|
+
const filteredToolsCte = filterPredicates.length > 0 ? `,
|
|
21480
|
+
filtered_tools AS (
|
|
21481
|
+
SELECT tools.*
|
|
21482
|
+
FROM tb_search_tools_v5 AS tools
|
|
21483
|
+
${hasPrefix ? "CROSS JOIN prefix_filter" : ""}
|
|
21484
|
+
WHERE ${filterPredicates.join("\n AND ")}
|
|
21485
|
+
)` : "";
|
|
21486
|
+
const candidateTable = filterPredicates.length > 0 ? "filtered_tools" : "tb_search_tools_v5";
|
|
20474
21487
|
return {
|
|
20475
|
-
params: [
|
|
21488
|
+
params: [
|
|
21489
|
+
...units.map((unit) => unit.pattern),
|
|
21490
|
+
...hasPrefix ? [constraints.pathPrefix] : [],
|
|
21491
|
+
limit + 1,
|
|
21492
|
+
offset
|
|
21493
|
+
],
|
|
20476
21494
|
sql: `
|
|
20477
|
-
WITH units(pattern, tier) AS (VALUES ${values})
|
|
20478
|
-
|
|
20479
|
-
SELECT tools.id, tools.path, tools.name,
|
|
20480
|
-
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
|
|
20488
|
-
|
|
20489
|
-
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
|
|
21495
|
+
WITH units(logical_term_id, pattern, tier, path_allowed) AS (VALUES ${values})${prefixCte}${filteredToolsCte},
|
|
21496
|
+
unit_matches AS (
|
|
21497
|
+
SELECT tools.id, tools.path, tools.name, units.logical_term_id,
|
|
21498
|
+
units.tier * CASE
|
|
21499
|
+
WHEN tools.name ${like} units.pattern ESCAPE '!' THEN 10
|
|
21500
|
+
WHEN units.path_allowed = 1
|
|
21501
|
+
AND tools.path ${like} units.pattern ESCAPE '!' THEN 5
|
|
21502
|
+
WHEN tools.description ${like} units.pattern ESCAPE '!' THEN 3
|
|
21503
|
+
WHEN tools.feedback ${like} units.pattern ESCAPE '!' THEN 1
|
|
21504
|
+
ELSE 0
|
|
21505
|
+
END AS unit_score
|
|
21506
|
+
FROM ${candidateTable} AS tools
|
|
21507
|
+
CROSS JOIN units
|
|
21508
|
+
),
|
|
21509
|
+
term_matches AS (
|
|
21510
|
+
SELECT id, path, name, logical_term_id, MAX(unit_score) AS term_score
|
|
21511
|
+
FROM unit_matches
|
|
21512
|
+
WHERE unit_score > 0
|
|
21513
|
+
GROUP BY id, path, name, logical_term_id
|
|
21514
|
+
),
|
|
21515
|
+
scored_tools AS (
|
|
21516
|
+
SELECT id, path, name,
|
|
21517
|
+
CAST(COUNT(*) AS INTEGER) AS matched_term_count,
|
|
21518
|
+
CAST(SUM(term_score) AS INTEGER) AS score
|
|
21519
|
+
FROM term_matches
|
|
21520
|
+
GROUP BY id, path, name
|
|
21521
|
+
)
|
|
21522
|
+
SELECT id, path, name, matched_term_count, ${totalTermCount} AS total_term_count
|
|
21523
|
+
FROM scored_tools
|
|
21524
|
+
WHERE matched_term_count >= ${requiredMatchedTerms}
|
|
21525
|
+
ORDER BY matched_term_count DESC, score DESC, path, name
|
|
20494
21526
|
LIMIT ${limitParam} OFFSET ${offsetParam}
|
|
20495
21527
|
`
|
|
20496
21528
|
};
|
|
20497
21529
|
}
|
|
20498
21530
|
var sqliteSearchDialect = {
|
|
20499
|
-
candidateStatement: (query, limit, offset) =>
|
|
20500
|
-
|
|
20501
|
-
|
|
20502
|
-
|
|
20503
|
-
|
|
20504
|
-
|
|
21531
|
+
candidateStatement: (query, limit, offset, rawConstraints) => {
|
|
21532
|
+
const constraints = normalizeToolSearchOptions(rawConstraints);
|
|
21533
|
+
const prepared = prepareToolSearchQuery(
|
|
21534
|
+
query,
|
|
21535
|
+
constraints.pathPrefix === void 0 ? TOOL_SEARCH_UNIT_LIMIT : TOOL_SEARCH_UNIT_LIMIT - 1
|
|
21536
|
+
);
|
|
21537
|
+
return toolSearchCandidateStatement(
|
|
21538
|
+
prepared.units,
|
|
21539
|
+
prepared.totalTermCount,
|
|
21540
|
+
limit,
|
|
21541
|
+
offset,
|
|
21542
|
+
constraints,
|
|
21543
|
+
{ likeOperator: "LIKE", placeholder: () => "?" }
|
|
21544
|
+
);
|
|
21545
|
+
},
|
|
20505
21546
|
schemaStatements: TOOL_SEARCH_SCHEMA_STATEMENTS,
|
|
20506
21547
|
statements: {
|
|
20507
21548
|
bumpRevision: BUMP_REVISION_SQL,
|
|
@@ -20555,6 +21596,10 @@ var SqlSearchIndex = class {
|
|
|
20555
21596
|
await this.driver.ensureSchema();
|
|
20556
21597
|
return (await this.meta()).seeded === 1;
|
|
20557
21598
|
}
|
|
21599
|
+
async revision() {
|
|
21600
|
+
await this.driver.ensureSchema();
|
|
21601
|
+
return (await this.meta()).revision;
|
|
21602
|
+
}
|
|
20558
21603
|
async replace(path, tools, opts = {}) {
|
|
20559
21604
|
const canonical = normalizeToolSearchPath(path);
|
|
20560
21605
|
const records = serializeToolSearchSnapshot(canonical, tools, opts.feedback ?? "");
|
|
@@ -20633,6 +21678,8 @@ var SqlSearchIndex = class {
|
|
|
20633
21678
|
async search(query, opts) {
|
|
20634
21679
|
assertKeywordToolSearchMode(opts);
|
|
20635
21680
|
const normalized = normalizeToolSearchQuery(query);
|
|
21681
|
+
const constraints = normalizeToolSearchOptions(opts);
|
|
21682
|
+
const optionsFingerprint = toolSearchOptionsFingerprint(constraints);
|
|
20636
21683
|
const mode = opts?.mode ?? "keyword";
|
|
20637
21684
|
await this.driver.ensureSchema();
|
|
20638
21685
|
const meta3 = await this.meta();
|
|
@@ -20642,20 +21689,28 @@ var SqlSearchIndex = class {
|
|
|
20642
21689
|
normalized,
|
|
20643
21690
|
mode,
|
|
20644
21691
|
revision,
|
|
20645
|
-
meta3.cursor_secret
|
|
21692
|
+
meta3.cursor_secret,
|
|
21693
|
+
constraints
|
|
20646
21694
|
);
|
|
20647
21695
|
const limit = Math.min(normalizeToolSearchLimit(opts?.limit), TOOL_SEARCH_BATCH_LIMIT);
|
|
20648
21696
|
const rows = await this.driver.all(
|
|
20649
|
-
this.dialect.candidateStatement(normalized, limit, offset)
|
|
21697
|
+
this.dialect.candidateStatement(normalized, limit, offset, constraints)
|
|
20650
21698
|
);
|
|
20651
|
-
const
|
|
20652
|
-
const
|
|
21699
|
+
const firstBand = rows[0]?.matched_term_count;
|
|
21700
|
+
const bandEnd = constraints.matching === "best" && firstBand !== void 0 ? rows.findIndex((row) => row.matched_term_count !== firstBand) : -1;
|
|
21701
|
+
const pageLength = bandEnd >= 0 ? Math.min(limit, bandEnd) : Math.min(limit, rows.length);
|
|
21702
|
+
const page = rows.slice(0, pageLength);
|
|
21703
|
+
const hasMore = rows.length > page.length;
|
|
20653
21704
|
const items = page.map((row, index) => ({
|
|
21705
|
+
coverage: row.matched_term_count / row.total_term_count,
|
|
21706
|
+
matchedTermCount: row.matched_term_count,
|
|
20654
21707
|
name: row.name,
|
|
20655
21708
|
path: row.path,
|
|
20656
21709
|
ref: String(row.id),
|
|
20657
21710
|
resumeOffset: offset + index + 1,
|
|
20658
|
-
revision
|
|
21711
|
+
revision,
|
|
21712
|
+
searchOptionsFingerprint: optionsFingerprint,
|
|
21713
|
+
totalTermCount: row.total_term_count
|
|
20659
21714
|
}));
|
|
20660
21715
|
const last = items[items.length - 1];
|
|
20661
21716
|
return hasMore && last !== void 0 ? {
|
|
@@ -20665,7 +21720,8 @@ var SqlSearchIndex = class {
|
|
|
20665
21720
|
mode,
|
|
20666
21721
|
revision,
|
|
20667
21722
|
last.resumeOffset,
|
|
20668
|
-
meta3.cursor_secret
|
|
21723
|
+
meta3.cursor_secret,
|
|
21724
|
+
constraints
|
|
20669
21725
|
)
|
|
20670
21726
|
} : { items };
|
|
20671
21727
|
}
|
|
@@ -20675,11 +21731,10 @@ var SqlSearchIndex = class {
|
|
|
20675
21731
|
if (candidate.revision !== meta3.revision) {
|
|
20676
21732
|
throw new TBError("invalid_argument", "\u641C\u7D22 cursor \u5DF2\u5931\u6548");
|
|
20677
21733
|
}
|
|
20678
|
-
return await
|
|
21734
|
+
return await encodeToolSearchCursorForCandidate(
|
|
20679
21735
|
query,
|
|
20680
21736
|
mode,
|
|
20681
|
-
|
|
20682
|
-
candidate.resumeOffset,
|
|
21737
|
+
candidate,
|
|
20683
21738
|
meta3.cursor_secret
|
|
20684
21739
|
);
|
|
20685
21740
|
}
|
|
@@ -20687,11 +21742,12 @@ var SqlSearchIndex = class {
|
|
|
20687
21742
|
|
|
20688
21743
|
// ../core/src/search/pgSearchDialect.ts
|
|
20689
21744
|
var PG_SEARCH_SCHEMA_STATEMENTS = [
|
|
20690
|
-
`CREATE TABLE IF NOT EXISTS
|
|
21745
|
+
`CREATE TABLE IF NOT EXISTS tb_search_tools_v5 (
|
|
20691
21746
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
20692
21747
|
path text NOT NULL,
|
|
20693
21748
|
name text NOT NULL,
|
|
20694
21749
|
description text NOT NULL DEFAULT '',
|
|
21750
|
+
effect text NOT NULL CHECK (effect IN ('read', 'write', 'destructive', 'unknown')),
|
|
20695
21751
|
feedback text NOT NULL DEFAULT '',
|
|
20696
21752
|
UNIQUE(path, name)
|
|
20697
21753
|
)`,
|
|
@@ -20699,7 +21755,7 @@ var PG_SEARCH_SCHEMA_STATEMENTS = [
|
|
|
20699
21755
|
// 样本上对单 unit 多列 ILIKE 一律选 Seq Scan;GIN 让写入和体积显著增加却未改善
|
|
20700
21756
|
// 检索。4000 行只是代表样本,不是严格工具行上限(节点内还受 20KB JSON 约束);
|
|
20701
21757
|
// 查询单元数或容量边界变化时,应重跑 searchBench 并重新评估查询形状与索引。
|
|
20702
|
-
`CREATE TABLE IF NOT EXISTS
|
|
21758
|
+
`CREATE TABLE IF NOT EXISTS tb_search_meta_v5 (
|
|
20703
21759
|
singleton integer PRIMARY KEY CHECK (singleton = 1),
|
|
20704
21760
|
revision bigint NOT NULL DEFAULT 0,
|
|
20705
21761
|
seeded integer NOT NULL DEFAULT 0,
|
|
@@ -20707,32 +21763,32 @@ var PG_SEARCH_SCHEMA_STATEMENTS = [
|
|
|
20707
21763
|
)`,
|
|
20708
21764
|
// cursor_secret 需 64 hex(32 字节)。两个内置 gen_random_uuid() 去连字符正好 64
|
|
20709
21765
|
// hex,避免依赖 pgcrypto 的 gen_random_bytes(自托管 PG 未必预装 pgcrypto)。
|
|
20710
|
-
`INSERT INTO
|
|
21766
|
+
`INSERT INTO tb_search_meta_v5(singleton, revision, seeded, cursor_secret)
|
|
20711
21767
|
VALUES (
|
|
20712
21768
|
1, 0, 0,
|
|
20713
21769
|
replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', '')
|
|
20714
21770
|
)
|
|
20715
21771
|
ON CONFLICT (singleton) DO NOTHING`,
|
|
20716
|
-
`CREATE TABLE IF NOT EXISTS
|
|
21772
|
+
`CREATE TABLE IF NOT EXISTS tb_search_snapshots_v5 (
|
|
20717
21773
|
path text PRIMARY KEY,
|
|
20718
21774
|
digest text NOT NULL
|
|
20719
21775
|
)`,
|
|
20720
|
-
`CREATE OR REPLACE FUNCTION
|
|
21776
|
+
`CREATE OR REPLACE FUNCTION tb_search_snapshots_v5_capacity()
|
|
20721
21777
|
RETURNS trigger AS $$
|
|
20722
21778
|
BEGIN
|
|
20723
|
-
IF (SELECT COUNT(*) FROM
|
|
21779
|
+
IF (SELECT COUNT(*) FROM tb_search_snapshots_v5) >= ${TOOL_SEARCH_AUDIT_NODE_LIMIT} THEN
|
|
20724
21780
|
RAISE EXCEPTION '${TOOL_SEARCH_CAPACITY_MARKER}';
|
|
20725
21781
|
END IF;
|
|
20726
21782
|
RETURN NEW;
|
|
20727
21783
|
END;
|
|
20728
21784
|
$$ LANGUAGE plpgsql`,
|
|
20729
|
-
`DROP TRIGGER IF EXISTS
|
|
20730
|
-
`CREATE TRIGGER
|
|
20731
|
-
BEFORE INSERT ON
|
|
20732
|
-
FOR EACH ROW EXECUTE FUNCTION
|
|
21785
|
+
`DROP TRIGGER IF EXISTS tb_search_snapshots_v5_capacity_trg ON tb_search_snapshots_v5`,
|
|
21786
|
+
`CREATE TRIGGER tb_search_snapshots_v5_capacity_trg
|
|
21787
|
+
BEFORE INSERT ON tb_search_snapshots_v5
|
|
21788
|
+
FOR EACH ROW EXECUTE FUNCTION tb_search_snapshots_v5_capacity()`
|
|
20733
21789
|
];
|
|
20734
21790
|
var PG_SEARCH_WRITE_LOCK_KEY = 2069799624;
|
|
20735
|
-
var PG_SEARCH_INSERT_SNAPSHOT_SQL = "INSERT INTO
|
|
21791
|
+
var PG_SEARCH_INSERT_SNAPSHOT_SQL = "INSERT INTO tb_search_snapshots_v5(path, digest) VALUES ($1, $2)";
|
|
20736
21792
|
function pgBulkInsertSql(table, columns, rows) {
|
|
20737
21793
|
const tuples = [];
|
|
20738
21794
|
for (let r = 0; r < rows; r++) {
|
|
@@ -20741,50 +21797,65 @@ function pgBulkInsertSql(table, columns, rows) {
|
|
|
20741
21797
|
}
|
|
20742
21798
|
return `INSERT INTO ${table} (${columns.join(", ")}) VALUES ${tuples.join(", ")}`;
|
|
20743
21799
|
}
|
|
20744
|
-
var PG_SEARCH_TOOLS_TABLE = "
|
|
20745
|
-
var PG_SEARCH_TOOLS_COLUMNS = [
|
|
20746
|
-
|
|
21800
|
+
var PG_SEARCH_TOOLS_TABLE = "tb_search_tools_v5";
|
|
21801
|
+
var PG_SEARCH_TOOLS_COLUMNS = [
|
|
21802
|
+
"path",
|
|
21803
|
+
"name",
|
|
21804
|
+
"description",
|
|
21805
|
+
"effect",
|
|
21806
|
+
"feedback"
|
|
21807
|
+
];
|
|
21808
|
+
var PG_SEARCH_SNAPSHOTS_TABLE = "tb_search_snapshots_v5";
|
|
20747
21809
|
var PG_SEARCH_SNAPSHOTS_COLUMNS = ["path", "digest"];
|
|
20748
21810
|
var PG_SEARCH_INSERT_ROWS_MAX = 1e3;
|
|
20749
21811
|
var META_SQL2 = `
|
|
20750
21812
|
SELECT revision::int AS revision, seeded::int AS seeded, cursor_secret
|
|
20751
|
-
FROM
|
|
21813
|
+
FROM tb_search_meta_v5 WHERE singleton = 1
|
|
20752
21814
|
`;
|
|
20753
|
-
var SNAPSHOT_DIGESTS_SQL2 = "SELECT path, digest FROM
|
|
21815
|
+
var SNAPSHOT_DIGESTS_SQL2 = "SELECT path, digest FROM tb_search_snapshots_v5 ORDER BY path";
|
|
20754
21816
|
var PATH_STATE_SQL2 = `
|
|
20755
21817
|
SELECT snapshots.digest,
|
|
20756
|
-
EXISTS(SELECT 1 FROM
|
|
20757
|
-
(SELECT COUNT(*) FROM
|
|
21818
|
+
EXISTS(SELECT 1 FROM tb_search_tools_v5 WHERE path = $1)::int AS has_tools,
|
|
21819
|
+
(SELECT COUNT(*) FROM tb_search_snapshots_v5)::int AS path_count
|
|
20758
21820
|
FROM (SELECT 1) AS singleton
|
|
20759
|
-
LEFT JOIN
|
|
21821
|
+
LEFT JOIN tb_search_snapshots_v5 AS snapshots ON snapshots.path = $2
|
|
20760
21822
|
`;
|
|
20761
|
-
var PRESENT_SQL2 = "SELECT 1 AS present FROM
|
|
21823
|
+
var PRESENT_SQL2 = "SELECT 1 AS present FROM tb_search_tools_v5 WHERE path = $1 LIMIT 1";
|
|
20762
21824
|
var PRESENT_PREFIX_SQL2 = `
|
|
20763
|
-
SELECT 1 AS present FROM
|
|
21825
|
+
SELECT 1 AS present FROM tb_search_tools_v5
|
|
20764
21826
|
WHERE path = $1 OR substr(path, 1, length($2) + 1) = $3 || '/'
|
|
20765
21827
|
LIMIT 1
|
|
20766
21828
|
`;
|
|
20767
|
-
var DELETE_TOOLS_SQL2 = "DELETE FROM
|
|
20768
|
-
var DELETE_SNAPSHOT_SQL2 = "DELETE FROM
|
|
21829
|
+
var DELETE_TOOLS_SQL2 = "DELETE FROM tb_search_tools_v5 WHERE path = $1";
|
|
21830
|
+
var DELETE_SNAPSHOT_SQL2 = "DELETE FROM tb_search_snapshots_v5 WHERE path = $1";
|
|
20769
21831
|
var DELETE_TOOLS_PREFIX_SQL2 = `
|
|
20770
|
-
DELETE FROM
|
|
21832
|
+
DELETE FROM tb_search_tools_v5
|
|
20771
21833
|
WHERE path = $1 OR substr(path, 1, length($2) + 1) = $3 || '/'
|
|
20772
21834
|
`;
|
|
20773
21835
|
var DELETE_SNAPSHOT_PREFIX_SQL2 = `
|
|
20774
|
-
DELETE FROM
|
|
21836
|
+
DELETE FROM tb_search_snapshots_v5
|
|
20775
21837
|
WHERE path = $1 OR substr(path, 1, length($2) + 1) = $3 || '/'
|
|
20776
21838
|
`;
|
|
20777
|
-
var DELETE_ALL_TOOLS_SQL2 = "DELETE FROM
|
|
20778
|
-
var DELETE_ALL_SNAPSHOTS_SQL2 = "DELETE FROM
|
|
20779
|
-
var BUMP_REVISION_SQL2 = "UPDATE
|
|
20780
|
-
var COMPLETE_REBUILD_SQL2 = "UPDATE
|
|
21839
|
+
var DELETE_ALL_TOOLS_SQL2 = "DELETE FROM tb_search_tools_v5";
|
|
21840
|
+
var DELETE_ALL_SNAPSHOTS_SQL2 = "DELETE FROM tb_search_snapshots_v5";
|
|
21841
|
+
var BUMP_REVISION_SQL2 = "UPDATE tb_search_meta_v5 SET revision = revision + 1 WHERE singleton = 1";
|
|
21842
|
+
var COMPLETE_REBUILD_SQL2 = "UPDATE tb_search_meta_v5 SET seeded = 1, revision = revision + 1 WHERE singleton = 1";
|
|
20781
21843
|
var pgSearchDialect = {
|
|
20782
|
-
candidateStatement: (query, limit, offset) =>
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
20786
|
-
|
|
20787
|
-
|
|
21844
|
+
candidateStatement: (query, limit, offset, rawConstraints) => {
|
|
21845
|
+
const constraints = normalizeToolSearchOptions(rawConstraints);
|
|
21846
|
+
const prepared = prepareToolSearchQuery(
|
|
21847
|
+
query,
|
|
21848
|
+
constraints.pathPrefix === void 0 ? TOOL_SEARCH_UNIT_LIMIT : TOOL_SEARCH_UNIT_LIMIT - 1
|
|
21849
|
+
);
|
|
21850
|
+
return toolSearchCandidateStatement(
|
|
21851
|
+
prepared.units,
|
|
21852
|
+
prepared.totalTermCount,
|
|
21853
|
+
limit,
|
|
21854
|
+
offset,
|
|
21855
|
+
constraints,
|
|
21856
|
+
{ likeOperator: "ILIKE", placeholder: (index) => `$${index}` }
|
|
21857
|
+
);
|
|
21858
|
+
},
|
|
20788
21859
|
schemaStatements: PG_SEARCH_SCHEMA_STATEMENTS,
|
|
20789
21860
|
statements: {
|
|
20790
21861
|
bumpRevision: BUMP_REVISION_SQL2,
|
|
@@ -21098,7 +22169,7 @@ function guessContentType(path) {
|
|
|
21098
22169
|
if (dot < 0) return "text/plain";
|
|
21099
22170
|
return CONTENT_TYPE_BY_EXT[path.slice(dot + 1).toLowerCase()] ?? "text/plain";
|
|
21100
22171
|
}
|
|
21101
|
-
function
|
|
22172
|
+
function clampLimit5(limit) {
|
|
21102
22173
|
if (limit === void 0) return LIST_LIMIT_DEFAULT;
|
|
21103
22174
|
if (!Number.isInteger(limit) || limit < 1) {
|
|
21104
22175
|
throw new TBError("invalid_argument", `limit \u975E\u6CD5:${limit}`);
|
|
@@ -21153,7 +22224,7 @@ function createSkillhubProvider(store, opts) {
|
|
|
21153
22224
|
};
|
|
21154
22225
|
return {
|
|
21155
22226
|
async list(listOpts) {
|
|
21156
|
-
const limit =
|
|
22227
|
+
const limit = clampLimit5(listOpts?.limit);
|
|
21157
22228
|
const res = await store.list(keyPrefix, {
|
|
21158
22229
|
delimiter: "/",
|
|
21159
22230
|
cursor: listOpts?.cursor,
|
|
@@ -21213,7 +22284,7 @@ function createSkillhubProvider(store, opts) {
|
|
|
21213
22284
|
if (typeof query !== "string" || query === "") {
|
|
21214
22285
|
throw new TBError("invalid_argument", "query \u4E0D\u80FD\u4E3A\u7A7A");
|
|
21215
22286
|
}
|
|
21216
|
-
const limit =
|
|
22287
|
+
const limit = clampLimit5(searchOpts?.limit);
|
|
21217
22288
|
const q = query.toLowerCase();
|
|
21218
22289
|
const after = searchOpts?.cursor;
|
|
21219
22290
|
const items = [];
|
|
@@ -21388,6 +22459,7 @@ function toolToCmd(nodePath, tool, index) {
|
|
|
21388
22459
|
if (tool.description !== void 0) {
|
|
21389
22460
|
cmd.h = index ? summarizeOneLine(tool.description) : tool.description;
|
|
21390
22461
|
}
|
|
22462
|
+
if (tool.delivery !== void 0) cmd.delivery = tool.delivery;
|
|
21391
22463
|
if (!index && tool.inputSchema !== void 0) cmd.inputSchema = tool.inputSchema;
|
|
21392
22464
|
if (!index && tool.outputSchema !== void 0) cmd.outputSchema = tool.outputSchema;
|
|
21393
22465
|
if (tool.effect !== void 0) cmd.effect = tool.effect;
|
|
@@ -21494,7 +22566,15 @@ function virtualizeTools(v, upstream) {
|
|
|
21494
22566
|
const hidden = new Set(v?.hide ?? []);
|
|
21495
22567
|
for (const tool of upstream) {
|
|
21496
22568
|
if (hidden.has(tool.name)) continue;
|
|
21497
|
-
const
|
|
22569
|
+
const projectedName = v ? virtualName(v, tool.name) : tool.name;
|
|
22570
|
+
const name = canonicalizeSegment(projectedName);
|
|
22571
|
+
const previous = reverse.get(name);
|
|
22572
|
+
if (previous !== void 0) {
|
|
22573
|
+
throw new TBError(
|
|
22574
|
+
"invalid_argument",
|
|
22575
|
+
`\u5DE5\u5177\u540D '${previous}' \u4E0E '${tool.name}' \u89C4\u8303\u5316\u540E\u51B2\u7A81:'${name}',\u8BF7\u663E\u5F0F rename`
|
|
22576
|
+
);
|
|
22577
|
+
}
|
|
21498
22578
|
const description = v?.describe?.[tool.name] ?? tool.description;
|
|
21499
22579
|
exposed.push({ ...tool, name, description });
|
|
21500
22580
|
reverse.set(name, tool.name);
|
|
@@ -21511,7 +22591,7 @@ function resolveUpstreamTool(v, source, virtualName2) {
|
|
|
21511
22591
|
}
|
|
21512
22592
|
|
|
21513
22593
|
// ../core/src/tree/registry.ts
|
|
21514
|
-
function
|
|
22594
|
+
function clampLimit6(limit) {
|
|
21515
22595
|
if (limit === void 0 || limit < 1) return LIST_LIMIT_DEFAULT;
|
|
21516
22596
|
return limit > LIST_LIMIT_MAX ? LIST_LIMIT_MAX : limit;
|
|
21517
22597
|
}
|
|
@@ -21612,7 +22692,7 @@ var NodeRegistryStore = class {
|
|
|
21612
22692
|
*/
|
|
21613
22693
|
async list(prefix, opts) {
|
|
21614
22694
|
const normPrefix = prefix === void 0 ? "" : canonicalizePath(prefix);
|
|
21615
|
-
const limit =
|
|
22695
|
+
const limit = clampLimit6(opts?.limit);
|
|
21616
22696
|
const all = (await this.scanAll()).filter((n) => isPrefixOf(normPrefix, n.path)).sort(byPath);
|
|
21617
22697
|
const cursor = opts?.cursor;
|
|
21618
22698
|
const start = cursor ? all.findIndex((n) => n.path === cursor) + 1 : 0;
|
|
@@ -21701,6 +22781,7 @@ var NodeRegistryStore = class {
|
|
|
21701
22781
|
description: node.description,
|
|
21702
22782
|
...node.config !== void 0 ? { config: node.config } : {},
|
|
21703
22783
|
...node.virtualize !== void 0 ? { virtualize: node.virtualize } : {},
|
|
22784
|
+
...opts.deviceId !== void 0 ? { deviceId: opts.deviceId } : {},
|
|
21704
22785
|
...opts.online !== void 0 ? { online: opts.online } : {},
|
|
21705
22786
|
...opts.lastSeenAt !== void 0 ? { lastSeenAt: opts.lastSeenAt } : {},
|
|
21706
22787
|
registeredBy,
|
|
@@ -21921,8 +23002,8 @@ function createPlugin(opts = {}) {
|
|
|
21921
23002
|
const b64 = raw.replaceAll("-", "+").replaceAll("_", "/");
|
|
21922
23003
|
const padded = b64.padEnd(b64.length + (4 - b64.length % 4) % 4, "=");
|
|
21923
23004
|
try {
|
|
21924
|
-
const
|
|
21925
|
-
return new TextDecoder().decode(
|
|
23005
|
+
const bytes2 = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
23006
|
+
return new TextDecoder().decode(bytes2);
|
|
21926
23007
|
} catch {
|
|
21927
23008
|
throw new TBError("invalid_argument", `${HEADER_TB_UPSTREAM_AUTH} is not valid base64url`);
|
|
21928
23009
|
}
|
|
@@ -22248,12 +23329,26 @@ export {
|
|
|
22248
23329
|
pluginTokenSecretName,
|
|
22249
23330
|
createStoreModule,
|
|
22250
23331
|
RemoteAllowlistStore,
|
|
23332
|
+
DeviceMailboxService,
|
|
22251
23333
|
tbErrorBodySchema,
|
|
22252
23334
|
nodeInputSchema,
|
|
22253
23335
|
helpJsonSchema,
|
|
22254
23336
|
treeJsonSchema,
|
|
23337
|
+
toolSearchRelevanceSchema,
|
|
23338
|
+
toolSearchSourceSchema,
|
|
23339
|
+
toolSearchSourceResultSchema,
|
|
22255
23340
|
toolSearchPageSchema,
|
|
22256
23341
|
toolSearchRequestSchema,
|
|
23342
|
+
deviceOperationSummarySchema,
|
|
23343
|
+
deviceOperationDetailSchema,
|
|
23344
|
+
deviceOperationListRequestSchema,
|
|
23345
|
+
deviceOperationListResponseSchema,
|
|
23346
|
+
deviceOperationIdentityRequestSchema,
|
|
23347
|
+
deviceOperationClaimRequestSchema,
|
|
23348
|
+
deviceOperationClaimResponseSchema,
|
|
23349
|
+
deviceOperationRenewRequestSchema,
|
|
23350
|
+
deviceOperationRenewResponseSchema,
|
|
23351
|
+
deviceOperationCompleteRequestSchema,
|
|
22257
23352
|
registryNodeSchema,
|
|
22258
23353
|
feedbackViewSchema,
|
|
22259
23354
|
feedbackDetailSchema,
|
|
@@ -22320,11 +23415,15 @@ export {
|
|
|
22320
23415
|
TOOL_SEARCH_BATCH_LIMIT,
|
|
22321
23416
|
TOOL_SEARCH_WORK_LIMIT,
|
|
22322
23417
|
TOOL_SEARCH_AUDIT_NODE_LIMIT,
|
|
23418
|
+
TOOL_SEARCH_DESCRIPTION_BYTES_MAX,
|
|
22323
23419
|
TOOL_SEARCH_PAGE_BYTES,
|
|
23420
|
+
TOOL_SEARCH_RANKING_VERSION,
|
|
22324
23421
|
normalizeToolSearchLimit,
|
|
22325
23422
|
normalizeToolSearchPath,
|
|
23423
|
+
normalizeToolSearchOptions,
|
|
22326
23424
|
serializeToolSearchSnapshot,
|
|
22327
23425
|
toolSearchSnapshotDigest,
|
|
23426
|
+
prepareToolSearchQuery,
|
|
22328
23427
|
TOOL_SEARCH_SCHEMA_STATEMENTS,
|
|
22329
23428
|
TOOL_SEARCH_INSERT_SQL,
|
|
22330
23429
|
TOOL_SEARCH_INSERT_SNAPSHOT_SQL,
|