@skaile/workspaces 3.7.3 → 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 +103 -0
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-6XDLVHQJ.js → asset-feeds-7HM3HONM.js} +4 -4
- package/dist/{asset-feeds-6XDLVHQJ.js.map → asset-feeds-7HM3HONM.js.map} +1 -1
- package/dist/asset-manager/index.js +1 -1
- package/dist/bridge/drivers/claude-sdk.js +229 -23
- 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-WHOM6NF3.js → chunk-2GREL3OD.js} +3 -3
- package/dist/{chunk-WHOM6NF3.js.map → chunk-2GREL3OD.js.map} +1 -1
- package/dist/{chunk-ZMCBWZ77.js → chunk-2JW5SWIU.js} +6 -6
- package/dist/{chunk-ZMCBWZ77.js.map → chunk-2JW5SWIU.js.map} +1 -1
- package/dist/{chunk-5Z4JLURL.js → chunk-4WTC6BSV.js} +3 -3
- package/dist/{chunk-5Z4JLURL.js.map → chunk-4WTC6BSV.js.map} +1 -1
- package/dist/{chunk-DGWJAPRC.js → chunk-6GDVGAIZ.js} +447 -95
- package/dist/chunk-6GDVGAIZ.js.map +1 -0
- package/dist/{chunk-BSQGQMGA.js → chunk-7SHXLJQP.js} +3 -3
- package/dist/{chunk-BSQGQMGA.js.map → chunk-7SHXLJQP.js.map} +1 -1
- package/dist/{chunk-YICH6VQB.js → chunk-B7TA2XI7.js} +2 -2
- package/dist/{chunk-YICH6VQB.js.map → chunk-B7TA2XI7.js.map} +1 -1
- package/dist/{chunk-ML6OHA3S.js → chunk-BGAAZMBK.js} +2 -2
- package/dist/chunk-BGAAZMBK.js.map +1 -0
- package/dist/{chunk-765ZGKVC.js → chunk-DGDELPOD.js} +3 -3
- package/dist/{chunk-765ZGKVC.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-6GTPLCMO.js → chunk-IH2KJYAU.js} +142 -13
- package/dist/chunk-IH2KJYAU.js.map +1 -0
- package/dist/{chunk-UGBN7MQZ.js → chunk-KNPBMR2P.js} +3 -3
- package/dist/{chunk-UGBN7MQZ.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-OOUQWDD5.js → chunk-OZZCPTOG.js} +6 -6
- package/dist/{chunk-OOUQWDD5.js.map → chunk-OZZCPTOG.js.map} +1 -1
- package/dist/{chunk-V7CKJGT4.js → chunk-PRUCNSBI.js} +4 -4
- package/dist/{chunk-V7CKJGT4.js.map → chunk-PRUCNSBI.js.map} +1 -1
- package/dist/{chunk-MGPHEYW2.js → chunk-QVQH6GV2.js} +2 -2
- package/dist/{chunk-MGPHEYW2.js.map → chunk-QVQH6GV2.js.map} +1 -1
- package/dist/{chunk-FSWYUSCP.js → chunk-RVXBOKHK.js} +3 -3
- package/dist/{chunk-FSWYUSCP.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-TSHLAVNU.js → ensure-sources-42LTJ5QI.js} +4 -4
- package/dist/{ensure-sources-TSHLAVNU.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 +28 -0
- 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-PVM4MQV2.js → helpers-BP7FX6CO.js} +4 -4
- package/dist/{helpers-PVM4MQV2.js.map → helpers-BP7FX6CO.js.map} +1 -1
- package/dist/{library-publish-P7ZVEY6I.js → library-publish-X5Z32WJY.js} +6 -6
- package/dist/{library-publish-P7ZVEY6I.js.map → library-publish-X5Z32WJY.js.map} +1 -1
- package/dist/{open-library-QG4LHFQQ.js → open-library-PHUMBYHP.js} +5 -5
- package/dist/{open-library-QG4LHFQQ.js.map → open-library-PHUMBYHP.js.map} +1 -1
- package/dist/{project-YE6GKQTZ.js → project-HAMCXPNE.js} +5 -5
- package/dist/{project-YE6GKQTZ.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-UYLZFES6.js → setup-IOOGR3EL.js} +4 -4
- package/dist/{setup-UYLZFES6.js.map → setup-IOOGR3EL.js.map} +1 -1
- package/dist/{source-5GTHOFT6.js → source-772GLNM6.js} +6 -6
- package/dist/{source-5GTHOFT6.js.map → source-772GLNM6.js.map} +1 -1
- package/dist/{source-manifest-publish-DK3CPBSI.js → source-manifest-publish-LAUGDQ5G.js} +7 -7
- package/dist/{source-manifest-publish-DK3CPBSI.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-6GTPLCMO.js.map +0 -1
- package/dist/chunk-DGWJAPRC.js.map +0 -1
- package/dist/chunk-ML6OHA3S.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';
|
|
@@ -1494,53 +1494,59 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1494
1494
|
if (!isValidGitRefOrSha(branch)) {
|
|
1495
1495
|
throw new Error(`Git connector: invalid branch name "${branch}"`);
|
|
1496
1496
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
await this.
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
this.startPeriodicFetch(state, targetDir);
|
|
1521
|
-
}
|
|
1522
|
-
if (declaration.mount?.exposeAccessToken === true) {
|
|
1523
|
-
try {
|
|
1524
|
-
this.exposeManagedCredential(declaration, state, url, initial);
|
|
1525
|
-
} catch (err) {
|
|
1526
|
-
this.log.error("managed-gitconfig write failed", err, { connectorId: declaration.id });
|
|
1527
|
-
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);
|
|
1528
1520
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
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
|
+
}
|
|
1536
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;
|
|
1537
1549
|
}
|
|
1538
|
-
this.log.info("mount ok", {
|
|
1539
|
-
targetDir,
|
|
1540
|
-
activeBranch: state.activeBranch,
|
|
1541
|
-
durationMs: Date.now() - started
|
|
1542
|
-
});
|
|
1543
|
-
return this.makeHandle(declaration, state, targetDir);
|
|
1544
1550
|
}
|
|
1545
1551
|
/**
|
|
1546
1552
|
* Bring `targetDir` into a usable checkout state for `connect()`.
|
|
@@ -1994,6 +2000,104 @@ var GitConnector = class extends AbstractConnector {
|
|
|
1994
2000
|
}
|
|
1995
2001
|
return { authRef, token, expiresAt: null };
|
|
1996
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
|
+
}
|
|
1997
2101
|
exposeManagedCredential(declaration, state, url, initial) {
|
|
1998
2102
|
const token = initial.token;
|
|
1999
2103
|
if (!token) {
|
|
@@ -2021,15 +2125,22 @@ var GitConnector = class extends AbstractConnector {
|
|
|
2021
2125
|
const urlPrefix = this.deriveUrlPrefix(url);
|
|
2022
2126
|
const refreshFlagPath = join(skaileDir, "refresh-request");
|
|
2023
2127
|
const helperScriptPath = join(skaileDir, `credential-helper-${declaration.id}.sh`);
|
|
2128
|
+
const refreshAttemptPath = join(skaileDir, `refresh-attempt-${declaration.id}`);
|
|
2024
2129
|
const credentialTag2 = `# skaile-mount: ${declaration.id}`;
|
|
2025
2130
|
const scriptBody = renderCredentialHelperScript({
|
|
2026
2131
|
credentialsPath,
|
|
2027
2132
|
refreshFlagPath,
|
|
2028
|
-
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
|
|
2029
2137
|
});
|
|
2138
|
+
this.ensureAttemptStamp(declaration.id, refreshAttemptPath);
|
|
2139
|
+
let helperWritten = true;
|
|
2030
2140
|
try {
|
|
2031
2141
|
writeHelperScript(helperScriptPath, scriptBody);
|
|
2032
2142
|
} catch (err) {
|
|
2143
|
+
helperWritten = false;
|
|
2033
2144
|
this.log.warn(
|
|
2034
2145
|
"managed-gitconfig: failed to write credential-helper script -- falling back to static helper",
|
|
2035
2146
|
{
|
|
@@ -2044,7 +2155,12 @@ var GitConnector = class extends AbstractConnector {
|
|
|
2044
2155
|
token,
|
|
2045
2156
|
gitconfigPath,
|
|
2046
2157
|
credentialsPath,
|
|
2047
|
-
|
|
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 } : {}
|
|
2048
2164
|
});
|
|
2049
2165
|
state.managedGitconfig = { gitconfigPath, credentialsPath, urlPrefix };
|
|
2050
2166
|
const providerKind = initial.authRef?.kind ?? "pat";
|
|
@@ -5925,6 +6041,13 @@ var ConnectorManager = class {
|
|
|
5925
6041
|
}
|
|
5926
6042
|
opts;
|
|
5927
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();
|
|
5928
6051
|
// Allocated absolute mount target → owning connector id; collisions rejected at setup, released on disconnect.
|
|
5929
6052
|
mountTargets = /* @__PURE__ */ new Map();
|
|
5930
6053
|
workspaceDir;
|
|
@@ -6063,6 +6186,30 @@ var ConnectorManager = class {
|
|
|
6063
6186
|
}
|
|
6064
6187
|
this.mountTargets.set(absPath, id);
|
|
6065
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
|
+
}
|
|
6066
6213
|
/** Release every mount target owned by `id` (on disconnect). */
|
|
6067
6214
|
releaseMountTarget(id) {
|
|
6068
6215
|
for (const [path, owner] of [...this.mountTargets]) {
|
|
@@ -6215,6 +6362,7 @@ var ConnectorManager = class {
|
|
|
6215
6362
|
handle,
|
|
6216
6363
|
declaration: decl
|
|
6217
6364
|
});
|
|
6365
|
+
this.failedConnectors.delete(decl.id);
|
|
6218
6366
|
this.opts.onStatusChange?.({
|
|
6219
6367
|
type: "connector_status",
|
|
6220
6368
|
connectorId: decl.id,
|
|
@@ -6229,6 +6377,7 @@ var ConnectorManager = class {
|
|
|
6229
6377
|
} catch (err) {
|
|
6230
6378
|
if (!this.active.has(decl.id)) this.releaseMountTarget(decl.id);
|
|
6231
6379
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6380
|
+
this.recordFailure(decl, msg);
|
|
6232
6381
|
this.mgrLog.error("connect failed", err, { id: decl.id, driver: decl.driver });
|
|
6233
6382
|
this.opts.onStatusChange?.({
|
|
6234
6383
|
type: "connector_status",
|
|
@@ -6273,6 +6422,7 @@ var ConnectorManager = class {
|
|
|
6273
6422
|
}
|
|
6274
6423
|
this.active.clear();
|
|
6275
6424
|
this.mountTargets.clear();
|
|
6425
|
+
this.failedConnectors.clear();
|
|
6276
6426
|
this.mgrLog.info("disconnectAll complete");
|
|
6277
6427
|
}
|
|
6278
6428
|
// ── Hot-plug ──────────────────────────────────────────────────────────────────
|
|
@@ -6307,6 +6457,7 @@ var ConnectorManager = class {
|
|
|
6307
6457
|
() => connector.connect(declaration, ctx)
|
|
6308
6458
|
);
|
|
6309
6459
|
this.active.set(declaration.id, { connector, handle, declaration });
|
|
6460
|
+
this.failedConnectors.delete(declaration.id);
|
|
6310
6461
|
this.opts.onStatusChange?.({
|
|
6311
6462
|
type: "connector_status",
|
|
6312
6463
|
connectorId: declaration.id,
|
|
@@ -6316,6 +6467,7 @@ var ConnectorManager = class {
|
|
|
6316
6467
|
} catch (err) {
|
|
6317
6468
|
this.releaseMountTarget(declaration.id);
|
|
6318
6469
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6470
|
+
this.recordFailure(declaration, msg);
|
|
6319
6471
|
this.opts.onStatusChange?.({
|
|
6320
6472
|
type: "connector_status",
|
|
6321
6473
|
connectorId: declaration.id,
|
|
@@ -6328,9 +6480,25 @@ var ConnectorManager = class {
|
|
|
6328
6480
|
/**
|
|
6329
6481
|
* Disconnect a single connector by ID and stop its watcher.
|
|
6330
6482
|
*
|
|
6331
|
-
*
|
|
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
|
|
6332
6490
|
*/
|
|
6333
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
|
+
}
|
|
6334
6502
|
const entry = this.get(id);
|
|
6335
6503
|
if (entry.watchHandle) {
|
|
6336
6504
|
try {
|
|
@@ -6350,6 +6518,7 @@ var ConnectorManager = class {
|
|
|
6350
6518
|
await entry.connector.disconnect(entry.handle);
|
|
6351
6519
|
} finally {
|
|
6352
6520
|
this.active.delete(id);
|
|
6521
|
+
this.failedConnectors.delete(id);
|
|
6353
6522
|
this.releaseMountTarget(id);
|
|
6354
6523
|
this.opts.onStatusChange?.({
|
|
6355
6524
|
type: "connector_status",
|
|
@@ -6465,6 +6634,22 @@ var ConnectorManager = class {
|
|
|
6465
6634
|
status: entry.handle.status
|
|
6466
6635
|
}));
|
|
6467
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
|
+
}
|
|
6468
6653
|
/**
|
|
6469
6654
|
* Lists git-backed connectors, surfacing the fields prompt-builders and the
|
|
6470
6655
|
* runner's wake-mid-401 refresh handler need.
|
|
@@ -6679,6 +6864,14 @@ var ConnectorManager = class {
|
|
|
6679
6864
|
}
|
|
6680
6865
|
};
|
|
6681
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
|
+
}
|
|
6682
6875
|
async function buildSdkConnectorTools(manager, options) {
|
|
6683
6876
|
const shellAccess = options?.shellAccess ?? true;
|
|
6684
6877
|
let sdk = options?.sdk;
|
|
@@ -6693,11 +6886,14 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6693
6886
|
const tools = [];
|
|
6694
6887
|
tools.push({
|
|
6695
6888
|
name: "connector_list",
|
|
6696
|
-
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).",
|
|
6697
6890
|
inputSchema: {},
|
|
6698
6891
|
handler: async () => {
|
|
6699
6892
|
try {
|
|
6700
|
-
const entries =
|
|
6893
|
+
const entries = {
|
|
6894
|
+
connected: manager.listConnectors(),
|
|
6895
|
+
failed: readFailedConnectors(manager)
|
|
6896
|
+
};
|
|
6701
6897
|
return { content: [{ type: "text", text: JSON.stringify(entries, null, 2) }] };
|
|
6702
6898
|
} catch (err) {
|
|
6703
6899
|
return {
|
|
@@ -6935,7 +7131,8 @@ function buildOperationInputSchema(args, z5) {
|
|
|
6935
7131
|
function buildConnectorPromptSection(manager, options) {
|
|
6936
7132
|
const shellAccess = options?.shellAccess ?? true;
|
|
6937
7133
|
const allConnectors = manager.listConnectors();
|
|
6938
|
-
|
|
7134
|
+
const failed = readFailedConnectors(manager);
|
|
7135
|
+
if (allConnectors.length === 0 && failed.length === 0) return "";
|
|
6939
7136
|
const sections = [];
|
|
6940
7137
|
const faces = allConnectors.map((c) => {
|
|
6941
7138
|
try {
|
|
@@ -6953,8 +7150,46 @@ function buildConnectorPromptSection(manager, options) {
|
|
|
6953
7150
|
if (toolConnectors.length > 0) {
|
|
6954
7151
|
sections.push(buildToolSection(manager, toolConnectors, shellAccess));
|
|
6955
7152
|
}
|
|
7153
|
+
if (failed.length > 0) {
|
|
7154
|
+
sections.unshift(buildFailedConnectorSection(failed));
|
|
7155
|
+
}
|
|
6956
7156
|
return sections.join("\n\n");
|
|
6957
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
|
+
}
|
|
6958
7193
|
function buildFilesystemSection(manager, fsConnectors) {
|
|
6959
7194
|
const lines = [
|
|
6960
7195
|
"## Mounts",
|
|
@@ -7058,15 +7293,29 @@ function safeHost(source) {
|
|
|
7058
7293
|
return source;
|
|
7059
7294
|
}
|
|
7060
7295
|
}
|
|
7061
|
-
|
|
7062
|
-
|
|
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
|
+
}
|
|
7063
7309
|
function renderCredentialHelperScript(opts) {
|
|
7064
7310
|
const triggerAgeSec = opts.triggerAgeSec ?? 300;
|
|
7065
7311
|
const waitTimeoutMs = opts.waitTimeoutMs ?? 5e3;
|
|
7066
7312
|
const waitTimeoutSec = Math.max(1, Math.ceil(waitTimeoutMs / 1e3));
|
|
7067
|
-
const
|
|
7068
|
-
const flagPath = opts.refreshFlagPath;
|
|
7313
|
+
const cooldownSec = Math.max(0, Math.trunc(opts.refreshCooldownSec ?? 30));
|
|
7069
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.";
|
|
7070
7319
|
return `#!/bin/sh
|
|
7071
7320
|
# Generated by skaile-runner -- do not edit.
|
|
7072
7321
|
# Tier-2 git credential helper: serves the latest cached token and triggers a
|
|
@@ -7080,15 +7329,29 @@ op="\${1:-}"
|
|
|
7080
7329
|
# (none today) also bail silently so we don't break future git versions.
|
|
7081
7330
|
[ "$op" = "get" ] || exit 0
|
|
7082
7331
|
|
|
7083
|
-
CRED_FILE
|
|
7084
|
-
FLAG_FILE
|
|
7085
|
-
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}
|
|
7086
7338
|
|
|
7087
7339
|
# Drain stdin: git always pipes the request envelope. Leaving it unread can
|
|
7088
7340
|
# wedge git's helper FD. We don't actually need any of it -- the credentials
|
|
7089
7341
|
# file already keys by URL prefix via the helper's gitconfig block.
|
|
7090
7342
|
cat > /dev/null 2>&1 || true
|
|
7091
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
|
+
|
|
7092
7355
|
# Cross-platform mtime in epoch seconds: GNU stat -c first (Linux), then BSD
|
|
7093
7356
|
# stat -f (macOS), then 0 if neither is present (in which case we skip the
|
|
7094
7357
|
# refresh-flag dance entirely and just serve what we have).
|
|
@@ -7106,38 +7369,59 @@ started_empty=0
|
|
|
7106
7369
|
[ -s "$CRED_FILE" ] || started_empty=1
|
|
7107
7370
|
|
|
7108
7371
|
if [ "$started_empty" -eq 1 ] || [ "$age" -gt ${triggerAgeSec} ]; then
|
|
7109
|
-
#
|
|
7110
|
-
|
|
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
|
|
7111
7383
|
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
else
|
|
7118
|
-
have_usleep=0
|
|
7119
|
-
fi
|
|
7120
|
-
end=$((now + ${waitTimeoutSec}))
|
|
7121
|
-
while :; do
|
|
7122
|
-
cur=$(date +%s 2>/dev/null || echo 0)
|
|
7123
|
-
if [ "$cur" -ge "$end" ]; then
|
|
7124
|
-
break
|
|
7125
|
-
fi
|
|
7126
|
-
new_mtime=$(get_mtime "$CRED_FILE")
|
|
7127
|
-
if [ "$new_mtime" -gt "$mtime" ]; then
|
|
7128
|
-
break
|
|
7129
|
-
fi
|
|
7130
|
-
# A same-second rewrite doesn't advance the second-resolution mtime, so an
|
|
7131
|
-
# empty file that gained content must break on content, not on mtime.
|
|
7132
|
-
if [ "$started_empty" -eq 1 ] && [ -s "$CRED_FILE" ]; then
|
|
7133
|
-
break
|
|
7134
|
-
fi
|
|
7135
|
-
if [ "$have_usleep" -eq 1 ]; then
|
|
7136
|
-
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
|
|
7137
7389
|
else
|
|
7138
|
-
|
|
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
|
|
7139
7423
|
fi
|
|
7140
|
-
|
|
7424
|
+
fi
|
|
7141
7425
|
fi
|
|
7142
7426
|
|
|
7143
7427
|
# Find the credential line for THIS mount (matched by the trailing tag) and
|
|
@@ -7146,7 +7430,16 @@ fi
|
|
|
7146
7430
|
# more than one matching line (shouldn't happen but defence-in-depth), we
|
|
7147
7431
|
# take the last one (most recently written).
|
|
7148
7432
|
line=$(grep -F " $TAG" "$CRED_FILE" 2>/dev/null | tail -n 1)
|
|
7149
|
-
[ -
|
|
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
|
|
7150
7443
|
|
|
7151
7444
|
# Strip the trailing ' <TAG>' from the URL.
|
|
7152
7445
|
url=\${line% *}
|
|
@@ -7291,6 +7584,7 @@ var ManagedGitconfigCollisionError = class extends Error {
|
|
|
7291
7584
|
var blockStart = (mountId) => `# skaile-mount: ${mountId}`;
|
|
7292
7585
|
var blockEnd = (mountId) => `# skaile-mount: ${mountId} (end)`;
|
|
7293
7586
|
var credentialTag = (mountId) => `# skaile-mount: ${mountId}`;
|
|
7587
|
+
var UNAVAILABLE_MARKER = "# skaile-mount-unavailable";
|
|
7294
7588
|
function readFileOrEmpty(path) {
|
|
7295
7589
|
try {
|
|
7296
7590
|
return readFileSync(path, "utf-8");
|
|
@@ -7395,17 +7689,22 @@ function blockUrlPrefix(block) {
|
|
|
7395
7689
|
}
|
|
7396
7690
|
return void 0;
|
|
7397
7691
|
}
|
|
7692
|
+
function isUnavailableBlock(block) {
|
|
7693
|
+
return block.lines.some((l) => l.trim() === UNAVAILABLE_MARKER);
|
|
7694
|
+
}
|
|
7398
7695
|
function writeMountBlock(opts) {
|
|
7399
7696
|
const { mountId, urlPrefix, token, gitconfigPath, credentialsPath, helperScriptPath } = opts;
|
|
7400
7697
|
const text = readFileOrEmpty(gitconfigPath);
|
|
7401
|
-
const
|
|
7402
|
-
for (const b of
|
|
7698
|
+
const parsed = parseBlocks(text);
|
|
7699
|
+
for (const b of parsed) {
|
|
7403
7700
|
if (b.mountId === "" || b.mountId === mountId) continue;
|
|
7701
|
+
if (isUnavailableBlock(b)) continue;
|
|
7404
7702
|
const otherPrefix = blockUrlPrefix(b);
|
|
7405
7703
|
if (otherPrefix === urlPrefix) {
|
|
7406
7704
|
throw new ManagedGitconfigCollisionError(urlPrefix, b.mountId, mountId);
|
|
7407
7705
|
}
|
|
7408
7706
|
}
|
|
7707
|
+
const blocks = parsed.filter((b) => !(isUnavailableBlock(b) && blockUrlPrefix(b) === urlPrefix));
|
|
7409
7708
|
const newBlock = {
|
|
7410
7709
|
mountId,
|
|
7411
7710
|
lines: buildBlock(mountId, urlPrefix, credentialsPath, helperScriptPath)
|
|
@@ -7423,6 +7722,48 @@ function writeMountBlock(opts) {
|
|
|
7423
7722
|
filtered.push(buildCredentialLine(urlPrefix, token, mountId));
|
|
7424
7723
|
writeCredentialLines(credentialsPath, filtered);
|
|
7425
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
|
+
}
|
|
7426
7767
|
function removeMountBlock(opts) {
|
|
7427
7768
|
const { mountId, gitconfigPath, credentialsPath } = opts;
|
|
7428
7769
|
const text = readFileOrEmpty(gitconfigPath);
|
|
@@ -7440,6 +7781,17 @@ function removeMountBlock(opts) {
|
|
|
7440
7781
|
function writeHelperScript(scriptPath, body) {
|
|
7441
7782
|
atomicWrite(scriptPath, body, 493);
|
|
7442
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
|
+
}
|
|
7443
7795
|
function atomicReplaceCredential(opts) {
|
|
7444
7796
|
const { mountId, urlPrefix, token, credentialsPath } = opts;
|
|
7445
7797
|
const lines = readCredentialLines(credentialsPath);
|
|
@@ -10200,6 +10552,6 @@ function createConnector() {
|
|
|
10200
10552
|
return new FlowAdapter();
|
|
10201
10553
|
}
|
|
10202
10554
|
|
|
10203
|
-
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 };
|
|
10204
|
-
//# sourceMappingURL=chunk-
|
|
10205
|
-
//# 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
|