@skaile/workspaces 3.7.2 → 3.8.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 +146 -0
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-44M2GV2W.js → asset-feeds-7HM3HONM.js} +4 -4
- package/dist/{asset-feeds-44M2GV2W.js.map → asset-feeds-7HM3HONM.js.map} +1 -1
- package/dist/asset-manager/index.js +1 -1
- package/dist/bridge/drivers/claude-sdk.js +231 -24
- package/dist/bridge/drivers/claude-sdk.js.map +1 -1
- package/dist/bridge/drivers/codex.js +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +2 -2
- package/dist/bridge/src/drivers/claude-sdk.d.ts +96 -1
- package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
- package/dist/bridge/src/types.d.ts +103 -0
- package/dist/bridge/src/types.d.ts.map +1 -1
- package/dist/{chunk-IKNQW3AW.js → chunk-2GREL3OD.js} +3 -3
- package/dist/{chunk-IKNQW3AW.js.map → chunk-2GREL3OD.js.map} +1 -1
- package/dist/{chunk-Z7ILXHIT.js → chunk-2JW5SWIU.js} +6 -6
- package/dist/{chunk-Z7ILXHIT.js.map → chunk-2JW5SWIU.js.map} +1 -1
- package/dist/{chunk-J4I2YPGE.js → chunk-4WTC6BSV.js} +3 -3
- package/dist/{chunk-J4I2YPGE.js.map → chunk-4WTC6BSV.js.map} +1 -1
- package/dist/{chunk-JTBF4OB2.js → chunk-6GDVGAIZ.js} +587 -114
- package/dist/chunk-6GDVGAIZ.js.map +1 -0
- package/dist/{chunk-SID35UAK.js → chunk-7SHXLJQP.js} +3 -3
- package/dist/{chunk-SID35UAK.js.map → chunk-7SHXLJQP.js.map} +1 -1
- package/dist/{chunk-JF73DP5W.js → chunk-B7TA2XI7.js} +2 -2
- package/dist/{chunk-JF73DP5W.js.map → chunk-B7TA2XI7.js.map} +1 -1
- package/dist/{chunk-32TJ55W5.js → chunk-BGAAZMBK.js} +2 -2
- package/dist/chunk-BGAAZMBK.js.map +1 -0
- package/dist/{chunk-XCG63ZNJ.js → chunk-DGDELPOD.js} +3 -3
- package/dist/{chunk-XCG63ZNJ.js.map → chunk-DGDELPOD.js.map} +1 -1
- package/dist/{chunk-26L2B6P6.js → chunk-FUCYTGAE.js} +4 -4
- package/dist/chunk-FUCYTGAE.js.map +1 -0
- package/dist/{chunk-F7S6NDIW.js → chunk-IH2KJYAU.js} +142 -13
- package/dist/chunk-IH2KJYAU.js.map +1 -0
- package/dist/{chunk-VXITGB7G.js → chunk-KNPBMR2P.js} +3 -3
- package/dist/{chunk-VXITGB7G.js.map → chunk-KNPBMR2P.js.map} +1 -1
- package/dist/{chunk-BCXI27BH.js → chunk-NVYEK4FI.js} +3 -3
- package/dist/{chunk-BCXI27BH.js.map → chunk-NVYEK4FI.js.map} +1 -1
- package/dist/{chunk-D4VHBAJC.js → chunk-O3LEV6KB.js} +4 -4
- package/dist/{chunk-D4VHBAJC.js.map → chunk-O3LEV6KB.js.map} +1 -1
- package/dist/{chunk-2GA2UGQI.js → chunk-OZZCPTOG.js} +6 -6
- package/dist/{chunk-2GA2UGQI.js.map → chunk-OZZCPTOG.js.map} +1 -1
- package/dist/{chunk-NXHRSUKL.js → chunk-PRUCNSBI.js} +4 -4
- package/dist/{chunk-NXHRSUKL.js.map → chunk-PRUCNSBI.js.map} +1 -1
- package/dist/{chunk-J7SEUNPD.js → chunk-QVQH6GV2.js} +2 -2
- package/dist/{chunk-J7SEUNPD.js.map → chunk-QVQH6GV2.js.map} +1 -1
- package/dist/{chunk-I5TSXDTD.js → chunk-RVXBOKHK.js} +3 -3
- package/dist/{chunk-I5TSXDTD.js.map → chunk-RVXBOKHK.js.map} +1 -1
- package/dist/cli/index.js +30 -30
- package/dist/connectors/index.js +3 -3
- package/dist/connectors/src/connector-manager.d.ts +34 -2
- package/dist/connectors/src/connector-manager.d.ts.map +1 -1
- package/dist/connectors/src/connector-prompt.d.ts.map +1 -1
- package/dist/connectors/src/connector-types.d.ts +25 -0
- package/dist/connectors/src/connector-types.d.ts.map +1 -1
- package/dist/connectors/src/credential-helper-script.d.ts +48 -0
- package/dist/connectors/src/credential-helper-script.d.ts.map +1 -1
- package/dist/connectors/src/index.d.ts +3 -3
- package/dist/connectors/src/index.d.ts.map +1 -1
- package/dist/connectors/src/managed-gitconfig.d.ts +80 -0
- package/dist/connectors/src/managed-gitconfig.d.ts.map +1 -1
- package/dist/{connectors-DY7UBBV5.js → connectors-2OHRZDJZ.js} +3 -3
- package/dist/{connectors-DY7UBBV5.js.map → connectors-2OHRZDJZ.js.map} +1 -1
- package/dist/{ensure-sources-QRURV6JB.js → ensure-sources-42LTJ5QI.js} +4 -4
- package/dist/{ensure-sources-QRURV6JB.js.map → ensure-sources-42LTJ5QI.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +3 -3
- package/dist/factory-assets/connectors/deploy.js +3 -3
- package/dist/factory-assets/connectors/devserver.js +3 -3
- package/dist/factory-assets/connectors/flow/adapter.js +3 -3
- package/dist/factory-assets/connectors/flow/run-flow.js +4 -4
- package/dist/factory-assets/connectors/flow.js +3 -3
- package/dist/factory-assets/connectors/git/driver.d.ts +64 -1
- package/dist/factory-assets/connectors/git/driver.d.ts.map +1 -1
- package/dist/factory-assets/connectors/git.js +3 -3
- package/dist/factory-assets/connectors/gmail.js +3 -3
- package/dist/factory-assets/connectors/googledrive.js +3 -3
- package/dist/factory-assets/connectors/local.js +3 -3
- package/dist/factory-assets/connectors/mattermost.js +3 -3
- package/dist/factory-assets/connectors/memory.js +3 -3
- package/dist/factory-assets/connectors/minio.js +3 -3
- package/dist/factory-assets/connectors/postgres.js +3 -3
- package/dist/factory-assets/connectors/s3.js +3 -3
- package/dist/factory-assets/connectors/sharepoint.js +3 -3
- package/dist/factory-assets/connectors/sqlite.js +3 -3
- package/dist/factory-assets/connectors/static-server.js +3 -3
- package/dist/factory-assets/connectors/tunnel.js +3 -3
- package/dist/factory-assets/connectors/webdav.js +3 -3
- package/dist/factory-assets/connectors/xstate-store.js +3 -3
- package/dist/factory-assets/connectors/xstate.js +3 -3
- package/dist/{helpers-TLTVVZRR.js → helpers-BP7FX6CO.js} +4 -4
- package/dist/{helpers-TLTVVZRR.js.map → helpers-BP7FX6CO.js.map} +1 -1
- package/dist/{library-publish-6HXFLOXC.js → library-publish-X5Z32WJY.js} +6 -6
- package/dist/{library-publish-6HXFLOXC.js.map → library-publish-X5Z32WJY.js.map} +1 -1
- package/dist/{open-library-FMVJICL5.js → open-library-PHUMBYHP.js} +5 -5
- package/dist/{open-library-FMVJICL5.js.map → open-library-PHUMBYHP.js.map} +1 -1
- package/dist/{project-BG3N4I3V.js → project-HAMCXPNE.js} +5 -5
- package/dist/{project-BG3N4I3V.js.map → project-HAMCXPNE.js.map} +1 -1
- package/dist/runner/index.js +7 -7
- package/dist/runner/src/ai-provider-switch.d.ts +95 -0
- package/dist/runner/src/ai-provider-switch.d.ts.map +1 -0
- package/dist/runner/src/credential-rotated.d.ts +23 -4
- package/dist/runner/src/credential-rotated.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/runner/src/session-builder.d.ts +19 -0
- package/dist/runner/src/session-builder.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +1 -1
- package/dist/sdk/bridge.js +2 -2
- package/dist/sdk/index.js +7 -7
- package/dist/sdk/runner.js +7 -7
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-O2N6PA7I.js → setup-IOOGR3EL.js} +4 -4
- package/dist/{setup-O2N6PA7I.js.map → setup-IOOGR3EL.js.map} +1 -1
- package/dist/{source-JDIBJZ3X.js → source-772GLNM6.js} +6 -6
- package/dist/{source-JDIBJZ3X.js.map → source-772GLNM6.js.map} +1 -1
- package/dist/{source-manifest-publish-567T25M6.js → source-manifest-publish-LAUGDQ5G.js} +7 -7
- package/dist/{source-manifest-publish-567T25M6.js.map → source-manifest-publish-LAUGDQ5G.js.map} +1 -1
- package/dist/tui/index.js +7 -7
- package/dist/types/index.js +1 -1
- package/dist/types/src/events.d.ts +34 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/lifecycle.d.ts +13 -0
- package/dist/types/src/lifecycle.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +22 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +2 -2
- package/dist/workspace-plugin/adapters/mcp.js.map +1 -1
- package/dist/workspace-plugin/adapters/omp.js +8 -8
- package/dist/workspace-plugin/adapters/omp.js.map +1 -1
- package/dist/workspace-plugin/index.js +1 -1
- package/dist/workspace-plugin/src/adapters/mcp.d.ts.map +1 -1
- package/dist/workspace-plugin/src/adapters/omp.d.ts.map +1 -1
- package/dist/workspace-plugin/src/tools/connectors.d.ts +7 -3
- package/dist/workspace-plugin/src/tools/connectors.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-26L2B6P6.js.map +0 -1
- package/dist/chunk-32TJ55W5.js.map +0 -1
- package/dist/chunk-F7S6NDIW.js.map +0 -1
- package/dist/chunk-JTBF4OB2.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveAuthRef } from './chunk-3UU3D2Q3.js';
|
|
2
|
-
import { EventNormalizer } from './chunk-
|
|
2
|
+
import { EventNormalizer } from './chunk-KNPBMR2P.js';
|
|
3
3
|
import { resolveModelTier } from './chunk-V34ZEXSE.js';
|
|
4
|
-
import { createDriver } from './chunk-
|
|
4
|
+
import { createDriver } from './chunk-BGAAZMBK.js';
|
|
5
5
|
import { pluginRegistry } from './chunk-6E6PKKAD.js';
|
|
6
6
|
import { computeStimulus } from './chunk-IFGTRL2I.js';
|
|
7
7
|
import { resolveNodeBindings, getConsumedOutputSchema, validateConsumedNodeOutput, appendAttempt, resolveTerminalFailureStatus, remainingActiveExecutionMs, interpolateNodeInstruction, deriveCheckEvidence, computeFlowStateFromSnapshots, availableRouterFrontier, evaluateRouterSelection, routerSelectionOutput, planRouterSkips, unsettledRouterBranchIds, countFailedAttempts, hasAutomaticRetryBudget, persistedRouterSelection } from './chunk-OYZIURPH.js';
|
|
@@ -17,7 +17,7 @@ import { mkdir, rename, writeFile, readFile, stat, cp, readdir, chmod, realpath
|
|
|
17
17
|
import * as z4 from 'zod';
|
|
18
18
|
import { execFile } from 'child_process';
|
|
19
19
|
import { promisify } from 'util';
|
|
20
|
-
import { createReadStream,
|
|
20
|
+
import { createReadStream, existsSync, mkdirSync, openSync, closeSync, chmodSync, utimesSync, readFileSync, writeSync, renameSync } from 'fs';
|
|
21
21
|
import { createServer } from 'http';
|
|
22
22
|
import mime from 'mime';
|
|
23
23
|
import { createRequire } from 'module';
|
|
@@ -1323,6 +1323,18 @@ function resolveGitConfig(args) {
|
|
|
1323
1323
|
}
|
|
1324
1324
|
var modeLog = createLogger({ kind: "connector", subkind: "git" });
|
|
1325
1325
|
var execFileAsync2 = promisify(execFile);
|
|
1326
|
+
function redactUrlAuth(text) {
|
|
1327
|
+
return text.replace(/\/\/[^/@\s]*:[^/@\s]*@/g, "//***@");
|
|
1328
|
+
}
|
|
1329
|
+
function redactError(err) {
|
|
1330
|
+
const original = err instanceof Error ? err : new Error(String(err));
|
|
1331
|
+
const copy = new Error(redactUrlAuth(original.message));
|
|
1332
|
+
copy.name = original.name;
|
|
1333
|
+
copy.stack = original.stack === void 0 ? void 0 : redactUrlAuth(original.stack);
|
|
1334
|
+
return copy;
|
|
1335
|
+
}
|
|
1336
|
+
var BOOTSTRAP_COMMIT_NAME = "Skaile";
|
|
1337
|
+
var BOOTSTRAP_COMMIT_EMAIL = "skaile@local";
|
|
1326
1338
|
function gitPrefix(cwd) {
|
|
1327
1339
|
return ["-c", `safe.directory=${cwd}`];
|
|
1328
1340
|
}
|
|
@@ -1482,53 +1494,59 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1482
1494
|
if (!isValidGitRefOrSha(branch)) {
|
|
1483
1495
|
throw new Error(`Git connector: invalid branch name "${branch}"`);
|
|
1484
1496
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
await this.
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
this.startPeriodicFetch(state, targetDir);
|
|
1509
|
-
}
|
|
1510
|
-
if (declaration.mount?.exposeAccessToken === true) {
|
|
1511
|
-
try {
|
|
1512
|
-
this.exposeManagedCredential(declaration, state, url, initial);
|
|
1513
|
-
} catch (err) {
|
|
1514
|
-
this.log.error("managed-gitconfig write failed", err, { connectorId: declaration.id });
|
|
1515
|
-
throw err;
|
|
1497
|
+
try {
|
|
1498
|
+
this.log.info("mount start", { url, branch, targetDir });
|
|
1499
|
+
this.clearUnavailableCredentialHelper(declaration.id, url);
|
|
1500
|
+
const initial = await this.acquireInitialToken(declaration, secrets, tokenMediator);
|
|
1501
|
+
const { session, sync, lifecycle } = resolveGitConfig({
|
|
1502
|
+
mode: declaration.mode,
|
|
1503
|
+
options: opts
|
|
1504
|
+
});
|
|
1505
|
+
const state = {
|
|
1506
|
+
url,
|
|
1507
|
+
branch,
|
|
1508
|
+
activeBranch: branch,
|
|
1509
|
+
token: initial.token,
|
|
1510
|
+
session,
|
|
1511
|
+
sync,
|
|
1512
|
+
lifecycle,
|
|
1513
|
+
targetDir,
|
|
1514
|
+
tokenMediator: initial.authRef?.kind === "backend" ? tokenMediator : void 0
|
|
1515
|
+
};
|
|
1516
|
+
const authUrl = this.authUrl(state);
|
|
1517
|
+
await this.prepareCheckout(targetDir, url, branch, authUrl, declaration.access, started);
|
|
1518
|
+
if (session.enabled && !session.isMain && session.slug) {
|
|
1519
|
+
await this.checkoutSessionBranch(state, session, targetDir);
|
|
1516
1520
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1521
|
+
if (sync.fetchIntervalSec > 0) {
|
|
1522
|
+
this.startPeriodicFetch(state, targetDir);
|
|
1523
|
+
}
|
|
1524
|
+
if (declaration.mount?.exposeAccessToken === true) {
|
|
1525
|
+
try {
|
|
1526
|
+
this.exposeManagedCredential(declaration, state, url, initial);
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
this.log.error("managed-gitconfig write failed", err, { connectorId: declaration.id });
|
|
1529
|
+
throw err;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
if (state.managedGitconfig) {
|
|
1533
|
+
await this.stripRemoteInlineToken(targetDir);
|
|
1534
|
+
} else {
|
|
1535
|
+
await this.rebakeRemoteAuth(targetDir, state);
|
|
1536
|
+
if (initial.authRef?.kind === "backend" && initial.expiresAt && state.tokenMediator) {
|
|
1537
|
+
this.scheduleRefresh(declaration.id, state, initial.expiresAt);
|
|
1538
|
+
}
|
|
1524
1539
|
}
|
|
1540
|
+
this.log.info("mount ok", {
|
|
1541
|
+
targetDir,
|
|
1542
|
+
activeBranch: state.activeBranch,
|
|
1543
|
+
durationMs: Date.now() - started
|
|
1544
|
+
});
|
|
1545
|
+
return this.makeHandle(declaration, state, targetDir);
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
this.registerUnavailableCredentialHelper(declaration, url, err);
|
|
1548
|
+
throw err;
|
|
1525
1549
|
}
|
|
1526
|
-
this.log.info("mount ok", {
|
|
1527
|
-
targetDir,
|
|
1528
|
-
activeBranch: state.activeBranch,
|
|
1529
|
-
durationMs: Date.now() - started
|
|
1530
|
-
});
|
|
1531
|
-
return this.makeHandle(declaration, state, targetDir);
|
|
1532
1550
|
}
|
|
1533
1551
|
/**
|
|
1534
1552
|
* Bring `targetDir` into a usable checkout state for `connect()`.
|
|
@@ -1541,40 +1559,58 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1541
1559
|
*
|
|
1542
1560
|
* Three cases:
|
|
1543
1561
|
* - `.git` exists → use as-is. No pull, no reset.
|
|
1544
|
-
* - dir empty → fresh clone (only network fetch on connect)
|
|
1562
|
+
* - dir empty → fresh clone (only network fetch on connect), or
|
|
1563
|
+
* `bootstrapEmptyRemote` when the remote has no refs at all.
|
|
1545
1564
|
* - dir populated, no `.git` → `git init` + `remote add origin` so
|
|
1546
1565
|
* lifecycle hooks (commit/push) can do their job. No fetch, no
|
|
1547
1566
|
* reset, no clean. Existing files become uncommitted-on-init; the
|
|
1548
1567
|
* agent or `pushAfterCommit` reconciles with remote later.
|
|
1549
1568
|
*/
|
|
1550
|
-
async prepareCheckout(targetDir, url, branch, authUrl, started) {
|
|
1569
|
+
async prepareCheckout(targetDir, url, branch, authUrl, access, started) {
|
|
1551
1570
|
try {
|
|
1552
1571
|
if (await pathExists(join(targetDir, ".git"))) {
|
|
1553
1572
|
if (await this.isInitialisedCheckout(targetDir)) {
|
|
1554
1573
|
this.log.info("found existing checkout \u2014 using as-is", { branch, targetDir });
|
|
1555
1574
|
} else {
|
|
1556
|
-
this.
|
|
1557
|
-
|
|
1575
|
+
if (await this.countRemoteHeads(targetDir, authUrl) === 0) {
|
|
1576
|
+
this.log.info("empty .git against a ref-less remote \u2014 bootstrapping branch", {
|
|
1577
|
+
branch,
|
|
1578
|
+
access
|
|
1579
|
+
});
|
|
1580
|
+
await this.bootstrapEmptyRemote(targetDir, branch, authUrl, access);
|
|
1581
|
+
} else {
|
|
1582
|
+
this.log.info("found empty .git \u2014 bootstrapping from origin", { branch });
|
|
1583
|
+
await this.bootstrapFromOrigin(targetDir, branch, authUrl);
|
|
1584
|
+
}
|
|
1558
1585
|
this.log.info("bootstrap ok", { branch, durationMs: Date.now() - started });
|
|
1559
1586
|
}
|
|
1560
1587
|
} else {
|
|
1561
1588
|
await withFsDeadline(`mkdir ${targetDir}`, () => mkdir(targetDir, { recursive: true }));
|
|
1562
1589
|
if (await isDirEmpty(targetDir)) {
|
|
1563
|
-
this.
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
"clone",
|
|
1568
|
-
"--branch",
|
|
1590
|
+
if (await this.countRemoteHeads(targetDir, authUrl) === 0) {
|
|
1591
|
+
this.log.info("remote has no refs \u2014 bootstrapping branch", { url, branch, access });
|
|
1592
|
+
await this.bootstrapEmptyRemote(targetDir, branch, authUrl, access);
|
|
1593
|
+
this.log.info("empty-remote bootstrap ok", {
|
|
1569
1594
|
branch,
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1595
|
+
durationMs: Date.now() - started
|
|
1596
|
+
});
|
|
1597
|
+
} else {
|
|
1598
|
+
this.log.info("clone start", { url, branch });
|
|
1599
|
+
await runGit(
|
|
1600
|
+
[
|
|
1601
|
+
...gitPrefix(targetDir),
|
|
1602
|
+
"clone",
|
|
1603
|
+
"--branch",
|
|
1604
|
+
branch,
|
|
1605
|
+
"--single-branch",
|
|
1606
|
+
"--recurse-submodules",
|
|
1607
|
+
authUrl,
|
|
1608
|
+
targetDir
|
|
1609
|
+
],
|
|
1610
|
+
{ timeout: 12e4 }
|
|
1611
|
+
);
|
|
1612
|
+
this.log.info("clone ok", { branch, durationMs: Date.now() - started });
|
|
1613
|
+
}
|
|
1578
1614
|
} else {
|
|
1579
1615
|
this.log.warn("target non-empty without .git \u2014 initialising in place", { targetDir });
|
|
1580
1616
|
await this.initInPlace(targetDir, authUrl);
|
|
@@ -1582,7 +1618,7 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1582
1618
|
}
|
|
1583
1619
|
}
|
|
1584
1620
|
} catch (err) {
|
|
1585
|
-
this.log.error("connect preparation failed", err, { url, branch });
|
|
1621
|
+
this.log.error("connect preparation failed", redactError(err), { url, branch });
|
|
1586
1622
|
throw err;
|
|
1587
1623
|
}
|
|
1588
1624
|
}
|
|
@@ -1964,6 +2000,104 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1964
2000
|
}
|
|
1965
2001
|
return { authRef, token, expiresAt: null };
|
|
1966
2002
|
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Register a credential helper that *reports* why there is no credential,
|
|
2005
|
+
* after this connector failed to connect.
|
|
2006
|
+
*
|
|
2007
|
+
* Without it git finds no helper for the host and asks the terminal for a
|
|
2008
|
+
* username, so the agent sees `fatal: could not read Username for
|
|
2009
|
+
* 'https://github.com'` — which names neither the mount nor the cause, and
|
|
2010
|
+
* which only ever appears on a write, since reads against a public remote
|
|
2011
|
+
* keep succeeding. The helper written here makes git stop with the mediator's
|
|
2012
|
+
* actual refusal text instead.
|
|
2013
|
+
*
|
|
2014
|
+
* Entirely best-effort: it runs on the failure path and must never mask the
|
|
2015
|
+
* connect error that is already on its way up.
|
|
2016
|
+
*/
|
|
2017
|
+
registerUnavailableCredentialHelper(declaration, url, err) {
|
|
2018
|
+
try {
|
|
2019
|
+
if (!url.startsWith("https://") && !url.startsWith("http://")) return;
|
|
2020
|
+
const gitconfigPath = process.env.GIT_CONFIG_GLOBAL;
|
|
2021
|
+
if (!gitconfigPath) return;
|
|
2022
|
+
const skaileDir = dirname(gitconfigPath);
|
|
2023
|
+
const credentialsPath = join(skaileDir, "git-credentials");
|
|
2024
|
+
const urlPrefix = this.deriveUrlPrefix(url);
|
|
2025
|
+
const helperScriptPath = join(skaileDir, `credential-helper-${declaration.id}.sh`);
|
|
2026
|
+
const refreshAttemptPath = join(skaileDir, `refresh-attempt-${declaration.id}`);
|
|
2027
|
+
const reason = redactUrlAuth(err instanceof Error ? err.message : String(err));
|
|
2028
|
+
this.ensureAttemptStamp(declaration.id, refreshAttemptPath);
|
|
2029
|
+
writeHelperScript(
|
|
2030
|
+
helperScriptPath,
|
|
2031
|
+
renderCredentialHelperScript({
|
|
2032
|
+
credentialsPath,
|
|
2033
|
+
refreshFlagPath: join(skaileDir, "refresh-request"),
|
|
2034
|
+
credentialTag: `# skaile-mount: ${declaration.id}`,
|
|
2035
|
+
refreshAttemptPath,
|
|
2036
|
+
unavailableReason: reason
|
|
2037
|
+
})
|
|
2038
|
+
);
|
|
2039
|
+
writeUnavailableMountBlock({
|
|
2040
|
+
mountId: declaration.id,
|
|
2041
|
+
urlPrefix,
|
|
2042
|
+
gitconfigPath,
|
|
2043
|
+
credentialsPath,
|
|
2044
|
+
helperScriptPath
|
|
2045
|
+
});
|
|
2046
|
+
this.log.warn("registered unavailable credential helper for failed mount", {
|
|
2047
|
+
connectorId: declaration.id,
|
|
2048
|
+
urlPrefix
|
|
2049
|
+
});
|
|
2050
|
+
} catch (writeErr) {
|
|
2051
|
+
this.log.warn("failed to register unavailable credential helper", {
|
|
2052
|
+
connectorId: declaration.id,
|
|
2053
|
+
error: writeErr instanceof Error ? writeErr.message : String(writeErr)
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Best-effort pre-creation of the helper's refresh-attempt stamp. A missing
|
|
2059
|
+
* stamp only costs the cooldown (the helper dances on every git op instead of
|
|
2060
|
+
* once per window), so a failure here is never worth surfacing.
|
|
2061
|
+
*/
|
|
2062
|
+
ensureAttemptStamp(connectorId, stampPath) {
|
|
2063
|
+
try {
|
|
2064
|
+
ensureRefreshAttemptStamp(stampPath);
|
|
2065
|
+
} catch (err) {
|
|
2066
|
+
this.log.warn("managed-gitconfig: could not pre-create refresh-attempt stamp", {
|
|
2067
|
+
connectorId,
|
|
2068
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Best-effort removal of the unavailable-mount blocks that would answer this
|
|
2074
|
+
* connector's git operations: its own from a previous failed session, and any
|
|
2075
|
+
* left under the same URL prefix by a sibling mount that failed earlier in
|
|
2076
|
+
* this `connectAll` (git matches helpers by URL, not by mount id).
|
|
2077
|
+
*/
|
|
2078
|
+
clearUnavailableCredentialHelper(connectorId, url) {
|
|
2079
|
+
const gitconfigPath = process.env.GIT_CONFIG_GLOBAL;
|
|
2080
|
+
if (!gitconfigPath) return;
|
|
2081
|
+
let urlPrefix;
|
|
2082
|
+
if (url.startsWith("https://") || url.startsWith("http://")) {
|
|
2083
|
+
try {
|
|
2084
|
+
urlPrefix = this.deriveUrlPrefix(url);
|
|
2085
|
+
} catch {
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
try {
|
|
2089
|
+
removeUnavailableMountBlock({
|
|
2090
|
+
mountId: connectorId,
|
|
2091
|
+
gitconfigPath,
|
|
2092
|
+
...urlPrefix !== void 0 ? { urlPrefix } : {}
|
|
2093
|
+
});
|
|
2094
|
+
} catch (err) {
|
|
2095
|
+
this.log.warn("failed to clear unavailable credential helper", {
|
|
2096
|
+
connectorId,
|
|
2097
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
1967
2101
|
exposeManagedCredential(declaration, state, url, initial) {
|
|
1968
2102
|
const token = initial.token;
|
|
1969
2103
|
if (!token) {
|
|
@@ -1991,15 +2125,22 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1991
2125
|
const urlPrefix = this.deriveUrlPrefix(url);
|
|
1992
2126
|
const refreshFlagPath = join(skaileDir, "refresh-request");
|
|
1993
2127
|
const helperScriptPath = join(skaileDir, `credential-helper-${declaration.id}.sh`);
|
|
2128
|
+
const refreshAttemptPath = join(skaileDir, `refresh-attempt-${declaration.id}`);
|
|
1994
2129
|
const credentialTag2 = `# skaile-mount: ${declaration.id}`;
|
|
1995
2130
|
const scriptBody = renderCredentialHelperScript({
|
|
1996
2131
|
credentialsPath,
|
|
1997
2132
|
refreshFlagPath,
|
|
1998
|
-
credentialTag: credentialTag2
|
|
2133
|
+
credentialTag: credentialTag2,
|
|
2134
|
+
// Passed explicitly on both call sites so the failure path and this one
|
|
2135
|
+
// never disagree about which stamp file bounds this mount's dance.
|
|
2136
|
+
refreshAttemptPath
|
|
1999
2137
|
});
|
|
2138
|
+
this.ensureAttemptStamp(declaration.id, refreshAttemptPath);
|
|
2139
|
+
let helperWritten = true;
|
|
2000
2140
|
try {
|
|
2001
2141
|
writeHelperScript(helperScriptPath, scriptBody);
|
|
2002
2142
|
} catch (err) {
|
|
2143
|
+
helperWritten = false;
|
|
2003
2144
|
this.log.warn(
|
|
2004
2145
|
"managed-gitconfig: failed to write credential-helper script -- falling back to static helper",
|
|
2005
2146
|
{
|
|
@@ -2014,7 +2155,12 @@ var GitConnector = class extends AbstractConnector {
|
|
|
2014
2155
|
token,
|
|
2015
2156
|
gitconfigPath,
|
|
2016
2157
|
credentialsPath,
|
|
2017
|
-
|
|
2158
|
+
// Only point at the script when it was actually written. Pointing at an
|
|
2159
|
+
// unwritten path is not a fallback: the file may still hold a previous
|
|
2160
|
+
// session's *unavailable* body, which would refuse this live credential
|
|
2161
|
+
// with a stale reason. Omitting it selects the static `store --file=`
|
|
2162
|
+
// form, which reads the credential line this call is about to write.
|
|
2163
|
+
...helperWritten ? { helperScriptPath } : {}
|
|
2018
2164
|
});
|
|
2019
2165
|
state.managedGitconfig = { gitconfigPath, credentialsPath, urlPrefix };
|
|
2020
2166
|
const providerKind = initial.authRef?.kind ?? "pat";
|
|
@@ -2163,6 +2309,97 @@ var GitConnector = class extends AbstractConnector {
|
|
|
2163
2309
|
}
|
|
2164
2310
|
await git(["remote", "add", "origin", authUrl], targetDir, 5e3);
|
|
2165
2311
|
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Count `refs/heads/*` on `authUrl`, optionally narrowed to one `branch`.
|
|
2314
|
+
*
|
|
2315
|
+
* Returns `null` when the probe itself fails (network, auth, bad URL).
|
|
2316
|
+
* "Cannot tell" must never collapse into "no refs": the empty-remote
|
|
2317
|
+
* fallback WRITES to origin, and every caller therefore treats `null` as
|
|
2318
|
+
* "assume populated" and falls through to the path that fails loudly.
|
|
2319
|
+
*/
|
|
2320
|
+
async countRemoteHeads(targetDir, authUrl, branch) {
|
|
2321
|
+
try {
|
|
2322
|
+
const out = await runGit(
|
|
2323
|
+
branch === void 0 ? [...gitPrefix(targetDir), "ls-remote", "--heads", authUrl] : [...gitPrefix(targetDir), "ls-remote", "--heads", authUrl, `refs/heads/${branch}`],
|
|
2324
|
+
{ timeout: 3e4 }
|
|
2325
|
+
);
|
|
2326
|
+
return out.length === 0 ? 0 : out.split("\n").length;
|
|
2327
|
+
} catch (err) {
|
|
2328
|
+
this.log.warn("ls-remote probe failed \u2014 assuming a populated remote", {
|
|
2329
|
+
error: redactUrlAuth(err instanceof Error ? err.message : String(err))
|
|
2330
|
+
});
|
|
2331
|
+
return null;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Bootstrap a checkout against a remote that has no refs at all — the state
|
|
2336
|
+
* of a GitHub repo created without "Add a README".
|
|
2337
|
+
*
|
|
2338
|
+
* A plain `git clone` is NOT a substitute: cloning a ref-less remote leaves
|
|
2339
|
+
* local HEAD on git's `init.defaultBranch` (`master`), not the declared
|
|
2340
|
+
* branch, so the branch is created explicitly here.
|
|
2341
|
+
*
|
|
2342
|
+
* The root commit is created REGARDLESS of `access`, and only the push to
|
|
2343
|
+
* origin is gated on it. An unborn HEAD is not a stable resting state: the
|
|
2344
|
+
* next connect sees `.git` with no resolvable HEAD, routes to
|
|
2345
|
+
* `bootstrapFromOrigin`, and its `fetch origin <branch>` dies on the still
|
|
2346
|
+
* ref-less remote — turning a fixed first connect into a broken second one.
|
|
2347
|
+
* A local commit costs a read-only connector nothing (it is never pushed)
|
|
2348
|
+
* and makes reconnect take the cheap "use as-is" path.
|
|
2349
|
+
*
|
|
2350
|
+
* With the push done, `origin/<branch>` exists for everything downstream —
|
|
2351
|
+
* notably `checkoutSessionBranch`, whose publish-on-create push needs a
|
|
2352
|
+
* commit to point at.
|
|
2353
|
+
*
|
|
2354
|
+
* Invariant: this method never throws on a failed push. The session must
|
|
2355
|
+
* come up with a usable checkout even when origin is unreachable — the
|
|
2356
|
+
* lifecycle hooks (`pushAfterCommit`, hibernate/close push) publish later.
|
|
2357
|
+
*/
|
|
2358
|
+
async bootstrapEmptyRemote(targetDir, branch, authUrl, access) {
|
|
2359
|
+
await this.initInPlace(targetDir, authUrl);
|
|
2360
|
+
await git(["symbolic-ref", "HEAD", `refs/heads/${branch}`], targetDir, 5e3);
|
|
2361
|
+
await git(
|
|
2362
|
+
[
|
|
2363
|
+
"-c",
|
|
2364
|
+
`user.name=${BOOTSTRAP_COMMIT_NAME}`,
|
|
2365
|
+
"-c",
|
|
2366
|
+
`user.email=${BOOTSTRAP_COMMIT_EMAIL}`,
|
|
2367
|
+
"commit",
|
|
2368
|
+
"--allow-empty",
|
|
2369
|
+
"-m",
|
|
2370
|
+
"chore: initialize repository"
|
|
2371
|
+
],
|
|
2372
|
+
targetDir,
|
|
2373
|
+
3e4
|
|
2374
|
+
);
|
|
2375
|
+
if (access === "read-only") {
|
|
2376
|
+
this.log.info("empty remote is read-only \u2014 branch created locally, nothing pushed", {
|
|
2377
|
+
branch
|
|
2378
|
+
});
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
try {
|
|
2382
|
+
await git(["push", "-u", "origin", branch], targetDir, 6e4);
|
|
2383
|
+
this.log.info("empty remote initialised on origin", { branch });
|
|
2384
|
+
return;
|
|
2385
|
+
} catch (err) {
|
|
2386
|
+
this.log.warn("empty-remote publish push failed; deferring to lifecycle push", {
|
|
2387
|
+
branch,
|
|
2388
|
+
error: redactUrlAuth(err instanceof Error ? err.message : String(err))
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
const publishedElsewhere = await this.countRemoteHeads(targetDir, authUrl, branch);
|
|
2392
|
+
if (publishedElsewhere === null || publishedElsewhere === 0) return;
|
|
2393
|
+
try {
|
|
2394
|
+
this.log.info("origin gained the branch concurrently \u2014 adopting it", { branch });
|
|
2395
|
+
await this.bootstrapFromOrigin(targetDir, branch, authUrl);
|
|
2396
|
+
} catch (err) {
|
|
2397
|
+
this.log.warn("adopting the concurrently-created branch failed; keeping local branch", {
|
|
2398
|
+
branch,
|
|
2399
|
+
error: redactUrlAuth(err instanceof Error ? err.message : String(err))
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2166
2403
|
/**
|
|
2167
2404
|
* True when `<targetDir>/.git` contains at least one resolvable commit
|
|
2168
2405
|
* (i.e. `HEAD` points at a real object). False when `.git` is an empty
|
|
@@ -5804,6 +6041,13 @@ var ConnectorManager = class {
|
|
|
5804
6041
|
}
|
|
5805
6042
|
opts;
|
|
5806
6043
|
active = /* @__PURE__ */ new Map();
|
|
6044
|
+
/**
|
|
6045
|
+
* Connectors whose `connect()` threw, keyed by id. A failed connector never
|
|
6046
|
+
* enters `active`, so without this map its failure would exist only in the
|
|
6047
|
+
* log store — invisible to the agent that is about to write into a mount
|
|
6048
|
+
* that will never sync. See {@link listFailedConnectors}.
|
|
6049
|
+
*/
|
|
6050
|
+
failedConnectors = /* @__PURE__ */ new Map();
|
|
5807
6051
|
// Allocated absolute mount target → owning connector id; collisions rejected at setup, released on disconnect.
|
|
5808
6052
|
mountTargets = /* @__PURE__ */ new Map();
|
|
5809
6053
|
workspaceDir;
|
|
@@ -5942,6 +6186,30 @@ var ConnectorManager = class {
|
|
|
5942
6186
|
}
|
|
5943
6187
|
this.mountTargets.set(absPath, id);
|
|
5944
6188
|
}
|
|
6189
|
+
/**
|
|
6190
|
+
* Remember a connect failure so the agent can be told about it. Called from
|
|
6191
|
+
* both the bulk (`attemptConnect`) and hot-plug (`connect`) failure paths.
|
|
6192
|
+
*/
|
|
6193
|
+
recordFailure(decl, error) {
|
|
6194
|
+
if (this.active.has(decl.id)) return;
|
|
6195
|
+
let mountPath;
|
|
6196
|
+
if (decl.mount != null) {
|
|
6197
|
+
try {
|
|
6198
|
+
mountPath = this.resolveMountTarget(decl);
|
|
6199
|
+
} catch {
|
|
6200
|
+
}
|
|
6201
|
+
}
|
|
6202
|
+
this.failedConnectors.set(decl.id, {
|
|
6203
|
+
id: decl.id,
|
|
6204
|
+
driver: decl.driver,
|
|
6205
|
+
access: decl.access,
|
|
6206
|
+
...decl.label !== void 0 ? { label: decl.label } : {},
|
|
6207
|
+
...decl.mount?.source !== void 0 ? { source: decl.mount.source } : {},
|
|
6208
|
+
...mountPath !== void 0 ? { mountPath } : {},
|
|
6209
|
+
required: decl.access === "read-write" && decl.mount != null,
|
|
6210
|
+
error
|
|
6211
|
+
});
|
|
6212
|
+
}
|
|
5945
6213
|
/** Release every mount target owned by `id` (on disconnect). */
|
|
5946
6214
|
releaseMountTarget(id) {
|
|
5947
6215
|
for (const [path, owner] of [...this.mountTargets]) {
|
|
@@ -6094,6 +6362,7 @@ var ConnectorManager = class {
|
|
|
6094
6362
|
handle,
|
|
6095
6363
|
declaration: decl
|
|
6096
6364
|
});
|
|
6365
|
+
this.failedConnectors.delete(decl.id);
|
|
6097
6366
|
this.opts.onStatusChange?.({
|
|
6098
6367
|
type: "connector_status",
|
|
6099
6368
|
connectorId: decl.id,
|
|
@@ -6108,6 +6377,7 @@ var ConnectorManager = class {
|
|
|
6108
6377
|
} catch (err) {
|
|
6109
6378
|
if (!this.active.has(decl.id)) this.releaseMountTarget(decl.id);
|
|
6110
6379
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6380
|
+
this.recordFailure(decl, msg);
|
|
6111
6381
|
this.mgrLog.error("connect failed", err, { id: decl.id, driver: decl.driver });
|
|
6112
6382
|
this.opts.onStatusChange?.({
|
|
6113
6383
|
type: "connector_status",
|
|
@@ -6152,6 +6422,7 @@ var ConnectorManager = class {
|
|
|
6152
6422
|
}
|
|
6153
6423
|
this.active.clear();
|
|
6154
6424
|
this.mountTargets.clear();
|
|
6425
|
+
this.failedConnectors.clear();
|
|
6155
6426
|
this.mgrLog.info("disconnectAll complete");
|
|
6156
6427
|
}
|
|
6157
6428
|
// ── Hot-plug ──────────────────────────────────────────────────────────────────
|
|
@@ -6186,6 +6457,7 @@ var ConnectorManager = class {
|
|
|
6186
6457
|
() => connector.connect(declaration, ctx)
|
|
6187
6458
|
);
|
|
6188
6459
|
this.active.set(declaration.id, { connector, handle, declaration });
|
|
6460
|
+
this.failedConnectors.delete(declaration.id);
|
|
6189
6461
|
this.opts.onStatusChange?.({
|
|
6190
6462
|
type: "connector_status",
|
|
6191
6463
|
connectorId: declaration.id,
|
|
@@ -6195,6 +6467,7 @@ var ConnectorManager = class {
|
|
|
6195
6467
|
} catch (err) {
|
|
6196
6468
|
this.releaseMountTarget(declaration.id);
|
|
6197
6469
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6470
|
+
this.recordFailure(declaration, msg);
|
|
6198
6471
|
this.opts.onStatusChange?.({
|
|
6199
6472
|
type: "connector_status",
|
|
6200
6473
|
connectorId: declaration.id,
|
|
@@ -6207,9 +6480,25 @@ var ConnectorManager = class {
|
|
|
6207
6480
|
/**
|
|
6208
6481
|
* Disconnect a single connector by ID and stop its watcher.
|
|
6209
6482
|
*
|
|
6210
|
-
*
|
|
6483
|
+
* A connector that only ever *failed* to connect is not in `active`, so
|
|
6484
|
+
* `get(id)` would throw for it. Removing such a declaration is still a
|
|
6485
|
+
* legitimate host action — and it is the only way to retract its
|
|
6486
|
+
* `## Mount failures` row — so that case is handled as a plain forget rather
|
|
6487
|
+
* than an error.
|
|
6488
|
+
*
|
|
6489
|
+
* @throws {Error} if the connector is neither connected nor recorded as failed
|
|
6211
6490
|
*/
|
|
6212
6491
|
async disconnect(id) {
|
|
6492
|
+
if (!this.active.has(id) && this.failedConnectors.has(id)) {
|
|
6493
|
+
this.failedConnectors.delete(id);
|
|
6494
|
+
this.releaseMountTarget(id);
|
|
6495
|
+
this.opts.onStatusChange?.({
|
|
6496
|
+
type: "connector_status",
|
|
6497
|
+
connectorId: id,
|
|
6498
|
+
status: "disconnected"
|
|
6499
|
+
});
|
|
6500
|
+
return;
|
|
6501
|
+
}
|
|
6213
6502
|
const entry = this.get(id);
|
|
6214
6503
|
if (entry.watchHandle) {
|
|
6215
6504
|
try {
|
|
@@ -6229,6 +6518,7 @@ var ConnectorManager = class {
|
|
|
6229
6518
|
await entry.connector.disconnect(entry.handle);
|
|
6230
6519
|
} finally {
|
|
6231
6520
|
this.active.delete(id);
|
|
6521
|
+
this.failedConnectors.delete(id);
|
|
6232
6522
|
this.releaseMountTarget(id);
|
|
6233
6523
|
this.opts.onStatusChange?.({
|
|
6234
6524
|
type: "connector_status",
|
|
@@ -6344,6 +6634,22 @@ var ConnectorManager = class {
|
|
|
6344
6634
|
status: entry.handle.status
|
|
6345
6635
|
}));
|
|
6346
6636
|
}
|
|
6637
|
+
/**
|
|
6638
|
+
* Return every connector whose `connect()` threw and that has not since
|
|
6639
|
+
* connected.
|
|
6640
|
+
*
|
|
6641
|
+
* A failed connector is deliberately **absent** from `listConnectors()` and
|
|
6642
|
+
* `get()` — it never entered the active map, and those surfaces describe what
|
|
6643
|
+
* the agent can actually use. That is exactly why this second list exists:
|
|
6644
|
+
* without it a failed mount is invisible to the prompt builder, and the agent
|
|
6645
|
+
* keeps treating a dead directory as a live, synced workspace.
|
|
6646
|
+
*
|
|
6647
|
+
* Entries are cleared by a later successful connect of the same id, by
|
|
6648
|
+
* `disconnect(id)`, and wholesale by `disconnectAll()`.
|
|
6649
|
+
*/
|
|
6650
|
+
listFailedConnectors() {
|
|
6651
|
+
return [...this.failedConnectors.values()].map((f) => ({ ...f }));
|
|
6652
|
+
}
|
|
6347
6653
|
/**
|
|
6348
6654
|
* Lists git-backed connectors, surfacing the fields prompt-builders and the
|
|
6349
6655
|
* runner's wake-mid-401 refresh handler need.
|
|
@@ -6558,6 +6864,14 @@ var ConnectorManager = class {
|
|
|
6558
6864
|
}
|
|
6559
6865
|
};
|
|
6560
6866
|
var zStatic = z4.z ?? z4.default ?? z4;
|
|
6867
|
+
function readFailedConnectors(manager) {
|
|
6868
|
+
if (typeof manager.listFailedConnectors !== "function") return [];
|
|
6869
|
+
try {
|
|
6870
|
+
return manager.listFailedConnectors();
|
|
6871
|
+
} catch {
|
|
6872
|
+
return [];
|
|
6873
|
+
}
|
|
6874
|
+
}
|
|
6561
6875
|
async function buildSdkConnectorTools(manager, options) {
|
|
6562
6876
|
const shellAccess = options?.shellAccess ?? true;
|
|
6563
6877
|
let sdk = options?.sdk;
|
|
@@ -6572,11 +6886,14 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6572
6886
|
const tools = [];
|
|
6573
6887
|
tools.push({
|
|
6574
6888
|
name: "connector_list",
|
|
6575
|
-
description: "List
|
|
6889
|
+
description: "List connectors as {connected, failed}. `connected` holds the usable connectors with their id, driver, access mode, and available operations; `failed` holds connectors that did not connect this session, with the failure reason \u2014 their mount paths are dead directories, not synced workspaces. Use this to discover connectors that may have been added after session start (e.g. shared-state stores like 'presence' or 'session' pushed by the host).",
|
|
6576
6890
|
inputSchema: {},
|
|
6577
6891
|
handler: async () => {
|
|
6578
6892
|
try {
|
|
6579
|
-
const entries =
|
|
6893
|
+
const entries = {
|
|
6894
|
+
connected: manager.listConnectors(),
|
|
6895
|
+
failed: readFailedConnectors(manager)
|
|
6896
|
+
};
|
|
6580
6897
|
return { content: [{ type: "text", text: JSON.stringify(entries, null, 2) }] };
|
|
6581
6898
|
} catch (err) {
|
|
6582
6899
|
return {
|
|
@@ -6814,7 +7131,8 @@ function buildOperationInputSchema(args, z5) {
|
|
|
6814
7131
|
function buildConnectorPromptSection(manager, options) {
|
|
6815
7132
|
const shellAccess = options?.shellAccess ?? true;
|
|
6816
7133
|
const allConnectors = manager.listConnectors();
|
|
6817
|
-
|
|
7134
|
+
const failed = readFailedConnectors(manager);
|
|
7135
|
+
if (allConnectors.length === 0 && failed.length === 0) return "";
|
|
6818
7136
|
const sections = [];
|
|
6819
7137
|
const faces = allConnectors.map((c) => {
|
|
6820
7138
|
try {
|
|
@@ -6832,8 +7150,46 @@ function buildConnectorPromptSection(manager, options) {
|
|
|
6832
7150
|
if (toolConnectors.length > 0) {
|
|
6833
7151
|
sections.push(buildToolSection(manager, toolConnectors, shellAccess));
|
|
6834
7152
|
}
|
|
7153
|
+
if (failed.length > 0) {
|
|
7154
|
+
sections.unshift(buildFailedConnectorSection(failed));
|
|
7155
|
+
}
|
|
6835
7156
|
return sections.join("\n\n");
|
|
6836
7157
|
}
|
|
7158
|
+
function buildFailedConnectorSection(failed) {
|
|
7159
|
+
const lines = [
|
|
7160
|
+
"## Mount failures \u2014 read this first",
|
|
7161
|
+
"",
|
|
7162
|
+
"The following connectors **failed to connect** in this session. Their directories are ordinary local folders: **not** connected, **not** synced, and **not** backend-managed git worktrees. Any other statement in this prompt \u2014 including the host platform's own description of the workspace \u2014 that describes these paths as a managed, synced, or version-controlled workspace **is superseded by this section**.",
|
|
7163
|
+
"",
|
|
7164
|
+
"| Mount | Driver | Path | Access | Required | Failure |",
|
|
7165
|
+
"|---|---|---|---|---|---|"
|
|
7166
|
+
];
|
|
7167
|
+
for (const f of failed) {
|
|
7168
|
+
const path = f.mountPath ?? f.source ?? "(no mount)";
|
|
7169
|
+
const required = f.required ? "yes" : "no";
|
|
7170
|
+
lines.push(
|
|
7171
|
+
`| ${f.id} | ${f.driver} | \`${path}\` | ${f.access} | ${required} | ${oneLine(f.error)} |`
|
|
7172
|
+
);
|
|
7173
|
+
}
|
|
7174
|
+
lines.push("");
|
|
7175
|
+
lines.push(
|
|
7176
|
+
"**Work written under these paths is not saved.** The mount's lifecycle automation \u2014 auto-commit, push-after-commit, push-on-hibernate, commit-on-hibernate, push-on-close \u2014 is dead for a failed connector: nothing is committed, nothing is pushed, and the contents do not survive the session. Do not tell the user their work is saved, synced, or pushed. If they ask you to write substantial work into one of these paths, say first that it will be lost. `Required: yes` marks a read-write mount the session was supposed to be built on \u2014 that session did not start degraded, it started with nowhere to save anything."
|
|
7177
|
+
);
|
|
7178
|
+
lines.push("");
|
|
7179
|
+
lines.push(
|
|
7180
|
+
"**There is nothing to fix inside this workspace.** The credential for a mount is minted by the host platform, not by anything in here and not by any project setting \u2014 there is no credential field to fill in. Report the failure text above verbatim to the user and ask them to reconnect the connector's provider link on the platform, then restart the session. Do not retry the connection yourself; a connector that failed at startup stays failed until the session restarts."
|
|
7181
|
+
);
|
|
7182
|
+
if (failed.some((f) => f.driver === "git")) {
|
|
7183
|
+
lines.push("");
|
|
7184
|
+
lines.push(
|
|
7185
|
+
"**Git specifically:** `git fetch` and `git ls-remote` can still succeed against a public remote with no credential at all, so a working read is **not** evidence that auth works. Only writes need the credential. `git push` will fail with `skaile: mount not connected: \u2026`. Do not retry it in a loop and do not attempt to configure git credentials by hand."
|
|
7186
|
+
);
|
|
7187
|
+
}
|
|
7188
|
+
return lines.join("\n");
|
|
7189
|
+
}
|
|
7190
|
+
function oneLine(text) {
|
|
7191
|
+
return text.replace(/\s*[\r\n]+\s*/g, " ").replace(/\|/g, "\\|").trim();
|
|
7192
|
+
}
|
|
6837
7193
|
function buildFilesystemSection(manager, fsConnectors) {
|
|
6838
7194
|
const lines = [
|
|
6839
7195
|
"## Mounts",
|
|
@@ -6937,15 +7293,29 @@ function safeHost(source) {
|
|
|
6937
7293
|
return source;
|
|
6938
7294
|
}
|
|
6939
7295
|
}
|
|
6940
|
-
|
|
6941
|
-
|
|
7296
|
+
function shQuote(value) {
|
|
7297
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
7298
|
+
}
|
|
7299
|
+
function singleLine(value) {
|
|
7300
|
+
return value.replace(/[\r\n]+/g, " ").trim();
|
|
7301
|
+
}
|
|
7302
|
+
function mountIdFromTag(tag) {
|
|
7303
|
+
const match = tag.match(/^#\s*skaile-mount:\s*(.+)$/);
|
|
7304
|
+
return (match?.[1] ?? tag).trim();
|
|
7305
|
+
}
|
|
7306
|
+
function stampSlug(tag) {
|
|
7307
|
+
return mountIdFromTag(tag).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "mount";
|
|
7308
|
+
}
|
|
6942
7309
|
function renderCredentialHelperScript(opts) {
|
|
6943
7310
|
const triggerAgeSec = opts.triggerAgeSec ?? 300;
|
|
6944
7311
|
const waitTimeoutMs = opts.waitTimeoutMs ?? 5e3;
|
|
6945
7312
|
const waitTimeoutSec = Math.max(1, Math.ceil(waitTimeoutMs / 1e3));
|
|
6946
|
-
const
|
|
6947
|
-
const flagPath = opts.refreshFlagPath;
|
|
7313
|
+
const cooldownSec = Math.max(0, Math.trunc(opts.refreshCooldownSec ?? 30));
|
|
6948
7314
|
const tag = opts.credentialTag;
|
|
7315
|
+
const unavailable = opts.unavailableReason != null && opts.unavailableReason.trim().length > 0;
|
|
7316
|
+
const attemptPath = opts.refreshAttemptPath ?? join(dirname(opts.credentialsPath), `refresh-attempt-${stampSlug(tag)}`);
|
|
7317
|
+
const message = unavailable ? `skaile: mount not connected: ${singleLine(opts.unavailableReason ?? "")}` : `skaile: no credential available for git mount ${mountIdFromTag(tag)} \u2014 the workspace mount is not authenticated.`;
|
|
7318
|
+
const readsStillWorkNote = "skaile: git fetch/ls-remote can still succeed on a public remote; only writes need this credential.";
|
|
6949
7319
|
return `#!/bin/sh
|
|
6950
7320
|
# Generated by skaile-runner -- do not edit.
|
|
6951
7321
|
# Tier-2 git credential helper: serves the latest cached token and triggers a
|
|
@@ -6959,15 +7329,29 @@ op="\${1:-}"
|
|
|
6959
7329
|
# (none today) also bail silently so we don't break future git versions.
|
|
6960
7330
|
[ "$op" = "get" ] || exit 0
|
|
6961
7331
|
|
|
6962
|
-
CRED_FILE
|
|
6963
|
-
FLAG_FILE
|
|
6964
|
-
TAG
|
|
7332
|
+
CRED_FILE=${shQuote(opts.credentialsPath)}
|
|
7333
|
+
FLAG_FILE=${shQuote(opts.refreshFlagPath)}
|
|
7334
|
+
TAG=${shQuote(tag)}
|
|
7335
|
+
ATTEMPT_FILE=${shQuote(attemptPath)}
|
|
7336
|
+
MSG=${shQuote(message)}
|
|
7337
|
+
UNAVAILABLE=${unavailable ? 1 : 0}
|
|
6965
7338
|
|
|
6966
7339
|
# Drain stdin: git always pipes the request envelope. Leaving it unread can
|
|
6967
7340
|
# wedge git's helper FD. We don't actually need any of it -- the credentials
|
|
6968
7341
|
# file already keys by URL prefix via the helper's gitconfig block.
|
|
6969
7342
|
cat > /dev/null 2>&1 || true
|
|
6970
7343
|
|
|
7344
|
+
# 'quit=1' is what actually stops git's helper chain. Exiting 0 with no output
|
|
7345
|
+
# sends git to the next helper and then the terminal prompt ("could not read
|
|
7346
|
+
# Username"); exiting non-zero does the same. Only quit=1 makes git report our
|
|
7347
|
+
# reason as the terminal failure.
|
|
7348
|
+
if [ "$UNAVAILABLE" -eq 1 ]; then
|
|
7349
|
+
printf '%s\\n' "$MSG" >&2
|
|
7350
|
+
printf '%s\\n' ${shQuote(readsStillWorkNote)} >&2
|
|
7351
|
+
printf 'quit=1\\n'
|
|
7352
|
+
exit 0
|
|
7353
|
+
fi
|
|
7354
|
+
|
|
6971
7355
|
# Cross-platform mtime in epoch seconds: GNU stat -c first (Linux), then BSD
|
|
6972
7356
|
# stat -f (macOS), then 0 if neither is present (in which case we skip the
|
|
6973
7357
|
# refresh-flag dance entirely and just serve what we have).
|
|
@@ -6985,38 +7369,59 @@ started_empty=0
|
|
|
6985
7369
|
[ -s "$CRED_FILE" ] || started_empty=1
|
|
6986
7370
|
|
|
6987
7371
|
if [ "$started_empty" -eq 1 ] || [ "$age" -gt ${triggerAgeSec} ]; then
|
|
6988
|
-
#
|
|
6989
|
-
|
|
7372
|
+
# Bound the dance. A refresh that keeps failing would otherwise cost every
|
|
7373
|
+
# git network op a fresh timeout, forever. The stamp below is written only
|
|
7374
|
+
# when a dance changed nothing, so a working refresh is never throttled.
|
|
7375
|
+
last_attempt=$(get_mtime "$ATTEMPT_FILE")
|
|
7376
|
+
if [ $((now - last_attempt)) -ge ${cooldownSec} ]; then
|
|
7377
|
+
# Touch the refresh-flag (atomic open+close emits 'change' to fs.watch).
|
|
7378
|
+
# The subshell is load-bearing: ':' is a POSIX *special built-in*, so a
|
|
7379
|
+
# redirection error on it terminates a non-interactive dash/ash outright --
|
|
7380
|
+
# '|| true' never runs, and the helper would die before reaching the
|
|
7381
|
+
# quit=1 block below. Run the redirection in a child instead.
|
|
7382
|
+
( : > "$FLAG_FILE" ) 2>/dev/null || true
|
|
6990
7383
|
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
else
|
|
6997
|
-
have_usleep=0
|
|
6998
|
-
fi
|
|
6999
|
-
end=$((now + ${waitTimeoutSec}))
|
|
7000
|
-
while :; do
|
|
7001
|
-
cur=$(date +%s 2>/dev/null || echo 0)
|
|
7002
|
-
if [ "$cur" -ge "$end" ]; then
|
|
7003
|
-
break
|
|
7004
|
-
fi
|
|
7005
|
-
new_mtime=$(get_mtime "$CRED_FILE")
|
|
7006
|
-
if [ "$new_mtime" -gt "$mtime" ]; then
|
|
7007
|
-
break
|
|
7008
|
-
fi
|
|
7009
|
-
# A same-second rewrite doesn't advance the second-resolution mtime, so an
|
|
7010
|
-
# empty file that gained content must break on content, not on mtime.
|
|
7011
|
-
if [ "$started_empty" -eq 1 ] && [ -s "$CRED_FILE" ]; then
|
|
7012
|
-
break
|
|
7013
|
-
fi
|
|
7014
|
-
if [ "$have_usleep" -eq 1 ]; then
|
|
7015
|
-
usleep 200000
|
|
7384
|
+
# Poll for the credentials file mtime to advance. usleep gives sub-second
|
|
7385
|
+
# resolution where available; sleep 1 is the portable fallback. Resolve which
|
|
7386
|
+
# once, up front, rather than probing PATH on every iteration.
|
|
7387
|
+
if command -v usleep >/dev/null 2>&1; then
|
|
7388
|
+
have_usleep=1
|
|
7016
7389
|
else
|
|
7017
|
-
|
|
7390
|
+
have_usleep=0
|
|
7391
|
+
fi
|
|
7392
|
+
refreshed=0
|
|
7393
|
+
end=$((now + ${waitTimeoutSec}))
|
|
7394
|
+
while :; do
|
|
7395
|
+
cur=$(date +%s 2>/dev/null || echo 0)
|
|
7396
|
+
if [ "$cur" -ge "$end" ]; then
|
|
7397
|
+
break
|
|
7398
|
+
fi
|
|
7399
|
+
new_mtime=$(get_mtime "$CRED_FILE")
|
|
7400
|
+
if [ "$new_mtime" -gt "$mtime" ]; then
|
|
7401
|
+
refreshed=1
|
|
7402
|
+
break
|
|
7403
|
+
fi
|
|
7404
|
+
# A same-second rewrite doesn't advance the second-resolution mtime, so an
|
|
7405
|
+
# empty file that gained content must break on content, not on mtime.
|
|
7406
|
+
if [ "$started_empty" -eq 1 ] && [ -s "$CRED_FILE" ]; then
|
|
7407
|
+
refreshed=1
|
|
7408
|
+
break
|
|
7409
|
+
fi
|
|
7410
|
+
if [ "$have_usleep" -eq 1 ]; then
|
|
7411
|
+
usleep 200000
|
|
7412
|
+
else
|
|
7413
|
+
sleep 1
|
|
7414
|
+
fi
|
|
7415
|
+
done
|
|
7416
|
+
if [ "$refreshed" -eq 0 ]; then
|
|
7417
|
+
# Subshell for the same reason as the flag write above. Here it also
|
|
7418
|
+
# matters in the ordinary case: the stamp lives in a runner-owned
|
|
7419
|
+
# directory, so under uid separation the agent can only truncate a file
|
|
7420
|
+
# the runner pre-created. A missing pre-create must cost the cooldown,
|
|
7421
|
+
# never the quit=1 report.
|
|
7422
|
+
( : > "$ATTEMPT_FILE" ) 2>/dev/null || true
|
|
7018
7423
|
fi
|
|
7019
|
-
|
|
7424
|
+
fi
|
|
7020
7425
|
fi
|
|
7021
7426
|
|
|
7022
7427
|
# Find the credential line for THIS mount (matched by the trailing tag) and
|
|
@@ -7025,7 +7430,16 @@ fi
|
|
|
7025
7430
|
# more than one matching line (shouldn't happen but defence-in-depth), we
|
|
7026
7431
|
# take the last one (most recently written).
|
|
7027
7432
|
line=$(grep -F " $TAG" "$CRED_FILE" 2>/dev/null | tail -n 1)
|
|
7028
|
-
[ -
|
|
7433
|
+
if [ -z "$line" ]; then
|
|
7434
|
+
# No credential to serve. Terminate the chain rather than exiting 0, which
|
|
7435
|
+
# would hand git back to its username prompt -- the misleading error this
|
|
7436
|
+
# helper exists to replace. The same fetch-succeeds/push-fails confusion
|
|
7437
|
+
# applies here as on the known-dead path, so say so either way.
|
|
7438
|
+
printf '%s\\n' "$MSG" >&2
|
|
7439
|
+
printf '%s\\n' ${shQuote(readsStillWorkNote)} >&2
|
|
7440
|
+
printf 'quit=1\\n'
|
|
7441
|
+
exit 0
|
|
7442
|
+
fi
|
|
7029
7443
|
|
|
7030
7444
|
# Strip the trailing ' <TAG>' from the URL.
|
|
7031
7445
|
url=\${line% *}
|
|
@@ -7170,6 +7584,7 @@ var ManagedGitconfigCollisionError = class extends Error {
|
|
|
7170
7584
|
var blockStart = (mountId) => `# skaile-mount: ${mountId}`;
|
|
7171
7585
|
var blockEnd = (mountId) => `# skaile-mount: ${mountId} (end)`;
|
|
7172
7586
|
var credentialTag = (mountId) => `# skaile-mount: ${mountId}`;
|
|
7587
|
+
var UNAVAILABLE_MARKER = "# skaile-mount-unavailable";
|
|
7173
7588
|
function readFileOrEmpty(path) {
|
|
7174
7589
|
try {
|
|
7175
7590
|
return readFileSync(path, "utf-8");
|
|
@@ -7274,17 +7689,22 @@ function blockUrlPrefix(block) {
|
|
|
7274
7689
|
}
|
|
7275
7690
|
return void 0;
|
|
7276
7691
|
}
|
|
7692
|
+
function isUnavailableBlock(block) {
|
|
7693
|
+
return block.lines.some((l) => l.trim() === UNAVAILABLE_MARKER);
|
|
7694
|
+
}
|
|
7277
7695
|
function writeMountBlock(opts) {
|
|
7278
7696
|
const { mountId, urlPrefix, token, gitconfigPath, credentialsPath, helperScriptPath } = opts;
|
|
7279
7697
|
const text = readFileOrEmpty(gitconfigPath);
|
|
7280
|
-
const
|
|
7281
|
-
for (const b of
|
|
7698
|
+
const parsed = parseBlocks(text);
|
|
7699
|
+
for (const b of parsed) {
|
|
7282
7700
|
if (b.mountId === "" || b.mountId === mountId) continue;
|
|
7701
|
+
if (isUnavailableBlock(b)) continue;
|
|
7283
7702
|
const otherPrefix = blockUrlPrefix(b);
|
|
7284
7703
|
if (otherPrefix === urlPrefix) {
|
|
7285
7704
|
throw new ManagedGitconfigCollisionError(urlPrefix, b.mountId, mountId);
|
|
7286
7705
|
}
|
|
7287
7706
|
}
|
|
7707
|
+
const blocks = parsed.filter((b) => !(isUnavailableBlock(b) && blockUrlPrefix(b) === urlPrefix));
|
|
7288
7708
|
const newBlock = {
|
|
7289
7709
|
mountId,
|
|
7290
7710
|
lines: buildBlock(mountId, urlPrefix, credentialsPath, helperScriptPath)
|
|
@@ -7302,6 +7722,48 @@ function writeMountBlock(opts) {
|
|
|
7302
7722
|
filtered.push(buildCredentialLine(urlPrefix, token, mountId));
|
|
7303
7723
|
writeCredentialLines(credentialsPath, filtered);
|
|
7304
7724
|
}
|
|
7725
|
+
function writeUnavailableMountBlock(opts) {
|
|
7726
|
+
const { mountId, urlPrefix, gitconfigPath, credentialsPath, helperScriptPath } = opts;
|
|
7727
|
+
const lines = readCredentialLines(credentialsPath);
|
|
7728
|
+
const tag = credentialTag(mountId);
|
|
7729
|
+
const filtered = lines.filter((l) => !l.endsWith(tag));
|
|
7730
|
+
if (filtered.length !== lines.length) {
|
|
7731
|
+
writeCredentialLines(credentialsPath, filtered);
|
|
7732
|
+
}
|
|
7733
|
+
const blocks = parseBlocks(readFileOrEmpty(gitconfigPath));
|
|
7734
|
+
for (const b of blocks) {
|
|
7735
|
+
if (b.mountId === "" || b.mountId === mountId) continue;
|
|
7736
|
+
if (isUnavailableBlock(b)) continue;
|
|
7737
|
+
if (blockUrlPrefix(b) === urlPrefix) return;
|
|
7738
|
+
}
|
|
7739
|
+
const newLines = [
|
|
7740
|
+
blockStart(mountId),
|
|
7741
|
+
UNAVAILABLE_MARKER,
|
|
7742
|
+
`[credential "${urlPrefix}"]`,
|
|
7743
|
+
` helper = !sh ${helperScriptPath}`,
|
|
7744
|
+
blockEnd(mountId)
|
|
7745
|
+
];
|
|
7746
|
+
const existing = findExistingBlock(blocks, mountId);
|
|
7747
|
+
if (existing) {
|
|
7748
|
+
existing.lines = newLines;
|
|
7749
|
+
} else {
|
|
7750
|
+
blocks.push({ mountId, lines: newLines });
|
|
7751
|
+
}
|
|
7752
|
+
atomicWrite(gitconfigPath, renderBlocks(blocks), 416);
|
|
7753
|
+
}
|
|
7754
|
+
function removeUnavailableMountBlock(opts) {
|
|
7755
|
+
const { mountId, gitconfigPath, urlPrefix } = opts;
|
|
7756
|
+
const text = readFileOrEmpty(gitconfigPath);
|
|
7757
|
+
if (text.length === 0) return;
|
|
7758
|
+
const blocks = parseBlocks(text);
|
|
7759
|
+
const doomed = new Set(
|
|
7760
|
+
blocks.filter(
|
|
7761
|
+
(b) => isUnavailableBlock(b) && (b.mountId === mountId || urlPrefix !== void 0 && blockUrlPrefix(b) === urlPrefix)
|
|
7762
|
+
)
|
|
7763
|
+
);
|
|
7764
|
+
if (doomed.size === 0) return;
|
|
7765
|
+
atomicWrite(gitconfigPath, renderBlocks(blocks.filter((b) => !doomed.has(b))), 416);
|
|
7766
|
+
}
|
|
7305
7767
|
function removeMountBlock(opts) {
|
|
7306
7768
|
const { mountId, gitconfigPath, credentialsPath } = opts;
|
|
7307
7769
|
const text = readFileOrEmpty(gitconfigPath);
|
|
@@ -7319,6 +7781,17 @@ function removeMountBlock(opts) {
|
|
|
7319
7781
|
function writeHelperScript(scriptPath, body) {
|
|
7320
7782
|
atomicWrite(scriptPath, body, 493);
|
|
7321
7783
|
}
|
|
7784
|
+
function ensureRefreshAttemptStamp(stampPath) {
|
|
7785
|
+
if (existsSync(stampPath)) return;
|
|
7786
|
+
const dir = dirname(stampPath);
|
|
7787
|
+
if (!existsSync(dir)) {
|
|
7788
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
7789
|
+
}
|
|
7790
|
+
const fd = openSync(stampPath, "a", 432);
|
|
7791
|
+
closeSync(fd);
|
|
7792
|
+
chmodSync(stampPath, 432);
|
|
7793
|
+
utimesSync(stampPath, 0, 0);
|
|
7794
|
+
}
|
|
7322
7795
|
function atomicReplaceCredential(opts) {
|
|
7323
7796
|
const { mountId, urlPrefix, token, credentialsPath } = opts;
|
|
7324
7797
|
const lines = readCredentialLines(credentialsPath);
|
|
@@ -10079,6 +10552,6 @@ function createConnector() {
|
|
|
10079
10552
|
return new FlowAdapter();
|
|
10080
10553
|
}
|
|
10081
10554
|
|
|
10082
|
-
export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PortPool, PostgresConnector, S3Connector, SQLiteConnector, SessionInlineSubFlowRunner, SessionNodeExecutor, SessionSubpromptRunner, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildFlowPromptEvent, buildSdkConnectorTools, buildSdkFlowTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, driveOrchestratorTurn, ensureDirMode, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
|
|
10083
|
-
//# sourceMappingURL=chunk-
|
|
10084
|
-
//# sourceMappingURL=chunk-
|
|
10555
|
+
export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PortPool, PostgresConnector, S3Connector, SQLiteConnector, SessionInlineSubFlowRunner, SessionNodeExecutor, SessionSubpromptRunner, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildFlowPromptEvent, buildSdkConnectorTools, buildSdkFlowTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, driveOrchestratorTurn, ensureDirMode, ensureFleetMounted, ensureRefreshAttemptStamp, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, removeUnavailableMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock, writeUnavailableMountBlock };
|
|
10556
|
+
//# sourceMappingURL=chunk-6GDVGAIZ.js.map
|
|
10557
|
+
//# sourceMappingURL=chunk-6GDVGAIZ.js.map
|