@tool-bridge/server 0.15.0 → 0.16.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-QYTOPRFV.js → airtable-S3T4TAN7.js} +4 -4
- package/dist/{alt_text_generator_ai-2CFYNSZV.js → alt_text_generator_ai-4DIRIDKL.js} +4 -4
- package/dist/{amap-HGOBOHSZ.js → amap-LGVGNPML.js} +4 -4
- package/dist/{anthropic-3OXOQNSR.js → anthropic-27RFZO35.js} +4 -4
- package/dist/{apify-PUV2QGKL.js → apify-6WLYAIAS.js} +4 -4
- package/dist/{brave_search-DG75AVKA.js → brave_search-K2SVJ66F.js} +4 -4
- package/dist/{chunk-KH663T3A.js → chunk-2O27H4Y2.js} +1 -1
- package/dist/{chunk-NLPTB3IL.js → chunk-BB6ZLMTU.js} +1120 -114
- package/dist/{chunk-UI4E4T5Q.js → chunk-GKX72JMJ.js} +1 -1
- package/dist/{chunk-NQ67PIIS.js → chunk-VK3V2WTY.js} +2 -2
- package/dist/{chunk-DXXYNIXV.js → chunk-YB2FLNWI.js} +1611 -42
- package/dist/{circleci-EFNLX5AW.js → circleci-WV3ITU7Q.js} +4 -4
- package/dist/{clerk-UWDLMZVM.js → clerk-NFQEFKS4.js} +4 -4
- package/dist/{cloudflare_dns-WQXVDSPU.js → cloudflare_dns-2RK5ASBJ.js} +4 -4
- package/dist/{cohere-VPDIKUDP.js → cohere-5KEW7LWT.js} +4 -4
- package/dist/{confluence-NBOOQNTR.js → confluence-SZE63S46.js} +4 -4
- package/dist/{convertapi-6Z6OP25T.js → convertapi-QPG2JC5K.js} +4 -4
- package/dist/{currencyapi-CAMUWCTV.js → currencyapi-UO2APQRY.js} +4 -4
- package/dist/{deepseek-GRALXYXR.js → deepseek-LJELIMM3.js} +4 -4
- package/dist/{docker_hub-5AASF3TJ.js → docker_hub-2MFXSBOF.js} +4 -4
- package/dist/{dropbox-HS3VGRD3.js → dropbox-GQYNTTVA.js} +4 -4
- package/dist/{dub-KHERUTW6.js → dub-KI2LHXST.js} +4 -4
- package/dist/{e2b-4MGHPFXY.js → e2b-WO6ISY3C.js} +4 -4
- package/dist/{exa-O77PKTBT.js → exa-IZ7B3BPT.js} +4 -4
- package/dist/{fal_ai-TX2TZFMP.js → fal_ai-LTMF36GJ.js} +4 -4
- package/dist/{feishu-I7VPWO3C.js → feishu-WM7SLSFD.js} +3 -3
- package/dist/{feishu_custom_bot-D4XZDLES.js → feishu_custom_bot-66W3MXH2.js} +4 -4
- package/dist/{figma-SW3HZCZ5.js → figma-2Z274VQZ.js} +4 -4
- package/dist/{firehydrant-ND5DEW3L.js → firehydrant-IFI4R65V.js} +4 -4
- package/dist/{fixer-ZTSV7ZK3.js → fixer-BCCZEB3D.js} +4 -4
- package/dist/{fly-RZ7SZKFT.js → fly-755FXYB5.js} +4 -4
- package/dist/{front-Q5MZVUGX.js → front-4FZKKXXK.js} +4 -4
- package/dist/{geocodio-QPHHODHG.js → geocodio-WYKDJ3DP.js} +4 -4
- package/dist/{ghost-3Q6O7EWZ.js → ghost-5BNQTCOH.js} +4 -4
- package/dist/{github-LDMGJHXV.js → github-ULELF5VX.js} +4 -4
- package/dist/{gmail-6QBCI3TT.js → gmail-G34WT64J.js} +4 -4
- package/dist/{googlecalendar-LLSYY4ZJ.js → googlecalendar-BQU255SC.js} +4 -4
- package/dist/{googledocs-KRGVBGWY.js → googledocs-HPCNQXYJ.js} +4 -4
- package/dist/{grafana-OHPZMRHT.js → grafana-VOV24FIC.js} +4 -4
- package/dist/{graphhopper-2PBY7OX4.js → graphhopper-D5RXW6AP.js} +4 -4
- package/dist/index.d.ts +147 -61
- package/dist/index.js +2 -2
- package/dist/{ip2proxy-GPBQZKKG.js → ip2proxy-DAYO7EKZ.js} +4 -4
- package/dist/{ipgeolocation_io-YNTOLOFH.js → ipgeolocation_io-XD4PSLII.js} +4 -4
- package/dist/{ipqualityscore-CZLQUAQ7.js → ipqualityscore-K27QEAWK.js} +4 -4
- package/dist/{jina_ai-JIFEYH5C.js → jina_ai-E6UXFV2P.js} +4 -4
- package/dist/{jira-2DKTIWHK.js → jira-ZDSXGQ6W.js} +4 -4
- package/dist/{langsmith-UCX7NHKP.js → langsmith-TIFJRREW.js} +4 -4
- package/dist/{lemlist-IS6K2VJG.js → lemlist-DASEMJMA.js} +4 -4
- package/dist/{linear-AK6547ZS.js → linear-KE6UP4SP.js} +4 -4
- package/dist/{logsnag-EB5LICOH.js → logsnag-IMNDN4R5.js} +4 -4
- package/dist/main.js +101 -101
- package/dist/{mattermost-YOSWUA3R.js → mattermost-HHHMUXQA.js} +4 -4
- package/dist/{mem0-IICTSHLB.js → mem0-D2FGGOIY.js} +4 -4
- package/dist/{memos-DIV6MZPB.js → memos-4OVIGFPH.js} +4 -4
- package/dist/{metabase-ZGHNTCAT.js → metabase-33WCX5YD.js} +4 -4
- package/dist/{mistral_ai-IAG3WZYO.js → mistral_ai-QIMZ2JYL.js} +4 -4
- package/dist/{mother_duck-PYKG4ZHO.js → mother_duck-M6VWS2VH.js} +4 -4
- package/dist/{ngrok-BXN3DGTK.js → ngrok-Z2PKQBHH.js} +4 -4
- package/dist/{notes-L2UO36HQ.js → notes-SS4RPC4K.js} +2 -2
- package/dist/{notion-PVHVOV4R.js → notion-FJCNQ6I7.js} +4 -4
- package/dist/{open_exchange_rates-DVMOPFNQ.js → open_exchange_rates-WU57WTT5.js} +4 -4
- package/dist/{openai-VW6F5YTK.js → openai-JODZ4GKT.js} +4 -4
- package/dist/{opengraph_io-MQUREYWM.js → opengraph_io-SQRAUSV5.js} +4 -4
- package/dist/{openrouter-5MI3ODCU.js → openrouter-Q5XXEJZ7.js} +4 -4
- package/dist/{outline-B2U7NIVA.js → outline-RT2EHJ2X.js} +4 -4
- package/dist/{paddle-LDZTVYQO.js → paddle-OF2V7GAC.js} +4 -4
- package/dist/{perplexity-P7BNWFMH.js → perplexity-LNZEWLHG.js} +4 -4
- package/dist/{pinecone-3Y67EJYP.js → pinecone-JSJIXZYN.js} +4 -4
- package/dist/{pipedrive-4KFLGCJW.js → pipedrive-D4CKVUXR.js} +4 -4
- package/dist/{polar-NIULQPUD.js → polar-M7ZYP6UR.js} +4 -4
- package/dist/{posthog-UXGCIHRP.js → posthog-YV7CZOH4.js} +4 -4
- package/dist/{postmark-XT77YNQM.js → postmark-KYHLECRG.js} +4 -4
- package/dist/{prerender-37E6ZN6Z.js → prerender-HCMHBKV2.js} +4 -4
- package/dist/{pubmed-BO2W4IR6.js → pubmed-WYVLCCN3.js} +4 -4
- package/dist/{railway-5BACZNYC.js → railway-TWIRD5KR.js} +4 -4
- package/dist/{readwise-DXABSDKY.js → readwise-UZ467IQ7.js} +4 -4
- package/dist/{render-4MCO6RLA.js → render-MECUC5CC.js} +4 -4
- package/dist/{replicate-IVEPY4EP.js → replicate-A5MEK3SW.js} +4 -4
- package/dist/{resend-XJBJO46B.js → resend-BC6EAWJQ.js} +4 -4
- package/dist/{rootly-R6QDVF3D.js → rootly-ZM3QDUIS.js} +4 -4
- package/dist/{scrapfly-E24XNN32.js → scrapfly-WAML42QJ.js} +4 -4
- package/dist/{scrapingbee-QT3O334C.js → scrapingbee-DG7PET3V.js} +4 -4
- package/dist/{screenshot_fyi-MJNFPUFV.js → screenshot_fyi-HY6WTHIO.js} +4 -4
- package/dist/{semantic_scholar-BVKWS72R.js → semantic_scholar-C5IZA7IP.js} +4 -4
- package/dist/{sentry-52T5XARK.js → sentry-OKBPSTDM.js} +4 -4
- package/dist/{shodan-RTOTTCX2.js → shodan-PPC6DYU4.js} +4 -4
- package/dist/{shopify-3RVWQDZ6.js → shopify-EWGUHBXO.js} +4 -4
- package/dist/{stripe-M3T2K3EW.js → stripe-6RPDPC5W.js} +4 -4
- package/dist/{supabase-UUUXAEYR.js → supabase-SK7DQ42N.js} +4 -4
- package/dist/{tavily-LVOZQQKJ.js → tavily-YCG5VQMH.js} +4 -4
- package/dist/{telegram-WJQPCKQT.js → telegram-2OES5FYB.js} +4 -4
- package/dist/{telnyx-THKSKAI2.js → telnyx-POOSBBR4.js} +4 -4
- package/dist/{todoist-YTRGFFRC.js → todoist-M7YD6YSS.js} +4 -4
- package/dist/{trello-QBK6WKN2.js → trello-IS6V5W3E.js} +4 -4
- package/dist/{turso-EA44ZKLR.js → turso-RCFHUFVH.js} +4 -4
- package/dist/{twilio-GSW3V55S.js → twilio-OS2RXFGO.js} +4 -4
- package/dist/{umami-DVZ7XUYC.js → umami-PQ2SJ2LN.js} +4 -4
- package/dist/{unsplash-ZWUWQ7OS.js → unsplash-C6BPZO4E.js} +4 -4
- package/dist/{upstash_redis-BQIQB5NM.js → upstash_redis-WH6UER7H.js} +4 -4
- package/dist/{uptimerobot-2OJHTUPV.js → uptimerobot-M3YZGS6Z.js} +4 -4
- package/dist/{v2ex-IZF4BB4O.js → v2ex-R4JWTEHN.js} +4 -4
- package/dist/{vercel-PI3BJENX.js → vercel-FZVYUOQD.js} +4 -4
- package/dist/{wolfram_alpha_api-EYQ2QCHH.js → wolfram_alpha_api-2KOLO5DC.js} +4 -4
- package/dist/{wordpress-ETDA6XLX.js → wordpress-XRWGO6LG.js} +4 -4
- package/dist/{workos-D7INO4Y4.js → workos-WR4NKOYL.js} +4 -4
- package/dist/{zhihu-Q7FNHEDM.js → zhihu-BKZIB3WA.js} +4 -4
- package/package.json +3 -3
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
CONTEXT_CAPABILITIES,
|
|
4
4
|
CONTEXT_DIRECT_UPLOAD_CAPABILITY,
|
|
5
5
|
DEFAULT_MAX_NODES,
|
|
6
|
+
DEFAULT_STORE_DRIVER_KEY_ROOT,
|
|
6
7
|
DEVICE_CALL_TIMEOUT_MS,
|
|
7
8
|
DeviceGatewaySession,
|
|
8
9
|
FEEDBACK_HIDE_SCORE,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
SKRegistryStore,
|
|
33
34
|
SecretStoreImpl,
|
|
34
35
|
SqlSearchIndex,
|
|
36
|
+
StoreService,
|
|
35
37
|
TBError,
|
|
36
38
|
TB_ERROR_CODES,
|
|
37
39
|
TOOL_SEARCH_AUDIT_NODE_LIMIT,
|
|
@@ -79,6 +81,7 @@ import {
|
|
|
79
81
|
createObjectContextProvider,
|
|
80
82
|
createObjectContextUploadGrant,
|
|
81
83
|
createSkillhubProvider,
|
|
84
|
+
createStoreModule,
|
|
82
85
|
decodeDeviceFrame,
|
|
83
86
|
derivePresence,
|
|
84
87
|
deviceDirectoryHelpModel,
|
|
@@ -136,6 +139,7 @@ import {
|
|
|
136
139
|
shouldRefresh,
|
|
137
140
|
skillhubHelpModel,
|
|
138
141
|
skillhubScopeForCmd,
|
|
142
|
+
storeUri,
|
|
139
143
|
string,
|
|
140
144
|
toJSONSchema,
|
|
141
145
|
toolHelpModel,
|
|
@@ -146,12 +150,13 @@ import {
|
|
|
146
150
|
url,
|
|
147
151
|
validatePath,
|
|
148
152
|
virtualizeTools
|
|
149
|
-
} from "./chunk-
|
|
153
|
+
} from "./chunk-YB2FLNWI.js";
|
|
150
154
|
|
|
151
155
|
// src/config.ts
|
|
152
156
|
var DEFAULT_PORT = 8787;
|
|
153
157
|
var DEFAULT_MAX_HOPS = 4;
|
|
154
158
|
var DEFAULT_DEVICE_RECLAIM_SEC = 24 * 60 * 60;
|
|
159
|
+
var DEFAULT_STORE_CLEANUP_INTERVAL_SEC = 15 * 60;
|
|
155
160
|
function positiveIntEnv(value) {
|
|
156
161
|
const n = Number(value);
|
|
157
162
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : void 0;
|
|
@@ -183,6 +188,7 @@ function configFromEnv(env = process.env) {
|
|
|
183
188
|
allowInsecure
|
|
184
189
|
},
|
|
185
190
|
deviceReclaimSec: positiveIntEnv(env.TB_DEVICE_RECLAIM_SEC) ?? DEFAULT_DEVICE_RECLAIM_SEC,
|
|
191
|
+
storeCleanupIntervalSec: positiveIntEnv(env.TB_STORE_CLEANUP_INTERVAL_SEC) ?? DEFAULT_STORE_CLEANUP_INTERVAL_SEC,
|
|
186
192
|
// 0 合法(立即关停,本地/单机默认);positiveIntEnv 拒 0,故单独解析。
|
|
187
193
|
shutdownDrainSec: (() => {
|
|
188
194
|
const n = Number(env.TB_SHUTDOWN_DRAIN_SEC);
|
|
@@ -237,6 +243,34 @@ function configFromEnv(env = process.env) {
|
|
|
237
243
|
if (refTtl !== void 0) config.refTtlSec = refTtl;
|
|
238
244
|
const uploadGrantTtl = presignTtlEnv(env.TB_UPLOAD_GRANT_TTL_SEC);
|
|
239
245
|
if (uploadGrantTtl !== void 0) config.uploadGrantTtlSec = uploadGrantTtl;
|
|
246
|
+
const storeMaxObjectBytes = positiveIntEnv(env.TB_STORE_MAX_OBJECT_BYTES);
|
|
247
|
+
if (storeMaxObjectBytes !== void 0) config.storeMaxObjectBytes = storeMaxObjectBytes;
|
|
248
|
+
const storeRelayMaxBytes = positiveIntEnv(env.TB_STORE_RELAY_MAX_BYTES);
|
|
249
|
+
if (storeRelayMaxBytes !== void 0) config.storeRelayMaxBytes = storeRelayMaxBytes;
|
|
250
|
+
const storeUploadTtlSec = presignTtlEnv(env.TB_STORE_UPLOAD_TTL_SEC);
|
|
251
|
+
if (storeUploadTtlSec !== void 0) config.storeUploadTtlSec = storeUploadTtlSec;
|
|
252
|
+
const storeShareTtlSec = presignTtlEnv(env.TB_STORE_SHARE_TTL_SEC);
|
|
253
|
+
if (storeShareTtlSec !== void 0) config.storeShareTtlSec = storeShareTtlSec;
|
|
254
|
+
const storeReadTtlSec = presignTtlEnv(env.TB_STORE_READ_TTL_SEC);
|
|
255
|
+
if (storeReadTtlSec !== void 0) config.storeReadTtlSec = storeReadTtlSec;
|
|
256
|
+
const storeCallMaxBytes = positiveIntEnv(env.TB_STORE_CALL_MAX_BYTES);
|
|
257
|
+
if (storeCallMaxBytes !== void 0) config.storeCallMaxBytes = storeCallMaxBytes;
|
|
258
|
+
const storeCallMaxObjectBytes = positiveIntEnv(env.TB_STORE_CALL_MAX_OBJECT_BYTES);
|
|
259
|
+
if (storeCallMaxObjectBytes !== void 0) {
|
|
260
|
+
config.storeCallMaxObjectBytes = storeCallMaxObjectBytes;
|
|
261
|
+
}
|
|
262
|
+
const storeCallMaxObjects = positiveIntEnv(env.TB_STORE_CALL_MAX_OBJECTS);
|
|
263
|
+
if (storeCallMaxObjects !== void 0) config.storeCallMaxObjects = storeCallMaxObjects;
|
|
264
|
+
const storeCallAllowedContentTypes = (env.TB_STORE_CALL_ALLOWED_CONTENT_TYPES ?? "").split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
265
|
+
if (storeCallAllowedContentTypes.length > 0) {
|
|
266
|
+
config.storeCallAllowedContentTypes = storeCallAllowedContentTypes;
|
|
267
|
+
}
|
|
268
|
+
if (env.TB_STORE_TOKEN_SECRET !== void 0 && env.TB_STORE_TOKEN_SECRET.length > 0) {
|
|
269
|
+
if (env.TB_STORE_TOKEN_SECRET.length < 16) {
|
|
270
|
+
throw new Error("TB_STORE_TOKEN_SECRET \u81F3\u5C11\u9700\u8981 16 \u4E2A\u5B57\u7B26");
|
|
271
|
+
}
|
|
272
|
+
config.storeTokenSecret = env.TB_STORE_TOKEN_SECRET;
|
|
273
|
+
}
|
|
240
274
|
return config;
|
|
241
275
|
}
|
|
242
276
|
|
|
@@ -439,7 +473,8 @@ var BUILTIN_MODULES = [
|
|
|
439
473
|
"plugin",
|
|
440
474
|
"catalog",
|
|
441
475
|
"federation",
|
|
442
|
-
"annotation"
|
|
476
|
+
"annotation",
|
|
477
|
+
"store"
|
|
443
478
|
];
|
|
444
479
|
var BUILTIN_DESCRIPTIONS = {
|
|
445
480
|
sk: "SecretKey registry",
|
|
@@ -449,7 +484,8 @@ var BUILTIN_DESCRIPTIONS = {
|
|
|
449
484
|
plugin: "Plugin registry (external plugins)",
|
|
450
485
|
catalog: "Built-in integration catalog (read-only)",
|
|
451
486
|
federation: "Remote federation host allowlist",
|
|
452
|
-
annotation: "Admin notes shown in ~help of any path"
|
|
487
|
+
annotation: "Admin notes shown in ~help of any path",
|
|
488
|
+
store: "Deployment-level private object Store"
|
|
453
489
|
};
|
|
454
490
|
async function mintAdminWithPlaintext(store, plaintext, now) {
|
|
455
491
|
const { key } = await mintKey(adminBootstrapInput(), now);
|
|
@@ -554,10 +590,14 @@ async function presignS3Url(client, url2, ttlSec) {
|
|
|
554
590
|
}
|
|
555
591
|
async function presignS3Put(client, url2, ttlSec, opts) {
|
|
556
592
|
assertPresignTtlSec(ttlSec);
|
|
593
|
+
if ("contentLength" in opts && (!Number.isSafeInteger(opts.contentLength) || opts.contentLength < 0)) {
|
|
594
|
+
throw new TBError("invalid_argument", "presigned PUT contentLength \u5FC5\u987B\u662F\u975E\u8D1F\u5B89\u5168\u6574\u6570");
|
|
595
|
+
}
|
|
557
596
|
const target = new URL(url2);
|
|
558
597
|
target.searchParams.set("X-Amz-Expires", String(ttlSec));
|
|
559
598
|
const headers = {
|
|
560
599
|
"content-type": opts.contentType,
|
|
600
|
+
..."contentLength" in opts ? { "content-length": String(opts.contentLength) } : {},
|
|
561
601
|
...opts.ifNoneMatch === void 0 ? {} : { "if-none-match": opts.ifNoneMatch }
|
|
562
602
|
};
|
|
563
603
|
const signed = await client.sign(
|
|
@@ -602,12 +642,19 @@ function createS3ObjectStore(cfg, opts) {
|
|
|
602
642
|
service: "s3",
|
|
603
643
|
region: cfg.region ?? "auto"
|
|
604
644
|
});
|
|
645
|
+
const putClient = new AwsClient({
|
|
646
|
+
accessKeyId: cfg.accessKeyId,
|
|
647
|
+
secretAccessKey: cfg.secretAccessKey,
|
|
648
|
+
service: "s3",
|
|
649
|
+
region: cfg.region ?? "auto",
|
|
650
|
+
retries: 0
|
|
651
|
+
});
|
|
605
652
|
const base = cfg.endpoint.replace(/\/+$/, "");
|
|
606
653
|
const bucketUrl = `${base}/${cfg.bucket}`;
|
|
607
654
|
const urlFor = (key) => `${bucketUrl}/${encodeObjectKey(key)}`;
|
|
608
|
-
const
|
|
655
|
+
const s3FetchWith = async (aws, url2, init) => {
|
|
609
656
|
try {
|
|
610
|
-
return await
|
|
657
|
+
return await aws.fetch(url2, init);
|
|
611
658
|
} catch (err) {
|
|
612
659
|
throw normalizeUpstreamError({
|
|
613
660
|
kind: "network",
|
|
@@ -615,6 +662,47 @@ function createS3ObjectStore(cfg, opts) {
|
|
|
615
662
|
});
|
|
616
663
|
}
|
|
617
664
|
};
|
|
665
|
+
const s3Fetch = async (url2, init) => {
|
|
666
|
+
return await s3FetchWith(client, url2, init);
|
|
667
|
+
};
|
|
668
|
+
const fetchBody = (body) => {
|
|
669
|
+
if (typeof body === "string" || body instanceof ArrayBuffer) return body;
|
|
670
|
+
if (body instanceof Uint8Array) {
|
|
671
|
+
return body.buffer instanceof ArrayBuffer ? new Uint8Array(body.buffer, body.byteOffset, body.byteLength) : new Uint8Array(body);
|
|
672
|
+
}
|
|
673
|
+
const source = body;
|
|
674
|
+
const reader = source.getReader();
|
|
675
|
+
return new ReadableStream({
|
|
676
|
+
async pull(controller) {
|
|
677
|
+
try {
|
|
678
|
+
const chunk = await reader.read();
|
|
679
|
+
if (chunk.done) {
|
|
680
|
+
reader.releaseLock();
|
|
681
|
+
controller.close();
|
|
682
|
+
} else if (chunk.value !== void 0) {
|
|
683
|
+
controller.enqueue(chunk.value);
|
|
684
|
+
}
|
|
685
|
+
} catch (error) {
|
|
686
|
+
try {
|
|
687
|
+
reader.releaseLock();
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
controller.error(error);
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
async cancel(reason) {
|
|
694
|
+
try {
|
|
695
|
+
if (reader.cancel !== void 0) await reader.cancel(reason);
|
|
696
|
+
else await source.cancel?.(reason);
|
|
697
|
+
} finally {
|
|
698
|
+
try {
|
|
699
|
+
reader.releaseLock();
|
|
700
|
+
} catch {
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}, { highWaterMark: 0 });
|
|
705
|
+
};
|
|
618
706
|
const metaFromHeaders = (key, headers) => {
|
|
619
707
|
const metadata = {};
|
|
620
708
|
headers.forEach((value, name) => {
|
|
@@ -633,13 +721,14 @@ function createS3ObjectStore(cfg, opts) {
|
|
|
633
721
|
if (contentType !== null) meta.contentType = contentType;
|
|
634
722
|
return meta;
|
|
635
723
|
};
|
|
724
|
+
const headObject = async (key) => {
|
|
725
|
+
const resp = await s3Fetch(urlFor(key), { method: "HEAD" });
|
|
726
|
+
if (resp.status === 404) return null;
|
|
727
|
+
if (!resp.ok) throw s3Error("head", resp.status);
|
|
728
|
+
return metaFromHeaders(key, resp.headers);
|
|
729
|
+
};
|
|
636
730
|
return {
|
|
637
|
-
|
|
638
|
-
const resp = await s3Fetch(urlFor(key), { method: "HEAD" });
|
|
639
|
-
if (resp.status === 404) return null;
|
|
640
|
-
if (!resp.ok) throw s3Error("head", resp.status);
|
|
641
|
-
return metaFromHeaders(key, resp.headers);
|
|
642
|
-
},
|
|
731
|
+
head: headObject,
|
|
643
732
|
async get(key) {
|
|
644
733
|
const resp = await s3Fetch(urlFor(key), { method: "GET" });
|
|
645
734
|
if (resp.status === 404) {
|
|
@@ -653,27 +742,31 @@ function createS3ObjectStore(cfg, opts) {
|
|
|
653
742
|
return { meta: metaFromHeaders(key, resp.headers), body: resp.body ?? EMPTY_STREAM };
|
|
654
743
|
},
|
|
655
744
|
async put(key, body, putOpts) {
|
|
656
|
-
const bytes = await objectBodyToBytes(body);
|
|
657
745
|
const headers = {};
|
|
746
|
+
headers["x-amz-content-sha256"] = "UNSIGNED-PAYLOAD";
|
|
658
747
|
if (putOpts?.contentType !== void 0) headers["content-type"] = putOpts.contentType;
|
|
659
748
|
for (const [name, value] of Object.entries(putOpts?.metadata ?? {})) {
|
|
660
749
|
headers[`${META_HEADER_PREFIX}${name}`] = value;
|
|
661
750
|
}
|
|
662
751
|
if (putOpts?.ifMatchEtag !== void 0) headers["if-match"] = `"${putOpts.ifMatchEtag}"`;
|
|
663
|
-
|
|
752
|
+
if (putOpts?.ifNoneMatch !== void 0) headers["if-none-match"] = putOpts.ifNoneMatch;
|
|
753
|
+
const resp = await s3FetchWith(putClient, urlFor(key), {
|
|
754
|
+
method: "PUT",
|
|
755
|
+
headers,
|
|
756
|
+
body: fetchBody(body)
|
|
757
|
+
});
|
|
664
758
|
await resp.body?.cancel();
|
|
665
|
-
|
|
666
|
-
|
|
759
|
+
const conditional = putOpts?.ifMatchEtag !== void 0 || putOpts?.ifNoneMatch !== void 0;
|
|
760
|
+
if (conditional && (resp.status === 409 || resp.status === 412) || putOpts?.ifMatchEtag !== void 0 && resp.status === 404) {
|
|
761
|
+
throw new TBError("conflict", `\u5BF9\u8C61\u6761\u4EF6\u5199\u51B2\u7A81:'${key}'`);
|
|
667
762
|
}
|
|
668
763
|
if (!resp.ok) throw s3Error("put", resp.status);
|
|
669
|
-
const meta =
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
};
|
|
676
|
-
if (putOpts?.contentType !== void 0) meta.contentType = putOpts.contentType;
|
|
764
|
+
const meta = await headObject(key);
|
|
765
|
+
if (meta === null) {
|
|
766
|
+
throw new TBError("unavailable", "s3 put succeeded but HEAD did not observe the object", {
|
|
767
|
+
retryable: true
|
|
768
|
+
});
|
|
769
|
+
}
|
|
677
770
|
return meta;
|
|
678
771
|
},
|
|
679
772
|
async delete(key) {
|
|
@@ -730,6 +823,9 @@ function createS3ObjectStore(cfg, opts) {
|
|
|
730
823
|
},
|
|
731
824
|
presignPut(key, ttlSec, putOpts) {
|
|
732
825
|
return presignS3Put(client, urlFor(key), ttlSec, putOpts);
|
|
826
|
+
},
|
|
827
|
+
presignPutExact(key, ttlSec, putOpts) {
|
|
828
|
+
return presignS3Put(client, urlFor(key), ttlSec, putOpts);
|
|
733
829
|
}
|
|
734
830
|
};
|
|
735
831
|
}
|
|
@@ -1029,6 +1125,461 @@ var SearchSynchronizer = class {
|
|
|
1029
1125
|
// ../app/src/deps.ts
|
|
1030
1126
|
var TOOL_CACHE_TTL_DEFAULT = 300;
|
|
1031
1127
|
|
|
1128
|
+
// ../app/src/storeRefToken.ts
|
|
1129
|
+
async function hmacKey(secret) {
|
|
1130
|
+
const digest = await crypto.subtle.digest(
|
|
1131
|
+
"SHA-256",
|
|
1132
|
+
new TextEncoder().encode(`tb-store-read-ref:v1:${secret}`)
|
|
1133
|
+
);
|
|
1134
|
+
return crypto.subtle.importKey("raw", digest, { name: "HMAC", hash: "SHA-256" }, false, [
|
|
1135
|
+
"sign",
|
|
1136
|
+
"verify"
|
|
1137
|
+
]);
|
|
1138
|
+
}
|
|
1139
|
+
async function signStoreRefToken(payload, secret) {
|
|
1140
|
+
const body = base64urlEncode(new TextEncoder().encode(JSON.stringify(payload)));
|
|
1141
|
+
const mac = await crypto.subtle.sign(
|
|
1142
|
+
"HMAC",
|
|
1143
|
+
await hmacKey(secret),
|
|
1144
|
+
new TextEncoder().encode(body)
|
|
1145
|
+
);
|
|
1146
|
+
return `${body}.${base64urlEncode(new Uint8Array(mac))}`;
|
|
1147
|
+
}
|
|
1148
|
+
async function verifyStoreRefToken(token, secret) {
|
|
1149
|
+
const dot = token.indexOf(".");
|
|
1150
|
+
if (dot <= 0) return null;
|
|
1151
|
+
const body = token.slice(0, dot);
|
|
1152
|
+
let signature;
|
|
1153
|
+
try {
|
|
1154
|
+
signature = base64urlDecode(token.slice(dot + 1));
|
|
1155
|
+
} catch {
|
|
1156
|
+
return null;
|
|
1157
|
+
}
|
|
1158
|
+
const valid = await crypto.subtle.verify(
|
|
1159
|
+
"HMAC",
|
|
1160
|
+
await hmacKey(secret),
|
|
1161
|
+
signature,
|
|
1162
|
+
new TextEncoder().encode(body)
|
|
1163
|
+
);
|
|
1164
|
+
if (!valid) return null;
|
|
1165
|
+
try {
|
|
1166
|
+
const payload = JSON.parse(
|
|
1167
|
+
new TextDecoder().decode(base64urlDecode(body))
|
|
1168
|
+
);
|
|
1169
|
+
if (payload.v !== 1 || typeof payload.objectId !== "string" || payload.objectId.length < 1 || typeof payload.exp !== "number" || !Number.isSafeInteger(payload.exp)) return null;
|
|
1170
|
+
return payload;
|
|
1171
|
+
} catch {
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// ../app/src/store.ts
|
|
1177
|
+
var STORE_CALL_CAPABILITY_HEADER = "x-tb-store-capability";
|
|
1178
|
+
var STORE_UPLOAD_HEADER = "x-tb-store-upload";
|
|
1179
|
+
var KEY_STORE_TOKEN_SECRET = "sys:store-token-secret:v1";
|
|
1180
|
+
var KEY_STORE_CLEANUP_PROGRESS = "sys:store-cleanup-progress:v1";
|
|
1181
|
+
var STORE_READ_TTL_SEC_DEFAULT = 15 * 60;
|
|
1182
|
+
var STORE_CALL_MAX_OBJECTS_DEFAULT = 4;
|
|
1183
|
+
var STORE_CALL_MAX_OBJECT_BYTES_DEFAULT = 256 * 1024 * 1024;
|
|
1184
|
+
var STORE_CALL_MAX_BYTES_DEFAULT = 512 * 1024 * 1024;
|
|
1185
|
+
function positiveInt(value, fallback, field) {
|
|
1186
|
+
const actual = value ?? fallback;
|
|
1187
|
+
if (!Number.isSafeInteger(actual) || actual < 1) {
|
|
1188
|
+
throw new TBError("invalid_argument", `${field} must be a positive integer`);
|
|
1189
|
+
}
|
|
1190
|
+
return actual;
|
|
1191
|
+
}
|
|
1192
|
+
function randomSecret() {
|
|
1193
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
1194
|
+
let out = "";
|
|
1195
|
+
for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
|
|
1196
|
+
return out;
|
|
1197
|
+
}
|
|
1198
|
+
async function derivedTokenSecret(root) {
|
|
1199
|
+
const digest = await crypto.subtle.digest(
|
|
1200
|
+
"SHA-256",
|
|
1201
|
+
new TextEncoder().encode(`tb-store-capability-root:v1:${root}`)
|
|
1202
|
+
);
|
|
1203
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1204
|
+
}
|
|
1205
|
+
async function storeTokenSecret(state, configured) {
|
|
1206
|
+
if (configured !== void 0) {
|
|
1207
|
+
if (configured.length < 16) {
|
|
1208
|
+
throw new TBError("invalid_argument", "storeTokenSecret must contain at least 16 characters");
|
|
1209
|
+
}
|
|
1210
|
+
return await derivedTokenSecret(configured);
|
|
1211
|
+
}
|
|
1212
|
+
const current = await state.get(KEY_STORE_TOKEN_SECRET);
|
|
1213
|
+
if (typeof current === "string" && current.length >= 16) return current;
|
|
1214
|
+
if (current !== null) {
|
|
1215
|
+
throw new TBError("internal", "default Store token secret state is invalid", {
|
|
1216
|
+
retryable: false
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
if (state.compareAndSwap === void 0) {
|
|
1220
|
+
throw new TBError("unavailable", "default Store requires StateStore.compareAndSwap", {
|
|
1221
|
+
retryable: false
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
const candidate = randomSecret();
|
|
1225
|
+
if (await state.compareAndSwap(KEY_STORE_TOKEN_SECRET, null, candidate)) return candidate;
|
|
1226
|
+
const winner = await state.get(KEY_STORE_TOKEN_SECRET);
|
|
1227
|
+
if (typeof winner !== "string" || winner.length < 16) {
|
|
1228
|
+
throw new TBError("unavailable", "default Store token secret initialization conflicted", {
|
|
1229
|
+
retryable: true
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
return winner;
|
|
1233
|
+
}
|
|
1234
|
+
function serviceOptions(deps, tokenSecret) {
|
|
1235
|
+
return {
|
|
1236
|
+
tokenSecret,
|
|
1237
|
+
...deps.storeMaxObjectBytes === void 0 ? {} : { maxObjectBytes: deps.storeMaxObjectBytes },
|
|
1238
|
+
...deps.storeRelayMaxBytes === void 0 ? {} : { relayMaxBytes: deps.storeRelayMaxBytes },
|
|
1239
|
+
...deps.storeUploadTtlSec === void 0 ? {} : { uploadTtlSec: deps.storeUploadTtlSec },
|
|
1240
|
+
...deps.storeShareTtlSec === void 0 ? {} : { shareTtlSec: deps.storeShareTtlSec }
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
async function defaultStoreRuntime(deps) {
|
|
1244
|
+
if (deps.objects === void 0) {
|
|
1245
|
+
throw new TBError("unavailable", "default Store object driver is not configured", {
|
|
1246
|
+
retryable: false
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
const [objects, tokenSecret] = await Promise.all([
|
|
1250
|
+
deps.objects(),
|
|
1251
|
+
storeTokenSecret(deps.state, deps.storeTokenSecret ?? deps.encryptionKey)
|
|
1252
|
+
]);
|
|
1253
|
+
return {
|
|
1254
|
+
objects,
|
|
1255
|
+
tokenSecret,
|
|
1256
|
+
service: new StoreService(deps.state, objects, serviceOptions(deps, tokenSecret))
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
function requestOrigin(runtime, deps) {
|
|
1260
|
+
const raw = deps.canonicalOrigin ?? runtime?.requestOrigin;
|
|
1261
|
+
if (raw === void 0) {
|
|
1262
|
+
throw new TBError("unavailable", "request origin is unavailable", { retryable: false });
|
|
1263
|
+
}
|
|
1264
|
+
let url2;
|
|
1265
|
+
try {
|
|
1266
|
+
url2 = new URL(raw);
|
|
1267
|
+
} catch {
|
|
1268
|
+
throw new TBError("unavailable", "request origin is invalid", { retryable: false });
|
|
1269
|
+
}
|
|
1270
|
+
if (url2.protocol !== "https:" && url2.protocol !== "http:" || url2.username !== "" || url2.password !== "") {
|
|
1271
|
+
throw new TBError("unavailable", "request origin is invalid", { retryable: false });
|
|
1272
|
+
}
|
|
1273
|
+
return url2.origin;
|
|
1274
|
+
}
|
|
1275
|
+
function storeUploadGrant(start, origin2) {
|
|
1276
|
+
const useRelay = start.alreadyCompleted || start.signedRequest === void 0;
|
|
1277
|
+
const url2 = useRelay ? `${origin2}/~store/uploads/${encodeURIComponent(start.uploadId)}` : start.signedRequest.url;
|
|
1278
|
+
const headers = useRelay ? { [STORE_UPLOAD_HEADER]: start.uploadToken } : start.signedRequest.headers;
|
|
1279
|
+
return {
|
|
1280
|
+
uploadId: start.uploadId,
|
|
1281
|
+
objectUri: start.objectUri,
|
|
1282
|
+
transport: useRelay ? "relay" : "presigned-put",
|
|
1283
|
+
method: "PUT",
|
|
1284
|
+
url: url2,
|
|
1285
|
+
headers,
|
|
1286
|
+
expiresAt: start.expiresAt,
|
|
1287
|
+
maxBytes: start.maxBytes,
|
|
1288
|
+
uploadToken: start.uploadToken,
|
|
1289
|
+
...start.alreadyCompleted && start.descriptor !== void 0 ? { alreadyCompleted: true, descriptor: start.descriptor } : {}
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
function readTtlSec(deps) {
|
|
1293
|
+
return positiveInt(
|
|
1294
|
+
deps.storeReadTtlSec,
|
|
1295
|
+
deps.refTtlSec ?? STORE_READ_TTL_SEC_DEFAULT,
|
|
1296
|
+
"storeReadTtlSec"
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
async function ownerReadGrant(object2, runtime, deps, tokenSecret) {
|
|
1300
|
+
if (object2.status !== "ready" || object2.size === void 0) {
|
|
1301
|
+
throw TBError.notFound("Store object not found");
|
|
1302
|
+
}
|
|
1303
|
+
const ttlSec = readTtlSec(deps);
|
|
1304
|
+
const expiresAt = new Date(Date.now() + ttlSec * 1e3).toISOString();
|
|
1305
|
+
const token = await signStoreRefToken({
|
|
1306
|
+
v: 1,
|
|
1307
|
+
objectId: object2.id,
|
|
1308
|
+
exp: Math.floor(Date.parse(expiresAt) / 1e3)
|
|
1309
|
+
}, tokenSecret);
|
|
1310
|
+
return {
|
|
1311
|
+
$ref: `${requestOrigin(runtime, deps)}/~store/refs/${encodeURIComponent(token)}`,
|
|
1312
|
+
uri: storeUri(object2.id),
|
|
1313
|
+
contentType: object2.contentType,
|
|
1314
|
+
size: object2.size,
|
|
1315
|
+
expiresAt
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function shareReadGrant(result, runtime, deps) {
|
|
1319
|
+
return {
|
|
1320
|
+
shareId: result.shareId,
|
|
1321
|
+
uri: result.uri,
|
|
1322
|
+
expiresAt: result.expiresAt,
|
|
1323
|
+
$ref: `${requestOrigin(runtime, deps)}/~store/shares/${encodeURIComponent(result.token)}`
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
function defaultStoreModuleDeps(deps, store) {
|
|
1327
|
+
return {
|
|
1328
|
+
service: store.service,
|
|
1329
|
+
callbacks: {
|
|
1330
|
+
createUpload: (start, runtime) => storeUploadGrant(start, requestOrigin(runtime, deps)),
|
|
1331
|
+
read: async (object2, runtime) => await ownerReadGrant(
|
|
1332
|
+
object2,
|
|
1333
|
+
runtime,
|
|
1334
|
+
deps,
|
|
1335
|
+
store.tokenSecret
|
|
1336
|
+
),
|
|
1337
|
+
share: (result, runtime) => shareReadGrant(result, runtime, deps)
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
function lazyDefaultStoreModule(deps) {
|
|
1342
|
+
const helpOnly = createStoreModule({
|
|
1343
|
+
// createStoreModule.help is static and never reads these placeholders.
|
|
1344
|
+
service: void 0,
|
|
1345
|
+
callbacks: {
|
|
1346
|
+
createUpload: () => {
|
|
1347
|
+
throw new TBError("unavailable", "default Store is unavailable");
|
|
1348
|
+
},
|
|
1349
|
+
read: () => {
|
|
1350
|
+
throw new TBError("unavailable", "default Store is unavailable");
|
|
1351
|
+
},
|
|
1352
|
+
share: () => {
|
|
1353
|
+
throw new TBError("unavailable", "default Store is unavailable");
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
return {
|
|
1358
|
+
module: helpOnly.module,
|
|
1359
|
+
description: helpOnly.description,
|
|
1360
|
+
help: (path) => helpOnly.help(path),
|
|
1361
|
+
async dispatch(cmd, args, ctx, runtime) {
|
|
1362
|
+
const store = await defaultStoreRuntime(deps);
|
|
1363
|
+
return await createStoreModule(defaultStoreModuleDeps(deps, store)).dispatch(
|
|
1364
|
+
cmd,
|
|
1365
|
+
args,
|
|
1366
|
+
ctx,
|
|
1367
|
+
runtime
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
async function beginCapabilityUpload(deps, input, capabilityToken, origin2) {
|
|
1373
|
+
const store = await defaultStoreRuntime(deps);
|
|
1374
|
+
const start = await store.service.beginCallUpload(input, capabilityToken);
|
|
1375
|
+
return storeUploadGrant(start, origin2);
|
|
1376
|
+
}
|
|
1377
|
+
async function completeCapabilityUpload(deps, uploadId2, uploadToken) {
|
|
1378
|
+
return await (await defaultStoreRuntime(deps)).service.completeUploadWithToken(
|
|
1379
|
+
uploadId2,
|
|
1380
|
+
uploadToken
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
async function abortCapabilityUpload(deps, uploadId2, uploadToken) {
|
|
1384
|
+
return await (await defaultStoreRuntime(deps)).service.abortUploadWithToken(
|
|
1385
|
+
uploadId2,
|
|
1386
|
+
uploadToken
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
async function relayStoreUpload(deps, uploadId2, uploadToken, body) {
|
|
1390
|
+
const store = await defaultStoreRuntime(deps);
|
|
1391
|
+
const session = await store.service.verifyUploadToken(uploadToken);
|
|
1392
|
+
if (session.id !== uploadId2) {
|
|
1393
|
+
throw new TBError("permission_denied", "upload capability does not match the URL");
|
|
1394
|
+
}
|
|
1395
|
+
return await store.service.commitRelayUpload({
|
|
1396
|
+
uploadToken,
|
|
1397
|
+
body: body ?? new Uint8Array()
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
async function storeObjectResponse(objects, object2) {
|
|
1401
|
+
const got = await objects.get(object2.driverKey);
|
|
1402
|
+
if (got === null) throw TBError.notFound("not found");
|
|
1403
|
+
const reader = got.body.getReader();
|
|
1404
|
+
const body = new ReadableStream({
|
|
1405
|
+
async pull(controller) {
|
|
1406
|
+
try {
|
|
1407
|
+
const chunk = await reader.read();
|
|
1408
|
+
if (chunk.done) {
|
|
1409
|
+
reader.releaseLock();
|
|
1410
|
+
controller.close();
|
|
1411
|
+
} else if (chunk.value !== void 0) {
|
|
1412
|
+
controller.enqueue(chunk.value);
|
|
1413
|
+
}
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
try {
|
|
1416
|
+
reader.releaseLock();
|
|
1417
|
+
} catch {
|
|
1418
|
+
}
|
|
1419
|
+
controller.error(error);
|
|
1420
|
+
}
|
|
1421
|
+
},
|
|
1422
|
+
async cancel(reason) {
|
|
1423
|
+
try {
|
|
1424
|
+
if (reader.cancel !== void 0) await reader.cancel(reason);
|
|
1425
|
+
else await got.body.cancel?.(reason);
|
|
1426
|
+
} finally {
|
|
1427
|
+
try {
|
|
1428
|
+
reader.releaseLock();
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
return new Response(body, {
|
|
1435
|
+
headers: {
|
|
1436
|
+
"content-type": object2.contentType,
|
|
1437
|
+
"content-length": String(got.meta.size),
|
|
1438
|
+
"cache-control": "private, no-store"
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
async function issueDeviceCallUpload(deps, deviceId, callId, context) {
|
|
1443
|
+
if (deps.objects === void 0) return null;
|
|
1444
|
+
const store = await defaultStoreRuntime(deps);
|
|
1445
|
+
const maxObjects = positiveInt(
|
|
1446
|
+
deps.storeCallMaxObjects,
|
|
1447
|
+
STORE_CALL_MAX_OBJECTS_DEFAULT,
|
|
1448
|
+
"storeCallMaxObjects"
|
|
1449
|
+
);
|
|
1450
|
+
const maxBytes = positiveInt(
|
|
1451
|
+
deps.storeCallMaxBytes,
|
|
1452
|
+
STORE_CALL_MAX_BYTES_DEFAULT,
|
|
1453
|
+
"storeCallMaxBytes"
|
|
1454
|
+
);
|
|
1455
|
+
const maxObjectBytes = Math.min(
|
|
1456
|
+
positiveInt(
|
|
1457
|
+
deps.storeCallMaxObjectBytes,
|
|
1458
|
+
deps.storeMaxObjectBytes ?? STORE_CALL_MAX_OBJECT_BYTES_DEFAULT,
|
|
1459
|
+
"storeCallMaxObjectBytes"
|
|
1460
|
+
),
|
|
1461
|
+
maxBytes
|
|
1462
|
+
);
|
|
1463
|
+
const issued = await store.service.issueCallUploadCapability({
|
|
1464
|
+
callId,
|
|
1465
|
+
owner: context.caller.owner,
|
|
1466
|
+
producer: `device:${deviceId}`,
|
|
1467
|
+
expiresAt: context.expiresAt,
|
|
1468
|
+
maxObjects,
|
|
1469
|
+
maxBytes,
|
|
1470
|
+
maxObjectBytes,
|
|
1471
|
+
allowedContentTypes: deps.storeCallAllowedContentTypes ?? ["*/*"]
|
|
1472
|
+
});
|
|
1473
|
+
return {
|
|
1474
|
+
context: {
|
|
1475
|
+
...context,
|
|
1476
|
+
upload: {
|
|
1477
|
+
token: issued.token,
|
|
1478
|
+
expiresAt: issued.capability.expiresAt,
|
|
1479
|
+
// The SDK uses this as an early per-object check; the server remains
|
|
1480
|
+
// authoritative for the aggregate call budget.
|
|
1481
|
+
maxBytes: issued.capability.maxObjectBytes,
|
|
1482
|
+
maxObjects: issued.capability.maxObjects
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
revoke: async () => await store.service.revokeCallUploadCapability(issued.token)
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
function cleanupProgress(value) {
|
|
1489
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1490
|
+
throw new TBError("internal", "default Store cleanup progress is invalid");
|
|
1491
|
+
}
|
|
1492
|
+
const record2 = value;
|
|
1493
|
+
if (!Number.isSafeInteger(record2.revision) || record2.revision < 1) {
|
|
1494
|
+
throw new TBError("internal", "default Store cleanup progress is invalid");
|
|
1495
|
+
}
|
|
1496
|
+
const cursors = record2.cursors;
|
|
1497
|
+
if (cursors === null || typeof cursors !== "object" || Array.isArray(cursors)) {
|
|
1498
|
+
throw new TBError("internal", "default Store cleanup progress is invalid");
|
|
1499
|
+
}
|
|
1500
|
+
for (const field of [
|
|
1501
|
+
"uploads",
|
|
1502
|
+
"objects",
|
|
1503
|
+
"shares",
|
|
1504
|
+
"callCapabilities",
|
|
1505
|
+
"driverObjects",
|
|
1506
|
+
"idempotencyBindings"
|
|
1507
|
+
]) {
|
|
1508
|
+
const cursor = cursors[field];
|
|
1509
|
+
if (cursor !== null && typeof cursor !== "string") {
|
|
1510
|
+
throw new TBError("internal", "default Store cleanup progress is invalid");
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return value;
|
|
1514
|
+
}
|
|
1515
|
+
function emptyCleanupResult() {
|
|
1516
|
+
return {
|
|
1517
|
+
abandonedObjects: 0,
|
|
1518
|
+
deletedBytes: 0,
|
|
1519
|
+
deletedOrphans: 0,
|
|
1520
|
+
deletedStaging: 0,
|
|
1521
|
+
expiredCallCapabilities: 0,
|
|
1522
|
+
expiredIdempotencyBindings: 0,
|
|
1523
|
+
expiredShares: 0,
|
|
1524
|
+
expiredUploads: 0
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
function addCleanupResult(target, page) {
|
|
1528
|
+
target.abandonedObjects += page.abandonedObjects;
|
|
1529
|
+
target.deletedBytes += page.deletedBytes;
|
|
1530
|
+
target.deletedOrphans += page.deletedOrphans;
|
|
1531
|
+
target.deletedStaging += page.deletedStaging;
|
|
1532
|
+
target.expiredCallCapabilities += page.expiredCallCapabilities;
|
|
1533
|
+
target.expiredIdempotencyBindings += page.expiredIdempotencyBindings;
|
|
1534
|
+
target.expiredShares += page.expiredShares;
|
|
1535
|
+
target.expiredUploads += page.expiredUploads;
|
|
1536
|
+
}
|
|
1537
|
+
async function cleanupDefaultStore(deps, opts = {}) {
|
|
1538
|
+
await deps.ensureReady?.();
|
|
1539
|
+
const store = await defaultStoreRuntime(deps);
|
|
1540
|
+
const maxPages = Math.min(positiveInt(opts.maxPages, 8, "maxPages"), 64);
|
|
1541
|
+
const limit = opts.limit === void 0 ? void 0 : positiveInt(opts.limit, 200, "limit");
|
|
1542
|
+
const rawProgress = await deps.state.get(KEY_STORE_CLEANUP_PROGRESS);
|
|
1543
|
+
let progress = rawProgress === null ? null : cleanupProgress(rawProgress);
|
|
1544
|
+
let cursors = progress?.cursors;
|
|
1545
|
+
const aggregate = emptyCleanupResult();
|
|
1546
|
+
for (let pageNumber = 0; pageNumber < maxPages; pageNumber++) {
|
|
1547
|
+
const page = await store.service.cleanup({
|
|
1548
|
+
...limit === void 0 ? {} : { limit },
|
|
1549
|
+
...cursors === void 0 ? {} : { cursors },
|
|
1550
|
+
// staging 不使用权威状态 cursor;同一 host tick 只运行一次,避免
|
|
1551
|
+
// maxPages 内重复扫描同一 driver maintenance 范围。
|
|
1552
|
+
runDriverMaintenance: pageNumber === 0
|
|
1553
|
+
});
|
|
1554
|
+
addCleanupResult(aggregate, page);
|
|
1555
|
+
if (page.cursors === void 0) {
|
|
1556
|
+
if (progress !== null) {
|
|
1557
|
+
await deps.state.compareAndSwap(
|
|
1558
|
+
KEY_STORE_CLEANUP_PROGRESS,
|
|
1559
|
+
progress.revision,
|
|
1560
|
+
null
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
return aggregate;
|
|
1564
|
+
}
|
|
1565
|
+
const next = {
|
|
1566
|
+
cursors: page.cursors,
|
|
1567
|
+
revision: (progress?.revision ?? 0) + 1
|
|
1568
|
+
};
|
|
1569
|
+
const advanced = await deps.state.compareAndSwap(
|
|
1570
|
+
KEY_STORE_CLEANUP_PROGRESS,
|
|
1571
|
+
progress?.revision ?? null,
|
|
1572
|
+
next
|
|
1573
|
+
);
|
|
1574
|
+
if (!advanced) {
|
|
1575
|
+
return aggregate;
|
|
1576
|
+
}
|
|
1577
|
+
progress = next;
|
|
1578
|
+
cursors = next.cursors;
|
|
1579
|
+
}
|
|
1580
|
+
return { ...aggregate, ...cursors === void 0 ? {} : { cursors } };
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1032
1583
|
// ../app/src/deviceNodes.ts
|
|
1033
1584
|
function tbErrorFromBody(body) {
|
|
1034
1585
|
return new TBError(body.code, body.message, { retryable: body.retryable });
|
|
@@ -1041,7 +1592,19 @@ function requireDevice(deps) {
|
|
|
1041
1592
|
}
|
|
1042
1593
|
async function invokeDevice(deps, deviceId, req) {
|
|
1043
1594
|
const id = crypto.randomUUID();
|
|
1044
|
-
const
|
|
1595
|
+
const upload = req.context === void 0 ? null : await issueDeviceCallUpload(deps, deviceId, id, req.context);
|
|
1596
|
+
let body;
|
|
1597
|
+
try {
|
|
1598
|
+
body = await requireDevice(deps).invoke(deviceId, {
|
|
1599
|
+
id,
|
|
1600
|
+
path: req.path,
|
|
1601
|
+
arguments: req.arguments,
|
|
1602
|
+
...req.context === void 0 ? {} : { context: upload?.context ?? req.context }
|
|
1603
|
+
});
|
|
1604
|
+
} finally {
|
|
1605
|
+
await upload?.revoke().catch(() => {
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1045
1608
|
if (!body || !("ok" in body)) {
|
|
1046
1609
|
throw new TBError("unavailable", "device session returned invalid result");
|
|
1047
1610
|
}
|
|
@@ -7158,9 +7721,9 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7158
7721
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
7159
7722
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
7160
7723
|
}
|
|
7161
|
-
optimizeNames(names,
|
|
7724
|
+
optimizeNames(names, constants2) {
|
|
7162
7725
|
if (!names[this.name.str]) return;
|
|
7163
|
-
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names,
|
|
7726
|
+
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
7164
7727
|
return this;
|
|
7165
7728
|
}
|
|
7166
7729
|
get names() {
|
|
@@ -7177,9 +7740,9 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7177
7740
|
render({ _n }) {
|
|
7178
7741
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
7179
7742
|
}
|
|
7180
|
-
optimizeNames(names,
|
|
7743
|
+
optimizeNames(names, constants2) {
|
|
7181
7744
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) return;
|
|
7182
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
7745
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
7183
7746
|
return this;
|
|
7184
7747
|
}
|
|
7185
7748
|
get names() {
|
|
@@ -7238,8 +7801,8 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7238
7801
|
optimizeNodes() {
|
|
7239
7802
|
return `${this.code}` ? this : void 0;
|
|
7240
7803
|
}
|
|
7241
|
-
optimizeNames(names,
|
|
7242
|
-
this.code = optimizeExpr(this.code, names,
|
|
7804
|
+
optimizeNames(names, constants2) {
|
|
7805
|
+
this.code = optimizeExpr(this.code, names, constants2);
|
|
7243
7806
|
return this;
|
|
7244
7807
|
}
|
|
7245
7808
|
get names() {
|
|
@@ -7265,12 +7828,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7265
7828
|
}
|
|
7266
7829
|
return nodes.length > 0 ? this : void 0;
|
|
7267
7830
|
}
|
|
7268
|
-
optimizeNames(names,
|
|
7831
|
+
optimizeNames(names, constants2) {
|
|
7269
7832
|
const { nodes } = this;
|
|
7270
7833
|
let i = nodes.length;
|
|
7271
7834
|
while (i--) {
|
|
7272
7835
|
const n = nodes[i];
|
|
7273
|
-
if (n.optimizeNames(names,
|
|
7836
|
+
if (n.optimizeNames(names, constants2)) continue;
|
|
7274
7837
|
subtractNames(names, n.names);
|
|
7275
7838
|
nodes.splice(i, 1);
|
|
7276
7839
|
}
|
|
@@ -7317,11 +7880,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7317
7880
|
if (cond === false || !this.nodes.length) return void 0;
|
|
7318
7881
|
return this;
|
|
7319
7882
|
}
|
|
7320
|
-
optimizeNames(names,
|
|
7883
|
+
optimizeNames(names, constants2) {
|
|
7321
7884
|
var _a;
|
|
7322
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
7323
|
-
if (!(super.optimizeNames(names,
|
|
7324
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
7885
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
|
|
7886
|
+
if (!(super.optimizeNames(names, constants2) || this.else)) return;
|
|
7887
|
+
this.condition = optimizeExpr(this.condition, names, constants2);
|
|
7325
7888
|
return this;
|
|
7326
7889
|
}
|
|
7327
7890
|
get names() {
|
|
@@ -7343,9 +7906,9 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7343
7906
|
render(opts) {
|
|
7344
7907
|
return `for(${this.iteration})` + super.render(opts);
|
|
7345
7908
|
}
|
|
7346
|
-
optimizeNames(names,
|
|
7347
|
-
if (!super.optimizeNames(names,
|
|
7348
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
7909
|
+
optimizeNames(names, constants2) {
|
|
7910
|
+
if (!super.optimizeNames(names, constants2)) return;
|
|
7911
|
+
this.iteration = optimizeExpr(this.iteration, names, constants2);
|
|
7349
7912
|
return this;
|
|
7350
7913
|
}
|
|
7351
7914
|
get names() {
|
|
@@ -7380,9 +7943,9 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7380
7943
|
render(opts) {
|
|
7381
7944
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
7382
7945
|
}
|
|
7383
|
-
optimizeNames(names,
|
|
7384
|
-
if (!super.optimizeNames(names,
|
|
7385
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
7946
|
+
optimizeNames(names, constants2) {
|
|
7947
|
+
if (!super.optimizeNames(names, constants2)) return;
|
|
7948
|
+
this.iterable = optimizeExpr(this.iterable, names, constants2);
|
|
7386
7949
|
return this;
|
|
7387
7950
|
}
|
|
7388
7951
|
get names() {
|
|
@@ -7421,11 +7984,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7421
7984
|
(_b = this.finally) === null || _b === void 0 || _b.optimizeNodes();
|
|
7422
7985
|
return this;
|
|
7423
7986
|
}
|
|
7424
|
-
optimizeNames(names,
|
|
7987
|
+
optimizeNames(names, constants2) {
|
|
7425
7988
|
var _a, _b;
|
|
7426
|
-
super.optimizeNames(names,
|
|
7427
|
-
(_a = this.catch) === null || _a === void 0 || _a.optimizeNames(names,
|
|
7428
|
-
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names,
|
|
7989
|
+
super.optimizeNames(names, constants2);
|
|
7990
|
+
(_a = this.catch) === null || _a === void 0 || _a.optimizeNames(names, constants2);
|
|
7991
|
+
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names, constants2);
|
|
7429
7992
|
return this;
|
|
7430
7993
|
}
|
|
7431
7994
|
get names() {
|
|
@@ -7674,7 +8237,7 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7674
8237
|
function addExprNames(names, from) {
|
|
7675
8238
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
7676
8239
|
}
|
|
7677
|
-
function optimizeExpr(expr, names,
|
|
8240
|
+
function optimizeExpr(expr, names, constants2) {
|
|
7678
8241
|
if (expr instanceof code_1.Name) return replaceName(expr);
|
|
7679
8242
|
if (!canOptimize(expr)) return expr;
|
|
7680
8243
|
return new code_1._Code(expr._items.reduce((items, c) => {
|
|
@@ -7684,13 +8247,13 @@ var require_codegen = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
7684
8247
|
return items;
|
|
7685
8248
|
}, []));
|
|
7686
8249
|
function replaceName(n) {
|
|
7687
|
-
const c =
|
|
8250
|
+
const c = constants2[n.str];
|
|
7688
8251
|
if (c === void 0 || names[n.str] !== 1) return n;
|
|
7689
8252
|
delete names[n.str];
|
|
7690
8253
|
return c;
|
|
7691
8254
|
}
|
|
7692
8255
|
function canOptimize(e) {
|
|
7693
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
8256
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== void 0);
|
|
7694
8257
|
}
|
|
7695
8258
|
}
|
|
7696
8259
|
function subtractNames(names, from) {
|
|
@@ -18725,8 +19288,8 @@ var GatewayMcpOAuthProvider = class {
|
|
|
18725
19288
|
}
|
|
18726
19289
|
get redirectUrl() {
|
|
18727
19290
|
if (this.opts.redirectUri !== void 0) return this.opts.redirectUri;
|
|
18728
|
-
const
|
|
18729
|
-
return `${
|
|
19291
|
+
const origin2 = this.opts.origin ?? "https://tool-bridge.invalid";
|
|
19292
|
+
return `${origin2}${OAUTH_CALLBACK_PATH}`;
|
|
18730
19293
|
}
|
|
18731
19294
|
get clientMetadata() {
|
|
18732
19295
|
return {
|
|
@@ -19608,7 +20171,7 @@ async function assertToolConfig(config, deps, ctx, mountPath) {
|
|
|
19608
20171
|
}
|
|
19609
20172
|
|
|
19610
20173
|
// ../app/src/refToken.ts
|
|
19611
|
-
async function
|
|
20174
|
+
async function hmacKey2(secret) {
|
|
19612
20175
|
const digest = await crypto.subtle.digest(
|
|
19613
20176
|
"SHA-256",
|
|
19614
20177
|
new TextEncoder().encode(`tb-ref-token:${secret}`)
|
|
@@ -19622,7 +20185,7 @@ async function signRefToken(payload, secret) {
|
|
|
19622
20185
|
const body = base64urlEncode(new TextEncoder().encode(JSON.stringify(payload)));
|
|
19623
20186
|
const mac = await crypto.subtle.sign(
|
|
19624
20187
|
"HMAC",
|
|
19625
|
-
await
|
|
20188
|
+
await hmacKey2(secret),
|
|
19626
20189
|
new TextEncoder().encode(body)
|
|
19627
20190
|
);
|
|
19628
20191
|
return `${body}.${base64urlEncode(new Uint8Array(mac))}`;
|
|
@@ -19639,7 +20202,7 @@ async function verifyRefToken(token, secret) {
|
|
|
19639
20202
|
}
|
|
19640
20203
|
const ok = await crypto.subtle.verify(
|
|
19641
20204
|
"HMAC",
|
|
19642
|
-
await
|
|
20205
|
+
await hmacKey2(secret),
|
|
19643
20206
|
// Node 的 WebCrypto 类型要求 Uint8Array<ArrayBuffer>;base64urlDecode 的产物满足但类型面是 ArrayBufferLike。
|
|
19644
20207
|
sig,
|
|
19645
20208
|
new TextEncoder().encode(body)
|
|
@@ -19750,11 +20313,11 @@ async function contextProviderFor(node, cfg, deps, requestUrl) {
|
|
|
19750
20313
|
if (deps.refTtlSec !== void 0) opts.presignTtlSec = deps.refTtlSec;
|
|
19751
20314
|
const encKey = deps.encryptionKey;
|
|
19752
20315
|
if (encKey !== void 0) {
|
|
19753
|
-
const
|
|
20316
|
+
const origin2 = new URL(requestUrl).origin;
|
|
19754
20317
|
const relayTtlSec = deps.refTtlSec ?? PRESIGN_TTL_SEC_DEFAULT;
|
|
19755
20318
|
opts.relayRefUrl = async (key) => {
|
|
19756
20319
|
const exp = Math.floor(Date.now() / 1e3) + relayTtlSec;
|
|
19757
|
-
return `${
|
|
20320
|
+
return `${origin2}/~ref/${await signRefToken({ p: node.path, k: key, exp }, encKey)}`;
|
|
19758
20321
|
};
|
|
19759
20322
|
}
|
|
19760
20323
|
return createObjectContextProvider(objects, opts);
|
|
@@ -19775,11 +20338,11 @@ async function skillhubProviderFor(node, cfg, deps, requestUrl) {
|
|
|
19775
20338
|
if (deps.refTtlSec !== void 0) opts.presignTtlSec = deps.refTtlSec;
|
|
19776
20339
|
const encKey = deps.encryptionKey;
|
|
19777
20340
|
if (encKey !== void 0) {
|
|
19778
|
-
const
|
|
20341
|
+
const origin2 = new URL(requestUrl).origin;
|
|
19779
20342
|
const relayTtlSec = deps.refTtlSec ?? PRESIGN_TTL_SEC_DEFAULT;
|
|
19780
20343
|
opts.relayRefUrl = async (key) => {
|
|
19781
20344
|
const exp = Math.floor(Date.now() / 1e3) + relayTtlSec;
|
|
19782
|
-
return `${
|
|
20345
|
+
return `${origin2}/~ref/${await signRefToken({ p: node.path, k: key, exp }, encKey)}`;
|
|
19783
20346
|
};
|
|
19784
20347
|
}
|
|
19785
20348
|
return createSkillhubProvider(objects, opts);
|
|
@@ -23282,8 +23845,8 @@ var LADDER_ERROR_HTTP_STATUS2 = {
|
|
|
23282
23845
|
[ProtocolErrorCode2.MissingRequiredClientCapability]: 400,
|
|
23283
23846
|
[HEADER_MISMATCH_ERROR_CODE2]: 400
|
|
23284
23847
|
};
|
|
23285
|
-
function httpStatusForErrorCode(code,
|
|
23286
|
-
if (
|
|
23848
|
+
function httpStatusForErrorCode(code, origin2) {
|
|
23849
|
+
if (origin2 === "in-band") return code === ProtocolErrorCode2.MissingRequiredClientCapability ? 400 : 200;
|
|
23287
23850
|
return LADDER_ERROR_HTTP_STATUS2[code] ?? 400;
|
|
23288
23851
|
}
|
|
23289
23852
|
function rejection(rung, cell, httpStatus, error, settled) {
|
|
@@ -25335,9 +25898,9 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25335
25898
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
25336
25899
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
25337
25900
|
}
|
|
25338
|
-
optimizeNames(names,
|
|
25901
|
+
optimizeNames(names, constants2) {
|
|
25339
25902
|
if (!names[this.name.str]) return;
|
|
25340
|
-
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names,
|
|
25903
|
+
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
25341
25904
|
return this;
|
|
25342
25905
|
}
|
|
25343
25906
|
get names() {
|
|
@@ -25354,9 +25917,9 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25354
25917
|
render({ _n }) {
|
|
25355
25918
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
25356
25919
|
}
|
|
25357
|
-
optimizeNames(names,
|
|
25920
|
+
optimizeNames(names, constants2) {
|
|
25358
25921
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) return;
|
|
25359
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
25922
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
25360
25923
|
return this;
|
|
25361
25924
|
}
|
|
25362
25925
|
get names() {
|
|
@@ -25415,8 +25978,8 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25415
25978
|
optimizeNodes() {
|
|
25416
25979
|
return `${this.code}` ? this : void 0;
|
|
25417
25980
|
}
|
|
25418
|
-
optimizeNames(names,
|
|
25419
|
-
this.code = optimizeExpr(this.code, names,
|
|
25981
|
+
optimizeNames(names, constants2) {
|
|
25982
|
+
this.code = optimizeExpr(this.code, names, constants2);
|
|
25420
25983
|
return this;
|
|
25421
25984
|
}
|
|
25422
25985
|
get names() {
|
|
@@ -25442,12 +26005,12 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25442
26005
|
}
|
|
25443
26006
|
return nodes.length > 0 ? this : void 0;
|
|
25444
26007
|
}
|
|
25445
|
-
optimizeNames(names,
|
|
26008
|
+
optimizeNames(names, constants2) {
|
|
25446
26009
|
const { nodes } = this;
|
|
25447
26010
|
let i = nodes.length;
|
|
25448
26011
|
while (i--) {
|
|
25449
26012
|
const n = nodes[i];
|
|
25450
|
-
if (n.optimizeNames(names,
|
|
26013
|
+
if (n.optimizeNames(names, constants2)) continue;
|
|
25451
26014
|
subtractNames(names, n.names);
|
|
25452
26015
|
nodes.splice(i, 1);
|
|
25453
26016
|
}
|
|
@@ -25494,11 +26057,11 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25494
26057
|
if (cond === false || !this.nodes.length) return void 0;
|
|
25495
26058
|
return this;
|
|
25496
26059
|
}
|
|
25497
|
-
optimizeNames(names,
|
|
26060
|
+
optimizeNames(names, constants2) {
|
|
25498
26061
|
var _a;
|
|
25499
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
25500
|
-
if (!(super.optimizeNames(names,
|
|
25501
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
26062
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
|
|
26063
|
+
if (!(super.optimizeNames(names, constants2) || this.else)) return;
|
|
26064
|
+
this.condition = optimizeExpr(this.condition, names, constants2);
|
|
25502
26065
|
return this;
|
|
25503
26066
|
}
|
|
25504
26067
|
get names() {
|
|
@@ -25520,9 +26083,9 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25520
26083
|
render(opts) {
|
|
25521
26084
|
return `for(${this.iteration})` + super.render(opts);
|
|
25522
26085
|
}
|
|
25523
|
-
optimizeNames(names,
|
|
25524
|
-
if (!super.optimizeNames(names,
|
|
25525
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
26086
|
+
optimizeNames(names, constants2) {
|
|
26087
|
+
if (!super.optimizeNames(names, constants2)) return;
|
|
26088
|
+
this.iteration = optimizeExpr(this.iteration, names, constants2);
|
|
25526
26089
|
return this;
|
|
25527
26090
|
}
|
|
25528
26091
|
get names() {
|
|
@@ -25557,9 +26120,9 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25557
26120
|
render(opts) {
|
|
25558
26121
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
25559
26122
|
}
|
|
25560
|
-
optimizeNames(names,
|
|
25561
|
-
if (!super.optimizeNames(names,
|
|
25562
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
26123
|
+
optimizeNames(names, constants2) {
|
|
26124
|
+
if (!super.optimizeNames(names, constants2)) return;
|
|
26125
|
+
this.iterable = optimizeExpr(this.iterable, names, constants2);
|
|
25563
26126
|
return this;
|
|
25564
26127
|
}
|
|
25565
26128
|
get names() {
|
|
@@ -25598,11 +26161,11 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25598
26161
|
(_b = this.finally) === null || _b === void 0 || _b.optimizeNodes();
|
|
25599
26162
|
return this;
|
|
25600
26163
|
}
|
|
25601
|
-
optimizeNames(names,
|
|
26164
|
+
optimizeNames(names, constants2) {
|
|
25602
26165
|
var _a, _b;
|
|
25603
|
-
super.optimizeNames(names,
|
|
25604
|
-
(_a = this.catch) === null || _a === void 0 || _a.optimizeNames(names,
|
|
25605
|
-
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names,
|
|
26166
|
+
super.optimizeNames(names, constants2);
|
|
26167
|
+
(_a = this.catch) === null || _a === void 0 || _a.optimizeNames(names, constants2);
|
|
26168
|
+
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names, constants2);
|
|
25606
26169
|
return this;
|
|
25607
26170
|
}
|
|
25608
26171
|
get names() {
|
|
@@ -25851,7 +26414,7 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25851
26414
|
function addExprNames(names, from) {
|
|
25852
26415
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
25853
26416
|
}
|
|
25854
|
-
function optimizeExpr(expr, names,
|
|
26417
|
+
function optimizeExpr(expr, names, constants2) {
|
|
25855
26418
|
if (expr instanceof code_1.Name) return replaceName(expr);
|
|
25856
26419
|
if (!canOptimize(expr)) return expr;
|
|
25857
26420
|
return new code_1._Code(expr._items.reduce((items, c) => {
|
|
@@ -25861,13 +26424,13 @@ var require_codegen2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
25861
26424
|
return items;
|
|
25862
26425
|
}, []));
|
|
25863
26426
|
function replaceName(n) {
|
|
25864
|
-
const c =
|
|
26427
|
+
const c = constants2[n.str];
|
|
25865
26428
|
if (c === void 0 || names[n.str] !== 1) return n;
|
|
25866
26429
|
delete names[n.str];
|
|
25867
26430
|
return c;
|
|
25868
26431
|
}
|
|
25869
26432
|
function canOptimize(e) {
|
|
25870
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
26433
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== void 0);
|
|
25871
26434
|
}
|
|
25872
26435
|
}
|
|
25873
26436
|
function subtractNames(names, from) {
|
|
@@ -32091,9 +32654,9 @@ function createListenRouter(options) {
|
|
|
32091
32654
|
const { bus, onerror } = options;
|
|
32092
32655
|
const maxSubscriptions = options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS;
|
|
32093
32656
|
const keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
|
|
32094
|
-
const
|
|
32657
|
+
const open2 = /* @__PURE__ */ new Set();
|
|
32095
32658
|
function serve2(message, signal, capabilities, serverInfo) {
|
|
32096
|
-
if (
|
|
32659
|
+
if (open2.size >= maxSubscriptions) {
|
|
32097
32660
|
onerror?.(/* @__PURE__ */ new Error(`subscriptions/listen refused: subscription limit reached (${maxSubscriptions})`));
|
|
32098
32661
|
return jsonRpcError(message.id, -32603, "Subscription limit reached");
|
|
32099
32662
|
}
|
|
@@ -32149,7 +32712,7 @@ data: ${JSON.stringify({
|
|
|
32149
32712
|
}
|
|
32150
32713
|
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
32151
32714
|
abortCleanup?.();
|
|
32152
|
-
|
|
32715
|
+
open2.delete(teardown);
|
|
32153
32716
|
try {
|
|
32154
32717
|
controller.close();
|
|
32155
32718
|
} catch {
|
|
@@ -32169,7 +32732,7 @@ data: ${JSON.stringify({
|
|
|
32169
32732
|
writeNotification(note.method, note.params);
|
|
32170
32733
|
});
|
|
32171
32734
|
keepAliveTimer = armSseKeepAlive(keepAliveMs, () => writeFrame(": keepalive\n\n"));
|
|
32172
|
-
|
|
32735
|
+
open2.add(teardown);
|
|
32173
32736
|
},
|
|
32174
32737
|
cancel() {
|
|
32175
32738
|
teardown(false);
|
|
@@ -32194,10 +32757,10 @@ data: ${JSON.stringify({
|
|
|
32194
32757
|
return {
|
|
32195
32758
|
serve: serve2,
|
|
32196
32759
|
closeAll() {
|
|
32197
|
-
for (const teardown of
|
|
32760
|
+
for (const teardown of open2) teardown(true);
|
|
32198
32761
|
},
|
|
32199
32762
|
get openCount() {
|
|
32200
|
-
return
|
|
32763
|
+
return open2.size;
|
|
32201
32764
|
}
|
|
32202
32765
|
};
|
|
32203
32766
|
}
|
|
@@ -36538,6 +37101,147 @@ async function handleDescribe(c, env) {
|
|
|
36538
37101
|
throw TBError.notFound(`no capabilities for kind '${node.kind}'`);
|
|
36539
37102
|
}
|
|
36540
37103
|
|
|
37104
|
+
// ../app/src/routes/store.ts
|
|
37105
|
+
var SECRET_BODY_FIELDS = [
|
|
37106
|
+
"callCapability",
|
|
37107
|
+
"capability",
|
|
37108
|
+
"uploadToken",
|
|
37109
|
+
"shareToken"
|
|
37110
|
+
];
|
|
37111
|
+
function hideCapabilityFailure(error) {
|
|
37112
|
+
if (isTBError(error) && error.code !== "permission_denied" && error.code !== "not_found") throw error;
|
|
37113
|
+
throw TBError.notFound("not found");
|
|
37114
|
+
}
|
|
37115
|
+
async function jsonObject(c) {
|
|
37116
|
+
const parsed = await c.req.json().catch(() => null);
|
|
37117
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
37118
|
+
throw new TBError("invalid_argument", "body must be a JSON object");
|
|
37119
|
+
}
|
|
37120
|
+
const body = parsed;
|
|
37121
|
+
for (const field of SECRET_BODY_FIELDS) {
|
|
37122
|
+
if (field in body) {
|
|
37123
|
+
throw new TBError(
|
|
37124
|
+
"invalid_argument",
|
|
37125
|
+
`${field} is not accepted in the request body; use the dedicated capability header`
|
|
37126
|
+
);
|
|
37127
|
+
}
|
|
37128
|
+
}
|
|
37129
|
+
return body;
|
|
37130
|
+
}
|
|
37131
|
+
function requiredHeader(c, name) {
|
|
37132
|
+
const value = c.req.header(name);
|
|
37133
|
+
if (value === void 0 || value.trim() === "") {
|
|
37134
|
+
throw new TBError("permission_denied", "Store capability is missing or invalid");
|
|
37135
|
+
}
|
|
37136
|
+
return value;
|
|
37137
|
+
}
|
|
37138
|
+
function uploadId(body) {
|
|
37139
|
+
if (typeof body.uploadId !== "string" || body.uploadId.trim() === "") {
|
|
37140
|
+
throw new TBError("invalid_argument", "uploadId must be a non-empty string");
|
|
37141
|
+
}
|
|
37142
|
+
return body.uploadId;
|
|
37143
|
+
}
|
|
37144
|
+
function origin(c, canonicalOrigin) {
|
|
37145
|
+
let url2;
|
|
37146
|
+
try {
|
|
37147
|
+
url2 = new URL(canonicalOrigin ?? c.req.url);
|
|
37148
|
+
} catch {
|
|
37149
|
+
throw new TBError("unavailable", "request origin is invalid", { retryable: false });
|
|
37150
|
+
}
|
|
37151
|
+
if (url2.protocol !== "https:" && url2.protocol !== "http:" || url2.username !== "" || url2.password !== "") throw new TBError("unavailable", "request origin is invalid", { retryable: false });
|
|
37152
|
+
return url2.origin;
|
|
37153
|
+
}
|
|
37154
|
+
function registerStoreCapabilityRoutes(app, deps) {
|
|
37155
|
+
app.post("/system/store/create_upload", async (c, next) => {
|
|
37156
|
+
if (c.req.header(STORE_CALL_CAPABILITY_HEADER) === void 0) return await next();
|
|
37157
|
+
return await runHandler(async () => {
|
|
37158
|
+
await deps.ensureReady?.();
|
|
37159
|
+
const body = await jsonObject(c);
|
|
37160
|
+
const capability = requiredHeader(c, STORE_CALL_CAPABILITY_HEADER);
|
|
37161
|
+
const result = await beginCapabilityUpload(
|
|
37162
|
+
deps,
|
|
37163
|
+
body,
|
|
37164
|
+
capability,
|
|
37165
|
+
origin(c, deps.canonicalOrigin)
|
|
37166
|
+
);
|
|
37167
|
+
return c.json(result);
|
|
37168
|
+
});
|
|
37169
|
+
});
|
|
37170
|
+
for (const command of ["complete_upload", "abort_upload"]) {
|
|
37171
|
+
app.post(`/system/store/${command}`, async (c, next) => {
|
|
37172
|
+
if (c.req.header(STORE_UPLOAD_HEADER) === void 0) return await next();
|
|
37173
|
+
return await runHandler(async () => {
|
|
37174
|
+
await deps.ensureReady?.();
|
|
37175
|
+
const body = await jsonObject(c);
|
|
37176
|
+
const token = requiredHeader(c, STORE_UPLOAD_HEADER);
|
|
37177
|
+
const result = command === "complete_upload" ? await completeCapabilityUpload(deps, uploadId(body), token) : await abortCapabilityUpload(deps, uploadId(body), token);
|
|
37178
|
+
return c.json(result);
|
|
37179
|
+
});
|
|
37180
|
+
});
|
|
37181
|
+
}
|
|
37182
|
+
app.put(
|
|
37183
|
+
"/~store/uploads/:uploadId",
|
|
37184
|
+
(c) => runHandler(async () => {
|
|
37185
|
+
await deps.ensureReady?.();
|
|
37186
|
+
const descriptor = await relayStoreUpload(
|
|
37187
|
+
deps,
|
|
37188
|
+
c.req.param("uploadId"),
|
|
37189
|
+
requiredHeader(c, STORE_UPLOAD_HEADER),
|
|
37190
|
+
c.req.raw.body
|
|
37191
|
+
);
|
|
37192
|
+
return c.json(descriptor);
|
|
37193
|
+
})
|
|
37194
|
+
);
|
|
37195
|
+
app.get(
|
|
37196
|
+
"/~store/refs/:token",
|
|
37197
|
+
(c) => runHandler(async () => {
|
|
37198
|
+
await deps.ensureReady?.();
|
|
37199
|
+
const store = await defaultStoreRuntime(deps);
|
|
37200
|
+
const payload = await verifyStoreRefToken(c.req.param("token"), store.tokenSecret);
|
|
37201
|
+
if (payload === null || payload.exp * 1e3 <= Date.now()) {
|
|
37202
|
+
throw TBError.notFound("not found");
|
|
37203
|
+
}
|
|
37204
|
+
let object2;
|
|
37205
|
+
try {
|
|
37206
|
+
object2 = await store.service.authorizeRead(storeUri(payload.objectId), {
|
|
37207
|
+
admin: true
|
|
37208
|
+
});
|
|
37209
|
+
} catch (error) {
|
|
37210
|
+
hideCapabilityFailure(error);
|
|
37211
|
+
}
|
|
37212
|
+
return await storeObjectResponse(store.objects, object2);
|
|
37213
|
+
})
|
|
37214
|
+
);
|
|
37215
|
+
app.get(
|
|
37216
|
+
"/~store/shares/:token",
|
|
37217
|
+
(c) => runHandler(async () => {
|
|
37218
|
+
await deps.ensureReady?.();
|
|
37219
|
+
const token = c.req.param("token");
|
|
37220
|
+
const store = await defaultStoreRuntime(deps);
|
|
37221
|
+
let object2;
|
|
37222
|
+
try {
|
|
37223
|
+
const grant = await store.service.verifyShareToken(token);
|
|
37224
|
+
const uri3 = storeUri(grant.objectId);
|
|
37225
|
+
object2 = await store.service.authorizeSharedRead(uri3, token);
|
|
37226
|
+
} catch (error) {
|
|
37227
|
+
hideCapabilityFailure(error);
|
|
37228
|
+
}
|
|
37229
|
+
return await storeObjectResponse(store.objects, object2);
|
|
37230
|
+
})
|
|
37231
|
+
);
|
|
37232
|
+
}
|
|
37233
|
+
function rejectStoreCapabilityBodyFields(module, args) {
|
|
37234
|
+
if (module !== "store") return;
|
|
37235
|
+
for (const field of SECRET_BODY_FIELDS) {
|
|
37236
|
+
if (field in args) {
|
|
37237
|
+
throw new TBError(
|
|
37238
|
+
"invalid_argument",
|
|
37239
|
+
`${field} is not accepted in the request body; use the dedicated capability header`
|
|
37240
|
+
);
|
|
37241
|
+
}
|
|
37242
|
+
}
|
|
37243
|
+
}
|
|
37244
|
+
|
|
36541
37245
|
// ../app/src/routes/publicRoutes.ts
|
|
36542
37246
|
async function finishToolAuthorization(opts) {
|
|
36543
37247
|
const config = opts.node.config;
|
|
@@ -37479,6 +38183,7 @@ async function handleInvoke(c, env) {
|
|
|
37479
38183
|
if (!mod) throw TBError.unimplemented(`builtin module '${node.config.module}' not available`);
|
|
37480
38184
|
const args = await readInvokeBody();
|
|
37481
38185
|
const cmd = command;
|
|
38186
|
+
rejectStoreCapabilityBodyFields(node.config.module, args);
|
|
37482
38187
|
const spec = scopeForCmd(mod, node.path, cmd);
|
|
37483
38188
|
if (!spec) throw new TBError("invalid_argument", `unknown cmd '${cmd}' on '${node.path}'`);
|
|
37484
38189
|
if (!check(ctx, node.path, spec.scope).allow) {
|
|
@@ -37522,7 +38227,9 @@ async function handleInvoke(c, env) {
|
|
|
37522
38227
|
const registryMarker = registryTarget === void 0 ? void 0 : await searchSync?.markNode(registryTarget);
|
|
37523
38228
|
let result;
|
|
37524
38229
|
try {
|
|
37525
|
-
result = await mod.dispatch(cmd, args, ctx
|
|
38230
|
+
result = await mod.dispatch(cmd, args, ctx, {
|
|
38231
|
+
requestOrigin: deps.canonicalOrigin ?? new URL(c.req.url).origin
|
|
38232
|
+
});
|
|
37526
38233
|
} catch (error) {
|
|
37527
38234
|
await searchSync?.abort(registryMarker);
|
|
37528
38235
|
await Promise.all(pluginMounts.map(async (mount) => await searchSync?.abort(mount.marker)));
|
|
@@ -37573,17 +38280,21 @@ async function handleInvoke(c, env) {
|
|
|
37573
38280
|
// ../app/src/routes/env.ts
|
|
37574
38281
|
function createRouteEnv(deps) {
|
|
37575
38282
|
const searchSync = isMutableSearchIndex(deps.search) ? new SearchSynchronizer(deps.state, deps.search) : void 0;
|
|
37576
|
-
const builtinsOf = (store) =>
|
|
37577
|
-
|
|
37578
|
-
|
|
37579
|
-
|
|
37580
|
-
|
|
37581
|
-
|
|
37582
|
-
|
|
37583
|
-
|
|
37584
|
-
|
|
37585
|
-
|
|
37586
|
-
|
|
38283
|
+
const builtinsOf = (store) => {
|
|
38284
|
+
const builtins = createBuiltins(
|
|
38285
|
+
buildDeps({
|
|
38286
|
+
store,
|
|
38287
|
+
secrets: deps.secrets,
|
|
38288
|
+
version: deps.version,
|
|
38289
|
+
allowInsecureHttp: deps.allowInsecureHttp,
|
|
38290
|
+
remoteAllowlistBase: deps.remote.allowlist,
|
|
38291
|
+
...deps.pluginBindings !== void 0 ? { pluginBindings: deps.pluginBindings } : {},
|
|
38292
|
+
...deps.pluginCatalog !== void 0 ? { pluginCatalog: deps.pluginCatalog } : {}
|
|
38293
|
+
})
|
|
38294
|
+
);
|
|
38295
|
+
builtins.set("store", lazyDefaultStoreModule(deps));
|
|
38296
|
+
return builtins;
|
|
38297
|
+
};
|
|
37587
38298
|
const globalSearchCapabilities = () => {
|
|
37588
38299
|
const declared = new Set(deps.search?.capabilities ?? []);
|
|
37589
38300
|
if (!declared.has("search")) return [];
|
|
@@ -37724,6 +38435,7 @@ function createTbApp(deps) {
|
|
|
37724
38435
|
if (secured !== response) c.res = secured;
|
|
37725
38436
|
});
|
|
37726
38437
|
registerPublicRoutes(app, env);
|
|
38438
|
+
registerStoreCapabilityRoutes(app, deps);
|
|
37727
38439
|
app.use("*", async (c, next) => {
|
|
37728
38440
|
const store = deps.state;
|
|
37729
38441
|
try {
|
|
@@ -38250,6 +38962,19 @@ var DeviceRouter = class _DeviceRouter {
|
|
|
38250
38962
|
}
|
|
38251
38963
|
};
|
|
38252
38964
|
|
|
38965
|
+
// src/objects.ts
|
|
38966
|
+
import {
|
|
38967
|
+
link,
|
|
38968
|
+
mkdir as mkdir2,
|
|
38969
|
+
open,
|
|
38970
|
+
readdir as readdir2,
|
|
38971
|
+
realpath as realpath2,
|
|
38972
|
+
rename,
|
|
38973
|
+
rm as rm2,
|
|
38974
|
+
stat as stat2
|
|
38975
|
+
} from "fs/promises";
|
|
38976
|
+
import { constants, mkdirSync } from "fs";
|
|
38977
|
+
|
|
38253
38978
|
// ../core/src/node/fsObjectStore.ts
|
|
38254
38979
|
import { mkdir, readdir, readFile, realpath, rm, stat, writeFile } from "fs/promises";
|
|
38255
38980
|
import { basename, dirname, join, resolve, sep } from "path";
|
|
@@ -38349,6 +39074,9 @@ var FsObjectStore = class {
|
|
|
38349
39074
|
throw new TBError("invalid_argument", `\u975E\u6CD5 entry \u8DEF\u5F84 '${key}':\u672A\u77E5\u6839\u6216\u7F3A\u76F8\u5BF9\u8DEF\u5F84`);
|
|
38350
39075
|
}
|
|
38351
39076
|
await this.assertInRoot(r.root, r.full, key);
|
|
39077
|
+
if (opts?.ifMatchEtag !== void 0 && opts.ifNoneMatch !== void 0) {
|
|
39078
|
+
throw new TBError("invalid_argument", "ifMatchEtag \u4E0E ifNoneMatch \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
|
|
39079
|
+
}
|
|
38352
39080
|
if (opts?.ifMatchEtag !== void 0) {
|
|
38353
39081
|
const existing = await this.head(key);
|
|
38354
39082
|
if (opts.ifMatchEtag !== existing?.etag) {
|
|
@@ -38357,7 +39085,14 @@ var FsObjectStore = class {
|
|
|
38357
39085
|
}
|
|
38358
39086
|
const bytes = await objectBodyToBytes(body);
|
|
38359
39087
|
await mkdir(dirname(r.full), { recursive: true });
|
|
38360
|
-
|
|
39088
|
+
try {
|
|
39089
|
+
await writeFile(r.full, bytes, opts?.ifNoneMatch === "*" ? { flag: "wx" } : void 0);
|
|
39090
|
+
} catch (e) {
|
|
39091
|
+
if (opts?.ifNoneMatch === "*" && isErrnoCode(e, "EEXIST")) {
|
|
39092
|
+
throw new TBError("conflict", `\u5BF9\u8C61\u5DF2\u5B58\u5728:'${key}'`);
|
|
39093
|
+
}
|
|
39094
|
+
throw e;
|
|
39095
|
+
}
|
|
38361
39096
|
const st = await stat(r.full);
|
|
38362
39097
|
return this.metaOf(r.key, st);
|
|
38363
39098
|
}
|
|
@@ -38491,9 +39226,14 @@ import { spawn as nodeSpawn } from "child_process";
|
|
|
38491
39226
|
var SHELL_OUTPUT_LIMIT_BYTES = 1024 * 1024;
|
|
38492
39227
|
|
|
38493
39228
|
// src/objects.ts
|
|
38494
|
-
import {
|
|
38495
|
-
import {
|
|
39229
|
+
import { dirname as dirname2, join as join2, sep as sep2 } from "path";
|
|
39230
|
+
import { randomUUID } from "crypto";
|
|
39231
|
+
import { Readable } from "stream";
|
|
38496
39232
|
var INTERNAL_ROOT = "objects";
|
|
39233
|
+
var TEMP_PREFIX = ".tb-object-upload-";
|
|
39234
|
+
var STORE_KEY_PREFIX = `${DEFAULT_STORE_DRIVER_KEY_ROOT}/`;
|
|
39235
|
+
var STORE_STAGING_DIR = ".tb-store-staging-v1";
|
|
39236
|
+
var UPLOAD_TEMP_NAME_RE = /^\.tb-object-upload-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.tmp$/i;
|
|
38497
39237
|
function toInternal(key) {
|
|
38498
39238
|
return `${INTERNAL_ROOT}/${key}`;
|
|
38499
39239
|
}
|
|
@@ -38503,6 +39243,108 @@ function toExternal(key) {
|
|
|
38503
39243
|
function externalMeta(meta) {
|
|
38504
39244
|
return { ...meta, key: toExternal(meta.key) };
|
|
38505
39245
|
}
|
|
39246
|
+
function isErrnoCode2(error, code) {
|
|
39247
|
+
return typeof error === "object" && error !== null && error.code === code;
|
|
39248
|
+
}
|
|
39249
|
+
function isUploadTempKey(key) {
|
|
39250
|
+
return key.split("/").some((segment) => segment.startsWith(TEMP_PREFIX));
|
|
39251
|
+
}
|
|
39252
|
+
async function writeChunk(handle, chunk) {
|
|
39253
|
+
let offset = 0;
|
|
39254
|
+
while (offset < chunk.byteLength) {
|
|
39255
|
+
const { bytesWritten } = await handle.write(chunk, offset, chunk.byteLength - offset, null);
|
|
39256
|
+
if (bytesWritten === 0) throw new Error("\u5BF9\u8C61\u4E34\u65F6\u6587\u4EF6\u5199\u5165\u672A\u53D6\u5F97\u8FDB\u5C55");
|
|
39257
|
+
offset += bytesWritten;
|
|
39258
|
+
}
|
|
39259
|
+
}
|
|
39260
|
+
async function writeStream(handle, stream) {
|
|
39261
|
+
const reader = stream.getReader();
|
|
39262
|
+
try {
|
|
39263
|
+
for (; ; ) {
|
|
39264
|
+
const { done, value } = await reader.read();
|
|
39265
|
+
if (done) break;
|
|
39266
|
+
if (value !== void 0 && value.byteLength > 0) await writeChunk(handle, value);
|
|
39267
|
+
}
|
|
39268
|
+
} catch (error) {
|
|
39269
|
+
try {
|
|
39270
|
+
await reader.cancel?.(error);
|
|
39271
|
+
} catch {
|
|
39272
|
+
}
|
|
39273
|
+
throw error;
|
|
39274
|
+
} finally {
|
|
39275
|
+
reader.releaseLock();
|
|
39276
|
+
}
|
|
39277
|
+
}
|
|
39278
|
+
async function writeBody(handle, body) {
|
|
39279
|
+
if (typeof body === "string") {
|
|
39280
|
+
await writeChunk(handle, Buffer.from(body));
|
|
39281
|
+
return;
|
|
39282
|
+
}
|
|
39283
|
+
if (body instanceof Uint8Array) {
|
|
39284
|
+
await writeChunk(handle, body);
|
|
39285
|
+
return;
|
|
39286
|
+
}
|
|
39287
|
+
if (body instanceof ArrayBuffer) {
|
|
39288
|
+
await writeChunk(handle, new Uint8Array(body));
|
|
39289
|
+
return;
|
|
39290
|
+
}
|
|
39291
|
+
await writeStream(handle, body);
|
|
39292
|
+
}
|
|
39293
|
+
async function assertParentInRoot(root, full, key) {
|
|
39294
|
+
const rootReal = await realpath2(root);
|
|
39295
|
+
const parentReal = await realpath2(dirname2(full));
|
|
39296
|
+
if (parentReal !== rootReal && !parentReal.startsWith(rootReal + sep2)) {
|
|
39297
|
+
throw new TBError("invalid_argument", `\u975E\u6CD5 entry \u8DEF\u5F84 '${key}':symlink \u9003\u9038\u6839\u76EE\u5F55`);
|
|
39298
|
+
}
|
|
39299
|
+
}
|
|
39300
|
+
async function assertAncestorInRoot(root, full, key) {
|
|
39301
|
+
const rootReal = await realpath2(root);
|
|
39302
|
+
let probe = dirname2(full);
|
|
39303
|
+
for (; ; ) {
|
|
39304
|
+
try {
|
|
39305
|
+
const ancestorReal = await realpath2(probe);
|
|
39306
|
+
if (ancestorReal !== rootReal && !ancestorReal.startsWith(rootReal + sep2)) {
|
|
39307
|
+
throw new TBError("invalid_argument", `\u975E\u6CD5 entry \u8DEF\u5F84 '${key}':symlink \u9003\u9038\u6839\u76EE\u5F55`);
|
|
39308
|
+
}
|
|
39309
|
+
return;
|
|
39310
|
+
} catch (error) {
|
|
39311
|
+
if (error instanceof TBError) throw error;
|
|
39312
|
+
if (!isErrnoCode2(error, "ENOENT")) throw error;
|
|
39313
|
+
const parent = dirname2(probe);
|
|
39314
|
+
if (parent === probe) {
|
|
39315
|
+
throw new TBError("invalid_argument", `\u975E\u6CD5 entry \u8DEF\u5F84 '${key}':symlink \u9003\u9038\u6839\u76EE\u5F55`);
|
|
39316
|
+
}
|
|
39317
|
+
probe = parent;
|
|
39318
|
+
}
|
|
39319
|
+
}
|
|
39320
|
+
}
|
|
39321
|
+
async function cleanupStagingTree(dir, cutoffMs) {
|
|
39322
|
+
let entries;
|
|
39323
|
+
try {
|
|
39324
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
39325
|
+
} catch (error) {
|
|
39326
|
+
if (isErrnoCode2(error, "ENOENT")) return 0;
|
|
39327
|
+
throw error;
|
|
39328
|
+
}
|
|
39329
|
+
let deleted = 0;
|
|
39330
|
+
for (const entry of entries) {
|
|
39331
|
+
const path = join2(dir, entry.name);
|
|
39332
|
+
if (entry.isDirectory()) {
|
|
39333
|
+
deleted += await cleanupStagingTree(path, cutoffMs);
|
|
39334
|
+
continue;
|
|
39335
|
+
}
|
|
39336
|
+
if (!entry.isFile() || !UPLOAD_TEMP_NAME_RE.test(entry.name)) continue;
|
|
39337
|
+
try {
|
|
39338
|
+
const info = await stat2(path);
|
|
39339
|
+
if (info.mtimeMs > cutoffMs) continue;
|
|
39340
|
+
await rm2(path);
|
|
39341
|
+
deleted++;
|
|
39342
|
+
} catch (error) {
|
|
39343
|
+
if (!isErrnoCode2(error, "ENOENT")) throw error;
|
|
39344
|
+
}
|
|
39345
|
+
}
|
|
39346
|
+
return deleted;
|
|
39347
|
+
}
|
|
38506
39348
|
function createDataObjectStore(dataDir) {
|
|
38507
39349
|
const root = join2(dataDir, INTERNAL_ROOT);
|
|
38508
39350
|
mkdirSync(root, { recursive: true });
|
|
@@ -38513,11 +39355,77 @@ function createDataObjectStore(dataDir) {
|
|
|
38513
39355
|
return meta === null ? null : externalMeta(meta);
|
|
38514
39356
|
},
|
|
38515
39357
|
async get(key) {
|
|
38516
|
-
const
|
|
38517
|
-
|
|
39358
|
+
const normalizedKey = normalizeEntryPath(key);
|
|
39359
|
+
const meta = await fs.head(toInternal(normalizedKey));
|
|
39360
|
+
if (meta === null) return null;
|
|
39361
|
+
const finalPath = join2(root, ...normalizedKey.split("/"));
|
|
39362
|
+
let handle;
|
|
39363
|
+
try {
|
|
39364
|
+
handle = await open(finalPath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
39365
|
+
} catch (error) {
|
|
39366
|
+
if (isErrnoCode2(error, "ENOENT")) return null;
|
|
39367
|
+
if (isErrnoCode2(error, "ELOOP")) {
|
|
39368
|
+
throw new TBError("invalid_argument", `\u975E\u6CD5 entry \u8DEF\u5F84 '${key}':symlink \u9003\u9038\u6839\u76EE\u5F55`);
|
|
39369
|
+
}
|
|
39370
|
+
throw error;
|
|
39371
|
+
}
|
|
39372
|
+
const body = Readable.toWeb(handle.createReadStream());
|
|
39373
|
+
return { meta: externalMeta(meta), body };
|
|
38518
39374
|
},
|
|
38519
39375
|
async put(key, body, opts) {
|
|
38520
|
-
|
|
39376
|
+
const normalizedKey = normalizeEntryPath(key);
|
|
39377
|
+
const internalKey = toInternal(normalizedKey);
|
|
39378
|
+
const finalPath = join2(root, ...normalizedKey.split("/"));
|
|
39379
|
+
const existing = await fs.head(internalKey);
|
|
39380
|
+
if (opts?.ifMatchEtag !== void 0 && opts.ifNoneMatch !== void 0) {
|
|
39381
|
+
throw new TBError("invalid_argument", "ifMatchEtag \u4E0E ifNoneMatch \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
|
|
39382
|
+
}
|
|
39383
|
+
if (opts?.ifMatchEtag !== void 0) {
|
|
39384
|
+
if (opts.ifMatchEtag !== existing?.etag) {
|
|
39385
|
+
throw new TBError("conflict", `etag \u4E0D\u5339\u914D:'${key}'`);
|
|
39386
|
+
}
|
|
39387
|
+
}
|
|
39388
|
+
await assertAncestorInRoot(root, finalPath, key);
|
|
39389
|
+
await mkdir2(dirname2(finalPath), { recursive: true });
|
|
39390
|
+
await assertParentInRoot(root, finalPath, key);
|
|
39391
|
+
const tempDir = normalizedKey.startsWith(STORE_KEY_PREFIX) ? join2(root, STORE_STAGING_DIR) : dirname2(finalPath);
|
|
39392
|
+
await assertAncestorInRoot(root, join2(tempDir, "staging-probe"), key);
|
|
39393
|
+
await mkdir2(tempDir, { recursive: true });
|
|
39394
|
+
const tempPath = join2(tempDir, `${TEMP_PREFIX}${randomUUID()}.tmp`);
|
|
39395
|
+
await assertParentInRoot(root, tempPath, key);
|
|
39396
|
+
let handle;
|
|
39397
|
+
try {
|
|
39398
|
+
handle = await open(tempPath, "wx");
|
|
39399
|
+
await writeBody(handle, body);
|
|
39400
|
+
await handle.sync();
|
|
39401
|
+
await handle.close();
|
|
39402
|
+
handle = void 0;
|
|
39403
|
+
if (opts?.ifNoneMatch === "*") {
|
|
39404
|
+
try {
|
|
39405
|
+
await link(tempPath, finalPath);
|
|
39406
|
+
} catch (error) {
|
|
39407
|
+
if (isErrnoCode2(error, "EEXIST")) {
|
|
39408
|
+
throw new TBError("conflict", `\u5BF9\u8C61\u5DF2\u5B58\u5728:'${key}'`);
|
|
39409
|
+
}
|
|
39410
|
+
throw error;
|
|
39411
|
+
}
|
|
39412
|
+
await rm2(tempPath);
|
|
39413
|
+
} else {
|
|
39414
|
+
await rename(tempPath, finalPath);
|
|
39415
|
+
}
|
|
39416
|
+
} catch (error) {
|
|
39417
|
+
if (handle !== void 0) {
|
|
39418
|
+
try {
|
|
39419
|
+
await handle.close();
|
|
39420
|
+
} catch {
|
|
39421
|
+
}
|
|
39422
|
+
}
|
|
39423
|
+
await rm2(tempPath, { force: true });
|
|
39424
|
+
throw error;
|
|
39425
|
+
}
|
|
39426
|
+
const meta = await fs.head(internalKey);
|
|
39427
|
+
if (meta === null) throw new TBError("unavailable", `\u5BF9\u8C61\u63D0\u4EA4\u540E\u4E0D\u53EF\u89C1:'${key}'`);
|
|
39428
|
+
return externalMeta(meta);
|
|
38521
39429
|
},
|
|
38522
39430
|
async delete(key) {
|
|
38523
39431
|
await fs.delete(toInternal(key));
|
|
@@ -38527,9 +39435,24 @@ function createDataObjectStore(dataDir) {
|
|
|
38527
39435
|
return {
|
|
38528
39436
|
items: result.items.map(
|
|
38529
39437
|
(item) => "prefix" in item ? { prefix: toExternal(item.prefix) } : externalMeta(item)
|
|
38530
|
-
),
|
|
39438
|
+
).filter((item) => !isUploadTempKey("prefix" in item ? item.prefix : item.key)),
|
|
38531
39439
|
...result.cursor !== void 0 ? { cursor: result.cursor } : {}
|
|
38532
39440
|
};
|
|
39441
|
+
},
|
|
39442
|
+
async cleanupStaging(prefix, olderThan) {
|
|
39443
|
+
const cutoffMs = Date.parse(olderThan);
|
|
39444
|
+
if (!Number.isFinite(cutoffMs)) {
|
|
39445
|
+
throw new TBError("invalid_argument", "cleanupStaging olderThan \u5FC5\u987B\u662F ISO timestamp");
|
|
39446
|
+
}
|
|
39447
|
+
const normalizedPrefix = normalizeEntryPath(prefix.replace(/\/+$/, ""));
|
|
39448
|
+
const isDefaultStore = normalizedPrefix === STORE_KEY_PREFIX.slice(0, -1);
|
|
39449
|
+
const prefixDir = isDefaultStore ? join2(root, STORE_STAGING_DIR) : join2(root, ...normalizedPrefix.split("/"));
|
|
39450
|
+
await assertAncestorInRoot(root, join2(prefixDir, "staging-probe"), prefix);
|
|
39451
|
+
if (isDefaultStore) {
|
|
39452
|
+
await mkdir2(prefixDir, { recursive: true });
|
|
39453
|
+
await assertParentInRoot(root, join2(prefixDir, "staging-probe"), prefix);
|
|
39454
|
+
}
|
|
39455
|
+
return await cleanupStagingTree(prefixDir, cutoffMs);
|
|
38533
39456
|
}
|
|
38534
39457
|
};
|
|
38535
39458
|
}
|
|
@@ -38652,6 +39575,29 @@ var PgStateStore = class {
|
|
|
38652
39575
|
ON CONFLICT (key) DO UPDATE SET value = excluded.value
|
|
38653
39576
|
`;
|
|
38654
39577
|
}
|
|
39578
|
+
async compareAndSwap(key, expectedRevision, value) {
|
|
39579
|
+
if (expectedRevision === null) {
|
|
39580
|
+
if (value === null) return false;
|
|
39581
|
+
const result2 = await this.sql`
|
|
39582
|
+
INSERT INTO tb_kv (key, value) VALUES (${key}, ${this.sql.json(value)})
|
|
39583
|
+
ON CONFLICT (key) DO NOTHING
|
|
39584
|
+
`;
|
|
39585
|
+
return result2.count > 0;
|
|
39586
|
+
}
|
|
39587
|
+
if (!Number.isInteger(expectedRevision) || expectedRevision < 0) return false;
|
|
39588
|
+
const predicate = this.sql`
|
|
39589
|
+
key = ${key}
|
|
39590
|
+
AND jsonb_typeof(value) = 'object'
|
|
39591
|
+
AND jsonb_typeof(value -> 'revision') = 'number'
|
|
39592
|
+
AND (value ->> 'revision') ~ '^(0|[1-9][0-9]*)$'
|
|
39593
|
+
AND (value ->> 'revision')::numeric = ${expectedRevision}
|
|
39594
|
+
`;
|
|
39595
|
+
const result = value === null ? await this.sql`DELETE FROM tb_kv WHERE ${predicate}` : await this.sql`
|
|
39596
|
+
UPDATE tb_kv SET value = ${this.sql.json(value)}
|
|
39597
|
+
WHERE ${predicate}
|
|
39598
|
+
`;
|
|
39599
|
+
return result.count > 0;
|
|
39600
|
+
}
|
|
38655
39601
|
async putIfAbsent(key, value) {
|
|
38656
39602
|
const result = await this.sql`
|
|
38657
39603
|
INSERT INTO tb_kv (key, value) VALUES (${key}, ${this.sql.json(value)})
|
|
@@ -38834,6 +39780,20 @@ var SqliteStateStore = class {
|
|
|
38834
39780
|
async put(key, value) {
|
|
38835
39781
|
this.stmtPut.run(key, JSON.stringify(value));
|
|
38836
39782
|
}
|
|
39783
|
+
async compareAndSwap(key, expectedRevision, value) {
|
|
39784
|
+
if (expectedRevision === null) {
|
|
39785
|
+
if (value === null) return false;
|
|
39786
|
+
const info2 = this.db.prepare("INSERT OR IGNORE INTO kv (key, value) VALUES (?, ?)").run(key, JSON.stringify(value));
|
|
39787
|
+
return info2.changes > 0;
|
|
39788
|
+
}
|
|
39789
|
+
if (!Number.isInteger(expectedRevision) || expectedRevision < 0) return false;
|
|
39790
|
+
const predicate = `key = ?
|
|
39791
|
+
AND json_type(value) = 'object'
|
|
39792
|
+
AND json_type(value, '$.revision') = 'integer'
|
|
39793
|
+
AND json_extract(value, '$.revision') = ?`;
|
|
39794
|
+
const info = value === null ? this.db.prepare(`DELETE FROM kv WHERE ${predicate}`).run(key, expectedRevision) : this.db.prepare(`UPDATE kv SET value = ? WHERE ${predicate}`).run(JSON.stringify(value), key, expectedRevision);
|
|
39795
|
+
return info.changes > 0;
|
|
39796
|
+
}
|
|
38837
39797
|
async putIfAbsent(key, value) {
|
|
38838
39798
|
const info = this.db.prepare("INSERT OR IGNORE INTO kv (key, value) VALUES (?, ?)").run(key, JSON.stringify(value));
|
|
38839
39799
|
return info.changes > 0;
|
|
@@ -38881,7 +39841,7 @@ import { join as join4 } from "path";
|
|
|
38881
39841
|
// package.json
|
|
38882
39842
|
var package_default = {
|
|
38883
39843
|
name: "@tool-bridge/server",
|
|
38884
|
-
version: "0.
|
|
39844
|
+
version: "0.16.0",
|
|
38885
39845
|
description: "tool-bridge self-hosted server: run the HTBP gateway on Node with SQLite or PostgreSQL persistence, filesystem object storage and WebSocket device channel \u2014 single container via docker run",
|
|
38886
39846
|
type: "module",
|
|
38887
39847
|
license: "MIT",
|
|
@@ -38954,7 +39914,7 @@ var package_default = {
|
|
|
38954
39914
|
|
|
38955
39915
|
// src/assets.ts
|
|
38956
39916
|
import { brotliCompressSync, gzipSync, constants as zlibConstants } from "zlib";
|
|
38957
|
-
import { dirname as
|
|
39917
|
+
import { dirname as dirname3, join as join3, resolve as resolve2, sep as sep3 } from "path";
|
|
38958
39918
|
import { readFile as readFile2 } from "fs/promises";
|
|
38959
39919
|
import { createRequire } from "module";
|
|
38960
39920
|
import { existsSync } from "fs";
|
|
@@ -38987,7 +39947,7 @@ function resolveUiDir(uiDirOverride) {
|
|
|
38987
39947
|
try {
|
|
38988
39948
|
const require2 = createRequire(import.meta.url);
|
|
38989
39949
|
const pkgPath = require2.resolve("@tool-bridge/dashboard/package.json");
|
|
38990
|
-
const dist = join3(
|
|
39950
|
+
const dist = join3(dirname3(pkgPath), "dist");
|
|
38991
39951
|
return existsSync(join3(dist, "index.html")) ? dist : void 0;
|
|
38992
39952
|
} catch {
|
|
38993
39953
|
return void 0;
|
|
@@ -39014,7 +39974,7 @@ function uiAssetsFetcher(uiDir) {
|
|
|
39014
39974
|
let pathname = decodeURIComponent(url2.pathname);
|
|
39015
39975
|
if (pathname.endsWith("/")) pathname += "index.html";
|
|
39016
39976
|
const full = resolve2(join3(root, pathname));
|
|
39017
|
-
if (full !== root && !full.startsWith(root +
|
|
39977
|
+
if (full !== root && !full.startsWith(root + sep3)) {
|
|
39018
39978
|
return new Response("not found", { status: 404 });
|
|
39019
39979
|
}
|
|
39020
39980
|
let asset = cache.get(full);
|
|
@@ -39196,8 +40156,49 @@ function createTbServer(config) {
|
|
|
39196
40156
|
if (config.uploadGrantTtlSec !== void 0) {
|
|
39197
40157
|
deps.uploadGrantTtlSec = config.uploadGrantTtlSec;
|
|
39198
40158
|
}
|
|
40159
|
+
if (config.storeTokenSecret !== void 0) deps.storeTokenSecret = config.storeTokenSecret;
|
|
40160
|
+
if (config.storeMaxObjectBytes !== void 0) {
|
|
40161
|
+
deps.storeMaxObjectBytes = config.storeMaxObjectBytes;
|
|
40162
|
+
}
|
|
40163
|
+
if (config.storeRelayMaxBytes !== void 0) {
|
|
40164
|
+
deps.storeRelayMaxBytes = config.storeRelayMaxBytes;
|
|
40165
|
+
}
|
|
40166
|
+
if (config.storeUploadTtlSec !== void 0) deps.storeUploadTtlSec = config.storeUploadTtlSec;
|
|
40167
|
+
if (config.storeShareTtlSec !== void 0) deps.storeShareTtlSec = config.storeShareTtlSec;
|
|
40168
|
+
if (config.storeReadTtlSec !== void 0) deps.storeReadTtlSec = config.storeReadTtlSec;
|
|
40169
|
+
if (config.storeCallMaxBytes !== void 0) deps.storeCallMaxBytes = config.storeCallMaxBytes;
|
|
40170
|
+
if (config.storeCallMaxObjectBytes !== void 0) {
|
|
40171
|
+
deps.storeCallMaxObjectBytes = config.storeCallMaxObjectBytes;
|
|
40172
|
+
}
|
|
40173
|
+
if (config.storeCallMaxObjects !== void 0) {
|
|
40174
|
+
deps.storeCallMaxObjects = config.storeCallMaxObjects;
|
|
40175
|
+
}
|
|
40176
|
+
if (config.storeCallAllowedContentTypes !== void 0) {
|
|
40177
|
+
deps.storeCallAllowedContentTypes = config.storeCallAllowedContentTypes;
|
|
40178
|
+
}
|
|
39199
40179
|
const app = createTbApp(deps);
|
|
39200
40180
|
let server;
|
|
40181
|
+
let storeCleanupTimer;
|
|
40182
|
+
let storeCleanupInFlight = false;
|
|
40183
|
+
const runStoreCleanup = async () => {
|
|
40184
|
+
if (storeCleanupInFlight) return;
|
|
40185
|
+
storeCleanupInFlight = true;
|
|
40186
|
+
try {
|
|
40187
|
+
await cleanupDefaultStore(deps);
|
|
40188
|
+
} finally {
|
|
40189
|
+
storeCleanupInFlight = false;
|
|
40190
|
+
}
|
|
40191
|
+
};
|
|
40192
|
+
const reportStoreCleanupFailure = () => {
|
|
40193
|
+
console.warn(JSON.stringify({ event: "tool_bridge_store_cleanup_failed" }));
|
|
40194
|
+
};
|
|
40195
|
+
const scheduleStoreCleanup = () => {
|
|
40196
|
+
void runStoreCleanup().catch(reportStoreCleanupFailure);
|
|
40197
|
+
storeCleanupTimer = setInterval(() => {
|
|
40198
|
+
void runStoreCleanup().catch(reportStoreCleanupFailure);
|
|
40199
|
+
}, config.storeCleanupIntervalSec * 1e3);
|
|
40200
|
+
storeCleanupTimer.unref?.();
|
|
40201
|
+
};
|
|
39201
40202
|
return {
|
|
39202
40203
|
app,
|
|
39203
40204
|
search,
|
|
@@ -39214,6 +40215,7 @@ function createTbServer(config) {
|
|
|
39214
40215
|
await hub.sweepOrphans();
|
|
39215
40216
|
return await new Promise((resolve3) => {
|
|
39216
40217
|
server = serve({ fetch: app.fetch, port: config.port, hostname: config.host }, (info) => {
|
|
40218
|
+
scheduleStoreCleanup();
|
|
39217
40219
|
resolve3({ port: info.port });
|
|
39218
40220
|
});
|
|
39219
40221
|
hub.attach(server);
|
|
@@ -39224,6 +40226,10 @@ function createTbServer(config) {
|
|
|
39224
40226
|
},
|
|
39225
40227
|
async close() {
|
|
39226
40228
|
draining = true;
|
|
40229
|
+
if (storeCleanupTimer !== void 0) {
|
|
40230
|
+
clearInterval(storeCleanupTimer);
|
|
40231
|
+
storeCleanupTimer = void 0;
|
|
40232
|
+
}
|
|
39227
40233
|
let closed;
|
|
39228
40234
|
if (server !== void 0) {
|
|
39229
40235
|
const s = server;
|