@skaile/workspaces 4.13.0 → 4.15.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 (148) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/app-sdk/index.js +1 -1
  3. package/dist/{asset-feeds-AAQOGRLM.js → asset-feeds-WJUCRFUC.js} +4 -4
  4. package/dist/{asset-feeds-AAQOGRLM.js.map → asset-feeds-WJUCRFUC.js.map} +1 -1
  5. package/dist/asset-manager/index.js +1 -1
  6. package/dist/bridge/drivers/claude-sdk.js +41 -10
  7. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  8. package/dist/bridge/drivers/codex.js +2 -2
  9. package/dist/bridge/drivers/continue.js +3 -3
  10. package/dist/bridge/drivers/echo.js +3 -3
  11. package/dist/bridge/drivers/gemini.js +5 -5
  12. package/dist/bridge/drivers/goose.js +3 -3
  13. package/dist/bridge/drivers/omp.js +2 -2
  14. package/dist/bridge/drivers/opencode.js +3 -3
  15. package/dist/bridge/drivers/qwen.js +5 -5
  16. package/dist/bridge/index.js +5 -5
  17. package/dist/bridge/src/drivers/claude-sdk.d.ts +7 -0
  18. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  19. package/dist/bridge/src/normalizer.d.ts.map +1 -1
  20. package/dist/bridge/src/types.d.ts +8 -0
  21. package/dist/bridge/src/types.d.ts.map +1 -1
  22. package/dist/{chunk-WIQDBNMF.js → chunk-2E3EGCQ3.js} +2 -2
  23. package/dist/{chunk-WIQDBNMF.js.map → chunk-2E3EGCQ3.js.map} +1 -1
  24. package/dist/{chunk-EUHB2BD2.js → chunk-3IAGNZH3.js} +3 -3
  25. package/dist/{chunk-EUHB2BD2.js.map → chunk-3IAGNZH3.js.map} +1 -1
  26. package/dist/{chunk-FKWGIDUG.js → chunk-6GHXBCE5.js} +6 -6
  27. package/dist/{chunk-FKWGIDUG.js.map → chunk-6GHXBCE5.js.map} +1 -1
  28. package/dist/{chunk-HXTBOZL6.js → chunk-DPKH7AK7.js} +2 -2
  29. package/dist/{chunk-HXTBOZL6.js.map → chunk-DPKH7AK7.js.map} +1 -1
  30. package/dist/{chunk-N6XJGVSK.js → chunk-E4Q5GKO2.js} +412 -34
  31. package/dist/chunk-E4Q5GKO2.js.map +1 -0
  32. package/dist/{chunk-ZUOZV7CS.js → chunk-EHKYZ7TL.js} +4 -4
  33. package/dist/{chunk-ZUOZV7CS.js.map → chunk-EHKYZ7TL.js.map} +1 -1
  34. package/dist/{chunk-NQ3OMJFK.js → chunk-FEEAYDKF.js} +28 -10
  35. package/dist/chunk-FEEAYDKF.js.map +1 -0
  36. package/dist/{chunk-BDFR5MHE.js → chunk-FVKSVVVP.js} +3 -3
  37. package/dist/{chunk-BDFR5MHE.js.map → chunk-FVKSVVVP.js.map} +1 -1
  38. package/dist/{chunk-YXUVOWEU.js → chunk-I6BO67AL.js} +3 -3
  39. package/dist/{chunk-YXUVOWEU.js.map → chunk-I6BO67AL.js.map} +1 -1
  40. package/dist/{chunk-KTSV5TDX.js → chunk-IOFPX6OO.js} +91 -16
  41. package/dist/chunk-IOFPX6OO.js.map +1 -0
  42. package/dist/{chunk-PHURDPQO.js → chunk-KFKOXYQC.js} +6 -6
  43. package/dist/{chunk-PHURDPQO.js.map → chunk-KFKOXYQC.js.map} +1 -1
  44. package/dist/{chunk-PQF3NDQH.js → chunk-LQMALDDB.js} +2 -2
  45. package/dist/{chunk-PQF3NDQH.js.map → chunk-LQMALDDB.js.map} +1 -1
  46. package/dist/{chunk-NC7YSNBP.js → chunk-LYENHKH5.js} +6 -6
  47. package/dist/{chunk-NC7YSNBP.js.map → chunk-LYENHKH5.js.map} +1 -1
  48. package/dist/{chunk-UOJW2DOZ.js → chunk-MK3OCWBG.js} +2 -2
  49. package/dist/chunk-MK3OCWBG.js.map +1 -0
  50. package/dist/{chunk-KUH4EKQQ.js → chunk-PB2Y4AMQ.js} +3 -3
  51. package/dist/{chunk-KUH4EKQQ.js.map → chunk-PB2Y4AMQ.js.map} +1 -1
  52. package/dist/{chunk-O64WU6WS.js → chunk-QQ3G5N7X.js} +4 -4
  53. package/dist/chunk-QQ3G5N7X.js.map +1 -0
  54. package/dist/{chunk-65VAYFIJ.js → chunk-RDOEZBVB.js} +3 -3
  55. package/dist/{chunk-65VAYFIJ.js.map → chunk-RDOEZBVB.js.map} +1 -1
  56. package/dist/{chunk-CPIAQTYM.js → chunk-TOXPRXAK.js} +3 -3
  57. package/dist/{chunk-CPIAQTYM.js.map → chunk-TOXPRXAK.js.map} +1 -1
  58. package/dist/{chunk-YZSMZK37.js → chunk-UF4AEO54.js} +3 -3
  59. package/dist/{chunk-YZSMZK37.js.map → chunk-UF4AEO54.js.map} +1 -1
  60. package/dist/{chunk-6Y3ICAPP.js → chunk-VJKTXQ6D.js} +3 -3
  61. package/dist/{chunk-6Y3ICAPP.js.map → chunk-VJKTXQ6D.js.map} +1 -1
  62. package/dist/{chunk-IQFSXFYG.js → chunk-W64ZXABT.js} +26 -19
  63. package/dist/chunk-W64ZXABT.js.map +1 -0
  64. package/dist/{chunk-HZBRHZPI.js → chunk-ZXPIKQAN.js} +3 -3
  65. package/dist/{chunk-HZBRHZPI.js.map → chunk-ZXPIKQAN.js.map} +1 -1
  66. package/dist/{chunk-XKPFQ3JT.js → chunk-ZXQMKXWR.js} +3 -3
  67. package/dist/{chunk-XKPFQ3JT.js.map → chunk-ZXQMKXWR.js.map} +1 -1
  68. package/dist/cli/index.js +36 -36
  69. package/dist/connectors/config.js +1 -1
  70. package/dist/connectors/index.js +8 -8
  71. package/dist/connectors/src/connector-manager.d.ts +6 -1
  72. package/dist/connectors/src/connector-manager.d.ts.map +1 -1
  73. package/dist/connectors/src/connector-types.d.ts +53 -0
  74. package/dist/connectors/src/connector-types.d.ts.map +1 -1
  75. package/dist/connectors/src/index.d.ts +1 -1
  76. package/dist/connectors/src/index.d.ts.map +1 -1
  77. package/dist/connectors-shared/index.js +2 -2
  78. package/dist/{ensure-sources-PSWARJPK.js → ensure-sources-6QOYW2PI.js} +4 -4
  79. package/dist/{ensure-sources-PSWARJPK.js.map → ensure-sources-6QOYW2PI.js.map} +1 -1
  80. package/dist/factory-assets/connectors/box.js +8 -8
  81. package/dist/factory-assets/connectors/deploy.js +8 -8
  82. package/dist/factory-assets/connectors/devserver.js +8 -8
  83. package/dist/factory-assets/connectors/flow/adapter.js +8 -8
  84. package/dist/factory-assets/connectors/flow/run-flow.js +9 -9
  85. package/dist/factory-assets/connectors/flow.js +8 -8
  86. package/dist/factory-assets/connectors/git.js +8 -8
  87. package/dist/factory-assets/connectors/gmail.js +8 -8
  88. package/dist/factory-assets/connectors/googledrive.js +8 -8
  89. package/dist/factory-assets/connectors/local.js +8 -8
  90. package/dist/factory-assets/connectors/mattermost.js +8 -8
  91. package/dist/factory-assets/connectors/memory.js +8 -8
  92. package/dist/factory-assets/connectors/minio.js +8 -8
  93. package/dist/factory-assets/connectors/postgres.js +8 -8
  94. package/dist/factory-assets/connectors/s3/CONNECTOR.md +19 -2
  95. package/dist/factory-assets/connectors/s3/driver.d.ts +108 -2
  96. package/dist/factory-assets/connectors/s3/driver.d.ts.map +1 -1
  97. package/dist/factory-assets/connectors/s3.js +8 -8
  98. package/dist/factory-assets/connectors/sharepoint.js +8 -8
  99. package/dist/factory-assets/connectors/sqlite.js +8 -8
  100. package/dist/factory-assets/connectors/static-server.js +8 -8
  101. package/dist/factory-assets/connectors/tunnel.js +8 -8
  102. package/dist/factory-assets/connectors/webdav.js +8 -8
  103. package/dist/factory-assets/connectors/xstate-store.js +8 -8
  104. package/dist/factory-assets/connectors/xstate.js +8 -8
  105. package/dist/{helpers-LHHGL2NL.js → helpers-334DC7KB.js} +4 -4
  106. package/dist/{helpers-LHHGL2NL.js.map → helpers-334DC7KB.js.map} +1 -1
  107. package/dist/{library-publish-4SBO437O.js → library-publish-B5TLDFPF.js} +6 -6
  108. package/dist/{library-publish-4SBO437O.js.map → library-publish-B5TLDFPF.js.map} +1 -1
  109. package/dist/{open-library-SF2F7NDS.js → open-library-2CMPISRN.js} +5 -5
  110. package/dist/{open-library-SF2F7NDS.js.map → open-library-2CMPISRN.js.map} +1 -1
  111. package/dist/{project-ZHPOPJBE.js → project-NGIFJGPQ.js} +5 -5
  112. package/dist/{project-ZHPOPJBE.js.map → project-NGIFJGPQ.js.map} +1 -1
  113. package/dist/runner/index.js +12 -12
  114. package/dist/runner/src/compaction/orchestrator.d.ts.map +1 -1
  115. package/dist/runner/src/resources.d.ts.map +1 -1
  116. package/dist/runner/src/s3-presign-client.d.ts +15 -0
  117. package/dist/runner/src/s3-presign-client.d.ts.map +1 -0
  118. package/dist/runner/src/serve.d.ts.map +1 -1
  119. package/dist/sdk/asset-manager.js +1 -1
  120. package/dist/sdk/bridge.js +5 -5
  121. package/dist/sdk/index.js +12 -12
  122. package/dist/sdk/runner.js +12 -12
  123. package/dist/sdk/session.js +2 -2
  124. package/dist/sdk/types.js +1 -1
  125. package/dist/session/index.js +2 -2
  126. package/dist/session/src/dispatcher.d.ts +7 -1
  127. package/dist/session/src/dispatcher.d.ts.map +1 -1
  128. package/dist/{setup-3NXXR7SH.js → setup-GVJL3V3D.js} +10 -10
  129. package/dist/{setup-3NXXR7SH.js.map → setup-GVJL3V3D.js.map} +1 -1
  130. package/dist/{source-C2OYNI3T.js → source-BPLSCZLB.js} +6 -6
  131. package/dist/{source-C2OYNI3T.js.map → source-BPLSCZLB.js.map} +1 -1
  132. package/dist/{source-manifest-publish-BKS763DB.js → source-manifest-publish-ZVR24GCL.js} +7 -7
  133. package/dist/{source-manifest-publish-BKS763DB.js.map → source-manifest-publish-ZVR24GCL.js.map} +1 -1
  134. package/dist/tui/index.js +12 -12
  135. package/dist/types/index.js +1 -1
  136. package/dist/types/src/events.d.ts +12 -0
  137. package/dist/types/src/events.d.ts.map +1 -1
  138. package/dist/types/src/version.d.ts +8 -1
  139. package/dist/types/src/version.d.ts.map +1 -1
  140. package/dist/workspace-plugin/adapters/mcp.js +2 -2
  141. package/dist/workspace-plugin/index.js +1 -1
  142. package/package.json +1 -1
  143. package/dist/chunk-IQFSXFYG.js.map +0 -1
  144. package/dist/chunk-KTSV5TDX.js.map +0 -1
  145. package/dist/chunk-N6XJGVSK.js.map +0 -1
  146. package/dist/chunk-NQ3OMJFK.js.map +0 -1
  147. package/dist/chunk-O64WU6WS.js.map +0 -1
  148. package/dist/chunk-UOJW2DOZ.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { declaredConnectErrorKind, ClassifiedConnectError, withConnectErrorKind } from './chunk-YZSMZK37.js';
2
- import { OAuthRequiredError, ConnectorFieldMissingError, LegacyAuthGrammarError, resolveAuthRef, readUnavailableMarker, ConnectorStartupError } from './chunk-UOJW2DOZ.js';
3
- import { EventNormalizer } from './chunk-IQFSXFYG.js';
4
- import { resolveModelTier } from './chunk-XKPFQ3JT.js';
5
- import { createDriver } from './chunk-PQF3NDQH.js';
1
+ import { declaredConnectErrorKind, ClassifiedConnectError, withConnectErrorKind } from './chunk-UF4AEO54.js';
2
+ import { OAuthRequiredError, ConnectorFieldMissingError, LegacyAuthGrammarError, resolveAuthRef, readUnavailableMarker, ConnectorStartupError } from './chunk-MK3OCWBG.js';
3
+ import { EventNormalizer } from './chunk-W64ZXABT.js';
4
+ import { resolveModelTier } from './chunk-ZXQMKXWR.js';
5
+ import { createDriver } from './chunk-LQMALDDB.js';
6
6
  import { pluginRegistry } from './chunk-6E6PKKAD.js';
7
7
  import { computeStimulus } from './chunk-IFGTRL2I.js';
8
8
  import { interpolateNodeInstruction, resolveNodeBindings, getConsumedOutputSchema, validateConsumedNodeOutput, appendAttempt, resolveTerminalFailureStatus, remainingActiveExecutionMs, deriveCheckEvidence, computeFlowStateFromSnapshots, availableRouterFrontier, evaluateRouterSelection, routerSelectionOutput, planRouterSkips, unsettledRouterBranchIds, countFailedAttempts, hasAutomaticRetryBudget, persistedRouterSelection } from './chunk-A2T4WIZL.js';
@@ -11,7 +11,7 @@ import { parseFlowDefinition } from './chunk-ALLCAFHQ.js';
11
11
  import { describeStimulus, buildOrchestratorPrompt, renderSubpromptPrompt, renderStimulusPrompt } from './chunk-RQLL7L72.js';
12
12
  import { deterministicNodeKindLabel, nodeInstruction, flowDefaults, isAgentNode, isGateNode, isCheckNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, isSubpromptNode, isRouterNode, isActiveRuntimeNode, nodeIsOptional, nodeLabel, nodeAssets } from './chunk-V4RGCKYC.js';
13
13
  import { classifierDecision, withoutReservedClassifierFields } from './chunk-DHMBE5J7.js';
14
- import { withActivityLabelDefs } from './chunk-O64WU6WS.js';
14
+ import { withActivityLabelDefs } from './chunk-QQ3G5N7X.js';
15
15
  import { processAlive } from './chunk-4GY63JU6.js';
16
16
  import { withFsDeadline, tryWithFsDeadline, FsDeadlineError, fsDeadlineMs } from './chunk-2FBPAR2V.js';
17
17
  import { portableSpawn } from './chunk-DPLIN2RA.js';
@@ -4876,13 +4876,44 @@ function createConnector12() {
4876
4876
  }
4877
4877
  var s3Pkg = "@aws-sdk/client-s3";
4878
4878
  var DOWNLOAD_DEADLINE_FACTOR = 20;
4879
+ var ERROR_BODY_WAIT_MS = 5e3;
4880
+ var LIST_PAGE_DEADLINE_FACTOR = 2;
4881
+ var PRESIGN_REFUSAL_KINDS = {
4882
+ "not-configured": "credential",
4883
+ "not-in-scope": "config",
4884
+ "invalid-request": "config"
4885
+ };
4886
+ var S3_PRESIGN_BATCH = 100;
4887
+ var MAX_PRESIGN_KEY_BYTES = 1024;
4888
+ var S3FetchStoppedError = class extends Error {
4889
+ constructor(message) {
4890
+ super(message);
4891
+ this.name = "S3FetchStoppedError";
4892
+ }
4893
+ };
4894
+ var S3PresignedRequestError = class extends Error {
4895
+ constructor(op, status, s3Code, expired, detail) {
4896
+ super(detail);
4897
+ this.op = op;
4898
+ this.status = status;
4899
+ this.s3Code = s3Code;
4900
+ this.expired = expired;
4901
+ this.name = "S3PresignedRequestError";
4902
+ }
4903
+ op;
4904
+ status;
4905
+ s3Code;
4906
+ expired;
4907
+ };
4879
4908
  var S3Connector = class extends AbstractConnector {
4880
4909
  name = "s3";
4881
4910
  /** Optional SDK module override — used in tests to avoid importing the real AWS SDK. */
4882
4911
  _sdkOverride;
4883
- constructor(sdkOverride) {
4912
+ _fetch;
4913
+ constructor(sdkOverride, deps = {}) {
4884
4914
  super();
4885
4915
  this._sdkOverride = sdkOverride;
4916
+ this._fetch = deps.fetch ?? ((url, init) => fetch(url, init));
4886
4917
  }
4887
4918
  filesystem = {
4888
4919
  sync: (handle, options) => {
@@ -4904,6 +4935,9 @@ var S3Connector = class extends AbstractConnector {
4904
4935
  const secrets = ctx.secrets;
4905
4936
  const started = Date.now();
4906
4937
  const opts = declaration.options;
4938
+ if (declaration.auth === "backend") {
4939
+ return this.connectPresigned(declaration, ctx, targetDir, started);
4940
+ }
4907
4941
  const bucket = String(opts?.bucket ?? "");
4908
4942
  if (!bucket) {
4909
4943
  this.log.error("connect failed: missing bucket option", void 0);
@@ -4922,7 +4956,7 @@ var S3Connector = class extends AbstractConnector {
4922
4956
  clientConfig.profile = profile;
4923
4957
  }
4924
4958
  const client = new S3Client(clientConfig);
4925
- const state = { client, bucket, prefix, region };
4959
+ const state = { kind: "sdk", client, bucket, prefix, region };
4926
4960
  await withFsDeadline(`mkdir ${targetDir}`, () => mkdir(targetDir, { recursive: true }));
4927
4961
  try {
4928
4962
  const result = await this.downloadAll(state, targetDir);
@@ -4937,10 +4971,47 @@ var S3Connector = class extends AbstractConnector {
4937
4971
  throw err;
4938
4972
  }
4939
4973
  }
4974
+ /**
4975
+ * `auth: backend`: the platform holds the keys and signs every request, so
4976
+ * nothing here builds an S3 client or loads the SDK. Bucket and endpoint are
4977
+ * the platform's business; the prefix is only needed to place files.
4978
+ */
4979
+ async connectPresigned(declaration, ctx, targetDir, started) {
4980
+ if (!ctx.s3Presigner) {
4981
+ const err = new ClassifiedConnectError(
4982
+ `S3Connector '${declaration.id}': auth: backend needs the platform presign channel (host.s3_presign), which this runner does not have`,
4983
+ "config"
4984
+ );
4985
+ this.log.error("connect failed: no presigner", err);
4986
+ throw err;
4987
+ }
4988
+ const prefix = presignScopePrefix(declaration);
4989
+ const state = {
4990
+ kind: "presign",
4991
+ presign: ctx.s3Presigner,
4992
+ mountId: declaration.id,
4993
+ prefix,
4994
+ ...ctx.abortSignal ? { abortSignal: ctx.abortSignal } : {}
4995
+ };
4996
+ this.log.info("mount start", { mode: "presign", prefix, targetDir });
4997
+ await withFsDeadline(`mkdir ${targetDir}`, () => mkdir(targetDir, { recursive: true }));
4998
+ try {
4999
+ const result = await this.downloadAll(state, targetDir);
5000
+ this.log.info("mount ok", {
5001
+ targetDir,
5002
+ filesWritten: result.filesWritten,
5003
+ durationMs: Date.now() - started
5004
+ });
5005
+ return this.makeHandle(declaration, state, targetDir);
5006
+ } catch (err) {
5007
+ this.log.error("connect failed", err, { mode: "presign", targetDir });
5008
+ throw err;
5009
+ }
5010
+ }
4940
5011
  async disconnect(handle) {
4941
5012
  this.log.info("unmount");
4942
- const { client } = handle.state;
4943
- client.destroy();
5013
+ const state = handle.state;
5014
+ if (state.kind === "sdk") state.client.destroy();
4944
5015
  }
4945
5016
  async _sync(handle, _options) {
4946
5017
  this.log.debug("sync start");
@@ -4977,7 +5048,22 @@ var S3Connector = class extends AbstractConnector {
4977
5048
  }
4978
5049
  }
4979
5050
  async downloadAll(state, targetDir) {
5051
+ if (state.kind === "presign") {
5052
+ const entries2 = await this.listPresigned(state);
5053
+ return this.writeEntries(
5054
+ entries2,
5055
+ targetDir,
5056
+ (deadlineAt) => this.presignedBodies(state, entries2, deadlineAt)
5057
+ );
5058
+ }
4980
5059
  const s3Mod = await this._loadSdk();
5060
+ const entries = await this.listSdk(s3Mod, state);
5061
+ const bodies = async function* (self) {
5062
+ for (const { key } of entries) yield await self.fetchObject(s3Mod, state, key);
5063
+ };
5064
+ return this.writeEntries(entries, targetDir, () => bodies(this));
5065
+ }
5066
+ async listSdk(s3Mod, state) {
4981
5067
  const { client, bucket, prefix } = state;
4982
5068
  const entries = [];
4983
5069
  let continuationToken;
@@ -4997,40 +5083,331 @@ var S3Connector = class extends AbstractConnector {
4997
5083
  }
4998
5084
  continuationToken = resp.IsTruncated ? resp.NextContinuationToken : void 0;
4999
5085
  } while (continuationToken);
5086
+ return entries;
5087
+ }
5088
+ /**
5089
+ * List the mount through presigned ListObjectsV2 URLs, page by page. Folder
5090
+ * markers and keys the platform would refuse to sign (see
5091
+ * {@link presignableKey}) are dropped here, so one odd key cannot fail a
5092
+ * whole `get` batch. Each page is bounded by {@link LIST_PAGE_DEADLINE_FACTOR}.
5093
+ */
5094
+ async listPresigned(state) {
5095
+ const entries = [];
5096
+ let continuationToken;
5097
+ do {
5098
+ const token = continuationToken;
5099
+ const xml = await this.withRepresign(
5100
+ () => this.presignOrThrow(state, {
5101
+ mountId: state.mountId,
5102
+ op: "list",
5103
+ ...token ? { continuationToken: token } : {}
5104
+ }),
5105
+ async (signed) => {
5106
+ if (signed.op !== "list") throw new Error("host.s3_presign answered list with get");
5107
+ const timeoutMs = fsDeadlineMs() * LIST_PAGE_DEADLINE_FACTOR;
5108
+ return this.readBounded(
5109
+ state,
5110
+ async (signal) => {
5111
+ const resp = await this._fetch(signed.url, { signal });
5112
+ if (!resp.ok) throw await presignedError("list", resp, signed.expiresAt, "listing");
5113
+ return resp.text();
5114
+ },
5115
+ timeoutMs,
5116
+ "listing"
5117
+ );
5118
+ }
5119
+ );
5120
+ const page = parseListObjectsV2(xml);
5121
+ if (page.prefix !== void 0 && page.prefix !== state.prefix) {
5122
+ this.log.info("listing prefix differs from the declaration; using the listing's", {
5123
+ declared: state.prefix,
5124
+ listed: page.prefix
5125
+ });
5126
+ state.prefix = normalizeScopePrefix(page.prefix);
5127
+ }
5128
+ for (const key of page.keys) {
5129
+ if (key.endsWith("/")) continue;
5130
+ if (!presignableKey(key, state.prefix)) {
5131
+ this.log.warn("object skipped: key cannot be presigned", { key });
5132
+ continue;
5133
+ }
5134
+ const relPath = key.slice(state.prefix.length);
5135
+ if (!safeRelPath(relPath)) {
5136
+ this.log.warn("object skipped: key does not map to a path inside the mount", { key });
5137
+ continue;
5138
+ }
5139
+ entries.push({ key, relPath });
5140
+ }
5141
+ continuationToken = page.isTruncated ? page.nextContinuationToken : void 0;
5142
+ if (page.isTruncated && !continuationToken) {
5143
+ throw new Error("S3 listing is truncated but carries no NextContinuationToken");
5144
+ }
5145
+ } while (continuationToken);
5146
+ return entries;
5147
+ }
5148
+ /**
5149
+ * Object bodies in `entries` order, presigned in batches of
5150
+ * {@link S3_PRESIGN_BATCH}. A batch whose URLs expired mid-download is
5151
+ * re-presigned once (the rest of it); a second expiry is an error. Every
5152
+ * download is bounded by the loop's shared budget (`deadlineAt`).
5153
+ */
5154
+ async *presignedBodies(state, entries, deadlineAt) {
5155
+ for (let start = 0; start < entries.length; start += S3_PRESIGN_BATCH) {
5156
+ const keys = entries.slice(start, start + S3_PRESIGN_BATCH).map((e) => e.key);
5157
+ let signed = await this.presignGet(state, keys);
5158
+ let represigned = false;
5159
+ for (let i = 0; i < keys.length; i++) {
5160
+ const key = keys[i];
5161
+ const url = signed.urls.get(key);
5162
+ if (url === void 0) {
5163
+ yield null;
5164
+ continue;
5165
+ }
5166
+ try {
5167
+ yield await this.fetchObjectPresigned(state, key, url, signed.expiresAt, deadlineAt);
5168
+ } catch (err) {
5169
+ if (!(err instanceof S3PresignedRequestError) || !err.expired || represigned) throw err;
5170
+ represigned = true;
5171
+ this.log.info("presigned URLs expired; presigning the batch again", { key });
5172
+ signed = await this.presignGet(state, keys.slice(i));
5173
+ i--;
5174
+ }
5175
+ }
5176
+ }
5177
+ }
5178
+ async presignGet(state, keys) {
5179
+ const signed = await this.presignOrThrow(state, { mountId: state.mountId, op: "get", keys });
5180
+ if (signed.op !== "get") throw new Error("host.s3_presign answered get with list");
5181
+ return { urls: new Map(signed.urls.map((u) => [u.key, u.url])), expiresAt: signed.expiresAt };
5182
+ }
5183
+ /**
5184
+ * One object through its presigned URL. Like an SDK `GetObject` miss, a 404
5185
+ * (deleted since the listing), a 5xx or a network failure is survivable and
5186
+ * skips the object. A 403 or other client error means the mount cannot be
5187
+ * read, and a spent budget or a disposed manager means the sync must stop:
5188
+ * those fail it.
5189
+ */
5190
+ async fetchObjectPresigned(state, key, url, expiresAt, deadlineAt) {
5191
+ const what = `object '${key}'`;
5192
+ try {
5193
+ const bytes = await this.readBounded(
5194
+ state,
5195
+ async (signal) => {
5196
+ const resp = await this._fetch(url, { signal });
5197
+ if (!resp.ok) throw await presignedError("get", resp, expiresAt, what);
5198
+ return resp.arrayBuffer();
5199
+ },
5200
+ deadlineAt - Date.now(),
5201
+ what
5202
+ );
5203
+ return Buffer.from(bytes);
5204
+ } catch (err) {
5205
+ const survivable = err instanceof S3PresignedRequestError ? err.status === 404 || err.status >= 500 : !(err instanceof S3FetchStoppedError);
5206
+ if (!survivable) throw err;
5207
+ this.log.warn("object download failed", {
5208
+ key,
5209
+ error: err instanceof Error ? err.message : String(err)
5210
+ });
5211
+ return null;
5212
+ }
5213
+ }
5214
+ async presignOrThrow(state, request) {
5215
+ const result = await state.presign(request);
5216
+ if (!result.ok) {
5217
+ const message = `S3 mount '${state.mountId}': the platform refused to presign ${request.op} (${result.code}): ${result.message}`;
5218
+ const kind = PRESIGN_REFUSAL_KINDS[result.code];
5219
+ throw kind ? new ClassifiedConnectError(message, kind) : new Error(message);
5220
+ }
5221
+ return result;
5222
+ }
5223
+ /** Run `use` on a fresh signature; if the URL turns out expired, sign once more and retry. */
5224
+ async withRepresign(sign, use) {
5225
+ try {
5226
+ return await use(await sign());
5227
+ } catch (err) {
5228
+ if (!(err instanceof S3PresignedRequestError) || !err.expired) throw err;
5229
+ this.log.info("presigned URL expired; presigning again", { op: err.op });
5230
+ return use(await sign());
5231
+ }
5232
+ }
5233
+ /**
5234
+ * Run one network step under a timeout and the manager's dispose signal,
5235
+ * naming which one stopped it. Raw `fetch` has no socket timeout of its own,
5236
+ * so without this a half-open connection would park `connect()` / `sync()`.
5237
+ */
5238
+ async readBounded(state, step, timeoutMs, what) {
5239
+ const disposed = `S3 read of ${what} stopped: the connector was disposed`;
5240
+ if (state.abortSignal?.aborted) throw new S3FetchStoppedError(disposed);
5241
+ if (timeoutMs <= 0) {
5242
+ throw new S3FetchStoppedError(`S3 read of ${what} stopped: the download budget is spent`);
5243
+ }
5244
+ const timeout = AbortSignal.timeout(timeoutMs);
5245
+ const signal = state.abortSignal ? AbortSignal.any([state.abortSignal, timeout]) : timeout;
5246
+ let onAbort;
5247
+ const stopped = new Promise((_, reject) => {
5248
+ onAbort = () => reject(
5249
+ new S3FetchStoppedError(
5250
+ state.abortSignal?.aborted ? disposed : `S3 read of ${what} timed out after ${timeoutMs}ms`
5251
+ )
5252
+ );
5253
+ signal.addEventListener("abort", onAbort, { once: true });
5254
+ });
5255
+ try {
5256
+ return await Promise.race([step(signal), stopped]);
5257
+ } finally {
5258
+ if (onAbort) signal.removeEventListener("abort", onAbort);
5259
+ }
5260
+ }
5261
+ /**
5262
+ * Write each entry's body under `targetDir`. `bodies` yields one body (or
5263
+ * `null` for a survivable miss) per entry, in order; it receives the loop's
5264
+ * deadline so a download inside it is bounded by the same budget.
5265
+ */
5266
+ async writeEntries(entries, targetDir, bodies) {
5000
5267
  let filesWritten = 0;
5001
5268
  let bytesTransferred = 0;
5002
5269
  const budgetMs = fsDeadlineMs() * DOWNLOAD_DEADLINE_FACTOR;
5003
5270
  const deadlineAt = Date.now() + budgetMs;
5004
5271
  const created = /* @__PURE__ */ new Set();
5005
- for (const { key, relPath } of entries) {
5006
- if (deadlineAt - Date.now() <= 0) {
5007
- throw new FsDeadlineError(`downloadAll ${targetDir}`, budgetMs);
5008
- }
5009
- const body = await this.fetchObject(s3Mod, state, key);
5010
- if (body == null) continue;
5011
- const localPath = join(targetDir, relPath);
5012
- const parent = dirname(localPath);
5013
- if (!created.has(parent)) {
5272
+ const iterator = bodies(deadlineAt)[Symbol.asyncIterator]();
5273
+ try {
5274
+ for (const { relPath } of entries) {
5275
+ if (deadlineAt - Date.now() <= 0) {
5276
+ throw new FsDeadlineError(`downloadAll ${targetDir}`, budgetMs);
5277
+ }
5278
+ const next = await iterator.next();
5279
+ if (next.done) {
5280
+ throw new Error(
5281
+ `S3 download produced fewer bodies than the ${entries.length} listed objects`
5282
+ );
5283
+ }
5284
+ const body = next.value;
5285
+ if (body == null) continue;
5286
+ const localPath = join(targetDir, relPath);
5287
+ const parent = dirname(localPath);
5288
+ if (!created.has(parent)) {
5289
+ await withFsDeadline(
5290
+ `mkdir ${parent}`,
5291
+ () => mkdir(parent, { recursive: true }),
5292
+ deadlineAt - Date.now()
5293
+ );
5294
+ created.add(parent);
5295
+ }
5014
5296
  await withFsDeadline(
5015
- `mkdir ${parent}`,
5016
- () => mkdir(parent, { recursive: true }),
5297
+ `writeFile ${localPath}`,
5298
+ () => writeFile(localPath, body),
5017
5299
  deadlineAt - Date.now()
5018
5300
  );
5019
- created.add(parent);
5301
+ filesWritten++;
5302
+ bytesTransferred += body.byteLength;
5020
5303
  }
5021
- await withFsDeadline(
5022
- `writeFile ${localPath}`,
5023
- () => writeFile(localPath, body),
5024
- deadlineAt - Date.now()
5025
- );
5026
- filesWritten++;
5027
- bytesTransferred += body.byteLength;
5304
+ } finally {
5305
+ await iterator.return?.(void 0);
5028
5306
  }
5029
5307
  return { filesWritten, filesDeleted: 0, bytesTransferred };
5030
5308
  }
5031
5309
  };
5032
- function createConnector13(sdkOverride) {
5033
- return new S3Connector(sdkOverride);
5310
+ function presignScopePrefix(declaration) {
5311
+ const opts = declaration.options ?? {};
5312
+ const explicit = typeof opts.prefix === "string" ? opts.prefix.trim() : "";
5313
+ let raw = explicit;
5314
+ if (raw === "") {
5315
+ const source = declaration.mount?.source ?? opts.source;
5316
+ const parsed = typeof source === "string" ? parseS3Source(source) : null;
5317
+ const bucket = typeof opts.bucket === "string" ? opts.bucket.trim() : "";
5318
+ if (parsed && (bucket === "" || bucket === parsed.bucket)) raw = parsed.prefix;
5319
+ }
5320
+ return normalizeScopePrefix(raw);
5321
+ }
5322
+ function normalizeScopePrefix(raw) {
5323
+ const trimmed = raw.replace(/^\/+/, "");
5324
+ return trimmed === "" || trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
5325
+ }
5326
+ function safeRelPath(relPath) {
5327
+ return relPath.replace(/^\/+/, "") !== "" && !relPath.split("/").some((segment) => segment === "." || segment === "..");
5328
+ }
5329
+ function parseS3Source(source) {
5330
+ let rest = source.trim();
5331
+ if (rest.toLowerCase().startsWith("s3://")) rest = rest.slice("s3://".length);
5332
+ else if (/^[a-z][a-z0-9+.-]*:/i.test(rest)) return null;
5333
+ rest = rest.replace(/^\/+/, "");
5334
+ const slash = rest.indexOf("/");
5335
+ const bucket = slash === -1 ? rest : rest.slice(0, slash);
5336
+ if (bucket === "") return null;
5337
+ return { bucket, prefix: slash === -1 ? "" : rest.slice(slash + 1) };
5338
+ }
5339
+ function presignableKey(key, prefix) {
5340
+ return key.startsWith(prefix) && key.length > prefix.length && Buffer.byteLength(key, "utf8") <= MAX_PRESIGN_KEY_BYTES && !key.split("/").some((segment) => segment === "." || segment === "..") && // biome-ignore lint/suspicious/noControlCharactersInRegex: rejecting control characters is the point
5341
+ !/[\u0000-\u001f\u007f]/.test(key);
5342
+ }
5343
+ function parseListObjectsV2(xml) {
5344
+ const urlEncoded = /<EncodingType>\s*url\s*<\/EncodingType>/i.test(xml);
5345
+ const decode = (raw) => {
5346
+ const text = unescapeXml(raw);
5347
+ if (!urlEncoded) return text;
5348
+ try {
5349
+ return decodeURIComponent(text.replace(/\+/g, " "));
5350
+ } catch {
5351
+ return text;
5352
+ }
5353
+ };
5354
+ const prefixMatch = /<Prefix>([\s\S]*?)<\/Prefix>|<Prefix\s*\/>/.exec(
5355
+ xml.replace(/<CommonPrefixes>[\s\S]*?<\/CommonPrefixes>/g, "")
5356
+ );
5357
+ const keys = [];
5358
+ for (const contents of xml.matchAll(/<Contents>([\s\S]*?)<\/Contents>/g)) {
5359
+ const raw = /<Key>([\s\S]*?)<\/Key>/.exec(contents[1])?.[1];
5360
+ if (raw === void 0) continue;
5361
+ keys.push(decode(raw));
5362
+ }
5363
+ const isTruncated = /<IsTruncated>\s*true\s*<\/IsTruncated>/i.test(xml);
5364
+ const token = /<NextContinuationToken>([\s\S]*?)<\/NextContinuationToken>/.exec(xml)?.[1];
5365
+ return {
5366
+ keys,
5367
+ ...prefixMatch ? { prefix: decode(prefixMatch[1] ?? "") } : {},
5368
+ isTruncated,
5369
+ ...token ? { nextContinuationToken: unescapeXml(token) } : {}
5370
+ };
5371
+ }
5372
+ function unescapeXml(text) {
5373
+ return text.replace(/&(#x[0-9a-fA-F]+|#[0-9]+|amp|lt|gt|quot|apos);/g, (_m, entity) => {
5374
+ switch (entity) {
5375
+ case "amp":
5376
+ return "&";
5377
+ case "lt":
5378
+ return "<";
5379
+ case "gt":
5380
+ return ">";
5381
+ case "quot":
5382
+ return '"';
5383
+ case "apos":
5384
+ return "'";
5385
+ default:
5386
+ return String.fromCodePoint(
5387
+ entity[1] === "x" ? Number.parseInt(entity.slice(2), 16) : Number.parseInt(entity.slice(1), 10)
5388
+ );
5389
+ }
5390
+ });
5391
+ }
5392
+ async function presignedError(op, resp, expiresAt, what) {
5393
+ let body = "";
5394
+ try {
5395
+ body = await Promise.race([
5396
+ resp.text(),
5397
+ new Promise((resolve7) => setTimeout(() => resolve7(""), ERROR_BODY_WAIT_MS).unref?.())
5398
+ ]);
5399
+ } catch {
5400
+ }
5401
+ const code = /<Code>([\s\S]*?)<\/Code>/.exec(body)?.[1];
5402
+ const message = /<Message>([\s\S]*?)<\/Message>/.exec(body)?.[1];
5403
+ const pastExpiry = Date.parse(expiresAt) <= Date.now();
5404
+ const expired = resp.status === 403 && (/expired/i.test(message ?? "") || code === "ExpiredToken" || pastExpiry);
5405
+ const detail = [code, message].filter(Boolean).join(": ");
5406
+ const lead = resp.status === 403 && !expired ? `S3 denied access to ${what} (HTTP 403${detail ? `, ${detail}` : ""}); check the S3 link's permissions for this bucket and prefix` : `S3 ${op} of ${what} failed (HTTP ${resp.status}${detail ? `, ${detail}` : ""})`;
5407
+ return new S3PresignedRequestError(op, resp.status, code, expired, lead);
5408
+ }
5409
+ function createConnector13(sdkOverride, deps) {
5410
+ return new S3Connector(sdkOverride, deps);
5034
5411
  }
5035
5412
 
5036
5413
  // factory-assets/connectors/sharepoint/driver.ts
@@ -6993,6 +7370,7 @@ var ConnectorManager = class {
6993
7370
  secrets: this.chain ?? this.secrets,
6994
7371
  // Both filesystem and tool faces use this for the auth: backend initial-mint path.
6995
7372
  tokenMediator: this.buildWrappedMediator(declaration),
7373
+ ...this.opts.s3Presigner ? { s3Presigner: this.opts.s3Presigner } : {},
6996
7374
  abortSignal: this.disposeController.signal
6997
7375
  };
6998
7376
  if (this.chain && connector.describeFields && declaration.auth !== "backend") {
@@ -11502,6 +11880,6 @@ function describeFlowOperations() {
11502
11880
  return new FlowAdapter().tools.describeOperations({ access: "read-write" });
11503
11881
  }
11504
11882
 
11505
- export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ClassifierSubpromptRunner, ConnectorManager, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, MAX_REARM_MS, MIN_REFRESH_DELAY_MS, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, NO_ACTIVE_FLOW_ERROR, PortPool, PostgresConnector, REFRESH_LEAD_MS, S3Connector, SQLiteConnector, SessionInlineSubFlowRunner, SessionNodeExecutor, SessionSubpromptRunner, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildConnectorToolDefs, buildFlowPromptEvent, buildFlowToolDefs, buildLazyFlowToolDefs, buildLazySdkFlowTools, buildSdkConnectorTools, buildSdkFlowTools, checkFleetMountLiveness, classifierQuestion, classifyConnectError, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, describeFlowOperations, driveOrchestratorTurn, ensureDirMode, ensureFleetMounted, ensureRefreshAttemptStamp, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, removeUnavailableMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock, writeUnavailableMountBlock };
11506
- //# sourceMappingURL=chunk-N6XJGVSK.js.map
11507
- //# sourceMappingURL=chunk-N6XJGVSK.js.map
11883
+ export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ClassifierSubpromptRunner, ConnectorManager, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, MAX_REARM_MS, MIN_REFRESH_DELAY_MS, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, NO_ACTIVE_FLOW_ERROR, PortPool, PostgresConnector, REFRESH_LEAD_MS, S3Connector, S3FetchStoppedError, S3PresignedRequestError, S3_PRESIGN_BATCH, SQLiteConnector, SessionInlineSubFlowRunner, SessionNodeExecutor, SessionSubpromptRunner, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildConnectorToolDefs, buildFlowPromptEvent, buildFlowToolDefs, buildLazyFlowToolDefs, buildLazySdkFlowTools, buildSdkConnectorTools, buildSdkFlowTools, checkFleetMountLiveness, classifierQuestion, classifyConnectError, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, describeFlowOperations, driveOrchestratorTurn, ensureDirMode, ensureFleetMounted, ensureRefreshAttemptStamp, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, parseListObjectsV2, presignScopePrefix, presignableKey, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, removeUnavailableMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock, writeUnavailableMountBlock };
11884
+ //# sourceMappingURL=chunk-E4Q5GKO2.js.map
11885
+ //# sourceMappingURL=chunk-E4Q5GKO2.js.map