@tool-bridge/server 0.14.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.
Files changed (108) hide show
  1. package/dist/{airtable-NVGQVZJW.js → airtable-S3T4TAN7.js} +4 -4
  2. package/dist/{alt_text_generator_ai-WF7NHVWE.js → alt_text_generator_ai-4DIRIDKL.js} +4 -4
  3. package/dist/{amap-EPI7AQ4O.js → amap-LGVGNPML.js} +4 -4
  4. package/dist/{anthropic-KVYWBZIV.js → anthropic-27RFZO35.js} +4 -4
  5. package/dist/{apify-MB56RQIA.js → apify-6WLYAIAS.js} +4 -4
  6. package/dist/{brave_search-JDAGJ7PO.js → brave_search-K2SVJ66F.js} +4 -4
  7. package/dist/{chunk-HGIPJFHA.js → chunk-2O27H4Y2.js} +1 -1
  8. package/dist/{chunk-X6LLG5OZ.js → chunk-BB6ZLMTU.js} +1194 -120
  9. package/dist/{chunk-CDOWN2AB.js → chunk-GKX72JMJ.js} +1 -1
  10. package/dist/{chunk-BWGAV3MR.js → chunk-VK3V2WTY.js} +2 -2
  11. package/dist/{chunk-ABHHWYEV.js → chunk-YB2FLNWI.js} +1689 -39
  12. package/dist/{circleci-2IL5RU2O.js → circleci-WV3ITU7Q.js} +4 -4
  13. package/dist/{clerk-A5YYGWYM.js → clerk-NFQEFKS4.js} +4 -4
  14. package/dist/{cloudflare_dns-GUA5BUWO.js → cloudflare_dns-2RK5ASBJ.js} +4 -4
  15. package/dist/{cohere-BS27QMKR.js → cohere-5KEW7LWT.js} +4 -4
  16. package/dist/{confluence-675IWLHU.js → confluence-SZE63S46.js} +4 -4
  17. package/dist/{convertapi-DGKXP2T3.js → convertapi-QPG2JC5K.js} +4 -4
  18. package/dist/{currencyapi-PFH3UKVU.js → currencyapi-UO2APQRY.js} +4 -4
  19. package/dist/{deepseek-5U3STDGY.js → deepseek-LJELIMM3.js} +4 -4
  20. package/dist/{docker_hub-UVXRU2L3.js → docker_hub-2MFXSBOF.js} +4 -4
  21. package/dist/{dropbox-BC54XUXF.js → dropbox-GQYNTTVA.js} +4 -4
  22. package/dist/{dub-GV5M6PWD.js → dub-KI2LHXST.js} +4 -4
  23. package/dist/{e2b-KEQ3PYPV.js → e2b-WO6ISY3C.js} +4 -4
  24. package/dist/{exa-OGQR7D47.js → exa-IZ7B3BPT.js} +4 -4
  25. package/dist/{fal_ai-ULI6YF4O.js → fal_ai-LTMF36GJ.js} +4 -4
  26. package/dist/{feishu-YWLRRUGP.js → feishu-WM7SLSFD.js} +3 -3
  27. package/dist/{feishu_custom_bot-7HP4UHB7.js → feishu_custom_bot-66W3MXH2.js} +4 -4
  28. package/dist/{figma-BFGC24YI.js → figma-2Z274VQZ.js} +4 -4
  29. package/dist/{firehydrant-R6DG7SBL.js → firehydrant-IFI4R65V.js} +4 -4
  30. package/dist/{fixer-ZVXOVTMF.js → fixer-BCCZEB3D.js} +4 -4
  31. package/dist/{fly-EBKWMDWV.js → fly-755FXYB5.js} +4 -4
  32. package/dist/{front-CFWVIQYX.js → front-4FZKKXXK.js} +4 -4
  33. package/dist/{geocodio-N27RDBFV.js → geocodio-WYKDJ3DP.js} +4 -4
  34. package/dist/{ghost-2HT2SJYO.js → ghost-5BNQTCOH.js} +4 -4
  35. package/dist/{github-ZDLAIRXP.js → github-ULELF5VX.js} +4 -4
  36. package/dist/{gmail-TICKPP6S.js → gmail-G34WT64J.js} +4 -4
  37. package/dist/{googlecalendar-GUR47UTF.js → googlecalendar-BQU255SC.js} +4 -4
  38. package/dist/{googledocs-QGDIJA4S.js → googledocs-HPCNQXYJ.js} +4 -4
  39. package/dist/{grafana-33Y3WEU5.js → grafana-VOV24FIC.js} +4 -4
  40. package/dist/{graphhopper-KWTMYEVH.js → graphhopper-D5RXW6AP.js} +4 -4
  41. package/dist/index.d.ts +164 -61
  42. package/dist/index.js +2 -2
  43. package/dist/{ip2proxy-4SAYDJPQ.js → ip2proxy-DAYO7EKZ.js} +4 -4
  44. package/dist/{ipgeolocation_io-4WT2WKHF.js → ipgeolocation_io-XD4PSLII.js} +4 -4
  45. package/dist/{ipqualityscore-VGZCROJJ.js → ipqualityscore-K27QEAWK.js} +4 -4
  46. package/dist/{jina_ai-X5VJVORK.js → jina_ai-E6UXFV2P.js} +4 -4
  47. package/dist/{jira-XUAF2UPM.js → jira-ZDSXGQ6W.js} +4 -4
  48. package/dist/{langsmith-RRLFNL6D.js → langsmith-TIFJRREW.js} +4 -4
  49. package/dist/{lemlist-KQKG6JHM.js → lemlist-DASEMJMA.js} +4 -4
  50. package/dist/{linear-I32UBCBL.js → linear-KE6UP4SP.js} +4 -4
  51. package/dist/{logsnag-TVNUQEM7.js → logsnag-IMNDN4R5.js} +4 -4
  52. package/dist/main.js +101 -101
  53. package/dist/{mattermost-OCIA7Z4P.js → mattermost-HHHMUXQA.js} +4 -4
  54. package/dist/{mem0-FARYP2ZM.js → mem0-D2FGGOIY.js} +4 -4
  55. package/dist/{memos-6M6QEUYI.js → memos-4OVIGFPH.js} +4 -4
  56. package/dist/{metabase-VCNMPHIR.js → metabase-33WCX5YD.js} +4 -4
  57. package/dist/{mistral_ai-SMPVEWBM.js → mistral_ai-QIMZ2JYL.js} +4 -4
  58. package/dist/{mother_duck-CMHFH5A6.js → mother_duck-M6VWS2VH.js} +4 -4
  59. package/dist/{ngrok-WRQJ3X54.js → ngrok-Z2PKQBHH.js} +4 -4
  60. package/dist/{notes-HLPED7H7.js → notes-SS4RPC4K.js} +2 -2
  61. package/dist/{notion-FFCK2KZ4.js → notion-FJCNQ6I7.js} +4 -4
  62. package/dist/{open_exchange_rates-VKQICRS6.js → open_exchange_rates-WU57WTT5.js} +4 -4
  63. package/dist/{openai-PJDJ2AZ2.js → openai-JODZ4GKT.js} +4 -4
  64. package/dist/{opengraph_io-DMQQXMEC.js → opengraph_io-SQRAUSV5.js} +4 -4
  65. package/dist/{openrouter-I52ZZHQT.js → openrouter-Q5XXEJZ7.js} +4 -4
  66. package/dist/{outline-I6EIBEHQ.js → outline-RT2EHJ2X.js} +4 -4
  67. package/dist/{paddle-4DU7GKCS.js → paddle-OF2V7GAC.js} +4 -4
  68. package/dist/{perplexity-DE6K444O.js → perplexity-LNZEWLHG.js} +4 -4
  69. package/dist/{pinecone-KMRF5YWJ.js → pinecone-JSJIXZYN.js} +4 -4
  70. package/dist/{pipedrive-4OC5UU3L.js → pipedrive-D4CKVUXR.js} +4 -4
  71. package/dist/{polar-OACUFN7P.js → polar-M7ZYP6UR.js} +4 -4
  72. package/dist/{posthog-PSGNT66O.js → posthog-YV7CZOH4.js} +4 -4
  73. package/dist/{postmark-CSOVREPD.js → postmark-KYHLECRG.js} +4 -4
  74. package/dist/{prerender-B7M6SAIA.js → prerender-HCMHBKV2.js} +4 -4
  75. package/dist/{pubmed-JJ5URMS6.js → pubmed-WYVLCCN3.js} +4 -4
  76. package/dist/{railway-VO42EWHC.js → railway-TWIRD5KR.js} +4 -4
  77. package/dist/{readwise-O7I4HLU4.js → readwise-UZ467IQ7.js} +4 -4
  78. package/dist/{render-24X3H3WB.js → render-MECUC5CC.js} +4 -4
  79. package/dist/{replicate-TO6OPISC.js → replicate-A5MEK3SW.js} +4 -4
  80. package/dist/{resend-IBVGUV4X.js → resend-BC6EAWJQ.js} +4 -4
  81. package/dist/{rootly-MD5GFDUC.js → rootly-ZM3QDUIS.js} +4 -4
  82. package/dist/{scrapfly-ZKDGE4IL.js → scrapfly-WAML42QJ.js} +4 -4
  83. package/dist/{scrapingbee-2FVX2BFI.js → scrapingbee-DG7PET3V.js} +4 -4
  84. package/dist/{screenshot_fyi-HNPNS43Z.js → screenshot_fyi-HY6WTHIO.js} +4 -4
  85. package/dist/{semantic_scholar-EJ2CYAKO.js → semantic_scholar-C5IZA7IP.js} +4 -4
  86. package/dist/{sentry-DXB3CQZA.js → sentry-OKBPSTDM.js} +4 -4
  87. package/dist/{shodan-HEFZ6NHP.js → shodan-PPC6DYU4.js} +4 -4
  88. package/dist/{shopify-SUN5J257.js → shopify-EWGUHBXO.js} +4 -4
  89. package/dist/{stripe-N3HNTHD5.js → stripe-6RPDPC5W.js} +4 -4
  90. package/dist/{supabase-IMZ6YRES.js → supabase-SK7DQ42N.js} +4 -4
  91. package/dist/{tavily-QMPSOIGW.js → tavily-YCG5VQMH.js} +4 -4
  92. package/dist/{telegram-SHZX3C73.js → telegram-2OES5FYB.js} +4 -4
  93. package/dist/{telnyx-SF6KMQ7W.js → telnyx-POOSBBR4.js} +4 -4
  94. package/dist/{todoist-Y252DAZ2.js → todoist-M7YD6YSS.js} +4 -4
  95. package/dist/{trello-BTRC6I5Y.js → trello-IS6V5W3E.js} +4 -4
  96. package/dist/{turso-SB4KFTUP.js → turso-RCFHUFVH.js} +4 -4
  97. package/dist/{twilio-CRRJZ6MR.js → twilio-OS2RXFGO.js} +4 -4
  98. package/dist/{umami-7PJKITQ5.js → umami-PQ2SJ2LN.js} +4 -4
  99. package/dist/{unsplash-PYZXMXH2.js → unsplash-C6BPZO4E.js} +4 -4
  100. package/dist/{upstash_redis-L2IJTHFW.js → upstash_redis-WH6UER7H.js} +4 -4
  101. package/dist/{uptimerobot-XDYEYFJQ.js → uptimerobot-M3YZGS6Z.js} +4 -4
  102. package/dist/{v2ex-TNACDCAO.js → v2ex-R4JWTEHN.js} +4 -4
  103. package/dist/{vercel-QRU4UC5M.js → vercel-FZVYUOQD.js} +4 -4
  104. package/dist/{wolfram_alpha_api-TS663Q5L.js → wolfram_alpha_api-2KOLO5DC.js} +4 -4
  105. package/dist/{wordpress-RTEUS26Y.js → wordpress-XRWGO6LG.js} +4 -4
  106. package/dist/{workos-4NGPU4VP.js → workos-WR4NKOYL.js} +4 -4
  107. package/dist/{zhihu-GGLOXAVO.js → zhihu-BKZIB3WA.js} +4 -4
  108. package/package.json +3 -3
@@ -16037,8 +16037,233 @@ function createPluginModule(deps) {
16037
16037
  };
16038
16038
  }
16039
16039
 
16040
+ // ../core/src/builtin/store.ts
16041
+ var STORE_SCOPE_BY_CMD = {
16042
+ create_upload: "write",
16043
+ complete_upload: "write",
16044
+ abort_upload: "write",
16045
+ stat: "read",
16046
+ read: "read",
16047
+ share: "write",
16048
+ revoke_share: "write",
16049
+ delete: "write",
16050
+ list: "read"
16051
+ };
16052
+ var DESCRIPTION3 = "Deployment-level private object Store for uploads, reads and short-lived sharing";
16053
+ function storeCmds(nodePath) {
16054
+ const path = cmdPath(nodePath);
16055
+ const objectFields = {
16056
+ contentType: { type: "string", description: "object MIME type" },
16057
+ filename: { type: "string", description: "display-only filename; never used as a storage key" },
16058
+ size: { type: "number", description: "expected byte size" },
16059
+ checksum: {
16060
+ type: "object",
16061
+ description: "optional sha256 checksum declaration",
16062
+ properties: {
16063
+ algorithm: { type: "string", enum: ["sha256"] },
16064
+ value: { type: "string" }
16065
+ }
16066
+ },
16067
+ idempotencyKey: { type: "string", description: "owner-scoped create retry key" }
16068
+ };
16069
+ const uriField = { uri: { type: "string", description: "store://default/<objectId>" } };
16070
+ const specs = [
16071
+ {
16072
+ name: "create_upload",
16073
+ method: "POST",
16074
+ path,
16075
+ h: "create a new opaque Store object and bounded upload session",
16076
+ inputSchema: {
16077
+ type: "object",
16078
+ properties: objectFields,
16079
+ required: ["contentType"]
16080
+ },
16081
+ returns: "StoreUploadGrant"
16082
+ },
16083
+ {
16084
+ name: "complete_upload",
16085
+ method: "POST",
16086
+ path,
16087
+ h: "verify a direct upload and atomically mark its object ready; idempotent",
16088
+ inputSchema: {
16089
+ type: "object",
16090
+ properties: {
16091
+ uploadId: { type: "string" }
16092
+ },
16093
+ required: ["uploadId"]
16094
+ },
16095
+ returns: "StoreObject"
16096
+ },
16097
+ {
16098
+ name: "abort_upload",
16099
+ method: "POST",
16100
+ path,
16101
+ h: "abandon an unfinished upload; idempotent until the object is ready",
16102
+ inputSchema: {
16103
+ type: "object",
16104
+ properties: {
16105
+ uploadId: { type: "string" }
16106
+ },
16107
+ required: ["uploadId"]
16108
+ },
16109
+ returns: "void"
16110
+ },
16111
+ {
16112
+ name: "stat",
16113
+ method: "POST",
16114
+ path,
16115
+ h: "return metadata for a ready object owned by the current principal",
16116
+ inputSchema: {
16117
+ type: "object",
16118
+ properties: uriField,
16119
+ required: ["uri"]
16120
+ },
16121
+ returns: "StoreObject"
16122
+ },
16123
+ {
16124
+ name: "read",
16125
+ method: "POST",
16126
+ path,
16127
+ h: "authorize an object read and return a short-lived $ref",
16128
+ inputSchema: {
16129
+ type: "object",
16130
+ properties: uriField,
16131
+ required: ["uri"]
16132
+ },
16133
+ returns: "{ $ref, contentType, size, expiresAt }"
16134
+ },
16135
+ {
16136
+ name: "share",
16137
+ method: "POST",
16138
+ path,
16139
+ h: "create a short-lived, revocable read share for an owned object",
16140
+ inputSchema: {
16141
+ type: "object",
16142
+ properties: { ...uriField, ttlSec: { type: "number" } },
16143
+ required: ["uri"]
16144
+ },
16145
+ returns: "StoreShare"
16146
+ },
16147
+ {
16148
+ name: "revoke_share",
16149
+ method: "POST",
16150
+ path,
16151
+ h: "revoke a Store share grant",
16152
+ inputSchema: {
16153
+ type: "object",
16154
+ properties: { shareId: { type: "string" } },
16155
+ required: ["shareId"]
16156
+ },
16157
+ returns: "void"
16158
+ },
16159
+ {
16160
+ name: "delete",
16161
+ method: "POST",
16162
+ path,
16163
+ h: "make an owned Store object immediately unreadable and delete its bytes",
16164
+ inputSchema: { type: "object", properties: uriField, required: ["uri"] },
16165
+ returns: "void"
16166
+ },
16167
+ {
16168
+ name: "list",
16169
+ method: "POST",
16170
+ path,
16171
+ h: "list ready Store objects owned by the current principal",
16172
+ inputSchema: { type: "object", properties: { opts: LIST_OPTS_SCHEMA } },
16173
+ returns: "Page<StoreObject>"
16174
+ }
16175
+ ];
16176
+ return withCommandPaths(nodePath, specs.map((spec) => ({
16177
+ ...spec,
16178
+ scope: STORE_SCOPE_BY_CMD[spec.name]
16179
+ })));
16180
+ }
16181
+ function optionalPositiveInt(args, field) {
16182
+ const value = args[field];
16183
+ if (value === void 0) return void 0;
16184
+ if (!Number.isSafeInteger(value) || value < 1) {
16185
+ throw new TBError("invalid_argument", `${field} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
16186
+ }
16187
+ return value;
16188
+ }
16189
+ function rejectUnknown(args, allowed) {
16190
+ const unknown2 = Object.keys(args).filter((key) => !allowed.includes(key));
16191
+ if (unknown2.length > 0) {
16192
+ throw new TBError("invalid_argument", `\u672A\u77E5\u5B57\u6BB5:${unknown2.join(",")}`);
16193
+ }
16194
+ }
16195
+ function createStoreModule(deps) {
16196
+ return {
16197
+ module: "store",
16198
+ description: DESCRIPTION3,
16199
+ help(nodePath) {
16200
+ return {
16201
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION3 },
16202
+ cmds: storeCmds(nodePath)
16203
+ };
16204
+ },
16205
+ async dispatch(cmd, args, ctx, runtime) {
16206
+ switch (cmd) {
16207
+ case "create_upload": {
16208
+ rejectUnknown(args, ["contentType", "filename", "size", "checksum", "idempotencyKey"]);
16209
+ const input = {
16210
+ contentType: requireString(args, "contentType"),
16211
+ ...args.filename !== void 0 ? { filename: args.filename } : {},
16212
+ ...args.size !== void 0 ? { size: args.size } : {},
16213
+ ...args.checksum !== void 0 ? { checksum: args.checksum } : {},
16214
+ ...args.idempotencyKey !== void 0 ? { idempotencyKey: args.idempotencyKey } : {}
16215
+ };
16216
+ const start = await deps.service.beginUpload(input, {
16217
+ owner: ctx.owner,
16218
+ producer: ctx.owner
16219
+ });
16220
+ return deps.callbacks.createUpload(start, runtime);
16221
+ }
16222
+ case "complete_upload":
16223
+ rejectUnknown(args, ["uploadId"]);
16224
+ return deps.service.completeUpload(requireString(args, "uploadId"), ctx.owner);
16225
+ case "abort_upload":
16226
+ rejectUnknown(args, ["uploadId"]);
16227
+ return deps.service.abortUpload(requireString(args, "uploadId"), ctx.owner);
16228
+ case "stat":
16229
+ rejectUnknown(args, ["uri"]);
16230
+ return deps.service.stat(requireString(args, "uri"), { owner: ctx.owner });
16231
+ case "read": {
16232
+ rejectUnknown(args, ["uri"]);
16233
+ const object2 = await deps.service.authorizeRead(requireString(args, "uri"), {
16234
+ owner: ctx.owner
16235
+ });
16236
+ return deps.callbacks.read(object2, runtime);
16237
+ }
16238
+ case "share": {
16239
+ rejectUnknown(args, ["uri", "ttlSec"]);
16240
+ const ttlSec = optionalPositiveInt(args, "ttlSec");
16241
+ const result = ttlSec === void 0 ? await deps.service.share(requireString(args, "uri"), ctx.owner) : await deps.service.share(requireString(args, "uri"), ctx.owner, ttlSec);
16242
+ return deps.callbacks.share(result, runtime);
16243
+ }
16244
+ case "revoke_share":
16245
+ rejectUnknown(args, ["shareId"]);
16246
+ return deps.service.revokeShare(requireString(args, "shareId"), ctx.owner);
16247
+ case "delete":
16248
+ rejectUnknown(args, ["uri"]);
16249
+ return deps.service.delete(requireString(args, "uri"), { owner: ctx.owner });
16250
+ case "list": {
16251
+ rejectUnknown(args, ["opts"]);
16252
+ const raw = args.opts;
16253
+ if (raw !== void 0 && (typeof raw !== "object" || raw === null || Array.isArray(raw))) {
16254
+ throw new TBError("invalid_argument", "opts \u5FC5\u987B\u662F\u5BF9\u8C61");
16255
+ }
16256
+ return deps.service.list(ctx.owner, raw ?? {});
16257
+ }
16258
+ default:
16259
+ throw new TBError("invalid_argument", `unknown cmd '${cmd}' on system/store`);
16260
+ }
16261
+ }
16262
+ };
16263
+ }
16264
+
16040
16265
  // ../core/src/builtin/annotation.ts
16041
- var DESCRIPTION3 = "Path annotations: admin-curated notes shown in ~help of the annotated path (set / get / remove / list)";
16266
+ var DESCRIPTION4 = "Path annotations: admin-curated notes shown in ~help of the annotated path (set / get / remove / list)";
16042
16267
  function annotationCmds(nodePath) {
16043
16268
  const path = cmdPath(nodePath);
16044
16269
  const cmds = [
@@ -16114,10 +16339,10 @@ function requireRootablePath(args) {
16114
16339
  function createAnnotationModule(deps) {
16115
16340
  return {
16116
16341
  module: "annotation",
16117
- description: DESCRIPTION3,
16342
+ description: DESCRIPTION4,
16118
16343
  help(nodePath) {
16119
16344
  return {
16120
- node: { path: nodePath, kind: "builtin", description: DESCRIPTION3 },
16345
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION4 },
16121
16346
  cmds: annotationCmds(nodePath)
16122
16347
  };
16123
16348
  },
@@ -16217,7 +16442,7 @@ var RemoteAllowlistStore = class {
16217
16442
  };
16218
16443
 
16219
16444
  // ../core/src/builtin/federation.ts
16220
- var DESCRIPTION4 = "Remote federation host allowlist: which hosts kind=remote nodes may connect to (env baseline is read-only; admin only)";
16445
+ var DESCRIPTION5 = "Remote federation host allowlist: which hosts kind=remote nodes may connect to (env baseline is read-only; admin only)";
16221
16446
  function federationCmds(nodePath) {
16222
16447
  const path = cmdPath(nodePath);
16223
16448
  const cmds = [
@@ -16280,10 +16505,10 @@ function mergedView(base, entries) {
16280
16505
  function createFederationModule(deps) {
16281
16506
  return {
16282
16507
  module: "federation",
16283
- description: DESCRIPTION4,
16508
+ description: DESCRIPTION5,
16284
16509
  help(nodePath) {
16285
16510
  return {
16286
- node: { path: nodePath, kind: "builtin", description: DESCRIPTION4 },
16511
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION5 },
16287
16512
  cmds: federationCmds(nodePath)
16288
16513
  };
16289
16514
  },
@@ -16319,7 +16544,7 @@ function createFederationModule(deps) {
16319
16544
  }
16320
16545
 
16321
16546
  // ../core/src/builtin/registry.ts
16322
- var DESCRIPTION5 = "Node registry: the single mount surface \u2014 everything on the tree (mcp/http/context/device/remote nodes) is mounted, listed and unmounted here";
16547
+ var DESCRIPTION6 = "Node registry: the single mount surface \u2014 everything on the tree (mcp/http/context/device/remote nodes) is mounted, listed and unmounted here";
16323
16548
  var NODE_FIELD_SCHEMAS = {
16324
16549
  path: { type: "string", description: 'tree path to mount at, e.g. "docs/context7"' },
16325
16550
  kind: {
@@ -16432,10 +16657,10 @@ function parseNodeInput(args) {
16432
16657
  function createRegistryModule(store, now, visibility) {
16433
16658
  return {
16434
16659
  module: "registry",
16435
- description: DESCRIPTION5,
16660
+ description: DESCRIPTION6,
16436
16661
  help(nodePath) {
16437
16662
  return {
16438
- node: { path: nodePath, kind: "builtin", description: DESCRIPTION5 },
16663
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION6 },
16439
16664
  cmds: registryCmds(nodePath)
16440
16665
  };
16441
16666
  },
@@ -16476,7 +16701,7 @@ function createRegistryModule(store, now, visibility) {
16476
16701
  }
16477
16702
 
16478
16703
  // ../core/src/builtin/secret.ts
16479
- var DESCRIPTION6 = "Upstream credential vault: write-only; mounts reference entries by name (authRef), values can never be read back (admin only)";
16704
+ var DESCRIPTION7 = "Upstream credential vault: write-only; mounts reference entries by name (authRef), values can never be read back (admin only)";
16480
16705
  function assertUserSecretName(name) {
16481
16706
  if (name.includes(":")) {
16482
16707
  throw new TBError(
@@ -16538,10 +16763,10 @@ function secretCmds(nodePath) {
16538
16763
  function createSecretModule(store, now) {
16539
16764
  return {
16540
16765
  module: "secret",
16541
- description: DESCRIPTION6,
16766
+ description: DESCRIPTION7,
16542
16767
  help(nodePath) {
16543
16768
  return {
16544
- node: { path: nodePath, kind: "builtin", description: DESCRIPTION6 },
16769
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION7 },
16545
16770
  cmds: secretCmds(nodePath)
16546
16771
  };
16547
16772
  },
@@ -16569,7 +16794,7 @@ function createSecretModule(store, now) {
16569
16794
  }
16570
16795
 
16571
16796
  // ../core/src/builtin/status.ts
16572
- var DESCRIPTION7 = "Gateway health and summary (readable without admin)";
16797
+ var DESCRIPTION8 = "Gateway health and summary (readable without admin)";
16573
16798
  function statusCmds(nodePath) {
16574
16799
  return withCommandPaths(nodePath, [
16575
16800
  {
@@ -16586,10 +16811,10 @@ function statusCmds(nodePath) {
16586
16811
  function createStatusModule(deps) {
16587
16812
  return {
16588
16813
  module: "status",
16589
- description: DESCRIPTION7,
16814
+ description: DESCRIPTION8,
16590
16815
  help(nodePath) {
16591
16816
  return {
16592
- node: { path: nodePath, kind: "builtin", description: DESCRIPTION7 },
16817
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION8 },
16593
16818
  cmds: statusCmds(nodePath)
16594
16819
  };
16595
16820
  },
@@ -16608,7 +16833,7 @@ function createStatusModule(deps) {
16608
16833
  }
16609
16834
 
16610
16835
  // ../core/src/builtin/sk.ts
16611
- var DESCRIPTION8 = "Secret Key registry: issue / list / update / revoke access keys (the only credential form; admin only)";
16836
+ var DESCRIPTION9 = "Secret Key registry: issue / list / update / revoke access keys (the only credential form; admin only)";
16612
16837
  var SK_FIELD_SCHEMAS = {
16613
16838
  owner: {
16614
16839
  type: "string",
@@ -16742,10 +16967,10 @@ function asSkUpdatePatch(args) {
16742
16967
  function createSkModule(store, now) {
16743
16968
  return {
16744
16969
  module: "sk",
16745
- description: DESCRIPTION8,
16970
+ description: DESCRIPTION9,
16746
16971
  help(nodePath) {
16747
16972
  return {
16748
- node: { path: nodePath, kind: "builtin", description: DESCRIPTION8 },
16973
+ node: { path: nodePath, kind: "builtin", description: DESCRIPTION9 },
16749
16974
  cmds: skCmds(nodePath)
16750
16975
  };
16751
16976
  },
@@ -16817,18 +17042,23 @@ function createBuiltins(deps) {
16817
17042
  createAnnotationModule({ store: deps.annotation.store, registry: deps.registry, now })
16818
17043
  );
16819
17044
  }
17045
+ if (deps.store !== void 0) {
17046
+ modules.set("store", createStoreModule(deps.store));
17047
+ }
16820
17048
  return modules;
16821
17049
  }
16822
17050
 
16823
17051
  // ../core/src/context/help.ts
16824
17052
  var CONTEXT_CAPABILITIES = ["search", "delete"];
17053
+ var CONTEXT_DIRECT_UPLOAD_CAPABILITY = "direct-upload";
16825
17054
  var SCOPE_BY_CMD = {
16826
17055
  list: "read",
16827
17056
  get: "read",
16828
17057
  search: "read",
16829
17058
  write: "write",
16830
17059
  update: "write",
16831
- delete: "write"
17060
+ delete: "write",
17061
+ create_upload: "write"
16832
17062
  };
16833
17063
  function contextScopeForCmd(command) {
16834
17064
  return SCOPE_BY_CMD[command] ?? null;
@@ -16976,6 +17206,29 @@ function contextCmds(nodePath) {
16976
17206
  ];
16977
17207
  return withCommandPaths(nodePath, cmds);
16978
17208
  }
17209
+ function contextUploadCmd(nodePath) {
17210
+ return withCommandPaths(nodePath, [{
17211
+ name: "create_upload",
17212
+ method: "POST",
17213
+ path: cmdPath(nodePath),
17214
+ h: "create a short-lived, path-scoped direct-upload grant; persist the returned uri, not url",
17215
+ inputSchema: {
17216
+ type: "object",
17217
+ required: ["path", "contentType"],
17218
+ properties: {
17219
+ path: { type: "string", description: "target entry path inside the namespace" },
17220
+ contentType: { type: "string", description: "media type signed into the PUT request" },
17221
+ overwrite: {
17222
+ type: "boolean",
17223
+ description: "allow replacing an existing object; default false"
17224
+ }
17225
+ }
17226
+ },
17227
+ returns: "ContextUploadGrant",
17228
+ scope: "write",
17229
+ effect: "write"
17230
+ }])[0];
17231
+ }
16979
17232
  function contextHelpModel(node, opts = {}) {
16980
17233
  const all = contextCmds(node.path);
16981
17234
  const present = opts.methods === void 0 ? all : all.filter((c) => opts.methods?.has(c.name));
@@ -17041,6 +17294,10 @@ function bytesToObjectStream(bytes) {
17041
17294
  };
17042
17295
  }
17043
17296
 
17297
+ // ../core/src/objectStoreService/types.ts
17298
+ var DEFAULT_STORE_NAME = "default";
17299
+ var DEFAULT_STORE_DRIVER_KEY_ROOT = "__tool_bridge_internal__/store/v1";
17300
+
17044
17301
  // ../core/src/context/path.ts
17045
17302
  function hasControlChar(s) {
17046
17303
  for (let i = 0; i < s.length; i++) {
@@ -17075,8 +17332,79 @@ function normalizeEntryPath(path) {
17075
17332
  // ../core/src/context/objectProvider.ts
17076
17333
  var REF_THRESHOLD_BYTES_DEFAULT = 1024 * 1024;
17077
17334
  var PRESIGN_TTL_SEC_DEFAULT = 900;
17335
+ var PRESIGN_TTL_SEC_MAX = 604800;
17336
+ var DEFAULT_STORE_RESERVED_KEY_ROOT = DEFAULT_STORE_DRIVER_KEY_ROOT;
17337
+ function normalizeKeyPrefix(keyPrefix) {
17338
+ return keyPrefix?.replace(/^\/+|\/+$/g, "") ?? "";
17339
+ }
17340
+ function isDefaultStoreReservedKey(key) {
17341
+ return key === DEFAULT_STORE_RESERVED_KEY_ROOT || key.startsWith(`${DEFAULT_STORE_RESERVED_KEY_ROOT}/`);
17342
+ }
17343
+ function assertContextKeyPrefixAllowed(keyPrefixBare) {
17344
+ if (keyPrefixBare === "") return;
17345
+ if (keyPrefixBare === DEFAULT_STORE_RESERVED_KEY_ROOT || keyPrefixBare.startsWith(`${DEFAULT_STORE_RESERVED_KEY_ROOT}/`) || DEFAULT_STORE_RESERVED_KEY_ROOT.startsWith(`${keyPrefixBare}/`)) {
17346
+ throw new TBError("invalid_argument", "\u5BF9\u8C61 Context keyPrefix \u4E0E Store \u4FDD\u7559 namespace \u51B2\u7A81");
17347
+ }
17348
+ }
17349
+ function assertContextKeyAllowed(key, path, writable) {
17350
+ if (!isDefaultStoreReservedKey(key)) return;
17351
+ if (!writable) throw TBError.notFound(`context entry \u4E0D\u5B58\u5728:'${path}'`);
17352
+ throw new TBError("permission_denied", "\u5BF9\u8C61 Context \u4E0D\u5F97\u4FEE\u6539 Store \u4FDD\u7559 namespace");
17353
+ }
17354
+ function assertPresignTtlSec(ttlSec) {
17355
+ if (!Number.isInteger(ttlSec) || ttlSec < 1 || ttlSec > PRESIGN_TTL_SEC_MAX) {
17356
+ throw new TBError(
17357
+ "invalid_argument",
17358
+ `presign TTL \u5FC5\u987B\u662F 1..${PRESIGN_TTL_SEC_MAX} \u7684\u6574\u6570\u79D2`
17359
+ );
17360
+ }
17361
+ return ttlSec;
17362
+ }
17078
17363
  var SEARCH_METADATA_HEAD_MAX = 200;
17079
17364
  var DIRECTORY_CONTENT_TYPE = "application/x-directory";
17365
+ async function createObjectContextUploadGrant(store, opts, input) {
17366
+ const keyPrefixBare = normalizeKeyPrefix(opts.keyPrefix);
17367
+ assertContextKeyPrefixAllowed(keyPrefixBare);
17368
+ if (opts.readOnly === true) {
17369
+ throw new TBError("permission_denied", "readOnly \u6302\u8F7D\u62D2\u7EDD create_upload");
17370
+ }
17371
+ if (typeof input?.path !== "string") {
17372
+ throw new TBError("invalid_argument", "create_upload \u9700\u8981\u5B57\u7B26\u4E32 'path'");
17373
+ }
17374
+ if (input.overwrite !== void 0 && typeof input.overwrite !== "boolean") {
17375
+ throw new TBError("invalid_argument", "create_upload \u7684 'overwrite' \u5FC5\u987B\u662F boolean");
17376
+ }
17377
+ const unknownKeys = Object.keys(input).filter(
17378
+ (key2) => key2 !== "path" && key2 !== "contentType" && key2 !== "overwrite"
17379
+ );
17380
+ if (unknownKeys.length > 0) {
17381
+ throw new TBError("invalid_argument", `create_upload \u542B\u672A\u77E5\u5B57\u6BB5:${unknownKeys.join(",")}`);
17382
+ }
17383
+ if (typeof input?.contentType !== "string" || input.contentType.trim() === "" || input.contentType.length > 255 || !input.contentType.includes("/") || /[\r\n\0]/.test(input.contentType)) {
17384
+ throw new TBError("invalid_argument", "create_upload \u9700\u8981\u5408\u6CD5\u7684 'contentType'");
17385
+ }
17386
+ const nsPath = normalizePath(opts.nsPath);
17387
+ const entryPath = normalizeEntryPath(input.path);
17388
+ const key = `${keyPrefixBare === "" ? "" : `${keyPrefixBare}/`}${entryPath}`;
17389
+ assertContextKeyAllowed(key, entryPath, true);
17390
+ if (store.presignPut === void 0) {
17391
+ throw new TBError("unavailable", "\u5BF9\u8C61\u5B58\u50A8\u672A\u914D\u7F6E\u76F4\u4F20\u7B7E\u540D\u80FD\u529B", { retryable: false });
17392
+ }
17393
+ const ttlSec = assertPresignTtlSec(opts.uploadGrantTtlSec ?? PRESIGN_TTL_SEC_DEFAULT);
17394
+ const contentType = input.contentType.trim();
17395
+ const expiresAt = new Date(Date.now() + ttlSec * 1e3).toISOString();
17396
+ const signed = await store.presignPut(key, ttlSec, {
17397
+ contentType,
17398
+ ...input.overwrite === true ? {} : { ifNoneMatch: "*" }
17399
+ });
17400
+ return {
17401
+ uri: `node://${nsPath}/${entryPath}`,
17402
+ method: "PUT",
17403
+ url: signed.url,
17404
+ headers: signed.headers,
17405
+ expiresAt
17406
+ };
17407
+ }
17080
17408
  function clampLimit3(limit) {
17081
17409
  if (limit === void 0) return LIST_LIMIT_DEFAULT;
17082
17410
  if (!Number.isInteger(limit) || limit < 1) {
@@ -17111,12 +17439,18 @@ function serializeInput(entry) {
17111
17439
  }
17112
17440
  function createObjectContextProvider(store, opts) {
17113
17441
  const nsPath = normalizePath(opts.nsPath);
17114
- const keyPrefixBare = opts.keyPrefix?.replace(/^\/+|\/+$/g, "") ?? "";
17442
+ const keyPrefixBare = normalizeKeyPrefix(opts.keyPrefix);
17443
+ assertContextKeyPrefixAllowed(keyPrefixBare);
17115
17444
  const keyPrefix = keyPrefixBare === "" ? "" : `${keyPrefixBare}/`;
17116
17445
  const readOnly = opts.readOnly ?? false;
17117
17446
  const refThreshold = opts.refThresholdBytes ?? REF_THRESHOLD_BYTES_DEFAULT;
17118
17447
  const presignTtlSec = opts.presignTtlSec ?? PRESIGN_TTL_SEC_DEFAULT;
17119
- const keyFor = (path) => keyPrefix + normalizeEntryPath(path);
17448
+ const keyFor = (path, writable) => {
17449
+ const entryPath = normalizeEntryPath(path);
17450
+ const key = keyPrefix + entryPath;
17451
+ assertContextKeyAllowed(key, entryPath, writable);
17452
+ return key;
17453
+ };
17120
17454
  const entryPathOf = (key) => key.slice(keyPrefix.length);
17121
17455
  const uriFor = (entryPath) => `node://${nsPath}/${entryPath}`;
17122
17456
  const toMeta = (m) => ({
@@ -17148,25 +17482,41 @@ function createObjectContextProvider(store, opts) {
17148
17482
  const limit = clampLimit3(listOpts?.limit);
17149
17483
  const rel = path === "" ? "" : `${normalizeEntryPath(path)}/`;
17150
17484
  const full = keyPrefix + rel;
17151
- const res = await store.list(full, { delimiter: "/", cursor: listOpts?.cursor, limit });
17485
+ assertContextKeyAllowed(full.slice(0, -1), path, false);
17152
17486
  const items = [];
17153
- for (const item of res.items) {
17154
- if ("prefix" in item) {
17155
- items.push({
17156
- uri: uriFor(entryPathOf(item.prefix)),
17157
- contentType: DIRECTORY_CONTENT_TYPE,
17158
- version: "",
17159
- updatedAt: "",
17160
- metadata: {}
17161
- });
17162
- } else if (item.key !== full) {
17163
- items.push(toMeta(item));
17487
+ let storeCursor = listOpts?.cursor;
17488
+ for (; ; ) {
17489
+ const previousCursor = storeCursor;
17490
+ const res = await store.list(full, {
17491
+ delimiter: "/",
17492
+ cursor: storeCursor,
17493
+ limit: Math.max(1, limit - items.length)
17494
+ });
17495
+ for (const item of res.items) {
17496
+ const itemKey = "prefix" in item ? item.prefix.replace(/\/$/, "") : item.key;
17497
+ if (isDefaultStoreReservedKey(itemKey)) continue;
17498
+ if ("prefix" in item) {
17499
+ items.push({
17500
+ uri: uriFor(entryPathOf(item.prefix)),
17501
+ contentType: DIRECTORY_CONTENT_TYPE,
17502
+ version: "",
17503
+ updatedAt: "",
17504
+ metadata: {}
17505
+ });
17506
+ } else if (item.key !== full) {
17507
+ items.push(toMeta(item));
17508
+ }
17509
+ }
17510
+ storeCursor = res.cursor;
17511
+ if (items.length >= limit || storeCursor === void 0) break;
17512
+ if (storeCursor === previousCursor) {
17513
+ throw new TBError("internal", "\u5BF9\u8C61\u5B58\u50A8 list cursor \u672A\u524D\u8FDB");
17164
17514
  }
17165
17515
  }
17166
- return res.cursor !== void 0 ? { items, cursor: res.cursor } : { items };
17516
+ return storeCursor !== void 0 ? { items, cursor: storeCursor } : { items };
17167
17517
  },
17168
17518
  async get(path) {
17169
- const key = keyFor(path);
17519
+ const key = keyFor(path, false);
17170
17520
  const head = await store.head(key);
17171
17521
  if (!head) throw notFound(path);
17172
17522
  const meta3 = toMeta(head);
@@ -17186,7 +17536,7 @@ function createObjectContextProvider(store, opts) {
17186
17536
  },
17187
17537
  async write(path, entry) {
17188
17538
  assertWritable("write");
17189
- const key = keyFor(path);
17539
+ const key = keyFor(path, true);
17190
17540
  const { body, contentType } = serializeInput(entry);
17191
17541
  const meta3 = await store.put(key, body, {
17192
17542
  contentType,
@@ -17197,7 +17547,7 @@ function createObjectContextProvider(store, opts) {
17197
17547
  },
17198
17548
  async update(path, patch) {
17199
17549
  assertWritable("update");
17200
- const key = keyFor(path);
17550
+ const key = keyFor(path, true);
17201
17551
  if (patch.content === void 0 && patch.metadata === void 0) {
17202
17552
  throw new TBError("invalid_argument", "patch \u81F3\u5C11\u63D0\u4F9B content \u6216 metadata \u4E4B\u4E00");
17203
17553
  }
@@ -17225,7 +17575,7 @@ function createObjectContextProvider(store, opts) {
17225
17575
  },
17226
17576
  async delete(path) {
17227
17577
  assertWritable("delete");
17228
- await store.delete(keyFor(path));
17578
+ await store.delete(keyFor(path, true));
17229
17579
  },
17230
17580
  async search(query, searchOpts) {
17231
17581
  const mode = searchOpts?.mode ?? "keyword";
@@ -17251,6 +17601,7 @@ function createObjectContextProvider(store, opts) {
17251
17601
  const page = await store.list(keyPrefix, { cursor: storeCursor, limit: LIST_LIMIT_MAX });
17252
17602
  for (const item of page.items) {
17253
17603
  if ("prefix" in item) continue;
17604
+ if (isDefaultStoreReservedKey(item.key)) continue;
17254
17605
  if (after !== void 0 && item.key <= after) continue;
17255
17606
  const entryPath = entryPathOf(item.key);
17256
17607
  let meta3 = item;
@@ -17332,7 +17683,13 @@ var deviceCallContextSchema = external_exports.object({
17332
17683
  }).passthrough(),
17333
17684
  traceId: external_exports.string().min(1),
17334
17685
  createdAt: external_exports.string().min(1),
17335
- expiresAt: external_exports.string().min(1)
17686
+ expiresAt: external_exports.string().min(1),
17687
+ upload: external_exports.object({
17688
+ token: external_exports.string().min(1),
17689
+ expiresAt: external_exports.iso.datetime({ offset: true }),
17690
+ maxBytes: external_exports.number().int().positive(),
17691
+ maxObjects: external_exports.number().int().positive()
17692
+ }).strict().optional()
17336
17693
  }).passthrough();
17337
17694
  var schemaByType = {
17338
17695
  hello: external_exports.object({
@@ -18106,6 +18463,1290 @@ async function buildTree(opts) {
18106
18463
  return build(rootEntry, opts.depth);
18107
18464
  }
18108
18465
 
18466
+ // ../core/src/objectStoreService/uri.ts
18467
+ var STORE_OBJECT_ID_RE = /^[A-Za-z0-9_-]{22,64}$/;
18468
+ function storeUri(objectId) {
18469
+ if (!STORE_OBJECT_ID_RE.test(objectId)) {
18470
+ throw new TBError("invalid_argument", "\u975E\u6CD5 Store object id");
18471
+ }
18472
+ return `store://${DEFAULT_STORE_NAME}/${objectId}`;
18473
+ }
18474
+ function parseStoreUri(uri) {
18475
+ if (typeof uri !== "string") {
18476
+ throw new TBError("invalid_argument", "Store URI \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
18477
+ }
18478
+ const prefix = `store://${DEFAULT_STORE_NAME}/`;
18479
+ if (!uri.startsWith(prefix)) {
18480
+ throw new TBError("invalid_argument", "Store URI \u5FC5\u987B\u4F7F\u7528 store://default/<objectId>");
18481
+ }
18482
+ const objectId = uri.slice(prefix.length);
18483
+ if (!STORE_OBJECT_ID_RE.test(objectId)) {
18484
+ throw new TBError("invalid_argument", "\u975E\u6CD5 Store URI");
18485
+ }
18486
+ return { store: DEFAULT_STORE_NAME, objectId };
18487
+ }
18488
+
18489
+ // ../core/src/objectStoreService/service.ts
18490
+ var KEY_STORE_OBJECT = "store:object:";
18491
+ var KEY_STORE_UPLOAD = "store:upload:";
18492
+ var KEY_STORE_CALL_CAPABILITY = "store:call-capability:";
18493
+ var KEY_STORE_SHARE = "store:share:";
18494
+ var KEY_STORE_IDEMPOTENCY = "store:idempotency:";
18495
+ var UPLOAD_TTL_SEC_DEFAULT = 15 * 60;
18496
+ var SHARE_TTL_SEC_DEFAULT = 15 * 60;
18497
+ var MAX_OBJECT_BYTES_DEFAULT = 256 * 1024 * 1024;
18498
+ var MAX_CAS_ATTEMPTS = 32;
18499
+ var CAPABILITY_TTL_SEC_MAX = 7 * 24 * 60 * 60;
18500
+ var TOKEN_PREFIX = {
18501
+ call: "tbc",
18502
+ share: "tbs",
18503
+ upload: "tbu"
18504
+ };
18505
+ function stateCorrupt(kind) {
18506
+ return new TBError("internal", `Store ${kind} \u72B6\u6001\u635F\u574F`, { retryable: false });
18507
+ }
18508
+ function asRecord2(value, kind) {
18509
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
18510
+ throw stateCorrupt(kind);
18511
+ }
18512
+ return value;
18513
+ }
18514
+ function assertRevisioned(value, kind) {
18515
+ const record2 = asRecord2(value, kind);
18516
+ if (!Number.isInteger(record2.revision) || record2.revision < 1) {
18517
+ throw stateCorrupt(kind);
18518
+ }
18519
+ return record2;
18520
+ }
18521
+ function parseObject(value) {
18522
+ const record2 = assertRevisioned(value, "object");
18523
+ if (typeof record2.id !== "string" || record2.store !== DEFAULT_STORE_NAME || typeof record2.driverKey !== "string" || typeof record2.owner !== "string" || typeof record2.contentType !== "string" || typeof record2.uploadId !== "string" || typeof record2.status !== "string") throw stateCorrupt("object");
18524
+ return value;
18525
+ }
18526
+ function parseSession(value) {
18527
+ const record2 = assertRevisioned(value, "upload session");
18528
+ if (typeof record2.id !== "string" || typeof record2.objectId !== "string" || typeof record2.capabilityHash !== "string" || typeof record2.status !== "string") throw stateCorrupt("upload session");
18529
+ return value;
18530
+ }
18531
+ function parseCallCapability(value) {
18532
+ const record2 = assertRevisioned(value, "call capability");
18533
+ if (typeof record2.id !== "string" || typeof record2.tokenHash !== "string" || typeof record2.owner !== "string" || typeof record2.producer !== "string" || typeof record2.callId !== "string" || !Array.isArray(record2.reservations)) throw stateCorrupt("call capability");
18534
+ return value;
18535
+ }
18536
+ function parseShare(value) {
18537
+ const record2 = assertRevisioned(value, "share grant");
18538
+ if (typeof record2.id !== "string" || typeof record2.objectId !== "string" || typeof record2.tokenHash !== "string" || typeof record2.createdBy !== "string") throw stateCorrupt("share grant");
18539
+ return value;
18540
+ }
18541
+ function parseIdempotencyBinding(value) {
18542
+ const record2 = assertRevisioned(value, "idempotency binding");
18543
+ if (typeof record2.owner !== "string" || record2.domain !== "owner" && record2.domain !== "call" || typeof record2.producer !== "string" || typeof record2.fingerprint !== "string" || typeof record2.objectId !== "string" || typeof record2.uploadId !== "string") throw stateCorrupt("idempotency binding");
18544
+ return value;
18545
+ }
18546
+ function requireOwner(owner, field = "owner") {
18547
+ if (typeof owner !== "string" || owner.trim() === "" || /[\r\n\0]/.test(owner)) {
18548
+ throw new TBError("invalid_argument", `${field} \u5FC5\u987B\u662F\u975E\u7A7A OwnerRef`);
18549
+ }
18550
+ return owner;
18551
+ }
18552
+ function requirePositiveInt(value, field) {
18553
+ if (!Number.isSafeInteger(value) || value < 1) {
18554
+ throw new TBError("invalid_argument", `${field} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
18555
+ }
18556
+ return value;
18557
+ }
18558
+ function requireCapabilityTtl(value, field) {
18559
+ const ttl = requirePositiveInt(value, field);
18560
+ if (ttl > CAPABILITY_TTL_SEC_MAX) {
18561
+ throw new TBError("invalid_argument", `${field} \u4E0D\u80FD\u8D85\u8FC7 ${CAPABILITY_TTL_SEC_MAX} \u79D2`);
18562
+ }
18563
+ return ttl;
18564
+ }
18565
+ function normalizeTimestamp(value, field) {
18566
+ try {
18567
+ return normalizeExpiresAt(value);
18568
+ } catch (error51) {
18569
+ if (error51 instanceof TBError && error51.code === "invalid_argument") {
18570
+ throw new TBError("invalid_argument", `${field} \u5FC5\u987B\u662F\u5E26\u65F6\u533A\u7684 ISO 8601 timestamp`);
18571
+ }
18572
+ throw error51;
18573
+ }
18574
+ }
18575
+ function normalizeContentType(value) {
18576
+ if (typeof value !== "string" || value.trim() === "" || value.length > 255 || !value.includes("/") || /[\r\n\0]/.test(value)) {
18577
+ throw new TBError("invalid_argument", "contentType \u4E0D\u5408\u6CD5");
18578
+ }
18579
+ return value.trim().toLowerCase();
18580
+ }
18581
+ function normalizeChecksum(value) {
18582
+ if (value === void 0) return void 0;
18583
+ const record2 = asRecord2(value, "checksum");
18584
+ if (record2.algorithm !== "sha256" || typeof record2.value !== "string") {
18585
+ throw new TBError("invalid_argument", "checksum \u4EC5\u652F\u6301 sha256");
18586
+ }
18587
+ const digest = record2.value.toLowerCase();
18588
+ if (!/^[a-f0-9]{64}$/.test(digest)) {
18589
+ throw new TBError("invalid_argument", "sha256 checksum \u5FC5\u987B\u662F 64 \u4F4D\u5341\u516D\u8FDB\u5236");
18590
+ }
18591
+ return { algorithm: "sha256", value: digest };
18592
+ }
18593
+ function normalizeUploadInput(input) {
18594
+ const contentType = normalizeContentType(input?.contentType);
18595
+ let filename;
18596
+ if (input.filename !== void 0) {
18597
+ if (typeof input.filename !== "string" || input.filename.trim() === "" || input.filename.length > 1024 || /[\r\n\0]/.test(input.filename)) throw new TBError("invalid_argument", "filename \u4E0D\u5408\u6CD5");
18598
+ filename = input.filename;
18599
+ }
18600
+ let size;
18601
+ if (input.size !== void 0) {
18602
+ if (!Number.isSafeInteger(input.size) || input.size < 0) {
18603
+ throw new TBError("invalid_argument", "size \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570");
18604
+ }
18605
+ size = input.size;
18606
+ }
18607
+ let idempotencyKey;
18608
+ if (input.idempotencyKey !== void 0) {
18609
+ if (typeof input.idempotencyKey !== "string" || input.idempotencyKey.length < 1 || input.idempotencyKey.length > 255 || /[\r\n\0]/.test(input.idempotencyKey)) throw new TBError("invalid_argument", "idempotencyKey \u4E0D\u5408\u6CD5");
18610
+ idempotencyKey = input.idempotencyKey;
18611
+ }
18612
+ const checksum = normalizeChecksum(input.checksum);
18613
+ return {
18614
+ contentType,
18615
+ ...filename !== void 0 ? { filename } : {},
18616
+ ...size !== void 0 ? { size } : {},
18617
+ ...idempotencyKey !== void 0 ? { idempotencyKey } : {},
18618
+ ...checksum !== void 0 ? { checksum } : {}
18619
+ };
18620
+ }
18621
+ function constantTimeEqual(left, right) {
18622
+ let diff = left.length ^ right.length;
18623
+ const length = Math.max(left.length, right.length);
18624
+ for (let i = 0; i < length; i++) {
18625
+ diff |= (left.charCodeAt(i % Math.max(1, left.length)) || 0) ^ (right.charCodeAt(i % Math.max(1, right.length)) || 0);
18626
+ }
18627
+ return diff === 0;
18628
+ }
18629
+ function contentTypeAllowed(contentType, patterns) {
18630
+ return patterns.some((pattern) => {
18631
+ const normalized = pattern.toLowerCase();
18632
+ if (normalized === "*/*") return true;
18633
+ if (normalized.endsWith("/*")) return contentType.startsWith(normalized.slice(0, -1));
18634
+ return normalized === contentType;
18635
+ });
18636
+ }
18637
+ function normalizeContentTypePattern(value) {
18638
+ if (value === "*/*") return value;
18639
+ if (typeof value === "string" && value.endsWith("/*")) {
18640
+ const prefix = value.slice(0, -1);
18641
+ if (prefix.length > 1 && !/[\r\n\0]/.test(value)) return value.toLowerCase();
18642
+ }
18643
+ return normalizeContentType(value);
18644
+ }
18645
+ function descriptorOf(object2) {
18646
+ if (object2.status !== "ready" || object2.readyAt === void 0 || object2.size === void 0) throw stateCorrupt("ready object");
18647
+ return {
18648
+ uri: storeUri(object2.id),
18649
+ status: "ready",
18650
+ contentType: object2.contentType,
18651
+ size: object2.size,
18652
+ owner: object2.owner,
18653
+ createdAt: object2.createdAt,
18654
+ updatedAt: object2.updatedAt,
18655
+ readyAt: object2.readyAt,
18656
+ ...object2.etag !== void 0 ? { etag: object2.etag } : {},
18657
+ ...object2.filename !== void 0 ? { filename: object2.filename } : {},
18658
+ ...object2.producer !== void 0 ? { producer: object2.producer } : {},
18659
+ ...object2.originCallId !== void 0 ? { originCallId: object2.originCallId } : {},
18660
+ ...object2.checksum !== void 0 ? { checksum: object2.checksum } : {},
18661
+ ...object2.expiresAt !== void 0 ? { expiresAt: object2.expiresAt } : {}
18662
+ };
18663
+ }
18664
+ var StoreService = class {
18665
+ constructor(state, objects, opts) {
18666
+ this.state = state;
18667
+ this.objects = objects;
18668
+ if (state.compareAndSwap === void 0) {
18669
+ throw new TBError("unavailable", "StoreService \u8981\u6C42 StateStore.compareAndSwap", {
18670
+ retryable: false
18671
+ });
18672
+ }
18673
+ if (typeof opts?.tokenSecret !== "string" || opts.tokenSecret.length < 16) {
18674
+ throw new TBError("invalid_argument", "Store tokenSecret \u81F3\u5C11\u9700\u8981 16 \u4E2A\u5B57\u7B26");
18675
+ }
18676
+ this.cas = state.compareAndSwap.bind(state);
18677
+ this.tokenSecret = opts.tokenSecret;
18678
+ this.now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
18679
+ this.maxObjectBytes = requirePositiveInt(
18680
+ opts.maxObjectBytes ?? MAX_OBJECT_BYTES_DEFAULT,
18681
+ "maxObjectBytes"
18682
+ );
18683
+ this.relayMaxBytes = Math.min(
18684
+ requirePositiveInt(opts.relayMaxBytes ?? this.maxObjectBytes, "relayMaxBytes"),
18685
+ this.maxObjectBytes
18686
+ );
18687
+ this.uploadTtlSec = requireCapabilityTtl(
18688
+ opts.uploadTtlSec ?? UPLOAD_TTL_SEC_DEFAULT,
18689
+ "uploadTtlSec"
18690
+ );
18691
+ this.shareTtlSec = requireCapabilityTtl(
18692
+ opts.shareTtlSec ?? SHARE_TTL_SEC_DEFAULT,
18693
+ "shareTtlSec"
18694
+ );
18695
+ }
18696
+ state;
18697
+ objects;
18698
+ maxObjectBytes;
18699
+ now;
18700
+ relayMaxBytes;
18701
+ shareTtlSec;
18702
+ tokenSecret;
18703
+ uploadTtlSec;
18704
+ cas;
18705
+ async issueCallUploadCapability(input) {
18706
+ const now = this.nowIso();
18707
+ const expiresAt = normalizeTimestamp(input.expiresAt, "expiresAt");
18708
+ if (Date.parse(expiresAt) <= Date.parse(now)) {
18709
+ throw new TBError("invalid_argument", "call capability expiresAt \u5FC5\u987B\u5728\u672A\u6765");
18710
+ }
18711
+ const owner = requireOwner(input.owner);
18712
+ const producer = requireOwner(input.producer, "producer");
18713
+ const callId = requireOwner(input.callId, "callId");
18714
+ const maxObjects = requirePositiveInt(input.maxObjects, "maxObjects");
18715
+ const maxBytes = requirePositiveInt(input.maxBytes, "maxBytes");
18716
+ const maxObjectBytes = Math.min(
18717
+ requirePositiveInt(input.maxObjectBytes, "maxObjectBytes"),
18718
+ maxBytes,
18719
+ this.maxObjectBytes
18720
+ );
18721
+ if (!Array.isArray(input.allowedContentTypes) || input.allowedContentTypes.length === 0) {
18722
+ throw new TBError("invalid_argument", "allowedContentTypes \u4E0D\u80FD\u4E3A\u7A7A");
18723
+ }
18724
+ const allowedContentTypes = input.allowedContentTypes.map(normalizeContentTypePattern);
18725
+ const id = this.randomId();
18726
+ const token = await this.tokenFor("call", id);
18727
+ const capability = {
18728
+ id,
18729
+ revision: 1,
18730
+ tokenHash: await sha256Hex(token),
18731
+ status: "active",
18732
+ owner,
18733
+ producer,
18734
+ callId,
18735
+ expiresAt,
18736
+ maxObjects,
18737
+ maxBytes,
18738
+ maxObjectBytes,
18739
+ allowedContentTypes,
18740
+ reservations: [],
18741
+ reservedBytes: 0,
18742
+ createdAt: now
18743
+ };
18744
+ if (!await this.cas(`${KEY_STORE_CALL_CAPABILITY}${id}`, null, capability)) {
18745
+ throw new TBError("conflict", "call capability id \u51B2\u7A81");
18746
+ }
18747
+ return { capability: omit2(capability, "tokenHash"), token };
18748
+ }
18749
+ async verifyCallUploadCapability(token) {
18750
+ const id = this.tokenId(token, "call");
18751
+ const raw = await this.state.get(`${KEY_STORE_CALL_CAPABILITY}${id}`);
18752
+ if (raw === null) throw new TBError("permission_denied", "call upload capability \u65E0\u6548");
18753
+ const capability = parseCallCapability(raw);
18754
+ await this.assertTokenHash(token, capability.tokenHash, "call upload capability \u65E0\u6548");
18755
+ if (Date.parse(capability.expiresAt) <= Date.parse(this.nowIso())) {
18756
+ await this.expireCallCapability(capability);
18757
+ throw new TBError("permission_denied", "call upload capability \u5DF2\u8FC7\u671F");
18758
+ }
18759
+ if (capability.status === "revoked" || capability.status === "expired") {
18760
+ throw new TBError("permission_denied", "call upload capability \u5DF2\u5931\u6548");
18761
+ }
18762
+ return capability;
18763
+ }
18764
+ async revokeCallUploadCapability(token) {
18765
+ const id = this.tokenId(token, "call");
18766
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
18767
+ const raw = await this.state.get(`${KEY_STORE_CALL_CAPABILITY}${id}`);
18768
+ if (raw === null) throw new TBError("permission_denied", "call upload capability \u65E0\u6548");
18769
+ const capability = parseCallCapability(raw);
18770
+ await this.assertTokenHash(token, capability.tokenHash, "call upload capability \u65E0\u6548");
18771
+ if (capability.status === "revoked") return;
18772
+ const next = {
18773
+ ...capability,
18774
+ status: "revoked",
18775
+ terminalAt: this.nowIso(),
18776
+ revision: capability.revision + 1
18777
+ };
18778
+ if (await this.cas(`${KEY_STORE_CALL_CAPABILITY}${capability.id}`, capability.revision, next)) {
18779
+ return;
18780
+ }
18781
+ }
18782
+ throw new TBError("conflict", "call capability \u5E76\u53D1\u66F4\u65B0\u51B2\u7A81");
18783
+ }
18784
+ async beginUpload(input, identity) {
18785
+ const owner = requireOwner(identity.owner);
18786
+ const producer = requireOwner(identity.producer ?? identity.owner, "producer");
18787
+ return this.beginNormalized(normalizeUploadInput(input), { owner, producer });
18788
+ }
18789
+ async beginCallUpload(input, capabilityToken) {
18790
+ const capability = await this.verifyCallUploadCapability(capabilityToken);
18791
+ return this.beginNormalized(normalizeUploadInput(input), {
18792
+ owner: capability.owner,
18793
+ producer: capability.producer,
18794
+ originCallId: capability.callId,
18795
+ callCapabilityToken: capabilityToken
18796
+ });
18797
+ }
18798
+ async verifyUploadToken(token) {
18799
+ const id = this.tokenId(token, "upload");
18800
+ const raw = await this.state.get(`${KEY_STORE_UPLOAD}${id}`);
18801
+ if (raw === null) throw new TBError("permission_denied", "upload capability \u65E0\u6548");
18802
+ const session = parseSession(raw);
18803
+ await this.assertTokenHash(token, session.capabilityHash, "upload capability \u65E0\u6548");
18804
+ if (Date.parse(session.expiresAt) <= Date.parse(this.nowIso())) {
18805
+ if (session.status !== "completed") await this.expireUpload(session);
18806
+ throw new TBError("permission_denied", "upload capability \u5DF2\u8FC7\u671F");
18807
+ }
18808
+ if (session.status === "aborted" || session.status === "expired" || session.status === "failed") {
18809
+ throw new TBError("permission_denied", "upload capability \u5DF2\u5931\u6548");
18810
+ }
18811
+ return session;
18812
+ }
18813
+ /** relay PUT 成功即在同一调用内把对象推进 ready;后续 complete 仅作幂等确认。 */
18814
+ async commitRelayUpload(input) {
18815
+ const session = await this.verifyUploadToken(input.uploadToken);
18816
+ if (session.status === "completed") return this.readyDescriptor(session.objectId);
18817
+ if (session.transport !== "relay") {
18818
+ throw new TBError("invalid_argument", "\u8BE5 upload session \u4E0D\u662F relay transport");
18819
+ }
18820
+ const object2 = await this.requireObject(session.objectId);
18821
+ if (object2.status === "ready") {
18822
+ await this.completeSessionBestEffort(session);
18823
+ return descriptorOf(object2);
18824
+ }
18825
+ if (object2.status !== "pending") {
18826
+ throw new TBError("conflict", `\u5BF9\u8C61\u72B6\u6001\u4E0D\u5141\u8BB8\u4E0A\u4F20:${object2.status}`);
18827
+ }
18828
+ let meta3;
18829
+ try {
18830
+ meta3 = await this.objects.put(object2.driverKey, this.boundedBody(input.body, session.maxBytes), {
18831
+ contentType: object2.contentType,
18832
+ ifNoneMatch: "*"
18833
+ });
18834
+ } catch (error51) {
18835
+ if (error51 instanceof TBError && error51.code === "conflict") {
18836
+ const existing = await this.objects.head(object2.driverKey);
18837
+ if (existing === null) throw error51;
18838
+ meta3 = existing;
18839
+ } else {
18840
+ if (error51 instanceof TBError && error51.code === "rate_limited") {
18841
+ await this.objects.delete(object2.driverKey);
18842
+ await this.failUpload(session, object2);
18843
+ }
18844
+ throw error51;
18845
+ }
18846
+ }
18847
+ try {
18848
+ this.assertObserved(session, meta3);
18849
+ } catch (error51) {
18850
+ await this.objects.delete(object2.driverKey);
18851
+ await this.failUpload(session, object2);
18852
+ throw error51;
18853
+ }
18854
+ return this.markReady(session, object2, meta3);
18855
+ }
18856
+ /** 普通管理面 complete:只接受稳定 owner,不接受 capability body 字段。 */
18857
+ async completeUpload(uploadId, actor) {
18858
+ const { session, object: object2 } = await this.authorizeOwnerUploadMutation(uploadId, actor);
18859
+ return this.completeAuthorizedUpload(session, object2);
18860
+ }
18861
+ /** capability-only complete:宿主从 Authorization header 取 token 后调用。 */
18862
+ async completeUploadWithToken(uploadId, uploadToken) {
18863
+ const session = await this.verifyUploadToken(uploadToken);
18864
+ if (session.id !== uploadId) {
18865
+ throw new TBError("permission_denied", "upload capability \u4E0E session \u4E0D\u5339\u914D");
18866
+ }
18867
+ return this.completeAuthorizedUpload(session, await this.requireObject(session.objectId));
18868
+ }
18869
+ async completeAuthorizedUpload(session, object2) {
18870
+ if (session.status === "completed" || object2.status === "ready") {
18871
+ if (object2.status !== "ready") return this.readyDescriptor(object2.id);
18872
+ await this.completeSessionBestEffort(session);
18873
+ return descriptorOf(object2);
18874
+ }
18875
+ if (Date.parse(session.expiresAt) <= Date.parse(this.nowIso())) {
18876
+ await this.expireUpload(session);
18877
+ throw new TBError("conflict", "upload session \u5DF2\u8FC7\u671F");
18878
+ }
18879
+ if (session.transport !== "presigned-put") {
18880
+ throw new TBError("conflict", "relay upload \u5C1A\u672A\u6210\u529F\u63D0\u4EA4\u5B57\u8282");
18881
+ }
18882
+ if (object2.status !== "pending") {
18883
+ throw new TBError("conflict", `\u5BF9\u8C61\u72B6\u6001\u4E0D\u5141\u8BB8 complete:${object2.status}`);
18884
+ }
18885
+ const meta3 = await this.objects.head(object2.driverKey);
18886
+ if (meta3 === null) throw new TBError("conflict", "\u76F4\u4F20\u5BF9\u8C61\u5C1A\u4E0D\u5B58\u5728");
18887
+ try {
18888
+ this.assertObserved(session, meta3);
18889
+ } catch (error51) {
18890
+ await this.objects.delete(object2.driverKey);
18891
+ await this.failUpload(session, object2);
18892
+ throw error51;
18893
+ }
18894
+ return this.markReady(session, object2, meta3);
18895
+ }
18896
+ /** 普通管理面 abort:owner 路径。 */
18897
+ async abortUpload(uploadId, actor) {
18898
+ const { object: object2, session } = await this.authorizeOwnerUploadMutation(uploadId, actor);
18899
+ return this.abortAuthorizedUpload(session, object2);
18900
+ }
18901
+ /** capability-only abort:token 只应来自 Authorization header。 */
18902
+ async abortUploadWithToken(uploadId, uploadToken) {
18903
+ const id = this.tokenId(uploadToken, "upload");
18904
+ const session = await this.requireSession(id);
18905
+ await this.assertTokenHash(uploadToken, session.capabilityHash, "upload capability \u65E0\u6548");
18906
+ if (session.id !== uploadId) {
18907
+ throw new TBError("permission_denied", "upload capability \u4E0E session \u4E0D\u5339\u914D");
18908
+ }
18909
+ return this.abortAuthorizedUpload(session, await this.requireObject(session.objectId));
18910
+ }
18911
+ async abortAuthorizedUpload(sessionInput, objectInput) {
18912
+ let object2 = objectInput;
18913
+ let session = sessionInput;
18914
+ if (object2.status === "ready" || session.status === "completed") {
18915
+ throw new TBError("conflict", "ready \u5BF9\u8C61\u4E0D\u80FD abort");
18916
+ }
18917
+ if (object2.status === "pending") {
18918
+ const next = {
18919
+ ...object2,
18920
+ status: "abandoned",
18921
+ updatedAt: this.nowIso(),
18922
+ revision: object2.revision + 1
18923
+ };
18924
+ if (await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, next)) object2 = next;
18925
+ else object2 = await this.requireObject(object2.id);
18926
+ }
18927
+ if (object2.status === "ready") throw new TBError("conflict", "ready \u5BF9\u8C61\u4E0D\u80FD abort");
18928
+ if (session.status === "created") {
18929
+ const terminalAt = this.nowIso();
18930
+ const next = {
18931
+ ...session,
18932
+ status: "aborted",
18933
+ terminalAt,
18934
+ revision: session.revision + 1
18935
+ };
18936
+ if (await this.cas(`${KEY_STORE_UPLOAD}${session.id}`, session.revision, next)) session = next;
18937
+ else session = await this.requireSession(session.id);
18938
+ }
18939
+ if (session.status === "completed") throw new TBError("conflict", "ready \u5BF9\u8C61\u4E0D\u80FD abort");
18940
+ await this.objects.delete(object2.driverKey);
18941
+ await this.markBytesDeleted(object2.id, object2.driverKey);
18942
+ return { ok: true };
18943
+ }
18944
+ async stat(uri, access) {
18945
+ const object2 = await this.authorizeReadyObject(uri, access);
18946
+ return descriptorOf(object2);
18947
+ }
18948
+ /** 宿主 read callback 在拿到此结果后签发 relay/presigned GET;core 不构造网络 URL。 */
18949
+ async authorizeRead(uri, access) {
18950
+ return this.authorizeReadyObject(uri, access);
18951
+ }
18952
+ /** share token 由宿主从 header 取出,不进入普通 builtin arguments。 */
18953
+ async authorizeSharedRead(uri, shareToken) {
18954
+ await this.verifyShareToken(shareToken, uri);
18955
+ const object2 = await this.requireObject(parseStoreUri(uri).objectId);
18956
+ if (object2.status !== "ready") throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
18957
+ return object2;
18958
+ }
18959
+ async list(owner, opts = {}) {
18960
+ const wantedOwner = requireOwner(owner);
18961
+ const limit = Math.min(Math.max(1, opts.limit ?? LIST_LIMIT_DEFAULT), LIST_LIMIT_MAX);
18962
+ const page = await this.state.list(KEY_STORE_OBJECT, {
18963
+ limit: Math.min(LIST_LIMIT_MAX, limit * 4),
18964
+ ...opts.cursor !== void 0 ? { cursor: opts.cursor } : {}
18965
+ });
18966
+ const items = [];
18967
+ for (let index = 0; index < page.items.length; index++) {
18968
+ const item = page.items[index];
18969
+ if (item === void 0) continue;
18970
+ const object2 = parseObject(item.value);
18971
+ if (object2.owner !== wantedOwner || object2.status !== "ready") continue;
18972
+ items.push(descriptorOf(object2));
18973
+ if (items.length >= limit) {
18974
+ const hasUnscanned = index < page.items.length - 1 || page.cursor !== void 0;
18975
+ return hasUnscanned ? { items, cursor: item.key } : { items };
18976
+ }
18977
+ }
18978
+ return page.cursor !== void 0 ? { items, cursor: page.cursor } : { items };
18979
+ }
18980
+ async delete(uri, actor) {
18981
+ const { objectId } = parseStoreUri(uri);
18982
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
18983
+ const object2 = await this.requireObject(objectId);
18984
+ if (!actor.admin && object2.owner !== actor.owner) {
18985
+ throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
18986
+ }
18987
+ if (object2.status === "deleted") return { ok: true };
18988
+ const next = {
18989
+ ...object2,
18990
+ status: "deleted",
18991
+ updatedAt: this.nowIso(),
18992
+ revision: object2.revision + 1
18993
+ };
18994
+ if (!await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, next)) continue;
18995
+ await this.objects.delete(object2.driverKey);
18996
+ await this.markBytesDeleted(object2.id, object2.driverKey);
18997
+ return { ok: true };
18998
+ }
18999
+ throw new TBError("conflict", "Store \u5BF9\u8C61\u5E76\u53D1\u5220\u9664\u51B2\u7A81");
19000
+ }
19001
+ async share(uri, actor, ttlSec = this.shareTtlSec) {
19002
+ const owner = requireOwner(actor);
19003
+ const object2 = await this.authorizeReadyObject(uri, { owner });
19004
+ const ttl = requireCapabilityTtl(ttlSec, "ttlSec");
19005
+ if (ttl > this.shareTtlSec) {
19006
+ throw new TBError("invalid_argument", `ttlSec \u4E0D\u80FD\u8D85\u8FC7 ${this.shareTtlSec}`);
19007
+ }
19008
+ const now = this.nowIso();
19009
+ const id = this.randomId();
19010
+ const token = await this.tokenFor("share", id);
19011
+ const grant = {
19012
+ id,
19013
+ objectId: object2.id,
19014
+ tokenHash: await sha256Hex(token),
19015
+ status: "active",
19016
+ createdBy: owner,
19017
+ createdAt: now,
19018
+ expiresAt: this.afterSeconds(now, ttl),
19019
+ revision: 1
19020
+ };
19021
+ if (!await this.cas(`${KEY_STORE_SHARE}${id}`, null, grant)) {
19022
+ throw new TBError("conflict", "share id \u51B2\u7A81");
19023
+ }
19024
+ return { shareId: id, token, uri: storeUri(object2.id), expiresAt: grant.expiresAt };
19025
+ }
19026
+ async verifyShareToken(token, expectedUri) {
19027
+ const id = this.tokenId(token, "share");
19028
+ const raw = await this.state.get(`${KEY_STORE_SHARE}${id}`);
19029
+ if (raw === null) throw new TBError("permission_denied", "share capability \u65E0\u6548");
19030
+ const grant = parseShare(raw);
19031
+ await this.assertTokenHash(token, grant.tokenHash, "share capability \u65E0\u6548");
19032
+ if (expectedUri !== void 0 && parseStoreUri(expectedUri).objectId !== grant.objectId) {
19033
+ throw new TBError("permission_denied", "share capability \u4E0E\u5BF9\u8C61\u4E0D\u5339\u914D");
19034
+ }
19035
+ if (Date.parse(grant.expiresAt) <= Date.parse(this.nowIso())) {
19036
+ await this.expireShare(grant);
19037
+ throw new TBError("permission_denied", "share capability \u5DF2\u8FC7\u671F");
19038
+ }
19039
+ if (grant.status !== "active") {
19040
+ throw new TBError("permission_denied", "share capability \u5DF2\u5931\u6548");
19041
+ }
19042
+ return grant;
19043
+ }
19044
+ async revokeShare(shareId, actor) {
19045
+ const owner = requireOwner(actor);
19046
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
19047
+ const raw = await this.state.get(`${KEY_STORE_SHARE}${shareId}`);
19048
+ if (raw === null) throw new TBError("not_found", "share grant \u4E0D\u5B58\u5728");
19049
+ const grant = parseShare(raw);
19050
+ const object2 = await this.requireObject(grant.objectId);
19051
+ if (object2.owner !== owner) throw new TBError("not_found", "share grant \u4E0D\u5B58\u5728");
19052
+ if (grant.status === "revoked") return { ok: true };
19053
+ const next = {
19054
+ ...grant,
19055
+ status: "revoked",
19056
+ terminalAt: this.nowIso(),
19057
+ revision: grant.revision + 1
19058
+ };
19059
+ if (await this.cas(`${KEY_STORE_SHARE}${grant.id}`, grant.revision, next)) {
19060
+ return { ok: true };
19061
+ }
19062
+ }
19063
+ throw new TBError("conflict", "share grant \u5E76\u53D1\u64A4\u9500\u51B2\u7A81");
19064
+ }
19065
+ /**
19066
+ * 宿主定时器/Cron 调用的幂等清理步。终态记录先保留一个 capability TTL 窗口,
19067
+ * 再以 CAS 物理删除;无 metadata 的 driver 对象可能属于 legacy Context,绝不猜测为 orphan。
19068
+ * 返回 cursor 时宿主必须续调,直到 cursor 缺省。
19069
+ */
19070
+ async cleanup(opts = {}) {
19071
+ const pageLimit = Math.min(Math.max(1, opts.limit ?? LIST_LIMIT_MAX), LIST_LIMIT_MAX);
19072
+ const result = {
19073
+ expiredUploads: 0,
19074
+ abandonedObjects: 0,
19075
+ deletedBytes: 0,
19076
+ deletedOrphans: 0,
19077
+ deletedStaging: 0,
19078
+ expiredCallCapabilities: 0,
19079
+ expiredIdempotencyBindings: 0,
19080
+ expiredShares: 0
19081
+ };
19082
+ const now = this.nowIso();
19083
+ const nowMs = Date.parse(now);
19084
+ const olderThan = new Date(nowMs - this.uploadTtlSec * 1e3).toISOString();
19085
+ if (opts.runDriverMaintenance !== false && this.objects.cleanupStaging !== void 0) {
19086
+ result.deletedStaging = await this.objects.cleanupStaging(
19087
+ `${DEFAULT_STORE_DRIVER_KEY_ROOT}/`,
19088
+ olderThan
19089
+ );
19090
+ }
19091
+ const uploads = opts.cursors?.uploads === null ? { items: [] } : await this.state.list(KEY_STORE_UPLOAD, {
19092
+ limit: pageLimit,
19093
+ ...opts.cursors?.uploads !== void 0 ? { cursor: opts.cursors.uploads } : {}
19094
+ });
19095
+ for (const item of uploads.items) {
19096
+ let session = parseSession(item.value);
19097
+ if (session.status === "created" && Date.parse(session.expiresAt) <= nowMs) {
19098
+ await this.expireUpload(session);
19099
+ const currentRaw = await this.state.get(item.key);
19100
+ if (currentRaw === null) continue;
19101
+ session = parseSession(currentRaw);
19102
+ if (session.status === "expired") result.expiredUploads++;
19103
+ }
19104
+ if (this.uploadSessionIsTerminal(session) && this.retentionElapsed(this.uploadSessionTerminalAt(session), this.uploadTtlSec, nowMs)) {
19105
+ await this.cas(item.key, session.revision, null);
19106
+ }
19107
+ }
19108
+ const shares = opts.cursors?.shares === null ? { items: [] } : await this.state.list(KEY_STORE_SHARE, {
19109
+ limit: pageLimit,
19110
+ ...opts.cursors?.shares !== void 0 ? { cursor: opts.cursors.shares } : {}
19111
+ });
19112
+ for (const item of shares.items) {
19113
+ let grant = parseShare(item.value);
19114
+ if (grant.status === "active" && Date.parse(grant.expiresAt) <= nowMs) {
19115
+ await this.expireShare(grant);
19116
+ const currentRaw = await this.state.get(item.key);
19117
+ if (currentRaw === null) continue;
19118
+ grant = parseShare(currentRaw);
19119
+ if (grant.status === "expired") result.expiredShares++;
19120
+ }
19121
+ if (grant.status !== "active" && this.retentionElapsed(grant.terminalAt ?? grant.expiresAt, this.shareTtlSec, nowMs)) {
19122
+ await this.cas(item.key, grant.revision, null);
19123
+ }
19124
+ }
19125
+ const capabilities = opts.cursors?.callCapabilities === null ? { items: [] } : await this.state.list(KEY_STORE_CALL_CAPABILITY, {
19126
+ limit: pageLimit,
19127
+ ...opts.cursors?.callCapabilities !== void 0 ? { cursor: opts.cursors.callCapabilities } : {}
19128
+ });
19129
+ for (const item of capabilities.items) {
19130
+ let capability = parseCallCapability(item.value);
19131
+ if ((capability.status === "active" || capability.status === "exhausted") && Date.parse(capability.expiresAt) <= nowMs) {
19132
+ await this.expireCallCapability(capability);
19133
+ const currentRaw = await this.state.get(item.key);
19134
+ if (currentRaw === null) continue;
19135
+ capability = parseCallCapability(currentRaw);
19136
+ if (capability.status === "expired") result.expiredCallCapabilities++;
19137
+ }
19138
+ if ((capability.status === "expired" || capability.status === "revoked") && this.retentionElapsed(
19139
+ capability.terminalAt ?? capability.expiresAt,
19140
+ this.uploadTtlSec,
19141
+ nowMs
19142
+ )) {
19143
+ await this.cas(item.key, capability.revision, null);
19144
+ }
19145
+ }
19146
+ const idempotencyBindings = opts.cursors?.idempotencyBindings === null ? { items: [] } : await this.state.list(KEY_STORE_IDEMPOTENCY, {
19147
+ limit: pageLimit,
19148
+ ...opts.cursors?.idempotencyBindings !== void 0 ? { cursor: opts.cursors.idempotencyBindings } : {}
19149
+ });
19150
+ for (const item of idempotencyBindings.items) {
19151
+ const binding = parseIdempotencyBinding(item.value);
19152
+ if (Date.parse(binding.expiresAt) > nowMs) continue;
19153
+ if (await this.cas(item.key, binding.revision, null)) {
19154
+ result.expiredIdempotencyBindings++;
19155
+ }
19156
+ }
19157
+ const objectRecords = opts.cursors?.objects === null ? { items: [] } : await this.state.list(KEY_STORE_OBJECT, {
19158
+ limit: pageLimit,
19159
+ ...opts.cursors?.objects !== void 0 ? { cursor: opts.cursors.objects } : {}
19160
+ });
19161
+ for (const item of objectRecords.items) {
19162
+ let object2 = parseObject(item.value);
19163
+ if (object2.status === "ready" && object2.expiresAt !== void 0 && Date.parse(object2.expiresAt) <= nowMs) {
19164
+ const deleted = {
19165
+ ...object2,
19166
+ status: "deleted",
19167
+ updatedAt: now,
19168
+ revision: object2.revision + 1
19169
+ };
19170
+ if (await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, deleted)) {
19171
+ object2 = deleted;
19172
+ } else {
19173
+ const currentRaw = await this.state.get(item.key);
19174
+ if (currentRaw === null) continue;
19175
+ object2 = parseObject(currentRaw);
19176
+ }
19177
+ } else if (object2.status === "pending" && Date.parse(object2.updatedAt) + this.uploadTtlSec * 1e3 <= nowMs) {
19178
+ const sessionRaw = await this.state.get(`${KEY_STORE_UPLOAD}${object2.uploadId}`);
19179
+ const session = sessionRaw === null ? void 0 : parseSession(sessionRaw);
19180
+ const uploadEnded = session === void 0 || session.status !== "created" || Date.parse(session.expiresAt) <= nowMs;
19181
+ if (uploadEnded) {
19182
+ const abandoned = {
19183
+ ...object2,
19184
+ status: "abandoned",
19185
+ updatedAt: now,
19186
+ revision: object2.revision + 1
19187
+ };
19188
+ if (await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, abandoned)) {
19189
+ object2 = abandoned;
19190
+ result.abandonedObjects++;
19191
+ } else {
19192
+ const currentRaw = await this.state.get(item.key);
19193
+ if (currentRaw === null) continue;
19194
+ object2 = parseObject(currentRaw);
19195
+ }
19196
+ }
19197
+ }
19198
+ if (this.objectIsTerminal(object2) && object2.bytesDeletedAt === void 0) {
19199
+ await this.objects.delete(object2.driverKey);
19200
+ result.deletedBytes++;
19201
+ await this.markBytesDeleted(object2.id, object2.driverKey);
19202
+ const currentRaw = await this.state.get(item.key);
19203
+ if (currentRaw === null) continue;
19204
+ object2 = parseObject(currentRaw);
19205
+ }
19206
+ if (this.objectIsTerminal(object2) && object2.bytesDeletedAt !== void 0 && this.retentionElapsed(object2.bytesDeletedAt, this.uploadTtlSec, nowMs)) {
19207
+ await this.cas(item.key, object2.revision, null);
19208
+ }
19209
+ }
19210
+ const cursors = {
19211
+ uploads: uploads.cursor ?? null,
19212
+ shares: shares.cursor ?? null,
19213
+ callCapabilities: capabilities.cursor ?? null,
19214
+ idempotencyBindings: idempotencyBindings.cursor ?? null,
19215
+ objects: objectRecords.cursor ?? null,
19216
+ driverObjects: null
19217
+ };
19218
+ return Object.values(cursors).some((cursor) => cursor !== null) ? { ...result, cursors } : result;
19219
+ }
19220
+ async beginNormalized(input, identity) {
19221
+ if (input.size !== void 0 && input.size > this.maxObjectBytes) {
19222
+ throw new TBError("rate_limited", `\u5BF9\u8C61\u8D85\u8FC7\u90E8\u7F72\u4E0A\u9650 ${this.maxObjectBytes} bytes`);
19223
+ }
19224
+ const resolved = await this.resolveBinding(identity, input);
19225
+ const binding = resolved.binding;
19226
+ const existingRaw = await this.state.get(`${KEY_STORE_UPLOAD}${binding.uploadId}`);
19227
+ if (existingRaw !== null) {
19228
+ const existing = parseSession(existingRaw);
19229
+ if (existing.objectId !== binding.objectId) throw stateCorrupt("idempotent upload");
19230
+ this.assertBindingObject(binding, await this.requireObject(binding.objectId));
19231
+ return this.startFor(existing);
19232
+ }
19233
+ const now = this.nowIso();
19234
+ const driverKey = this.driverKey(binding.objectId);
19235
+ let transport = "relay";
19236
+ let effectiveMaxBytes = this.relayMaxBytes;
19237
+ let signedRequest;
19238
+ if (input.size !== void 0 && this.objects.presignPutExact !== void 0) {
19239
+ try {
19240
+ signedRequest = await this.objects.presignPutExact(driverKey, this.uploadTtlSec, {
19241
+ contentType: input.contentType,
19242
+ contentLength: input.size,
19243
+ ifNoneMatch: "*"
19244
+ });
19245
+ transport = "presigned-put";
19246
+ effectiveMaxBytes = this.maxObjectBytes;
19247
+ } catch {
19248
+ }
19249
+ }
19250
+ let maxBytes = effectiveMaxBytes;
19251
+ if (identity.callCapabilityToken !== void 0) {
19252
+ try {
19253
+ maxBytes = await this.reserveCallCapability(
19254
+ identity.callCapabilityToken,
19255
+ binding.objectId,
19256
+ input,
19257
+ effectiveMaxBytes
19258
+ );
19259
+ } catch (error51) {
19260
+ await this.releaseFreshBinding(resolved);
19261
+ throw error51;
19262
+ }
19263
+ }
19264
+ if (input.size !== void 0 && input.size > maxBytes) {
19265
+ await this.releaseFreshBinding(resolved);
19266
+ throw new TBError("rate_limited", `\u5BF9\u8C61\u8D85\u8FC7\u672C\u6B21\u4E0A\u4F20\u4E0A\u9650 ${maxBytes} bytes`);
19267
+ }
19268
+ const object2 = {
19269
+ id: binding.objectId,
19270
+ store: DEFAULT_STORE_NAME,
19271
+ driverKey,
19272
+ uploadId: binding.uploadId,
19273
+ status: "pending",
19274
+ owner: binding.owner,
19275
+ producer: binding.producer,
19276
+ contentType: input.contentType,
19277
+ createdAt: now,
19278
+ updatedAt: now,
19279
+ revision: 1,
19280
+ ...input.filename !== void 0 ? { filename: input.filename } : {},
19281
+ ...input.size !== void 0 ? { expectedSize: input.size } : {},
19282
+ ...input.checksum !== void 0 ? { expectedChecksum: input.checksum } : {},
19283
+ ...binding.originCallId !== void 0 ? { originCallId: binding.originCallId } : {}
19284
+ };
19285
+ const uploadToken = await this.tokenFor("upload", binding.uploadId);
19286
+ const session = {
19287
+ id: binding.uploadId,
19288
+ objectId: object2.id,
19289
+ status: "created",
19290
+ capabilityHash: await sha256Hex(uploadToken),
19291
+ transport,
19292
+ contentType: input.contentType,
19293
+ maxBytes,
19294
+ expiresAt: this.afterSeconds(now, this.uploadTtlSec),
19295
+ attempts: 0,
19296
+ createdAt: now,
19297
+ revision: 1,
19298
+ ...input.size !== void 0 ? { expectedSize: input.size } : {},
19299
+ ...input.checksum !== void 0 ? { expectedChecksum: input.checksum } : {},
19300
+ ...input.idempotencyKey !== void 0 ? { idempotencyKeyHash: await sha256Hex(input.idempotencyKey) } : {}
19301
+ };
19302
+ const objectCreated = await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, null, object2);
19303
+ if (!objectCreated) {
19304
+ const current = await this.requireObject(object2.id);
19305
+ if (current.owner !== object2.owner || current.producer !== object2.producer || current.originCallId !== object2.originCallId || current.uploadId !== object2.uploadId || current.contentType !== object2.contentType) {
19306
+ throw new TBError("conflict", "Store object id \u51B2\u7A81");
19307
+ }
19308
+ }
19309
+ if (!await this.cas(`${KEY_STORE_UPLOAD}${session.id}`, null, session)) {
19310
+ const current = await this.requireSession(session.id);
19311
+ if (current.objectId !== object2.id) throw new TBError("conflict", "upload id \u51B2\u7A81");
19312
+ this.assertBindingObject(binding, await this.requireObject(current.objectId));
19313
+ return this.startFor(current);
19314
+ }
19315
+ return {
19316
+ uploadId: session.id,
19317
+ objectUri: storeUri(object2.id),
19318
+ uploadToken,
19319
+ transport,
19320
+ expiresAt: session.expiresAt,
19321
+ maxBytes,
19322
+ alreadyCompleted: false,
19323
+ ...signedRequest !== void 0 ? { signedRequest } : {}
19324
+ };
19325
+ }
19326
+ async resolveBinding(identity, input) {
19327
+ const now = this.nowIso();
19328
+ const domain2 = identity.originCallId === void 0 ? "owner" : "call";
19329
+ const fresh = () => ({
19330
+ owner: identity.owner,
19331
+ producer: identity.producer,
19332
+ domain: domain2,
19333
+ objectId: this.randomId(),
19334
+ uploadId: this.randomId(),
19335
+ fingerprint: "",
19336
+ createdAt: now,
19337
+ expiresAt: this.afterSeconds(now, this.uploadTtlSec),
19338
+ revision: 1,
19339
+ ...identity.originCallId !== void 0 ? { originCallId: identity.originCallId } : {}
19340
+ });
19341
+ if (input.idempotencyKey === void 0) return { binding: fresh(), created: false };
19342
+ const fingerprint = await sha256Hex(JSON.stringify([
19343
+ input.contentType,
19344
+ input.filename ?? null,
19345
+ input.size ?? null,
19346
+ input.checksum ?? null
19347
+ ]));
19348
+ const ownerHash = await sha256Hex(identity.owner);
19349
+ const scopeHash = await sha256Hex(JSON.stringify([
19350
+ domain2,
19351
+ ...domain2 === "call" ? [identity.producer, identity.originCallId] : []
19352
+ ]));
19353
+ const keyHash = await sha256Hex(input.idempotencyKey);
19354
+ const key = `${KEY_STORE_IDEMPOTENCY}${ownerHash}:${domain2}:${scopeHash}:${keyHash}`;
19355
+ const candidate = { ...fresh(), fingerprint };
19356
+ if (await this.cas(key, null, candidate)) return { binding: candidate, created: true, key };
19357
+ const raw = await this.state.get(key);
19358
+ if (raw === null) throw new TBError("conflict", "idempotency binding \u5E76\u53D1\u521B\u5EFA\u51B2\u7A81");
19359
+ const existing = parseIdempotencyBinding(raw);
19360
+ if (!this.bindingMatchesIdentity(existing, identity) || existing.fingerprint !== fingerprint) {
19361
+ throw new TBError("conflict", "idempotencyKey \u5DF2\u7ED1\u5B9A\u5230\u4E0D\u540C\u4E0A\u4F20\u58F0\u660E");
19362
+ }
19363
+ if (Date.parse(existing.expiresAt) <= Date.parse(now)) {
19364
+ throw new TBError("conflict", "idempotencyKey \u5BF9\u5E94\u7684 upload session \u5DF2\u8FC7\u671F");
19365
+ }
19366
+ return { binding: existing, created: false, key };
19367
+ }
19368
+ async releaseFreshBinding(resolved) {
19369
+ if (!resolved.created || resolved.key === void 0) return;
19370
+ if (await this.state.get(`${KEY_STORE_UPLOAD}${resolved.binding.uploadId}`) !== null) return;
19371
+ await this.cas(resolved.key, resolved.binding.revision, null);
19372
+ }
19373
+ async reserveCallCapability(token, objectId, input, transportMaxBytes) {
19374
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
19375
+ const capability = await this.verifyCallUploadCapability(token);
19376
+ const previous = capability.reservations.find((r) => r.objectId === objectId);
19377
+ if (previous !== void 0) return previous.maxBytes;
19378
+ if (capability.status !== "active") {
19379
+ throw new TBError("rate_limited", "call upload capability \u914D\u989D\u5DF2\u8017\u5C3D");
19380
+ }
19381
+ if (!contentTypeAllowed(input.contentType, capability.allowedContentTypes)) {
19382
+ throw new TBError("permission_denied", `call \u4E0D\u5141\u8BB8\u4E0A\u4F20 ${input.contentType}`);
19383
+ }
19384
+ if (capability.reservations.length >= capability.maxObjects) {
19385
+ throw new TBError("rate_limited", "call upload object \u6570\u91CF\u5DF2\u8FBE\u4E0A\u9650");
19386
+ }
19387
+ const remaining = capability.maxBytes - capability.reservedBytes;
19388
+ const maxBytes = Math.min(
19389
+ capability.maxObjectBytes,
19390
+ input.size ?? capability.maxObjectBytes,
19391
+ remaining,
19392
+ transportMaxBytes
19393
+ );
19394
+ if (maxBytes < 1 || input.size !== void 0 && input.size > maxBytes) {
19395
+ throw new TBError("rate_limited", "call upload bytes \u914D\u989D\u4E0D\u8DB3");
19396
+ }
19397
+ const reservations = [...capability.reservations, { objectId, maxBytes }];
19398
+ const reservedBytes = capability.reservedBytes + maxBytes;
19399
+ const exhausted = reservations.length >= capability.maxObjects || reservedBytes >= capability.maxBytes;
19400
+ const next = {
19401
+ ...capability,
19402
+ reservations,
19403
+ reservedBytes,
19404
+ status: exhausted ? "exhausted" : "active",
19405
+ revision: capability.revision + 1
19406
+ };
19407
+ if (await this.cas(
19408
+ `${KEY_STORE_CALL_CAPABILITY}${capability.id}`,
19409
+ capability.revision,
19410
+ next
19411
+ )) return maxBytes;
19412
+ }
19413
+ throw new TBError("conflict", "call upload capability \u5E76\u53D1\u6D88\u8D39\u51B2\u7A81");
19414
+ }
19415
+ async startFor(session) {
19416
+ const object2 = await this.requireObject(session.objectId);
19417
+ const uploadToken = await this.tokenFor("upload", session.id);
19418
+ if (session.status === "completed" && object2.status === "ready") {
19419
+ return {
19420
+ uploadId: session.id,
19421
+ objectUri: storeUri(object2.id),
19422
+ uploadToken,
19423
+ transport: session.transport,
19424
+ expiresAt: session.expiresAt,
19425
+ maxBytes: session.maxBytes,
19426
+ alreadyCompleted: true,
19427
+ descriptor: descriptorOf(object2)
19428
+ };
19429
+ }
19430
+ if (session.status !== "created" || object2.status !== "pending") {
19431
+ throw new TBError("conflict", `upload session \u4E0D\u53EF\u6062\u590D:${session.status}/${object2.status}`);
19432
+ }
19433
+ const remainingMs = Date.parse(session.expiresAt) - Date.parse(this.nowIso());
19434
+ if (remainingMs <= 0) {
19435
+ await this.expireUpload(session);
19436
+ throw new TBError("conflict", "upload session \u5DF2\u8FC7\u671F");
19437
+ }
19438
+ let signedRequest;
19439
+ if (session.transport === "presigned-put" && session.expectedSize !== void 0 && this.objects.presignPutExact !== void 0) {
19440
+ signedRequest = await this.objects.presignPutExact(
19441
+ object2.driverKey,
19442
+ Math.max(1, Math.floor(remainingMs / 1e3)),
19443
+ {
19444
+ contentType: object2.contentType,
19445
+ contentLength: session.expectedSize,
19446
+ ifNoneMatch: "*"
19447
+ }
19448
+ );
19449
+ }
19450
+ return {
19451
+ uploadId: session.id,
19452
+ objectUri: storeUri(object2.id),
19453
+ uploadToken,
19454
+ transport: session.transport,
19455
+ expiresAt: session.expiresAt,
19456
+ maxBytes: session.maxBytes,
19457
+ alreadyCompleted: false,
19458
+ ...signedRequest !== void 0 ? { signedRequest } : {}
19459
+ };
19460
+ }
19461
+ bindingMatchesIdentity(binding, identity) {
19462
+ if (binding.owner !== identity.owner) return false;
19463
+ if (binding.domain === "owner") {
19464
+ return identity.originCallId === void 0 && binding.originCallId === void 0 && binding.producer === identity.producer;
19465
+ }
19466
+ return identity.originCallId !== void 0 && binding.producer === identity.producer && binding.originCallId === identity.originCallId;
19467
+ }
19468
+ assertBindingObject(binding, object2) {
19469
+ if (object2.id !== binding.objectId || object2.uploadId !== binding.uploadId || object2.owner !== binding.owner || object2.producer !== binding.producer || object2.originCallId !== binding.originCallId) throw stateCorrupt("idempotent upload identity");
19470
+ }
19471
+ uploadSessionIsTerminal(session) {
19472
+ return session.status === "completed" || session.status === "aborted" || session.status === "expired" || session.status === "failed";
19473
+ }
19474
+ uploadSessionTerminalAt(session) {
19475
+ return session.terminalAt ?? session.completedAt ?? session.expiresAt;
19476
+ }
19477
+ objectIsTerminal(object2) {
19478
+ return object2.status === "failed" || object2.status === "abandoned" || object2.status === "deleted";
19479
+ }
19480
+ retentionElapsed(timestamp, ttlSec, nowMs) {
19481
+ return Date.parse(timestamp) + ttlSec * 1e3 <= nowMs;
19482
+ }
19483
+ /** driver DELETE 成功后持久化证据;后续 cleanup 只收 tombstone,不再重复外部 DELETE。 */
19484
+ async markBytesDeleted(objectId, expectedDriverKey) {
19485
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {
19486
+ const raw = await this.state.get(`${KEY_STORE_OBJECT}${objectId}`);
19487
+ if (raw === null) return;
19488
+ const object2 = parseObject(raw);
19489
+ if (object2.driverKey !== expectedDriverKey) throw stateCorrupt("object driver key");
19490
+ if (!this.objectIsTerminal(object2) || object2.bytesDeletedAt !== void 0) return;
19491
+ const next = {
19492
+ ...object2,
19493
+ bytesDeletedAt: this.nowIso(),
19494
+ revision: object2.revision + 1
19495
+ };
19496
+ if (await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, next)) return;
19497
+ }
19498
+ throw new TBError("conflict", "Store \u5BF9\u8C61\u5B57\u8282\u5220\u9664\u72B6\u6001\u5E76\u53D1\u66F4\u65B0\u51B2\u7A81");
19499
+ }
19500
+ async authorizeOwnerUploadMutation(uploadId, actorInput) {
19501
+ const session = await this.requireSession(uploadId);
19502
+ const object2 = await this.requireObject(session.objectId);
19503
+ const actor = requireOwner(actorInput, "actor");
19504
+ if (actor !== object2.owner) {
19505
+ throw new TBError("not_found", "upload session \u4E0D\u5B58\u5728");
19506
+ }
19507
+ return { session, object: object2 };
19508
+ }
19509
+ async authorizeReadyObject(uri, access) {
19510
+ const { objectId } = parseStoreUri(uri);
19511
+ const object2 = await this.requireObject(objectId);
19512
+ if (object2.status !== "ready") throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
19513
+ if (access.admin === true || access.owner !== void 0 && access.owner === object2.owner) {
19514
+ return object2;
19515
+ }
19516
+ throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
19517
+ }
19518
+ assertObserved(session, meta3) {
19519
+ if (!Number.isSafeInteger(meta3.size) || meta3.size < 0 || meta3.size > session.maxBytes) {
19520
+ throw new TBError("rate_limited", `\u4E0A\u4F20\u5BF9\u8C61\u8D85\u8FC7 ${session.maxBytes} bytes`);
19521
+ }
19522
+ if (session.expectedSize !== void 0 && meta3.size !== session.expectedSize) {
19523
+ throw new TBError("conflict", "\u4E0A\u4F20\u5BF9\u8C61 size \u4E0E create_upload \u58F0\u660E\u4E0D\u4E00\u81F4");
19524
+ }
19525
+ if (session.expectedChecksum !== void 0) {
19526
+ const actual = meta3.metadata?.["checksum-sha256"]?.toLowerCase();
19527
+ if (actual !== void 0 && actual !== session.expectedChecksum.value) {
19528
+ throw new TBError("conflict", "\u4E0A\u4F20\u5BF9\u8C61 checksum \u4E0D\u4E00\u81F4");
19529
+ }
19530
+ }
19531
+ }
19532
+ /**
19533
+ * relay 的权威流中限额。已知内存 body 先快速拒绝;未知流逐 chunk 累计,
19534
+ * 越界立刻 cancel。driver 必须在源流抛错时删除自己的临时文件。
19535
+ */
19536
+ boundedBody(body, maxBytes) {
19537
+ let knownBytes;
19538
+ if (typeof body === "string") knownBytes = new TextEncoder().encode(body).byteLength;
19539
+ else if (body instanceof Uint8Array) knownBytes = body.byteLength;
19540
+ else if (body instanceof ArrayBuffer) knownBytes = body.byteLength;
19541
+ if (knownBytes !== void 0) {
19542
+ if (knownBytes > maxBytes) {
19543
+ throw new TBError("rate_limited", `\u4E0A\u4F20\u5BF9\u8C61\u8D85\u8FC7 ${maxBytes} bytes`);
19544
+ }
19545
+ return body;
19546
+ }
19547
+ const source = body;
19548
+ const bounded = {
19549
+ async cancel(reason) {
19550
+ await source.cancel?.(reason);
19551
+ },
19552
+ getReader() {
19553
+ const reader = source.getReader();
19554
+ let consumed = 0;
19555
+ return {
19556
+ async read() {
19557
+ const chunk = await reader.read();
19558
+ if (!chunk.done && chunk.value !== void 0) {
19559
+ consumed += chunk.value.byteLength;
19560
+ if (consumed > maxBytes) {
19561
+ try {
19562
+ if (reader.cancel !== void 0) await reader.cancel("Store upload size limit");
19563
+ else await source.cancel?.("Store upload size limit");
19564
+ } catch {
19565
+ }
19566
+ throw new TBError("rate_limited", `\u4E0A\u4F20\u5BF9\u8C61\u8D85\u8FC7 ${maxBytes} bytes`);
19567
+ }
19568
+ }
19569
+ return chunk;
19570
+ },
19571
+ releaseLock() {
19572
+ reader.releaseLock();
19573
+ },
19574
+ async cancel(reason) {
19575
+ if (reader.cancel !== void 0) await reader.cancel(reason);
19576
+ else await source.cancel?.(reason);
19577
+ }
19578
+ };
19579
+ }
19580
+ };
19581
+ return bounded;
19582
+ }
19583
+ async markReady(sessionInput, objectInput, meta3) {
19584
+ let object2 = await this.requireObject(objectInput.id);
19585
+ if (object2.status === "ready") {
19586
+ const currentSession = await this.requireSession(sessionInput.id);
19587
+ await this.completeSessionBestEffort(currentSession);
19588
+ return descriptorOf(object2);
19589
+ }
19590
+ if (object2.status !== "pending") {
19591
+ await this.objects.delete(object2.driverKey);
19592
+ await this.markBytesDeleted(object2.id, object2.driverKey);
19593
+ throw new TBError("conflict", `\u5BF9\u8C61\u72B6\u6001\u4E0D\u5141\u8BB8 ready:${object2.status}`);
19594
+ }
19595
+ const now = this.nowIso();
19596
+ const actualChecksum = sessionInput.expectedChecksum !== void 0 && meta3.metadata?.["checksum-sha256"]?.toLowerCase() === sessionInput.expectedChecksum.value ? sessionInput.expectedChecksum : void 0;
19597
+ const ready = {
19598
+ ...object2,
19599
+ status: "ready",
19600
+ size: meta3.size,
19601
+ etag: meta3.etag,
19602
+ readyAt: now,
19603
+ updatedAt: now,
19604
+ revision: object2.revision + 1,
19605
+ ...actualChecksum !== void 0 ? { checksum: actualChecksum } : {}
19606
+ };
19607
+ if (!await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, ready)) {
19608
+ object2 = await this.requireObject(object2.id);
19609
+ if (object2.status !== "ready") {
19610
+ await this.objects.delete(object2.driverKey);
19611
+ await this.markBytesDeleted(object2.id, object2.driverKey);
19612
+ throw new TBError("conflict", `\u5BF9\u8C61\u5E76\u53D1\u72B6\u6001\u51B2\u7A81:${object2.status}`);
19613
+ }
19614
+ } else {
19615
+ object2 = ready;
19616
+ }
19617
+ const session = await this.requireSession(sessionInput.id);
19618
+ await this.completeSessionBestEffort(session);
19619
+ return descriptorOf(object2);
19620
+ }
19621
+ async completeSessionBestEffort(session) {
19622
+ if (session.status === "completed") return;
19623
+ if (session.status !== "created") return;
19624
+ const completedAt = this.nowIso();
19625
+ const next = {
19626
+ ...session,
19627
+ status: "completed",
19628
+ completedAt,
19629
+ terminalAt: completedAt,
19630
+ revision: session.revision + 1
19631
+ };
19632
+ await this.cas(`${KEY_STORE_UPLOAD}${session.id}`, session.revision, next);
19633
+ }
19634
+ async failUpload(session, object2) {
19635
+ const terminalAt = this.nowIso();
19636
+ if (object2.status === "pending") {
19637
+ const failed = {
19638
+ ...object2,
19639
+ status: "failed",
19640
+ bytesDeletedAt: terminalAt,
19641
+ updatedAt: terminalAt,
19642
+ revision: object2.revision + 1
19643
+ };
19644
+ await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, failed);
19645
+ }
19646
+ if (session.status === "created") {
19647
+ const failed = {
19648
+ ...session,
19649
+ status: "failed",
19650
+ terminalAt,
19651
+ revision: session.revision + 1
19652
+ };
19653
+ await this.cas(`${KEY_STORE_UPLOAD}${session.id}`, session.revision, failed);
19654
+ }
19655
+ await this.markBytesDeleted(object2.id, object2.driverKey);
19656
+ }
19657
+ async expireUpload(session) {
19658
+ if (session.status !== "created") return;
19659
+ const next = {
19660
+ ...session,
19661
+ status: "expired",
19662
+ terminalAt: this.nowIso(),
19663
+ revision: session.revision + 1
19664
+ };
19665
+ if (!await this.cas(`${KEY_STORE_UPLOAD}${session.id}`, session.revision, next)) return;
19666
+ const object2 = await this.requireObject(session.objectId);
19667
+ if (object2.status === "pending") {
19668
+ const abandoned = {
19669
+ ...object2,
19670
+ status: "abandoned",
19671
+ updatedAt: this.nowIso(),
19672
+ revision: object2.revision + 1
19673
+ };
19674
+ await this.cas(`${KEY_STORE_OBJECT}${object2.id}`, object2.revision, abandoned);
19675
+ }
19676
+ }
19677
+ async expireCallCapability(capability) {
19678
+ if (capability.status === "expired" || capability.status === "revoked") return;
19679
+ const next = {
19680
+ ...capability,
19681
+ status: "expired",
19682
+ terminalAt: this.nowIso(),
19683
+ revision: capability.revision + 1
19684
+ };
19685
+ await this.cas(`${KEY_STORE_CALL_CAPABILITY}${capability.id}`, capability.revision, next);
19686
+ }
19687
+ async expireShare(grant) {
19688
+ if (grant.status !== "active") return;
19689
+ const next = {
19690
+ ...grant,
19691
+ status: "expired",
19692
+ terminalAt: this.nowIso(),
19693
+ revision: grant.revision + 1
19694
+ };
19695
+ await this.cas(`${KEY_STORE_SHARE}${grant.id}`, grant.revision, next);
19696
+ }
19697
+ async readyDescriptor(objectId) {
19698
+ return descriptorOf(await this.requireObject(objectId));
19699
+ }
19700
+ async requireObject(id) {
19701
+ const raw = await this.state.get(`${KEY_STORE_OBJECT}${id}`);
19702
+ if (raw === null) throw new TBError("not_found", "Store \u5BF9\u8C61\u4E0D\u5B58\u5728");
19703
+ return parseObject(raw);
19704
+ }
19705
+ async requireSession(id) {
19706
+ const raw = await this.state.get(`${KEY_STORE_UPLOAD}${id}`);
19707
+ if (raw === null) throw new TBError("not_found", "upload session \u4E0D\u5B58\u5728");
19708
+ return parseSession(raw);
19709
+ }
19710
+ async assertTokenHash(token, expected, message) {
19711
+ if (!constantTimeEqual(await sha256Hex(token), expected)) {
19712
+ throw new TBError("permission_denied", message);
19713
+ }
19714
+ }
19715
+ tokenId(token, domain2) {
19716
+ if (typeof token !== "string") throw new TBError("permission_denied", "capability token \u65E0\u6548");
19717
+ const prefix = TOKEN_PREFIX[domain2];
19718
+ const match = new RegExp(`^${prefix}_([A-Za-z0-9_-]{22,64})\\.([A-Za-z0-9_-]{43})$`).exec(token);
19719
+ if (match?.[1] === void 0) {
19720
+ throw new TBError("permission_denied", "capability token \u65E0\u6548");
19721
+ }
19722
+ return match[1];
19723
+ }
19724
+ async tokenFor(domain2, id) {
19725
+ const encoder = new TextEncoder();
19726
+ const key = await crypto.subtle.importKey(
19727
+ "raw",
19728
+ encoder.encode(this.tokenSecret),
19729
+ { name: "HMAC", hash: "SHA-256" },
19730
+ false,
19731
+ ["sign"]
19732
+ );
19733
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(`store:${domain2}:${id}`));
19734
+ return `${TOKEN_PREFIX[domain2]}_${id}.${base64urlEncode(new Uint8Array(signature))}`;
19735
+ }
19736
+ randomId() {
19737
+ return base64urlEncode(crypto.getRandomValues(new Uint8Array(18)));
19738
+ }
19739
+ driverKey(objectId) {
19740
+ return `${DEFAULT_STORE_DRIVER_KEY_ROOT}/${objectId.slice(0, 2)}/${objectId}`;
19741
+ }
19742
+ nowIso() {
19743
+ return normalizeTimestamp(this.now(), "now");
19744
+ }
19745
+ afterSeconds(now, seconds) {
19746
+ return new Date(Date.parse(now) + seconds * 1e3).toISOString();
19747
+ }
19748
+ };
19749
+
18109
19750
  // ../core/src/operation/registry.ts
18110
19751
  function normalizeSchema(input) {
18111
19752
  return input instanceof external_exports.ZodType ? input : external_exports.object(input);
@@ -20293,16 +21934,23 @@ export {
20293
21934
  normalizeUpstreamError,
20294
21935
  assertSecureUrl,
20295
21936
  pluginTokenSecretName,
21937
+ createStoreModule,
20296
21938
  RemoteAllowlistStore,
20297
21939
  parseNodeInput,
20298
21940
  createBuiltins,
20299
21941
  CONTEXT_CAPABILITIES,
21942
+ CONTEXT_DIRECT_UPLOAD_CAPABILITY,
20300
21943
  contextScopeForCmd,
21944
+ contextUploadCmd,
20301
21945
  contextHelpModel,
20302
21946
  objectBodyToBytes,
20303
21947
  bytesToObjectStream,
21948
+ DEFAULT_STORE_DRIVER_KEY_ROOT,
20304
21949
  normalizeEntryPath,
20305
21950
  PRESIGN_TTL_SEC_DEFAULT,
21951
+ PRESIGN_TTL_SEC_MAX,
21952
+ assertPresignTtlSec,
21953
+ createObjectContextUploadGrant,
20306
21954
  createObjectContextProvider,
20307
21955
  isContextExpired,
20308
21956
  decodeDeviceFrame,
@@ -20326,6 +21974,8 @@ export {
20326
21974
  DEFAULT_MAX_NODES,
20327
21975
  clampDepth,
20328
21976
  buildTree,
21977
+ storeUri,
21978
+ StoreService,
20329
21979
  toToolResult,
20330
21980
  OperationRegistry,
20331
21981
  normalizeCanonicalOrigin,