@skaile/workspaces 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/dist/{asset-feeds-DX5JPM76.js → asset-feeds-PCLTE2W5.js} +20 -12
- package/dist/asset-feeds-PCLTE2W5.js.map +1 -0
- package/dist/asset-manager/index.js +8 -8
- package/dist/asset-manager/installer.js +7 -7
- package/dist/{chunk-PKPUVQJP.js → chunk-2Q6OY3SU.js} +3 -3
- package/dist/{chunk-PKPUVQJP.js.map → chunk-2Q6OY3SU.js.map} +1 -1
- package/dist/{chunk-LRFZNLB2.js → chunk-52HINMW4.js} +3 -3
- package/dist/{chunk-LRFZNLB2.js.map → chunk-52HINMW4.js.map} +1 -1
- package/dist/{chunk-AZODLVKH.js → chunk-56YT6CWS.js} +5 -5
- package/dist/{chunk-AZODLVKH.js.map → chunk-56YT6CWS.js.map} +1 -1
- package/dist/{chunk-RQG5TXIR.js → chunk-5DPQ766L.js} +4 -4
- package/dist/{chunk-RQG5TXIR.js.map → chunk-5DPQ766L.js.map} +1 -1
- package/dist/{chunk-BUMDC3R2.js → chunk-6422UOMQ.js} +5 -5
- package/dist/{chunk-BUMDC3R2.js.map → chunk-6422UOMQ.js.map} +1 -1
- package/dist/{chunk-XQRAXAL5.js → chunk-7B3CL5JT.js} +7 -7
- package/dist/{chunk-XQRAXAL5.js.map → chunk-7B3CL5JT.js.map} +1 -1
- package/dist/{chunk-LFDZYCEO.js → chunk-BM7XANVN.js} +2 -2
- package/dist/chunk-BM7XANVN.js.map +1 -0
- package/dist/{chunk-QUWXW2N3.js → chunk-EOCKR5TN.js} +3 -3
- package/dist/{chunk-QUWXW2N3.js.map → chunk-EOCKR5TN.js.map} +1 -1
- package/dist/{chunk-EJZFQCQG.js → chunk-I3TERDTR.js} +2 -2
- package/dist/{chunk-EJZFQCQG.js.map → chunk-I3TERDTR.js.map} +1 -1
- package/dist/{chunk-N74UQJPN.js → chunk-KYC6JH75.js} +3 -3
- package/dist/{chunk-N74UQJPN.js.map → chunk-KYC6JH75.js.map} +1 -1
- package/dist/{chunk-4AGIEU7V.js → chunk-MAPRKFBD.js} +3 -3
- package/dist/{chunk-4AGIEU7V.js.map → chunk-MAPRKFBD.js.map} +1 -1
- package/dist/{chunk-362QBS57.js → chunk-NAMD4AIK.js} +235 -13
- package/dist/chunk-NAMD4AIK.js.map +1 -0
- package/dist/{chunk-KKERAIPQ.js → chunk-O2OHGE4S.js} +4 -4
- package/dist/{chunk-KKERAIPQ.js.map → chunk-O2OHGE4S.js.map} +1 -1
- package/dist/{chunk-7MANNJ3U.js → chunk-O4PANZI2.js} +5 -5
- package/dist/{chunk-7MANNJ3U.js.map → chunk-O4PANZI2.js.map} +1 -1
- package/dist/{chunk-H5YXL4KI.js → chunk-SAXDHVJZ.js} +3 -3
- package/dist/{chunk-H5YXL4KI.js.map → chunk-SAXDHVJZ.js.map} +1 -1
- package/dist/{chunk-R2I7RHT7.js → chunk-SKVUDDHE.js} +191 -23
- package/dist/chunk-SKVUDDHE.js.map +1 -0
- package/dist/{chunk-PWXN2KHV.js → chunk-UAYAK7N4.js} +5 -5
- package/dist/{chunk-PWXN2KHV.js.map → chunk-UAYAK7N4.js.map} +1 -1
- package/dist/{chunk-PTDVTMQL.js → chunk-WEQN6EWW.js} +4 -4
- package/dist/{chunk-PTDVTMQL.js.map → chunk-WEQN6EWW.js.map} +1 -1
- package/dist/{chunk-VXW2K4YH.js → chunk-XOBPOVFT.js} +3 -3
- package/dist/{chunk-VXW2K4YH.js.map → chunk-XOBPOVFT.js.map} +1 -1
- package/dist/{chunk-4ZVS6KV4.js → chunk-ZGJE4ZDP.js} +2 -2
- package/dist/{chunk-4ZVS6KV4.js.map → chunk-ZGJE4ZDP.js.map} +1 -1
- package/dist/cli/index.js +196 -101
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/asset-feeds.d.ts +8 -1
- package/dist/cli/src/asset-feeds.d.ts.map +1 -1
- package/dist/cli/src/commands/manage.d.ts +33 -22
- package/dist/cli/src/commands/manage.d.ts.map +1 -1
- package/dist/connectors/config.js +7 -7
- package/dist/connectors/index.js +8 -8
- package/dist/connectors/src/connector-manager.d.ts +56 -3
- package/dist/connectors/src/connector-manager.d.ts.map +1 -1
- package/dist/connectors/src/fleet-health-gate.d.ts +86 -0
- package/dist/connectors/src/fleet-health-gate.d.ts.map +1 -0
- package/dist/connectors/src/fleet-utils.d.ts +16 -0
- package/dist/connectors/src/fleet-utils.d.ts.map +1 -1
- package/dist/connectors/src/index.d.ts +4 -1
- package/dist/connectors/src/index.d.ts.map +1 -1
- package/dist/connectors/src/mount-placeholder.d.ts +8 -0
- package/dist/connectors/src/mount-placeholder.d.ts.map +1 -1
- package/dist/core/index.js +6 -6
- package/dist/core/manifest.js +2 -2
- package/dist/core/models.js +1 -1
- package/dist/core/publish-manifest.js +2 -2
- package/dist/core/runtime-assets.js +5 -5
- package/dist/core/src/index.d.ts +1 -1
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/models.d.ts +28 -0
- package/dist/core/src/models.d.ts.map +1 -1
- package/dist/core/src/workspace-config.d.ts +13 -0
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/workspace-config.js +4 -4
- package/dist/deploy/index.js +6 -6
- package/dist/discovery/index.js +4 -4
- package/dist/{ensure-sources-O6RMWP6C.js → ensure-sources-HMMEQEZC.js} +11 -11
- package/dist/{ensure-sources-O6RMWP6C.js.map → ensure-sources-HMMEQEZC.js.map} +1 -1
- package/dist/factory-assets/connectors/deploy.js +8 -8
- package/dist/factory-assets/connectors/devserver.js +8 -8
- package/dist/factory-assets/connectors/flow/adapter.js +8 -8
- package/dist/factory-assets/connectors/flow/engine.js +6 -6
- package/dist/factory-assets/connectors/flow/run-flow.js +9 -9
- package/dist/factory-assets/connectors/flow.js +8 -8
- package/dist/factory-assets/connectors/git.js +8 -8
- package/dist/factory-assets/connectors/gmail.js +8 -8
- package/dist/factory-assets/connectors/googledrive.js +8 -8
- package/dist/factory-assets/connectors/local.js +8 -8
- package/dist/factory-assets/connectors/mattermost.js +8 -8
- package/dist/factory-assets/connectors/memory.js +8 -8
- package/dist/factory-assets/connectors/minio.js +8 -8
- package/dist/factory-assets/connectors/postgres.js +8 -8
- package/dist/factory-assets/connectors/s3.js +8 -8
- package/dist/factory-assets/connectors/sharepoint.js +8 -8
- package/dist/factory-assets/connectors/sqlite.js +8 -8
- package/dist/factory-assets/connectors/static-server.js +8 -8
- package/dist/factory-assets/connectors/tunnel.js +8 -8
- package/dist/factory-assets/connectors/webdav.js +8 -8
- package/dist/factory-assets/connectors/xstate-store.js +8 -8
- package/dist/factory-assets/connectors/xstate.js +8 -8
- package/dist/{flows-QWYIUDA2.js → flows-O7OXGFIY.js} +3 -3
- package/dist/{flows-QWYIUDA2.js.map → flows-O7OXGFIY.js.map} +1 -1
- package/dist/library/index.js +9 -9
- package/dist/library/install.js +7 -7
- package/dist/open-library-5WWTBHQD.js +23 -0
- package/dist/{open-library-B7BLYXYK.js.map → open-library-5WWTBHQD.js.map} +1 -1
- package/dist/paths-UC3ZHLKC.js +15 -0
- package/dist/{paths-5IUR235S.js.map → paths-UC3ZHLKC.js.map} +1 -1
- package/dist/{plugin-store-WWOJXWG7.js → plugin-store-R2WLXRT3.js} +8 -8
- package/dist/{plugin-store-WWOJXWG7.js.map → plugin-store-R2WLXRT3.js.map} +1 -1
- package/dist/runner/index.js +10 -10
- package/dist/runner/src/external-mcp.d.ts +45 -4
- package/dist/runner/src/external-mcp.d.ts.map +1 -1
- package/dist/runner/src/fleet-health-flag-dispatcher.d.ts +40 -0
- package/dist/runner/src/fleet-health-flag-dispatcher.d.ts.map +1 -0
- package/dist/runner/src/resources.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +8 -8
- package/dist/sdk/core.js +6 -6
- package/dist/sdk/flow.js +6 -6
- package/dist/sdk/index.js +10 -10
- package/dist/sdk/runner.js +10 -10
- package/dist/{setup-2RD6EDBZ.js → setup-BSDHVMR6.js} +8 -8
- package/dist/{setup-2RD6EDBZ.js.map → setup-BSDHVMR6.js.map} +1 -1
- package/dist/store-client-4GKV4LOD.js +15 -0
- package/dist/{store-client-XQQRHYDW.js.map → store-client-4GKV4LOD.js.map} +1 -1
- package/dist/tui/index.js +10 -10
- package/dist/workspace-plugin/adapters/mcp.js +1 -1
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/asset-feeds-DX5JPM76.js.map +0 -1
- package/dist/chunk-362QBS57.js.map +0 -1
- package/dist/chunk-LFDZYCEO.js.map +0 -1
- package/dist/chunk-R2I7RHT7.js.map +0 -1
- package/dist/open-library-B7BLYXYK.js +0 -23
- package/dist/paths-5IUR235S.js +0 -15
- package/dist/store-client-XQQRHYDW.js +0 -15
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { renderGoogleDriveConfig, renderOneDriveConfig, renderWebDAVConfig } from './chunk-OVRSNIKJ.js';
|
|
2
|
-
import { resolveAuthRef, resolveAuth, resolveMountCredentialString, resolveMountCredential } from './chunk-
|
|
2
|
+
import { resolveAuthRef, resolveAuth, resolveMountCredentialString, resolveMountCredential } from './chunk-52HINMW4.js';
|
|
3
3
|
import { PortPool, createFsWatcher, RcloneProcessManager, ensureDirMode } from './chunk-XT6AZWK3.js';
|
|
4
4
|
import { pluginRegistry } from './chunk-6E6PKKAD.js';
|
|
5
5
|
import { computeFlowStateFromSnapshots } from './chunk-X46FY45B.js';
|
|
6
6
|
import { renderStimulusPrompt } from './chunk-DEYYRFQU.js';
|
|
7
7
|
import { computeStimulus } from './chunk-IFGTRL2I.js';
|
|
8
|
-
import { portableSpawn } from './chunk-
|
|
8
|
+
import { portableSpawn } from './chunk-2Q6OY3SU.js';
|
|
9
9
|
import { createLogger } from './chunk-JB35MVBN.js';
|
|
10
10
|
import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, cpSync, statSync, createReadStream, readdirSync, openSync, writeSync, closeSync, chmodSync } from 'fs';
|
|
11
11
|
import path, { join, dirname, resolve, sep } from 'path';
|
|
@@ -14,7 +14,7 @@ import { execFile, execSync, spawn } from 'child_process';
|
|
|
14
14
|
import { createServer } from 'http';
|
|
15
15
|
import mime from 'mime';
|
|
16
16
|
import { promisify } from 'util';
|
|
17
|
-
import { stat, readdir,
|
|
17
|
+
import { stat, readdir, writeFile, chmod, rm, mkdir } from 'fs/promises';
|
|
18
18
|
import { createRequire } from 'module';
|
|
19
19
|
import { pathToFileURL } from 'url';
|
|
20
20
|
|
|
@@ -5273,6 +5273,116 @@ var ConnectorStartupError = class extends Error {
|
|
|
5273
5273
|
this.failedReadWrite = failedReadWrite;
|
|
5274
5274
|
}
|
|
5275
5275
|
};
|
|
5276
|
+
|
|
5277
|
+
// connectors/src/fleet-health-gate.ts
|
|
5278
|
+
var FleetHealthGate = class {
|
|
5279
|
+
constructor(target, barrier, log, opts) {
|
|
5280
|
+
this.target = target;
|
|
5281
|
+
this.barrier = barrier;
|
|
5282
|
+
this.log = log;
|
|
5283
|
+
this.unhealthyThreshold = Math.max(1, opts?.unhealthyThreshold ?? 2);
|
|
5284
|
+
this.healthyThreshold = Math.max(1, opts?.healthyThreshold ?? 2);
|
|
5285
|
+
}
|
|
5286
|
+
target;
|
|
5287
|
+
barrier;
|
|
5288
|
+
log;
|
|
5289
|
+
unhealthyThreshold;
|
|
5290
|
+
healthyThreshold;
|
|
5291
|
+
/** True while the read-only barrier is established over the target. */
|
|
5292
|
+
gated = false;
|
|
5293
|
+
/**
|
|
5294
|
+
* The gate state that the most recently *enqueued* transition will settle on.
|
|
5295
|
+
* Hysteresis is measured against this, not `gated`, so that verdicts arriving
|
|
5296
|
+
* while a (slow, async) establish/remove is still in flight are counted toward
|
|
5297
|
+
* the post-transition state rather than the stale pre-transition one. Without
|
|
5298
|
+
* this, fast healthy verdicts arriving during a slow `establish()` could meet
|
|
5299
|
+
* `healthyThreshold` before the gate finished tripping, ungating it on the
|
|
5300
|
+
* first healthy verdict afterwards and defeating the threshold.
|
|
5301
|
+
*/
|
|
5302
|
+
pendingGated = false;
|
|
5303
|
+
/** Consecutive same-verdict run length, signed by the *current* run's verdict. */
|
|
5304
|
+
unhealthyRun = 0;
|
|
5305
|
+
healthyRun = 0;
|
|
5306
|
+
/** Serialises establish/remove so overlapping observe() calls can't race. */
|
|
5307
|
+
transition = Promise.resolve();
|
|
5308
|
+
/** True while the read-only barrier is established (test/observability hook). */
|
|
5309
|
+
get isGated() {
|
|
5310
|
+
return this.gated;
|
|
5311
|
+
}
|
|
5312
|
+
/**
|
|
5313
|
+
* Feed one health verdict. Applies hysteresis, then establishes or removes the
|
|
5314
|
+
* barrier on a state change. Resolves once the (possibly no-op) transition is
|
|
5315
|
+
* applied. Never throws — a failed establish is treated as gated (fail-closed).
|
|
5316
|
+
*/
|
|
5317
|
+
observe(verdict) {
|
|
5318
|
+
if (verdict === "healthy") {
|
|
5319
|
+
this.healthyRun += 1;
|
|
5320
|
+
this.unhealthyRun = 0;
|
|
5321
|
+
if (this.pendingGated && this.healthyRun >= this.healthyThreshold) {
|
|
5322
|
+
this.pendingGated = false;
|
|
5323
|
+
this.healthyRun = 0;
|
|
5324
|
+
return this.enqueue(() => this.ungate());
|
|
5325
|
+
}
|
|
5326
|
+
} else {
|
|
5327
|
+
this.unhealthyRun += 1;
|
|
5328
|
+
this.healthyRun = 0;
|
|
5329
|
+
if (!this.pendingGated && this.unhealthyRun >= this.unhealthyThreshold) {
|
|
5330
|
+
this.pendingGated = true;
|
|
5331
|
+
this.unhealthyRun = 0;
|
|
5332
|
+
return this.enqueue(() => this.gate());
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
return this.transition;
|
|
5336
|
+
}
|
|
5337
|
+
/**
|
|
5338
|
+
* Tear the gate down. If currently gated, removes the barrier so a leftover
|
|
5339
|
+
* read-only tmpfs is not leaked. Call only after the connector above the target
|
|
5340
|
+
* is disconnected (top-down, no leaked mount).
|
|
5341
|
+
*/
|
|
5342
|
+
dispose() {
|
|
5343
|
+
if (!this.gated && !this.pendingGated) return this.transition;
|
|
5344
|
+
this.pendingGated = false;
|
|
5345
|
+
return this.enqueue(() => this.ungate());
|
|
5346
|
+
}
|
|
5347
|
+
enqueue(step) {
|
|
5348
|
+
this.transition = this.transition.then(step, step);
|
|
5349
|
+
return this.transition;
|
|
5350
|
+
}
|
|
5351
|
+
async gate() {
|
|
5352
|
+
if (this.gated) {
|
|
5353
|
+
this.pendingGated = true;
|
|
5354
|
+
return;
|
|
5355
|
+
}
|
|
5356
|
+
try {
|
|
5357
|
+
await this.barrier.establish(this.target, this.log);
|
|
5358
|
+
this.gated = true;
|
|
5359
|
+
this.log?.warn("fleet mount unhealthy \u2014 gated read-only", { target: this.target });
|
|
5360
|
+
} catch (err) {
|
|
5361
|
+
this.gated = true;
|
|
5362
|
+
this.log?.error(
|
|
5363
|
+
"fleet read-only gate establish threw \u2014 treating as gated (fail-closed)",
|
|
5364
|
+
err,
|
|
5365
|
+
{
|
|
5366
|
+
target: this.target
|
|
5367
|
+
}
|
|
5368
|
+
);
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
async ungate() {
|
|
5372
|
+
if (!this.gated) return;
|
|
5373
|
+
try {
|
|
5374
|
+
await this.barrier.remove(this.target, this.log);
|
|
5375
|
+
} catch (err) {
|
|
5376
|
+
this.pendingGated = true;
|
|
5377
|
+
this.log?.error("fleet read-only gate remove failed \u2014 staying gated", err, {
|
|
5378
|
+
target: this.target
|
|
5379
|
+
});
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
this.gated = false;
|
|
5383
|
+
this.log?.info("fleet mount recovered \u2014 gate lifted, writable again", { target: this.target });
|
|
5384
|
+
}
|
|
5385
|
+
};
|
|
5276
5386
|
function isFleetManaged(connectorId) {
|
|
5277
5387
|
const raw = process.env.SKAILE_FLEET_MANAGED_MOUNTS ?? "";
|
|
5278
5388
|
if (raw.length === 0) return false;
|
|
@@ -5301,7 +5411,21 @@ async function ensureFleetMounted(dir, log, options) {
|
|
|
5301
5411
|
`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)}`
|
|
5302
5412
|
);
|
|
5303
5413
|
}
|
|
5414
|
+
async function probeFleetHealth(dir) {
|
|
5415
|
+
try {
|
|
5416
|
+
const st = await stat(dir);
|
|
5417
|
+
if (!st.isDirectory()) return "error";
|
|
5418
|
+
await readdir(dir);
|
|
5419
|
+
return "healthy";
|
|
5420
|
+
} catch {
|
|
5421
|
+
return "error";
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5304
5424
|
var NOT_READY_SENTINEL = ".skaile-workspace-not-ready";
|
|
5425
|
+
var TARGET_ALREADY_PRESENT_CODES = /* @__PURE__ */ new Set(["EEXIST", "ENOTCONN", "EBUSY", "ENOTDIR"]);
|
|
5426
|
+
function errnoCode(err) {
|
|
5427
|
+
return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
5428
|
+
}
|
|
5305
5429
|
var SENTINEL_BODY = [
|
|
5306
5430
|
"This workspace is backed by a remote mount that is not live yet.",
|
|
5307
5431
|
"It is mounted read-only on purpose: writing here would create a phantom",
|
|
@@ -5309,6 +5433,9 @@ var SENTINEL_BODY = [
|
|
|
5309
5433
|
"Wait for the mount to finish syncing (watch connector_status) before writing.",
|
|
5310
5434
|
""
|
|
5311
5435
|
].join("\n");
|
|
5436
|
+
var defaultMkdir = async (target) => {
|
|
5437
|
+
await mkdir(target, { recursive: true });
|
|
5438
|
+
};
|
|
5312
5439
|
var defaultRunner = (cmd, args) => new Promise((resolve5, reject) => {
|
|
5313
5440
|
const proc = spawn(cmd, args, { stdio: "ignore" });
|
|
5314
5441
|
proc.once("error", reject);
|
|
@@ -5319,13 +5446,27 @@ var defaultRunner = (cmd, args) => new Promise((resolve5, reject) => {
|
|
|
5319
5446
|
});
|
|
5320
5447
|
var RoMountPlaceholderBarrier = class {
|
|
5321
5448
|
run;
|
|
5449
|
+
mkdirFn;
|
|
5322
5450
|
modes = /* @__PURE__ */ new Map();
|
|
5323
5451
|
constructor(opts) {
|
|
5324
5452
|
this.run = opts?.run ?? defaultRunner;
|
|
5453
|
+
this.mkdirFn = opts?.mkdirFn ?? defaultMkdir;
|
|
5325
5454
|
}
|
|
5326
5455
|
async establish(target, log) {
|
|
5327
5456
|
if (this.modes.has(target)) return;
|
|
5328
|
-
|
|
5457
|
+
try {
|
|
5458
|
+
await this.mkdirFn(target);
|
|
5459
|
+
} catch (err) {
|
|
5460
|
+
const code = errnoCode(err);
|
|
5461
|
+
if (!code || !TARGET_ALREADY_PRESENT_CODES.has(code)) {
|
|
5462
|
+
await this.chmodFallback(target, err, log);
|
|
5463
|
+
return;
|
|
5464
|
+
}
|
|
5465
|
+
log?.debug("barrier target already present (live/broken mount) \u2014 protecting in place", {
|
|
5466
|
+
target,
|
|
5467
|
+
code
|
|
5468
|
+
});
|
|
5469
|
+
}
|
|
5329
5470
|
let tmpfsMounted = false;
|
|
5330
5471
|
try {
|
|
5331
5472
|
await this.run("mount", [
|
|
@@ -5427,6 +5568,12 @@ var defaultMountPlaceholderBarrier = new RoMountPlaceholderBarrier();
|
|
|
5427
5568
|
|
|
5428
5569
|
// connectors/src/connector-manager.ts
|
|
5429
5570
|
var KNOWN_PREFIXES = ["env:", "forge:", "op:", "kp:", "oauth:"];
|
|
5571
|
+
var DEFAULT_FLEET_HEALTH_POLL_MS = 5e3;
|
|
5572
|
+
function parseFleetHealthPollMs(raw = process.env.SKAILE_FLEET_HEALTH_POLL_MS) {
|
|
5573
|
+
if (raw === void 0) return DEFAULT_FLEET_HEALTH_POLL_MS;
|
|
5574
|
+
const n = Number(raw);
|
|
5575
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_FLEET_HEALTH_POLL_MS;
|
|
5576
|
+
}
|
|
5430
5577
|
function hasKnownPrefix(ref) {
|
|
5431
5578
|
return KNOWN_PREFIXES.some((p) => ref.startsWith(p));
|
|
5432
5579
|
}
|
|
@@ -5527,6 +5674,7 @@ var ConnectorManager = class {
|
|
|
5527
5674
|
this.tokenMediator = opts.tokenMediator;
|
|
5528
5675
|
this.preMintedSecrets = opts.preMintedSecrets;
|
|
5529
5676
|
this.placeholderBarrier = opts.placeholderBarrier ?? defaultMountPlaceholderBarrier;
|
|
5677
|
+
this.fleetHealthPollMs = opts.fleetHealthPollMs ?? parseFleetHealthPollMs();
|
|
5530
5678
|
const secrets = opts.secrets;
|
|
5531
5679
|
if (secrets && typeof secrets === "object" && "prepareRef" in secrets) {
|
|
5532
5680
|
this.chain = secrets;
|
|
@@ -5555,6 +5703,10 @@ var ConnectorManager = class {
|
|
|
5555
5703
|
disposeController = new AbortController();
|
|
5556
5704
|
/** Tracks the background task driving deferred (rclone-backed) mounts. */
|
|
5557
5705
|
deferredConnect;
|
|
5706
|
+
/** Periodic local fleet-health probe; runs only while a fleet gate is active. */
|
|
5707
|
+
fleetHealthTimer;
|
|
5708
|
+
/** Local fleet-health poll interval (ms). 0 disables the local probe. */
|
|
5709
|
+
fleetHealthPollMs;
|
|
5558
5710
|
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
5559
5711
|
/**
|
|
5560
5712
|
* Wrap the user-supplied `TokenMediator` so `reason: 'initial'` calls for an
|
|
@@ -5826,7 +5978,9 @@ var ConnectorManager = class {
|
|
|
5826
5978
|
connector,
|
|
5827
5979
|
handle,
|
|
5828
5980
|
declaration: decl,
|
|
5829
|
-
placeholderTarget: previous?.placeholderTarget
|
|
5981
|
+
placeholderTarget: previous?.placeholderTarget,
|
|
5982
|
+
fleetHealthGate: previous?.fleetHealthGate,
|
|
5983
|
+
fleetHealthTarget: previous?.fleetHealthTarget
|
|
5830
5984
|
});
|
|
5831
5985
|
this.opts.onStatusChange?.({
|
|
5832
5986
|
type: "connector_status",
|
|
@@ -5862,16 +6016,25 @@ var ConnectorManager = class {
|
|
|
5862
6016
|
* prompt before the background mount completes.
|
|
5863
6017
|
*
|
|
5864
6018
|
* Before exposing the target, a read-only barrier is established so the agent
|
|
5865
|
-
* cannot write a phantom workspace into a not-yet-mounted remote.
|
|
5866
|
-
*
|
|
5867
|
-
*
|
|
6019
|
+
* cannot write a phantom workspace into a not-yet-mounted remote.
|
|
6020
|
+
*
|
|
6021
|
+
* Fleet-managed mounts skip the *static* boot-time barrier (the host already
|
|
6022
|
+
* bound the live share at the target — a static barrier would shadow real
|
|
6023
|
+
* content) but instead get a *dynamic* health gate: while the host fleet rclone
|
|
6024
|
+
* is healthy the target stays writable exactly as before; when it wedges, the
|
|
6025
|
+
* gate establishes the same read-only barrier until the mount recovers.
|
|
5868
6026
|
*/
|
|
5869
6027
|
async preRegisterDeferred(decl, connector) {
|
|
5870
6028
|
const mountTarget = this.resolveMountTarget(decl);
|
|
5871
6029
|
if (!existsSync(mountTarget)) mkdirSync(mountTarget, { recursive: true });
|
|
5872
6030
|
const fleetManaged = isFleetManaged(decl.id);
|
|
5873
6031
|
let placeholderTarget;
|
|
5874
|
-
|
|
6032
|
+
let fleetHealthGate;
|
|
6033
|
+
let fleetHealthTarget;
|
|
6034
|
+
if (fleetManaged) {
|
|
6035
|
+
fleetHealthGate = new FleetHealthGate(mountTarget, this.placeholderBarrier, this.mgrLog);
|
|
6036
|
+
fleetHealthTarget = mountTarget;
|
|
6037
|
+
} else {
|
|
5875
6038
|
try {
|
|
5876
6039
|
await this.placeholderBarrier.establish(mountTarget, this.mgrLog);
|
|
5877
6040
|
placeholderTarget = mountTarget;
|
|
@@ -5890,7 +6053,58 @@ var ConnectorManager = class {
|
|
|
5890
6053
|
// returns cleanly instead of dereferencing null state.
|
|
5891
6054
|
state: {}
|
|
5892
6055
|
};
|
|
5893
|
-
this.active.set(decl.id, {
|
|
6056
|
+
this.active.set(decl.id, {
|
|
6057
|
+
connector,
|
|
6058
|
+
handle,
|
|
6059
|
+
declaration: decl,
|
|
6060
|
+
placeholderTarget,
|
|
6061
|
+
fleetHealthGate,
|
|
6062
|
+
fleetHealthTarget
|
|
6063
|
+
});
|
|
6064
|
+
if (fleetHealthGate) this.ensureFleetHealthPoll();
|
|
6065
|
+
}
|
|
6066
|
+
/**
|
|
6067
|
+
* Start the periodic local fleet-health probe if it is not already running and
|
|
6068
|
+
* the poll interval is enabled. Each tick `stat`+`readdir`s every fleet target
|
|
6069
|
+
* and feeds the verdict to its gate (hysteresis lives in the gate). The probe
|
|
6070
|
+
* only catches directly-detectable failures (EIO / dead bind); silent
|
|
6071
|
+
* degradation is covered by the platform signal via `reportFleetHealth`.
|
|
6072
|
+
*/
|
|
6073
|
+
ensureFleetHealthPoll() {
|
|
6074
|
+
if (this.fleetHealthTimer || this.fleetHealthPollMs <= 0 || this.disposed) return;
|
|
6075
|
+
this.fleetHealthTimer = setInterval(() => {
|
|
6076
|
+
void this.runFleetHealthProbe();
|
|
6077
|
+
}, this.fleetHealthPollMs);
|
|
6078
|
+
this.fleetHealthTimer.unref?.();
|
|
6079
|
+
}
|
|
6080
|
+
async runFleetHealthProbe() {
|
|
6081
|
+
if (this.disposed) return;
|
|
6082
|
+
const gates = [...this.active.values()].filter(
|
|
6083
|
+
(e) => e.fleetHealthGate != null && e.fleetHealthTarget != null
|
|
6084
|
+
);
|
|
6085
|
+
await Promise.all(
|
|
6086
|
+
gates.map(async (e) => {
|
|
6087
|
+
if (e.fleetHealthGate.isGated) return;
|
|
6088
|
+
const verdict = await probeFleetHealth(e.fleetHealthTarget);
|
|
6089
|
+
await e.fleetHealthGate.observe(verdict).catch(() => {
|
|
6090
|
+
});
|
|
6091
|
+
})
|
|
6092
|
+
);
|
|
6093
|
+
}
|
|
6094
|
+
/**
|
|
6095
|
+
* Feed an externally-derived fleet-health verdict for a connector into its gate.
|
|
6096
|
+
*
|
|
6097
|
+
* This is the consume side of the platform fleet-health signal: the host fleet
|
|
6098
|
+
* health poll knows `core/stats.errors` and can detect a *silently-empty-but-
|
|
6099
|
+
* writable* degraded mount that the local probe cannot. The runner watches the
|
|
6100
|
+
* `<workspace>/.skaile/fleet-health/<connectorId>` flag and forwards its verdict
|
|
6101
|
+
* here. Unknown / non-fleet ids are ignored.
|
|
6102
|
+
*/
|
|
6103
|
+
async reportFleetHealth(connectorId, verdict) {
|
|
6104
|
+
const entry = this.active.get(connectorId);
|
|
6105
|
+
if (!entry?.fleetHealthGate) return;
|
|
6106
|
+
await entry.fleetHealthGate.observe(verdict).catch(() => {
|
|
6107
|
+
});
|
|
5894
6108
|
}
|
|
5895
6109
|
/** Drive all deferred mounts concurrently; resolves when every one settles. */
|
|
5896
6110
|
async connectDeferred(deferred) {
|
|
@@ -5937,6 +6151,10 @@ var ConnectorManager = class {
|
|
|
5937
6151
|
this.mgrLog.info("disconnectAll start", { count: this.active.size });
|
|
5938
6152
|
this.disposed = true;
|
|
5939
6153
|
this.disposeController.abort();
|
|
6154
|
+
if (this.fleetHealthTimer) {
|
|
6155
|
+
clearInterval(this.fleetHealthTimer);
|
|
6156
|
+
this.fleetHealthTimer = void 0;
|
|
6157
|
+
}
|
|
5940
6158
|
if (this.deferredConnect) {
|
|
5941
6159
|
try {
|
|
5942
6160
|
await this.deferredConnect;
|
|
@@ -5954,6 +6172,8 @@ var ConnectorManager = class {
|
|
|
5954
6172
|
await this.placeholderBarrier.remove(entry.placeholderTarget, this.mgrLog).catch(() => {
|
|
5955
6173
|
});
|
|
5956
6174
|
}
|
|
6175
|
+
if (entry.fleetHealthGate) await entry.fleetHealthGate.dispose().catch(() => {
|
|
6176
|
+
});
|
|
5957
6177
|
this.opts.onStatusChange?.({
|
|
5958
6178
|
type: "connector_status",
|
|
5959
6179
|
connectorId: id,
|
|
@@ -6044,6 +6264,8 @@ var ConnectorManager = class {
|
|
|
6044
6264
|
await this.placeholderBarrier.remove(entry.placeholderTarget, this.mgrLog).catch(() => {
|
|
6045
6265
|
});
|
|
6046
6266
|
}
|
|
6267
|
+
if (entry.fleetHealthGate) await entry.fleetHealthGate.dispose().catch(() => {
|
|
6268
|
+
});
|
|
6047
6269
|
this.active.delete(id);
|
|
6048
6270
|
this.opts.onStatusChange?.({
|
|
6049
6271
|
type: "connector_status",
|
|
@@ -8164,6 +8386,6 @@ function createConnector() {
|
|
|
8164
8386
|
return new FlowAdapter();
|
|
8165
8387
|
}
|
|
8166
8388
|
|
|
8167
|
-
export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DEFAULT_REACTIVE_COOLDOWN_MS, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, NOT_READY_SENTINEL, PROACTIVE_RCLONE_REFRESH_MARGIN_MS, PostgresConnector, RoMountPlaceholderBarrier, S3Connector, SQLiteConnector, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createRcloneTokenRefresh, createWorktree, defaultMountPlaceholderBarrier, defaultSpawn, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
|
|
8168
|
-
//# sourceMappingURL=chunk-
|
|
8169
|
-
//# sourceMappingURL=chunk-
|
|
8389
|
+
export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DEFAULT_REACTIVE_COOLDOWN_MS, DeployConnector, DevServerConnector, FleetHealthGate, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, NOT_READY_SENTINEL, PROACTIVE_RCLONE_REFRESH_MARGIN_MS, PostgresConnector, RoMountPlaceholderBarrier, S3Connector, SQLiteConnector, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createRcloneTokenRefresh, createWorktree, defaultMountPlaceholderBarrier, defaultSpawn, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, probeFleetHealth, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
|
|
8390
|
+
//# sourceMappingURL=chunk-NAMD4AIK.js.map
|
|
8391
|
+
//# sourceMappingURL=chunk-NAMD4AIK.js.map
|