first-tree-staging 0.5.2-staging.23.1 → 0.5.2-staging.24.1
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/dist/cli/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as cliFetch, f as channelConfig, l as print, n as bindFeishuUser, o as CLI_USER_AGENT, r as documentContextSchema, s as COMMAND_VERSION, t as bindFeishuBot, u as setJsonMode } from "../feishu-DmpOdXci.mjs";
|
|
3
|
-
import { $ as agentConfigSchema, A as installClientService, C as checkClientConfig, D as printResults, E as checkWebSocket, F as startClientService, G as loadCredentials, I as stopClientService, J as saveAgentConfig, M as isServiceUnitDriftDetected, O as reconcileAgentConfigs, P as restartClientService, Q as removeLocalAgent, R as ClientRuntime, S as checkBackgroundService, T as checkServerReachable, U as ensureFreshAccessToken, W as ensureFreshAdminToken, X as findStaleAliases, Y as saveCredentials, Z as formatStaleReason, _t as buildMessageDocumentSnapshots, a as declineUpdate, at as initConfig, b as migrateLocalAgentDirs, bt as applyClientLoggerConfig, c as detectInstallMode, ct as resetConfig, d as reconcileLocalRuntimeProviders, dt as setConfigValue, et as clientConfigSchema, f as uploadClientCapabilities, ft as ClientOrgMismatchError, gt as SessionRegistry, h as promptMissingFields, ht as SdkError, i as createExecuteUpdate, it as getConfigValue, j as isServiceSupported, k as getClientServiceStatus, l as fetchLatestVersion, lt as resetConfigMeta, m as promptAddAgent, mt as FirstTreeHubSDK, n as decodeJwtPayload, nt as defaultDataDir, o as promptUpdate, ot as loadAgents, pt as ClientUserMismatchError, q as resolveServerUrl, r as deriveHubUrlFromToken, rt as defaultHome, s as PACKAGE_NAME, st as readConfigFile, t as HubUrlDerivationError, tt as defaultConfigDir, u as installGlobalLatest, ut as resolveConfigReadonly, vt as cleanWorkspaces, w as checkNodeVersion, x as checkAgentConfigs, xt as configureClientLoggerForService, y as createApiNameResolver, yt as probeCapabilities, z as handleClientOrgMismatch } from "../connect-token-
|
|
3
|
+
import { $ as agentConfigSchema, A as installClientService, C as checkClientConfig, D as printResults, E as checkWebSocket, F as startClientService, G as loadCredentials, I as stopClientService, J as saveAgentConfig, M as isServiceUnitDriftDetected, O as reconcileAgentConfigs, P as restartClientService, Q as removeLocalAgent, R as ClientRuntime, S as checkBackgroundService, T as checkServerReachable, U as ensureFreshAccessToken, W as ensureFreshAdminToken, X as findStaleAliases, Y as saveCredentials, Z as formatStaleReason, _t as buildMessageDocumentSnapshots, a as declineUpdate, at as initConfig, b as migrateLocalAgentDirs, bt as applyClientLoggerConfig, c as detectInstallMode, ct as resetConfig, d as reconcileLocalRuntimeProviders, dt as setConfigValue, et as clientConfigSchema, f as uploadClientCapabilities, ft as ClientOrgMismatchError, gt as SessionRegistry, h as promptMissingFields, ht as SdkError, i as createExecuteUpdate, it as getConfigValue, j as isServiceSupported, k as getClientServiceStatus, l as fetchLatestVersion, lt as resetConfigMeta, m as promptAddAgent, mt as FirstTreeHubSDK, n as decodeJwtPayload, nt as defaultDataDir, o as promptUpdate, ot as loadAgents, pt as ClientUserMismatchError, q as resolveServerUrl, r as deriveHubUrlFromToken, rt as defaultHome, s as PACKAGE_NAME, st as readConfigFile, t as HubUrlDerivationError, tt as defaultConfigDir, u as installGlobalLatest, ut as resolveConfigReadonly, vt as cleanWorkspaces, w as checkNodeVersion, x as checkAgentConfigs, xt as configureClientLoggerForService, y as createApiNameResolver, yt as probeCapabilities, z as handleClientOrgMismatch } from "../connect-token-CJozTiUf.mjs";
|
|
4
4
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
import { homedir, tmpdir } from "node:os";
|
|
6
6
|
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -7332,6 +7332,33 @@ const DEFAULT_CLONE_TIMEOUT_MS = 300 * 1e3;
|
|
|
7332
7332
|
const FETCH_REFSPEC = "+refs/heads/*:refs/remotes/origin/*";
|
|
7333
7333
|
const SESSION_BRANCH_PREFIX = "hub-session";
|
|
7334
7334
|
/**
|
|
7335
|
+
* Backoff schedule for retrying a remote-talking git op (`fetch`,
|
|
7336
|
+
* `remote set-head --auto`) on a transient network-layer failure. Each entry
|
|
7337
|
+
* is the wait BEFORE the next attempt, so this lays out 4 attempts total
|
|
7338
|
+
* (1 initial + 3 retries) with a worst-case sleep budget of ~5s **per
|
|
7339
|
+
* protocol attempt**.
|
|
7340
|
+
*
|
|
7341
|
+
* Per-call totals depend on whether the helper chains a primary + fallback:
|
|
7342
|
+
* - `fetchOrigin` ≤ 5s sleep budget: the SSH fallback only fires when
|
|
7343
|
+
* the primary's terminal failure is credential-shaped, which a transient
|
|
7344
|
+
* stderr will never be. So a transient-only failure burns ~5s.
|
|
7345
|
+
* - `setHeadAuto` ≤ 10s sleep budget: the fallback fires on ANY terminal
|
|
7346
|
+
* primary failure, so a doubly-transient run burns ~5s + ~5s ≈ 10s.
|
|
7347
|
+
*
|
|
7348
|
+
* Tuned for the operator-visible case of session start/resume hitting a
|
|
7349
|
+
* proxy / VPN that flips a rule, restarts a TUN, or drops a TLS handshake
|
|
7350
|
+
* mid-flight. The user's own manual workaround in those cases is "@-mention
|
|
7351
|
+
* the agent again 2 seconds later" — this is that, automated.
|
|
7352
|
+
*
|
|
7353
|
+
* Per-step jitter (up to 25% of the delay) prevents thundering-herd retries
|
|
7354
|
+
* when several agents resume in lockstep against the same flaky proxy.
|
|
7355
|
+
*/
|
|
7356
|
+
const NETWORK_RETRY_DELAYS_MS = [
|
|
7357
|
+
500,
|
|
7358
|
+
1500,
|
|
7359
|
+
3e3
|
|
7360
|
+
];
|
|
7361
|
+
/**
|
|
7335
7362
|
* Hash a repo URL into the mirror directory name.
|
|
7336
7363
|
*
|
|
7337
7364
|
* The hash is computed over a *canonical* form (host + path, lowercased
|
|
@@ -7510,6 +7537,20 @@ function createGitMirrorManager(opts) {
|
|
|
7510
7537
|
return false;
|
|
7511
7538
|
}
|
|
7512
7539
|
}
|
|
7540
|
+
async function gitWithNetworkRetry(args, cwd, timeoutMs, opLabel) {
|
|
7541
|
+
return retryOnTransientNetwork(() => git(args, cwd, timeoutMs), {
|
|
7542
|
+
delaysMs: NETWORK_RETRY_DELAYS_MS,
|
|
7543
|
+
isRetryable: isLikelyTransientNetworkError,
|
|
7544
|
+
onRetry: ({ attempt, nextDelayMs, message }) => {
|
|
7545
|
+
log?.warn({
|
|
7546
|
+
op: opLabel,
|
|
7547
|
+
attempt,
|
|
7548
|
+
nextDelayMs,
|
|
7549
|
+
stderr: message.slice(0, 512)
|
|
7550
|
+
}, "git remote op hit transient network error — retrying");
|
|
7551
|
+
}
|
|
7552
|
+
});
|
|
7553
|
+
}
|
|
7513
7554
|
/**
|
|
7514
7555
|
* `git fetch --prune origin` with one-shot bidirectional protocol fallback.
|
|
7515
7556
|
*
|
|
@@ -7547,23 +7588,38 @@ function createGitMirrorManager(opts) {
|
|
|
7547
7588
|
* here: callers that need origin/HEAD already get a clear
|
|
7548
7589
|
* `GitMirrorError("Cannot resolve default branch …")` if both attempts fail.
|
|
7549
7590
|
*/
|
|
7591
|
+
/**
|
|
7592
|
+
* Worst-case sleep budget: ~10s (primary ~5s + fallback ~5s). Unlike
|
|
7593
|
+
* `fetchOrigin`, which gates fallback on a credential-shaped terminal
|
|
7594
|
+
* error, `setHeadAuto` always falls through on any primary failure, so
|
|
7595
|
+
* a doubly-transient run pays both retry budgets. Per-call 30s timeout
|
|
7596
|
+
* still caps each individual git invocation.
|
|
7597
|
+
*/
|
|
7550
7598
|
async function setHeadAuto(mirrorPath, originUrl) {
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7599
|
+
try {
|
|
7600
|
+
await gitWithNetworkRetry([
|
|
7601
|
+
"remote",
|
|
7602
|
+
"set-head",
|
|
7603
|
+
"origin",
|
|
7604
|
+
"--auto"
|
|
7605
|
+
], mirrorPath, 3e4, "set-head:primary");
|
|
7606
|
+
return true;
|
|
7607
|
+
} catch {}
|
|
7557
7608
|
const direction = pickFallbackDirection(originUrl);
|
|
7558
7609
|
if (!direction) return false;
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7610
|
+
try {
|
|
7611
|
+
await gitWithNetworkRetry([
|
|
7612
|
+
"-c",
|
|
7613
|
+
`url.${direction.peerBase}.insteadOf=${direction.originBase}`,
|
|
7614
|
+
"remote",
|
|
7615
|
+
"set-head",
|
|
7616
|
+
"origin",
|
|
7617
|
+
"--auto"
|
|
7618
|
+
], mirrorPath, 3e4, "set-head:fallback");
|
|
7619
|
+
return true;
|
|
7620
|
+
} catch {
|
|
7621
|
+
return false;
|
|
7622
|
+
}
|
|
7567
7623
|
}
|
|
7568
7624
|
async function readOriginUrl(mirrorPath) {
|
|
7569
7625
|
try {
|
|
@@ -7580,11 +7636,11 @@ function createGitMirrorManager(opts) {
|
|
|
7580
7636
|
async function fetchOrigin(mirrorPath, originUrl) {
|
|
7581
7637
|
const direction = pickFallbackDirection(originUrl);
|
|
7582
7638
|
try {
|
|
7583
|
-
const { elapsedMs } = await
|
|
7639
|
+
const { elapsedMs } = await gitWithNetworkRetry([
|
|
7584
7640
|
"fetch",
|
|
7585
7641
|
"--prune",
|
|
7586
7642
|
"origin"
|
|
7587
|
-
], mirrorPath, cloneTimeoutMs);
|
|
7643
|
+
], mirrorPath, cloneTimeoutMs, "fetch:primary");
|
|
7588
7644
|
return {
|
|
7589
7645
|
elapsedMs,
|
|
7590
7646
|
usedFallback: false
|
|
@@ -7599,13 +7655,13 @@ function createGitMirrorManager(opts) {
|
|
|
7599
7655
|
peerBase: direction.peerBase
|
|
7600
7656
|
}, "fetch failed with credential-shaped error; retrying with peer-protocol insteadOf rewrite");
|
|
7601
7657
|
try {
|
|
7602
|
-
const { elapsedMs } = await
|
|
7658
|
+
const { elapsedMs } = await gitWithNetworkRetry([
|
|
7603
7659
|
"-c",
|
|
7604
7660
|
`url.${direction.peerBase}.insteadOf=${direction.originBase}`,
|
|
7605
7661
|
"fetch",
|
|
7606
7662
|
"--prune",
|
|
7607
7663
|
"origin"
|
|
7608
|
-
], mirrorPath, cloneTimeoutMs);
|
|
7664
|
+
], mirrorPath, cloneTimeoutMs, "fetch:fallback");
|
|
7609
7665
|
log?.info({
|
|
7610
7666
|
gitUrl: originUrl,
|
|
7611
7667
|
toProtocol: direction.toProtocol
|
|
@@ -8134,6 +8190,46 @@ function isLikelySshAuthFailure(message) {
|
|
|
8134
8190
|
return /Permission denied\s*(?:\(|,)/i.test(message) || /Could not read from remote repository/i.test(message) || /Host key verification failed/i.test(message) || /no matching host key type/i.test(message) || /no mutual signature algorithm/i.test(message);
|
|
8135
8191
|
}
|
|
8136
8192
|
/**
|
|
8193
|
+
* Heuristic for transient network-layer failures emitted by `git` over
|
|
8194
|
+
* HTTPS or SSH. These are the failure modes a brief proxy/VPN hiccup, TLS
|
|
8195
|
+
* handshake blip, or peer connection reset produces mid-fetch — exactly
|
|
8196
|
+
* what `SSL_connect: SSL_ERROR_SYSCALL` looks like when Surge / Clash
|
|
8197
|
+
* swaps a rule mid-flight, what `early EOF` looks like when an HTTP/2
|
|
8198
|
+
* stream is reset, and what `Connection refused` looks like when a local
|
|
8199
|
+
* proxy listener restarts.
|
|
8200
|
+
*
|
|
8201
|
+
* Used by the `gitWithNetworkRetry` wrapper around `fetch` and
|
|
8202
|
+
* `remote set-head --auto` to absorb the kind of hiccup that today
|
|
8203
|
+
* surfaces as `Session start/resume failed (…)` in chat and only goes
|
|
8204
|
+
* away when the operator manually @-mentions the agent again two seconds
|
|
8205
|
+
* later.
|
|
8206
|
+
*
|
|
8207
|
+
* Negative space (intentionally NOT matched):
|
|
8208
|
+
* - credential failures — handled by the protocol-fallback path; retrying
|
|
8209
|
+
* in the same protocol won't help.
|
|
8210
|
+
* - `Repository not found`, `couldn't find remote ref` — deterministic
|
|
8211
|
+
* content errors; a 500ms retry won't fix them.
|
|
8212
|
+
* - `SSL certificate problem` — TLS trust failures; retrying won't help
|
|
8213
|
+
* and silently masking them would hide a real misconfiguration.
|
|
8214
|
+
* - `git … timed out after Xms` — our own per-call timeout. The op was
|
|
8215
|
+
* making progress (or wasn't); either way another full timeout window
|
|
8216
|
+
* is the wrong response.
|
|
8217
|
+
*
|
|
8218
|
+
* On localhost-proxy specifically (the common case for this codebase),
|
|
8219
|
+
* `ECONNREFUSED` IS a transient signal — when Surge / Clash bounces the
|
|
8220
|
+
* listener, the next attempt sees the same listener back up within
|
|
8221
|
+
* seconds. This is why we diverge from the SDK's `doFetch` policy (which
|
|
8222
|
+
* does NOT retry `ECONNREFUSED` because there the peer is the remote hub).
|
|
8223
|
+
*
|
|
8224
|
+
* Exported for unit testing.
|
|
8225
|
+
*/
|
|
8226
|
+
function isLikelyTransientNetworkError(message) {
|
|
8227
|
+
if (!message) return false;
|
|
8228
|
+
if (isLikelyHttpsAuthFailure(message) || isLikelySshAuthFailure(message)) return false;
|
|
8229
|
+
if (/SSL certificate problem/i.test(message) || /server certificate verification failed/i.test(message) || /certificate verify failed/i.test(message) || /self.signed certificate/i.test(message) || /unable to get local issuer certificate/i.test(message) || /certificate has expired/i.test(message)) return false;
|
|
8230
|
+
return /SSL_ERROR_SYSCALL/i.test(message) || /unexpected eof while reading/i.test(message) || /TLS handshake|gnutls_handshake|gnutls\s+recv\s+error/i.test(message) || /\bConnection reset(?:\s+by\s+peer)?\b/i.test(message) || /\bConnection refused\b/i.test(message) || /\bConnection timed out\b/i.test(message) || /\bOperation timed out\b/i.test(message) || /\bNetwork is unreachable\b/i.test(message) || /Could not resolve host(?:name)?/i.test(message) || /Temporary failure in name resolution/i.test(message) || /\bRPC failed\b/i.test(message) || /\bearly EOF\b/i.test(message) || /the remote end hung up unexpectedly/i.test(message) || /transfer closed with outstanding read data remaining/i.test(message) || /HTTP\/2 stream\s+\d+\s+was\s+(?:not\s+)?(?:reset|closed)/i.test(message) || /HTTP\/2 stream was reset/i.test(message) || /unexpected disconnect while reading sideband packet/i.test(message) || /fetch-pack: unexpected disconnect/i.test(message) || /\bsend-pack:\s+unexpected\s+disconnect\b/i.test(message);
|
|
8231
|
+
}
|
|
8232
|
+
/**
|
|
8137
8233
|
* Map an HTTPS git URL to the `insteadOf` rewrite needed to make git resolve
|
|
8138
8234
|
* it through SSH. Returns *base* strings (suitable for
|
|
8139
8235
|
* `git -c url.<sshBase>.insteadOf=<httpsBase>`) — git's `insteadOf` is a
|
|
@@ -8250,6 +8346,44 @@ function truncate(text, max = 512) {
|
|
|
8250
8346
|
if (text.length <= max) return text;
|
|
8251
8347
|
return `${text.slice(0, max)}…[truncated]`;
|
|
8252
8348
|
}
|
|
8349
|
+
/**
|
|
8350
|
+
* Run `op` once, and on a `isRetryable`-classified failure replay it on the
|
|
8351
|
+
* given backoff schedule. Non-retryable failures propagate to the caller
|
|
8352
|
+
* immediately — those won't be cured by waiting and silently retrying would
|
|
8353
|
+
* mask real bugs and exhaust the retry budget.
|
|
8354
|
+
*
|
|
8355
|
+
* Per-attempt timeouts (when `op` enforces one of its own) are NOT reset
|
|
8356
|
+
* across attempts: each attempt gets its own full budget. Right policy for
|
|
8357
|
+
* `git fetch` where a slow but progressing transfer on attempt N+1 should
|
|
8358
|
+
* not be aborted because attempt N ate part of a shared budget.
|
|
8359
|
+
*
|
|
8360
|
+
* Exported so unit tests can drive the retry policy with a mock `op`
|
|
8361
|
+
* instead of standing up a real flaky network. Module-scope so the helper
|
|
8362
|
+
* stays pure and side-effect-free.
|
|
8363
|
+
*/
|
|
8364
|
+
async function retryOnTransientNetwork(op, options) {
|
|
8365
|
+
const { delaysMs, isRetryable, onRetry } = options;
|
|
8366
|
+
const maxAttempts = delaysMs.length + 1;
|
|
8367
|
+
let lastErr;
|
|
8368
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
|
|
8369
|
+
return await op(attempt);
|
|
8370
|
+
} catch (err) {
|
|
8371
|
+
lastErr = err;
|
|
8372
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8373
|
+
if (!isRetryable(message)) throw err;
|
|
8374
|
+
if (attempt === maxAttempts) throw err;
|
|
8375
|
+
const baseDelay = delaysMs[attempt - 1];
|
|
8376
|
+
if (baseDelay === void 0) throw err;
|
|
8377
|
+
const delayMs = baseDelay + Math.floor(Math.random() * Math.max(1, Math.floor(baseDelay / 4)));
|
|
8378
|
+
onRetry?.({
|
|
8379
|
+
attempt,
|
|
8380
|
+
nextDelayMs: delayMs,
|
|
8381
|
+
message
|
|
8382
|
+
});
|
|
8383
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
8384
|
+
}
|
|
8385
|
+
throw lastErr;
|
|
8386
|
+
}
|
|
8253
8387
|
function contextTreeReposDir() {
|
|
8254
8388
|
return join(defaultDataDir$1(), "context-tree-repos");
|
|
8255
8389
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { c as blank, d as status, n as bindFeishuUser, t as bindFeishuBot } from "./feishu-DmpOdXci.mjs";
|
|
2
|
-
import { A as installClientService, B as rotateClientIdWithBackup, C as checkClientConfig, D as printResults, E as checkWebSocket, F as startClientService, H as AuthRefreshRateLimitedError, I as stopClientService, K as resolveAccessToken, L as uninstallClientService, N as resolveCliInvocation, P as restartClientService, R as ClientRuntime, T as checkServerReachable, U as ensureFreshAccessToken, V as AuthRefreshFailedError, W as ensureFreshAdminToken, _ as onboardCheck, g as formatCheckReport, h as promptMissingFields, ht as SdkError, j as isServiceSupported, k as getClientServiceStatus, m as promptAddAgent, mt as FirstTreeHubSDK, p as isInteractive, q as resolveServerUrl, r as deriveHubUrlFromToken, t as HubUrlDerivationError, v as onboardCreate, w as checkNodeVersion, x as checkAgentConfigs, z as handleClientOrgMismatch } from "./connect-token-
|
|
2
|
+
import { A as installClientService, B as rotateClientIdWithBackup, C as checkClientConfig, D as printResults, E as checkWebSocket, F as startClientService, H as AuthRefreshRateLimitedError, I as stopClientService, K as resolveAccessToken, L as uninstallClientService, N as resolveCliInvocation, P as restartClientService, R as ClientRuntime, T as checkServerReachable, U as ensureFreshAccessToken, V as AuthRefreshFailedError, W as ensureFreshAdminToken, _ as onboardCheck, g as formatCheckReport, h as promptMissingFields, ht as SdkError, j as isServiceSupported, k as getClientServiceStatus, m as promptAddAgent, mt as FirstTreeHubSDK, p as isInteractive, q as resolveServerUrl, r as deriveHubUrlFromToken, t as HubUrlDerivationError, v as onboardCreate, w as checkNodeVersion, x as checkAgentConfigs, z as handleClientOrgMismatch } from "./connect-token-CJozTiUf.mjs";
|
|
3
3
|
export { AuthRefreshFailedError, AuthRefreshRateLimitedError, ClientRuntime, FirstTreeHubSDK, HubUrlDerivationError, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkNodeVersion, checkServerReachable, checkWebSocket, deriveHubUrlFromToken, ensureFreshAccessToken, ensureFreshAdminToken, formatCheckReport, getClientServiceStatus, handleClientOrgMismatch, installClientService, isInteractive, isServiceSupported, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveCliInvocation, resolveServerUrl, restartClientService, rotateClientIdWithBackup, startClientService, status, stopClientService, uninstallClientService };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "first-tree-staging",
|
|
3
|
-
"version": "0.5.2-staging.
|
|
3
|
+
"version": "0.5.2-staging.24.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "First Tree — unified CLI for Context Tree onboarding, GitHub Scan automation, agent management, and Hub messaging. (Source-tree dev build; CI rewrites `name` and `bin` for prod / staging publishes — see docs/local-dev-isolation.md.)",
|
|
6
6
|
"keywords": [
|