@skaile/workspaces 0.22.0 → 0.24.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 (136) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/{asset-feeds-Y2CDCM3W.js → asset-feeds-WKIKSZ6Z.js} +9 -9
  3. package/dist/{asset-feeds-Y2CDCM3W.js.map → asset-feeds-WKIKSZ6Z.js.map} +1 -1
  4. package/dist/asset-manager/index.js +7 -7
  5. package/dist/asset-manager/installer.js +6 -6
  6. package/dist/asset-manager/src/index.d.ts +15 -0
  7. package/dist/asset-manager/src/index.d.ts.map +1 -1
  8. package/dist/base-assets/connectors/deploy.js +8 -8
  9. package/dist/base-assets/connectors/devserver.js +8 -8
  10. package/dist/base-assets/connectors/flow/adapter.js +8 -8
  11. package/dist/base-assets/connectors/flow/run-flow.js +9 -9
  12. package/dist/base-assets/connectors/flow.js +8 -8
  13. package/dist/base-assets/connectors/git.js +8 -8
  14. package/dist/base-assets/connectors/gmail.js +8 -8
  15. package/dist/base-assets/connectors/googledrive/driver.d.ts.map +1 -1
  16. package/dist/base-assets/connectors/googledrive.js +8 -8
  17. package/dist/base-assets/connectors/local.js +8 -8
  18. package/dist/base-assets/connectors/mattermost.js +8 -8
  19. package/dist/base-assets/connectors/memory.js +8 -8
  20. package/dist/base-assets/connectors/minio.js +8 -8
  21. package/dist/base-assets/connectors/postgres.js +8 -8
  22. package/dist/base-assets/connectors/s3.js +8 -8
  23. package/dist/base-assets/connectors/sharepoint/driver.d.ts.map +1 -1
  24. package/dist/base-assets/connectors/sharepoint.js +8 -8
  25. package/dist/base-assets/connectors/sqlite.js +8 -8
  26. package/dist/base-assets/connectors/static-server.js +8 -8
  27. package/dist/base-assets/connectors/tunnel.js +8 -8
  28. package/dist/base-assets/connectors/webdav/driver.d.ts.map +1 -1
  29. package/dist/base-assets/connectors/webdav.js +8 -8
  30. package/dist/base-assets/connectors/xstate-store/adapter.d.ts +1 -1
  31. package/dist/base-assets/connectors/xstate-store/adapter.d.ts.map +1 -1
  32. package/dist/base-assets/connectors/xstate-store.js +8 -8
  33. package/dist/base-assets/connectors/xstate.js +8 -8
  34. package/dist/{chunk-MNAHNDUI.js → chunk-2F3RUZXC.js} +3 -3
  35. package/dist/{chunk-MNAHNDUI.js.map → chunk-2F3RUZXC.js.map} +1 -1
  36. package/dist/{chunk-2RYQERIT.js → chunk-2RFOFHSM.js} +4 -4
  37. package/dist/{chunk-2RYQERIT.js.map → chunk-2RFOFHSM.js.map} +1 -1
  38. package/dist/{chunk-K2HDYSAM.js → chunk-46COM7M5.js} +5 -5
  39. package/dist/{chunk-K2HDYSAM.js.map → chunk-46COM7M5.js.map} +1 -1
  40. package/dist/{chunk-V5TBKO5Q.js → chunk-542K7SR6.js} +59 -35
  41. package/dist/chunk-542K7SR6.js.map +1 -0
  42. package/dist/{chunk-PFOXL4SH.js → chunk-5ESCS2OS.js} +4 -4
  43. package/dist/{chunk-PFOXL4SH.js.map → chunk-5ESCS2OS.js.map} +1 -1
  44. package/dist/{chunk-WH2EB2SF.js → chunk-AFLH7B64.js} +3 -3
  45. package/dist/{chunk-WH2EB2SF.js.map → chunk-AFLH7B64.js.map} +1 -1
  46. package/dist/{chunk-K7WPR77X.js → chunk-DH4N5AW4.js} +14 -8
  47. package/dist/chunk-DH4N5AW4.js.map +1 -0
  48. package/dist/{chunk-OJN25VJO.js → chunk-HIIARTRZ.js} +240 -179
  49. package/dist/chunk-HIIARTRZ.js.map +1 -0
  50. package/dist/{chunk-NDD5VMN5.js → chunk-J2TITSXF.js} +2 -2
  51. package/dist/{chunk-NDD5VMN5.js.map → chunk-J2TITSXF.js.map} +1 -1
  52. package/dist/{chunk-SKXCTV55.js → chunk-JQBHCJ6N.js} +30 -14
  53. package/dist/chunk-JQBHCJ6N.js.map +1 -0
  54. package/dist/{chunk-JN2CUVSU.js → chunk-LJ52ZKIU.js} +3 -3
  55. package/dist/{chunk-JN2CUVSU.js.map → chunk-LJ52ZKIU.js.map} +1 -1
  56. package/dist/{chunk-7HSXUKNB.js → chunk-N6TA6RSH.js} +19 -18
  57. package/dist/chunk-N6TA6RSH.js.map +1 -0
  58. package/dist/{chunk-NBJ5TOEC.js → chunk-ODPII24X.js} +3 -3
  59. package/dist/{chunk-NBJ5TOEC.js.map → chunk-ODPII24X.js.map} +1 -1
  60. package/dist/{chunk-53UNDY6K.js → chunk-OYRW5RCM.js} +5 -5
  61. package/dist/{chunk-53UNDY6K.js.map → chunk-OYRW5RCM.js.map} +1 -1
  62. package/dist/{chunk-6MB7CRME.js → chunk-QMONOHXT.js} +268 -37
  63. package/dist/chunk-QMONOHXT.js.map +1 -0
  64. package/dist/{chunk-4NDWKA64.js → chunk-WSZAFRQL.js} +8 -3
  65. package/dist/chunk-WSZAFRQL.js.map +1 -0
  66. package/dist/{chunk-VUCPJBAG.js → chunk-YX3UWPJ5.js} +53 -28
  67. package/dist/chunk-YX3UWPJ5.js.map +1 -0
  68. package/dist/{chunk-ETMUGBHF.js → chunk-Z3M5K67G.js} +8 -8
  69. package/dist/chunk-Z3M5K67G.js.map +1 -0
  70. package/dist/cli/index.js +38 -38
  71. package/dist/cli/index.js.map +1 -1
  72. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  73. package/dist/cli/src/helpers.d.ts +7 -0
  74. package/dist/cli/src/helpers.d.ts.map +1 -1
  75. package/dist/connectors/config.js +6 -6
  76. package/dist/connectors/index.js +8 -8
  77. package/dist/connectors/rclone.js +2 -1
  78. package/dist/connectors/src/connector-manager.d.ts +49 -0
  79. package/dist/connectors/src/connector-manager.d.ts.map +1 -1
  80. package/dist/connectors/src/connector-types.d.ts +16 -0
  81. package/dist/connectors/src/connector-types.d.ts.map +1 -1
  82. package/dist/connectors/src/rclone-process-manager.d.ts +91 -3
  83. package/dist/connectors/src/rclone-process-manager.d.ts.map +1 -1
  84. package/dist/connectors/src/watcher.d.ts +6 -0
  85. package/dist/connectors/src/watcher.d.ts.map +1 -1
  86. package/dist/core/index.js +5 -5
  87. package/dist/core/manifest.js +2 -2
  88. package/dist/core/models.js +1 -1
  89. package/dist/core/runtime-assets.js +4 -4
  90. package/dist/core/src/lock.d.ts +6 -6
  91. package/dist/core/src/manifest.d.ts.map +1 -1
  92. package/dist/core/src/models.d.ts +25 -18
  93. package/dist/core/src/models.d.ts.map +1 -1
  94. package/dist/core/src/repo-manager.d.ts +8 -2
  95. package/dist/core/src/repo-manager.d.ts.map +1 -1
  96. package/dist/core/workspace-config.js +3 -3
  97. package/dist/deploy/index.js +5 -5
  98. package/dist/discovery/index.js +3 -3
  99. package/dist/{ensure-sources-REWWBH2K.js → ensure-sources-OJUBGX6Z.js} +10 -10
  100. package/dist/{ensure-sources-REWWBH2K.js.map → ensure-sources-OJUBGX6Z.js.map} +1 -1
  101. package/dist/helpers-LTN3HMD3.js +4 -0
  102. package/dist/{helpers-I3SREIC3.js.map → helpers-LTN3HMD3.js.map} +1 -1
  103. package/dist/library/index.js +4 -4
  104. package/dist/open-library-67FSSQWE.js +13 -0
  105. package/dist/{open-library-CT4VVESU.js.map → open-library-67FSSQWE.js.map} +1 -1
  106. package/dist/{plugin-store-QS7TC5HY.js → plugin-store-IZ5SCRAV.js} +7 -7
  107. package/dist/{plugin-store-QS7TC5HY.js.map → plugin-store-IZ5SCRAV.js.map} +1 -1
  108. package/dist/runner/index.js +10 -10
  109. package/dist/runner/src/resources.d.ts +14 -1
  110. package/dist/runner/src/resources.d.ts.map +1 -1
  111. package/dist/runner/src/serve.d.ts.map +1 -1
  112. package/dist/runner/src/session-builder.d.ts +7 -0
  113. package/dist/runner/src/session-builder.d.ts.map +1 -1
  114. package/dist/sdk/asset-manager.js +7 -7
  115. package/dist/sdk/core.js +5 -5
  116. package/dist/sdk/index.js +10 -10
  117. package/dist/sdk/runner.js +10 -10
  118. package/dist/{setup-F6DGKL7J.js → setup-J7CYEQOF.js} +7 -7
  119. package/dist/{setup-F6DGKL7J.js.map → setup-J7CYEQOF.js.map} +1 -1
  120. package/dist/store-client-AEI6Y3KD.js +14 -0
  121. package/dist/{store-client-JP642EEI.js.map → store-client-AEI6Y3KD.js.map} +1 -1
  122. package/dist/tui/index.js +10 -10
  123. package/dist/workspace-plugin/index.js +1 -1
  124. package/package.json +1 -1
  125. package/dist/chunk-4NDWKA64.js.map +0 -1
  126. package/dist/chunk-6MB7CRME.js.map +0 -1
  127. package/dist/chunk-7HSXUKNB.js.map +0 -1
  128. package/dist/chunk-ETMUGBHF.js.map +0 -1
  129. package/dist/chunk-K7WPR77X.js.map +0 -1
  130. package/dist/chunk-OJN25VJO.js.map +0 -1
  131. package/dist/chunk-SKXCTV55.js.map +0 -1
  132. package/dist/chunk-V5TBKO5Q.js.map +0 -1
  133. package/dist/chunk-VUCPJBAG.js.map +0 -1
  134. package/dist/helpers-I3SREIC3.js +0 -4
  135. package/dist/open-library-CT4VVESU.js +0 -13
  136. package/dist/store-client-JP642EEI.js +0 -14
@@ -1,14 +1,14 @@
1
- import { resolveAuthRef, resolveAuth } from './chunk-NBJ5TOEC.js';
2
- import { RcloneProcessManager, ensureDirMode, PortPool } from './chunk-6MB7CRME.js';
1
+ import { resolveAuthRef, resolveAuth } from './chunk-ODPII24X.js';
2
+ import { createFsWatcher, RcloneProcessManager, ensureDirMode, PortPool } from './chunk-QMONOHXT.js';
3
3
  import { renderWebDAVConfig, renderOneDriveConfig, renderGoogleDriveConfig } from './chunk-QAVZOJCV.js';
4
4
  import { pluginRegistry } from './chunk-6E6PKKAD.js';
5
5
  import { computeFlowStateFromSnapshots } from './chunk-ICS76R4T.js';
6
6
  import { renderStimulusPrompt } from './chunk-GZWJGNNN.js';
7
7
  import { computeStimulus } from './chunk-FVTV7M76.js';
8
- import { portableSpawn } from './chunk-ETMUGBHF.js';
8
+ import { portableSpawn } from './chunk-Z3M5K67G.js';
9
9
  import { createLogger } from './chunk-24UIWON4.js';
10
10
  import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, cpSync, statSync, createReadStream, readdirSync, openSync, writeSync, closeSync, chmodSync } from 'fs';
11
- import { join, dirname, resolve, sep, relative } from 'path';
11
+ import { join, dirname, resolve, sep } from 'path';
12
12
  import * as zNS from 'zod';
13
13
  import { execFile, execSync } from 'child_process';
14
14
  import { promisify } from 'util';
@@ -17,7 +17,6 @@ import mime from 'mime';
17
17
  import { createRequire } from 'module';
18
18
  import { pathToFileURL } from 'url';
19
19
  import { stat, readdir } from 'fs/promises';
20
- import chokidar from 'chokidar';
21
20
 
22
21
  // connectors/src/connector-base.ts
23
22
  var AbstractConnector = class {
@@ -1183,6 +1182,7 @@ var WebDAVConnector = class extends AbstractConnector {
1183
1182
  name = "webdav";
1184
1183
  rclone = new RcloneProcessManager();
1185
1184
  filesystem = {
1185
+ deferMount: true,
1186
1186
  sync: (handle, options) => {
1187
1187
  return this._sync(handle, options);
1188
1188
  }
@@ -1250,6 +1250,7 @@ var WebDAVConnector = class extends AbstractConnector {
1250
1250
  await ensureDirMode(targetDir, this.log);
1251
1251
  const cacheMaxSize = opts?.cacheMaxSize !== void 0 ? String(opts.cacheMaxSize) : void 0;
1252
1252
  const cacheMaxAge = opts?.cacheMaxAge !== void 0 ? String(opts.cacheMaxAge) : void 0;
1253
+ const vfsWriteBack = opts?.vfsWriteBack !== void 0 ? String(opts.vfsWriteBack) : void 0;
1253
1254
  const rcloneHandle = await this.rclone.spawn({
1254
1255
  mountId: declaration.id,
1255
1256
  driverName: "webdav",
@@ -1260,6 +1261,7 @@ var WebDAVConnector = class extends AbstractConnector {
1260
1261
  vfsCacheMode,
1261
1262
  ...cacheMaxSize !== void 0 ? { cacheMaxSize } : {},
1262
1263
  ...cacheMaxAge !== void 0 ? { cacheMaxAge } : {},
1264
+ ...vfsWriteBack !== void 0 ? { vfsWriteBack } : {},
1263
1265
  log: this.log
1264
1266
  });
1265
1267
  this.log.info("mount ready", { pid: rcloneHandle.pid });
@@ -1342,11 +1344,15 @@ async function obscurePassword(plain) {
1342
1344
  }
1343
1345
 
1344
1346
  // base-assets/connectors/sharepoint/driver.ts
1345
- var CACHE_BASE2 = "/var/cache/skaile";
1347
+ var DEFAULT_CACHE_BASE = "/var/cache/skaile";
1348
+ function cacheBase() {
1349
+ return process.env.SKAILE_RCLONE_CACHE_BASE ?? DEFAULT_CACHE_BASE;
1350
+ }
1346
1351
  var SharePointConnector = class extends AbstractConnector {
1347
1352
  name = "sharepoint";
1348
1353
  rclone = new RcloneProcessManager();
1349
1354
  filesystem = {
1355
+ deferMount: true,
1350
1356
  sync: (handle, options) => {
1351
1357
  return this._sync(handle, options);
1352
1358
  }
@@ -1391,7 +1397,8 @@ var SharePointConnector = class extends AbstractConnector {
1391
1397
  const remoteName = `skaile-${declaration.id}`;
1392
1398
  const remotePath = buildRemotePath(folderPath, basePath);
1393
1399
  const remote = `${remoteName}:${remotePath}`;
1394
- const cacheDir = `${CACHE_BASE2}/${declaration.id}`;
1400
+ const base = cacheBase();
1401
+ const cacheDir = `${base}/${declaration.id}`;
1395
1402
  const vfsCacheMode = declaration.access === "read-only" ? "minimal" : "full";
1396
1403
  this.log.info("mount params resolved", {
1397
1404
  driveId,
@@ -1410,11 +1417,19 @@ var SharePointConnector = class extends AbstractConnector {
1410
1417
  clientSecret: auth.clientSecret,
1411
1418
  driveId
1412
1419
  });
1413
- await ensureDirMode(CACHE_BASE2, this.log);
1420
+ await ensureDirMode(base, this.log);
1414
1421
  await ensureDirMode(cacheDir, this.log);
1415
1422
  await ensureDirMode(targetDir, this.log);
1416
1423
  const cacheMaxSize = opts?.cacheMaxSize !== void 0 ? String(opts.cacheMaxSize) : void 0;
1417
1424
  const cacheMaxAge = opts?.cacheMaxAge !== void 0 ? String(opts.cacheMaxAge) : void 0;
1425
+ const vfsWriteBack = opts?.vfsWriteBack !== void 0 ? String(opts.vfsWriteBack) : void 0;
1426
+ const ignoreSizeCheck = opts?.ignoreSizeCheck === true;
1427
+ if (ignoreSizeCheck) {
1428
+ this.log.warn(
1429
+ "ignoreSizeCheck enabled \u2014 rclone post-transfer size/checksum verification disabled for this mount",
1430
+ { connectorId: declaration.id }
1431
+ );
1432
+ }
1418
1433
  const rcloneHandle = await this.rclone.spawn({
1419
1434
  mountId: declaration.id,
1420
1435
  driverName: "sharepoint",
@@ -1425,6 +1440,8 @@ var SharePointConnector = class extends AbstractConnector {
1425
1440
  vfsCacheMode,
1426
1441
  ...cacheMaxSize !== void 0 ? { cacheMaxSize } : {},
1427
1442
  ...cacheMaxAge !== void 0 ? { cacheMaxAge } : {},
1443
+ ...vfsWriteBack !== void 0 ? { vfsWriteBack } : {},
1444
+ ...ignoreSizeCheck ? { ignoreSizeCheck: true } : {},
1428
1445
  log: this.log
1429
1446
  });
1430
1447
  this.log.info("mount ready", { pid: rcloneHandle.pid });
@@ -1512,7 +1529,7 @@ function buildRemotePath(folderPath, basePath) {
1512
1529
  }
1513
1530
 
1514
1531
  // base-assets/connectors/googledrive/driver.ts
1515
- var CACHE_BASE3 = "/var/cache/skaile";
1532
+ var CACHE_BASE2 = "/var/cache/skaile";
1516
1533
  var ALLOWED_SCOPES = /* @__PURE__ */ new Set([
1517
1534
  "drive",
1518
1535
  "drive.readonly",
@@ -1524,6 +1541,7 @@ var GoogleDriveConnector = class extends AbstractConnector {
1524
1541
  name = "googledrive";
1525
1542
  rclone = new RcloneProcessManager();
1526
1543
  filesystem = {
1544
+ deferMount: true,
1527
1545
  sync: (handle, options) => {
1528
1546
  return this._sync(handle, options);
1529
1547
  }
@@ -1569,7 +1587,7 @@ var GoogleDriveConnector = class extends AbstractConnector {
1569
1587
  const remoteName = `skaile-${declaration.id}`;
1570
1588
  const remotePath = basePath.length > 0 ? `/${basePath}` : "/";
1571
1589
  const remote = `${remoteName}:${remotePath}`;
1572
- const cacheDir = `${CACHE_BASE3}/${declaration.id}`;
1590
+ const cacheDir = `${CACHE_BASE2}/${declaration.id}`;
1573
1591
  const vfsCacheMode = declaration.access === "read-only" ? "minimal" : "full";
1574
1592
  this.log.info("mount params resolved", {
1575
1593
  scope,
@@ -1592,11 +1610,12 @@ var GoogleDriveConnector = class extends AbstractConnector {
1592
1610
  teamDrive,
1593
1611
  serviceAccountFile: auth.serviceAccountFile
1594
1612
  });
1595
- await ensureDirMode(CACHE_BASE3, this.log);
1613
+ await ensureDirMode(CACHE_BASE2, this.log);
1596
1614
  await ensureDirMode(cacheDir, this.log);
1597
1615
  await ensureDirMode(targetDir, this.log);
1598
1616
  const cacheMaxSize = opts?.cacheMaxSize !== void 0 ? String(opts.cacheMaxSize) : void 0;
1599
1617
  const cacheMaxAge = opts?.cacheMaxAge !== void 0 ? String(opts.cacheMaxAge) : void 0;
1618
+ const vfsWriteBack = opts?.vfsWriteBack !== void 0 ? String(opts.vfsWriteBack) : void 0;
1600
1619
  const rcloneHandle = await this.rclone.spawn({
1601
1620
  mountId: declaration.id,
1602
1621
  driverName: "googledrive",
@@ -1607,6 +1626,7 @@ var GoogleDriveConnector = class extends AbstractConnector {
1607
1626
  vfsCacheMode,
1608
1627
  ...cacheMaxSize !== void 0 ? { cacheMaxSize } : {},
1609
1628
  ...cacheMaxAge !== void 0 ? { cacheMaxAge } : {},
1629
+ ...vfsWriteBack !== void 0 ? { vfsWriteBack } : {},
1610
1630
  log: this.log
1611
1631
  });
1612
1632
  this.log.info("mount ready", { pid: rcloneHandle.pid });
@@ -3070,6 +3090,15 @@ var XStateStoreConnector = class extends AbstractConnector {
3070
3090
  ],
3071
3091
  examples: [`merge --data '{"theme":"dark","locale":"de"}'`]
3072
3092
  },
3093
+ {
3094
+ name: "delete",
3095
+ description: "Delete a single key from the store context.",
3096
+ accessLevel: "write",
3097
+ args: [
3098
+ { name: "key", type: "string", required: true, description: "Key name to delete" }
3099
+ ],
3100
+ examples: ["delete --key theme"]
3101
+ },
3073
3102
  {
3074
3103
  name: "reset",
3075
3104
  description: "Reset the store to its initial context.",
@@ -3126,6 +3155,14 @@ var XStateStoreConnector = class extends AbstractConnector {
3126
3155
  }
3127
3156
  return "OK";
3128
3157
  }
3158
+ case "delete": {
3159
+ const key = String(args.key ?? "");
3160
+ if (!(key in this.context(handle))) return "OK";
3161
+ s.store.send({ type: "delete", key });
3162
+ this._recordHistory(handle, "delete", { key });
3163
+ this.emitChange?.({ path: key, action: "delete", source: "operation" });
3164
+ return "OK";
3165
+ }
3129
3166
  case "reset": {
3130
3167
  s.store.send({ type: "reset" });
3131
3168
  s.history = [];
@@ -5095,6 +5132,12 @@ var ConnectorManager = class {
5095
5132
  tokenMediator;
5096
5133
  preMintedSecrets;
5097
5134
  mgrLog;
5135
+ /** Set once `disconnectAll()` starts, so in-flight deferred mounts bail. */
5136
+ disposed = false;
5137
+ /** Aborted on dispose; threaded into every `connect()` via `ctx.abortSignal`. */
5138
+ disposeController = new AbortController();
5139
+ /** Tracks the background task driving deferred (rclone-backed) mounts. */
5140
+ deferredConnect;
5098
5141
  // ── Private helpers ──────────────────────────────────────────────────────────
5099
5142
  /**
5100
5143
  * Wrap the user-supplied `TokenMediator` so `reason: 'initial'` calls for an
@@ -5199,7 +5242,8 @@ var ConnectorManager = class {
5199
5242
  async buildConnectContext(declaration, connector) {
5200
5243
  const ctx = {
5201
5244
  secrets: this.chain ?? this.secrets,
5202
- tokenMediator: void 0
5245
+ tokenMediator: void 0,
5246
+ abortSignal: this.disposeController.signal
5203
5247
  };
5204
5248
  if (this.chain && connector.describeFields) {
5205
5249
  const fields = connector.describeFields();
@@ -5251,65 +5295,55 @@ var ConnectorManager = class {
5251
5295
  * connector with a `mount` config failed → throws `ConnectorStartupError`
5252
5296
  * (fail-fast lifecycle semantics for mountable connectors).
5253
5297
  *
5298
+ * When `options.deferMounts` is set, connectors whose filesystem face is
5299
+ * marked `deferMount` (rclone-backed FUSE mounts) are NOT awaited here:
5300
+ * their target is pre-registered (so the mount path renders in the system
5301
+ * prompt immediately) and the actual mount is driven on a background task.
5302
+ * The returned report therefore covers only the eagerly-connected set, so a
5303
+ * slow remote can never gate whatever the caller does next (e.g. emitting
5304
+ * `session_init_ack`). Deferred connectors surface their progress through
5305
+ * `onStatusChange` (connecting → connected | error).
5306
+ *
5254
5307
  * @param declarations - Connector declarations from `skaile.yaml`.
5255
5308
  * @param options.failOnReadWriteError - Throw `ConnectorStartupError` if any
5256
5309
  * read-write connector fails. Read-only failures are always tolerated.
5310
+ * Only the eager set is considered — deferred mounts never throw.
5311
+ * @param options.deferMounts - Bring `deferMount` filesystem-face connectors
5312
+ * up in the background instead of awaiting them.
5257
5313
  */
5258
5314
  async connectAll(declarations, options) {
5259
5315
  this.mgrLog.info("connectAll start", { count: declarations.length });
5260
5316
  const results = [];
5261
- for (const decl of declarations) {
5262
- this.opts.onStatusChange?.({
5263
- type: "connector_status",
5264
- connectorId: decl.id,
5265
- status: "connecting"
5266
- });
5267
- try {
5268
- const connector = await this.resolveConnector(decl.driver);
5269
- const connectorLog = createLogger({
5270
- kind: "connector",
5271
- subkind: connector.name ?? decl.driver,
5272
- instance: decl.id
5273
- });
5274
- connector.setLogger?.(connectorLog);
5275
- const ctx = await this.buildConnectContext(decl, connector);
5276
- const handle = await this.withSpan(
5277
- "connector.connect",
5278
- {
5279
- "skaile.connector.id": decl.id,
5280
- "skaile.connector.driver": decl.driver,
5281
- "skaile.connector.operation": "connect"
5282
- },
5283
- () => connector.connect(decl, ctx)
5284
- );
5285
- this.active.set(decl.id, { connector, handle, declaration: decl });
5286
- results.push({
5287
- id: decl.id,
5288
- connected: true,
5289
- mountPath: handle.mountPath,
5290
- access: decl.access
5291
- });
5292
- this.opts.onStatusChange?.({
5293
- type: "connector_status",
5294
- connectorId: decl.id,
5295
- status: "connected"
5296
- });
5297
- } catch (err) {
5298
- const msg = err instanceof Error ? err.message : String(err);
5299
- this.mgrLog.error("connect failed", err, { id: decl.id, driver: decl.driver });
5300
- results.push({
5301
- id: decl.id,
5302
- connected: false,
5303
- error: msg,
5304
- access: decl.access
5305
- });
5306
- this.opts.onStatusChange?.({
5307
- type: "connector_status",
5308
- connectorId: decl.id,
5309
- status: "error",
5310
- error: msg
5311
- });
5317
+ const eager = [];
5318
+ const deferred = [];
5319
+ if (options?.deferMounts) {
5320
+ for (const decl of declarations) {
5321
+ let connector;
5322
+ try {
5323
+ connector = await this.resolveConnector(decl.driver);
5324
+ } catch {
5325
+ }
5326
+ if (connector?.filesystem?.deferMount === true) {
5327
+ deferred.push({ decl, connector });
5328
+ } else {
5329
+ eager.push({ decl, connector });
5330
+ }
5331
+ }
5332
+ } else {
5333
+ for (const decl of declarations) eager.push({ decl });
5334
+ }
5335
+ for (const { decl, connector } of eager) {
5336
+ results.push(await this.attemptConnect(decl, connector));
5337
+ }
5338
+ if (deferred.length > 0) {
5339
+ for (const { decl, connector } of deferred) {
5340
+ this.preRegisterDeferred(decl, connector);
5312
5341
  }
5342
+ const prior = this.deferredConnect;
5343
+ this.deferredConnect = (async () => {
5344
+ if (prior) await prior;
5345
+ await this.connectDeferred(deferred);
5346
+ })();
5313
5347
  }
5314
5348
  const report = { results };
5315
5349
  this.mgrLog.info("connectAll complete", {
@@ -5335,9 +5369,140 @@ var ConnectorManager = class {
5335
5369
  }
5336
5370
  return report;
5337
5371
  }
5372
+ /**
5373
+ * Connect a single declaration, emitting `connecting` → `connected | error`.
5374
+ * Never throws — failures are recorded in the returned result so `connectAll`
5375
+ * can keep going. Shared by the eager loop and the deferred background task.
5376
+ */
5377
+ async attemptConnect(decl, preResolved) {
5378
+ this.opts.onStatusChange?.({
5379
+ type: "connector_status",
5380
+ connectorId: decl.id,
5381
+ status: "connecting"
5382
+ });
5383
+ try {
5384
+ const connector = preResolved ?? await this.resolveConnector(decl.driver);
5385
+ const connectorLog = createLogger({
5386
+ kind: "connector",
5387
+ subkind: connector.name ?? decl.driver,
5388
+ instance: decl.id
5389
+ });
5390
+ connector.setLogger?.(connectorLog);
5391
+ const ctx = await this.buildConnectContext(decl, connector);
5392
+ const handle = await this.withSpan(
5393
+ "connector.connect",
5394
+ {
5395
+ "skaile.connector.id": decl.id,
5396
+ "skaile.connector.driver": decl.driver,
5397
+ "skaile.connector.operation": "connect"
5398
+ },
5399
+ () => connector.connect(decl, ctx)
5400
+ );
5401
+ const previous = this.active.get(decl.id);
5402
+ if (previous?.watchHandle) {
5403
+ try {
5404
+ await previous.watchHandle.close();
5405
+ } catch {
5406
+ }
5407
+ }
5408
+ this.active.set(decl.id, { connector, handle, declaration: decl });
5409
+ this.opts.onStatusChange?.({
5410
+ type: "connector_status",
5411
+ connectorId: decl.id,
5412
+ status: "connected"
5413
+ });
5414
+ return {
5415
+ id: decl.id,
5416
+ connected: true,
5417
+ mountPath: handle.mountPath,
5418
+ access: decl.access
5419
+ };
5420
+ } catch (err) {
5421
+ const msg = err instanceof Error ? err.message : String(err);
5422
+ this.mgrLog.error("connect failed", err, { id: decl.id, driver: decl.driver });
5423
+ this.opts.onStatusChange?.({
5424
+ type: "connector_status",
5425
+ connectorId: decl.id,
5426
+ status: "error",
5427
+ error: msg
5428
+ });
5429
+ return {
5430
+ id: decl.id,
5431
+ connected: false,
5432
+ error: msg,
5433
+ access: decl.access
5434
+ };
5435
+ }
5436
+ }
5437
+ /**
5438
+ * Register a provisional entry for a deferred filesystem-face connector so
5439
+ * its (deterministic) mount path shows up in `listConnectors()` / the system
5440
+ * prompt before the background mount completes. The empty target dir is
5441
+ * created up front so the agent can `ls` it while it is still syncing.
5442
+ */
5443
+ preRegisterDeferred(decl, connector) {
5444
+ const mountTarget = this.resolveMountTarget(decl);
5445
+ if (!existsSync(mountTarget)) mkdirSync(mountTarget, { recursive: true });
5446
+ const handle = {
5447
+ id: decl.id,
5448
+ driver: connector.name ?? decl.driver,
5449
+ access: decl.access,
5450
+ status: "connecting",
5451
+ mountPath: mountTarget,
5452
+ // Empty (not null) so a best-effort disconnect of a still-provisional
5453
+ // entry — e.g. a deferred mount that errored — finds no live handle and
5454
+ // returns cleanly instead of dereferencing null state.
5455
+ state: {}
5456
+ };
5457
+ this.active.set(decl.id, { connector, handle, declaration: decl });
5458
+ }
5459
+ /** Drive all deferred mounts concurrently; resolves when every one settles. */
5460
+ async connectDeferred(deferred) {
5461
+ await Promise.allSettled(
5462
+ deferred.map(({ decl, connector }) => this.connectDeferredOne(decl, connector))
5463
+ );
5464
+ }
5465
+ /**
5466
+ * Background-connect one deferred mount. On success `attemptConnect` swaps the
5467
+ * provisional entry for the real handle; on failure the provisional entry is
5468
+ * marked `error` so the mount path keeps rendering as "still syncing". If the
5469
+ * manager was disposed mid-flight, the connector that came up is torn down
5470
+ * immediately so no rclone process leaks.
5471
+ */
5472
+ async connectDeferredOne(decl, connector) {
5473
+ if (this.disposed) return;
5474
+ const result = await this.attemptConnect(decl, connector);
5475
+ if (!result.connected) {
5476
+ const entry = this.active.get(decl.id);
5477
+ if (entry && entry.handle.status === "connecting") entry.handle.status = "error";
5478
+ return;
5479
+ }
5480
+ if (this.disposed && this.active.has(decl.id)) {
5481
+ await this.disconnect(decl.id).catch(() => {
5482
+ });
5483
+ }
5484
+ }
5485
+ /**
5486
+ * Resolve once every background (deferred) mount started by `connectAll` has
5487
+ * settled. Resolves immediately when none are in flight. Useful for tests and
5488
+ * for callers that want to await full readiness.
5489
+ */
5490
+ async whenDeferredMountsSettled() {
5491
+ if (this.deferredConnect) await this.deferredConnect.catch(() => {
5492
+ });
5493
+ }
5338
5494
  /** Disconnect all connectors and stop all watchers. Best-effort — ignores disconnect errors. */
5339
5495
  async disconnectAll() {
5340
5496
  this.mgrLog.info("disconnectAll start", { count: this.active.size });
5497
+ this.disposed = true;
5498
+ this.disposeController.abort();
5499
+ if (this.deferredConnect) {
5500
+ try {
5501
+ await this.deferredConnect;
5502
+ } catch {
5503
+ }
5504
+ this.deferredConnect = void 0;
5505
+ }
5341
5506
  await this.unwatchAll();
5342
5507
  for (const [id, entry] of this.active) {
5343
5508
  try {
@@ -5490,6 +5655,10 @@ var ConnectorManager = class {
5490
5655
  async runLifecycleHook(hook) {
5491
5656
  const results = [];
5492
5657
  for (const [id, entry] of this.active) {
5658
+ if (entry.handle.status !== "connected") {
5659
+ results.push({ id, ok: true });
5660
+ continue;
5661
+ }
5493
5662
  const fn = entry.connector[hook];
5494
5663
  if (!fn) {
5495
5664
  results.push({ id, ok: true });
@@ -5530,7 +5699,10 @@ var ConnectorManager = class {
5530
5699
  id,
5531
5700
  driver: entry.connector.name,
5532
5701
  access: entry.declaration.access,
5533
- mountPath: entry.handle.mountPath
5702
+ mountPath: entry.handle.mountPath,
5703
+ // Surfaced so wire projections (splitConnectorsForWire) can distinguish a
5704
+ // still-connecting deferred mount from a fully-mounted one.
5705
+ status: entry.handle.status
5534
5706
  }));
5535
5707
  }
5536
5708
  /**
@@ -5572,6 +5744,7 @@ var ConnectorManager = class {
5572
5744
  const results = [];
5573
5745
  for (const [id, entry] of this.active) {
5574
5746
  if (!entry.connector.filesystem) continue;
5747
+ if (entry.handle.status !== "connected") continue;
5575
5748
  try {
5576
5749
  await entry.connector.filesystem.sync(entry.handle, options);
5577
5750
  results.push({ id, ok: true });
@@ -6381,118 +6554,6 @@ async function ensureFleetMounted(dir, log, options) {
6381
6554
  `fleet mount ${dir} not ready after ${timeoutMs}ms \u2014 check SKAILE_FLEET_MANAGED_MOUNTS env var and host bind: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}`
6382
6555
  );
6383
6556
  }
6384
- function globToMatcher(pattern) {
6385
- const segmentMatch = pattern.match(/^\*\*\/([^*/?]+)\/\*\*$/);
6386
- if (segmentMatch) {
6387
- const name = segmentMatch[1];
6388
- return (p) => p.includes(`/${name}/`) || p.endsWith(`/${name}`) || p === name;
6389
- }
6390
- const dotSegment = pattern.match(/^\*\*\/(\.[\w#]+)(?:\/\*\*)?$/);
6391
- if (dotSegment) {
6392
- const name = dotSegment[1];
6393
- return (p) => p.includes(`/${name}/`) || p.endsWith(`/${name}`) || p === name;
6394
- }
6395
- const extMatch = pattern.match(/^\*\*\/\*(\.\w+(?:\.\*)?)/);
6396
- if (extMatch) {
6397
- const suffix = extMatch[1];
6398
- if (suffix.endsWith(".*")) {
6399
- const base = suffix.slice(0, -2);
6400
- return (p) => p.includes(base);
6401
- }
6402
- return (p) => p.endsWith(suffix);
6403
- }
6404
- const suffixMatch = pattern.match(/^\*\*\/\*(.+)$/);
6405
- if (suffixMatch) {
6406
- const sfx = suffixMatch[1];
6407
- return (p) => p.endsWith(sfx);
6408
- }
6409
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\{\{GLOBSTAR\}\}/g, ".*");
6410
- const re = new RegExp(`^${escaped}$`);
6411
- return (p) => re.test(p);
6412
- }
6413
- var DEFAULT_IGNORED = [
6414
- (p) => p.includes("/.git/") || p.endsWith("/.git"),
6415
- (p) => p.includes("/node_modules/") || p.endsWith("/node_modules"),
6416
- (p) => p.includes("/.connectors/") || p.endsWith("/.connectors"),
6417
- // Atomic-write tempfiles (Bun.write creates `<name>.tmp.<pid>.<ts>` then renames).
6418
- // These vanish between readdir and fs.watch, causing EINVAL that crashes the process.
6419
- (p) => /\.tmp\.\d/.test(p),
6420
- // Editor swap/backup files that also race with save operations.
6421
- (p) => p.endsWith(".swp"),
6422
- (p) => p.endsWith(".swx"),
6423
- (p) => p.includes("/.#"),
6424
- (p) => p.endsWith("~")
6425
- ];
6426
- var DEFAULT_DEBOUNCE_MS = 150;
6427
- function parseGitignore(rootDir) {
6428
- const gitignorePath = join(rootDir, ".gitignore");
6429
- if (!existsSync(gitignorePath)) return [];
6430
- try {
6431
- const content = readFileSync(gitignorePath, "utf-8");
6432
- const matchers = [];
6433
- for (const raw of content.split("\n")) {
6434
- const line = raw.trim();
6435
- if (!line || line.startsWith("#") || line.startsWith("!")) continue;
6436
- let pattern = line;
6437
- if (pattern.startsWith("/")) {
6438
- pattern = pattern.slice(1);
6439
- } else if (!pattern.includes("/")) {
6440
- pattern = `**/${pattern}`;
6441
- }
6442
- if (pattern.endsWith("/")) {
6443
- matchers.push(globToMatcher(`${pattern}**`));
6444
- } else {
6445
- matchers.push(globToMatcher(pattern));
6446
- if (!pattern.includes("*") && !pattern.includes(".")) {
6447
- matchers.push(globToMatcher(`${pattern}/**`));
6448
- }
6449
- }
6450
- }
6451
- return matchers;
6452
- } catch {
6453
- return [];
6454
- }
6455
- }
6456
- var CHOKIDAR_EVENT_MAP = {
6457
- add: "create",
6458
- change: "edit",
6459
- unlink: "delete",
6460
- addDir: "create",
6461
- unlinkDir: "delete"
6462
- };
6463
- async function createFsWatcher(rootDir, callback, options) {
6464
- const log = createLogger({ kind: "connector", subkind: "watcher", instance: rootDir });
6465
- const gitignorePatterns = parseGitignore(rootDir);
6466
- const optionsIgnored = (options?.ignored ?? []).map(
6467
- (entry) => typeof entry === "string" ? globToMatcher(entry) : entry
6468
- );
6469
- const ignored = [...DEFAULT_IGNORED, ...gitignorePatterns, ...optionsIgnored];
6470
- const debounceMs = options?.debounceMs ?? DEFAULT_DEBOUNCE_MS;
6471
- const watcher = chokidar.watch(rootDir, {
6472
- ignoreInitial: true,
6473
- ignored,
6474
- awaitWriteFinish: { stabilityThreshold: debounceMs, pollInterval: 50 },
6475
- usePolling: false
6476
- });
6477
- watcher.on("error", (err) => {
6478
- const message = err instanceof Error ? err.message : String(err);
6479
- log.warn("watcher error (non-fatal)", { message });
6480
- });
6481
- for (const [chokidarEvent, action] of Object.entries(CHOKIDAR_EVENT_MAP)) {
6482
- watcher.on(
6483
- chokidarEvent,
6484
- (fullPath) => {
6485
- const rel = relative(rootDir, fullPath);
6486
- if (!rel || rel.startsWith("..")) return;
6487
- const normalizedPath = rel.split("\\").join("/");
6488
- callback({ path: normalizedPath, action, source: "filesystem" });
6489
- }
6490
- );
6491
- }
6492
- return {
6493
- close: () => watcher.close()
6494
- };
6495
- }
6496
6557
  var WorktreeRegistryImpl = class {
6497
6558
  entries = /* @__PURE__ */ new Map();
6498
6559
  register(path, owner) {
@@ -7491,6 +7552,6 @@ function createConnector() {
7491
7552
  return new FlowAdapter();
7492
7553
  }
7493
7554
 
7494
- export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PostgresConnector, S3Connector, SQLiteConnector, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, parseAuthBlob2 as parseAuthBlob, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
7495
- //# sourceMappingURL=chunk-OJN25VJO.js.map
7496
- //# sourceMappingURL=chunk-OJN25VJO.js.map
7555
+ export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PostgresConnector, S3Connector, SQLiteConnector, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createWorktree, defaultSpawn, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, parseAuthBlob2 as parseAuthBlob, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
7556
+ //# sourceMappingURL=chunk-HIIARTRZ.js.map
7557
+ //# sourceMappingURL=chunk-HIIARTRZ.js.map