glimmervoid 0.42.3 → 0.43.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/dist/bin/glimmervoid.js +13 -13
- package/dist/chunks/{adapters-DeqV4wUv.js → adapters-EqOJN5vD.js} +1 -1
- package/dist/chunks/{adapters-Bga1N6Ie.js → adapters-jRUJYLhH.js} +5 -5
- package/dist/chunks/{agent-api-cli-YvQ8WxHA.js → agent-api-cli-B0fpc41n.js} +3 -3
- package/dist/chunks/{agent-api-cli-DWVeqlLu.js → agent-api-cli-DYwXyAmN.js} +1 -1
- package/dist/chunks/{agent-api-core-DFYLP1bF.js → agent-api-core-BeSecYrA.js} +3 -3
- package/dist/chunks/{agent-setup-cli-C9qDV2g5.js → agent-setup-cli-NfJQxNdp.js} +1 -1
- package/dist/chunks/{config-store-DOGtTJ1d.js → config-store-CBjnq-OI.js} +2 -2
- package/dist/chunks/config-store-DCrosJLg.js +1 -0
- package/dist/chunks/{contracts-Ds_eevZs.js → contracts-BJGgl8et.js} +2 -2
- package/dist/chunks/{control-messages-Dgap3JRR.js → control-messages-CD99vE0V.js} +9 -3
- package/dist/chunks/{git-version-DqagvnAZ.js → git-version-ByOV7a_h.js} +1 -1
- package/dist/chunks/{grok-DwrAcNHC.js → grok-DPmX1ozS.js} +1 -1
- package/dist/chunks/{main-DOZJniDn.js → main-_78bVmTr.js} +1898 -1426
- package/dist/chunks/{pair-cli-PCXITdEd.js → pair-cli-DAJNPgsi.js} +2 -2
- package/dist/chunks/{remote-config-DeWURqqr.js → remote-config-BdCTswa1.js} +1 -1
- package/dist/chunks/{session-3eSP3t1i.js → session-BUcx9uJa.js} +2 -0
- package/dist/chunks/{session-routes-B2oSzmQn.js → session-routes-DdeSkLqC.js} +1 -1
- package/dist/chunks/{settings-cli-xpR7vk9L.js → settings-cli-CMLEvC05.js} +5 -5
- package/dist/chunks/{settings-readout-core-C2mOk9Yz.js → settings-readout-core-CBqSjZiW.js} +2 -2
- package/dist/chunks/{ssh-sign-cli-rKiqiNem.js → ssh-sign-cli-CvqtQNQe.js} +6 -6
- package/dist/client/assets/{index-NhAGtpDY.css → index-CW2LelmE.css} +1 -1
- package/dist/client/assets/index-Cl0UmezJ.js +177 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunks/config-store-CGrS8s6x.js +0 -1
- package/dist/client/assets/index-CuuOi3Bp.js +0 -177
|
@@ -2,13 +2,13 @@ import { i as sanitizeOneLine, n as requireExecutableSpawnHelper, r as firstLine
|
|
|
2
2
|
import { a as isMissingFileError, i as errorText$1, r as errorMessage, t as errorCode } from "./text-DoirglWV.js";
|
|
3
3
|
import { a as sanitizeWorktreeName, i as safePathSegment, n as equalsIgnoringCaseOnWindows, r as isPathInside, t as caseFoldedPathKey } from "./paths-Bu4eH1Yd.js";
|
|
4
4
|
import { a as packageRoot, n as cliPath, o as relayPath, r as clientDir, s as resolvePackageBin, t as bundled } from "./runtime-paths-BkeftE60.js";
|
|
5
|
-
import { $t as STATES, A as SIGN_URL_ENV, At as TeamReviewStatus, B as LinkedPr, Bt as shortSha, Ct as ReviewThreadId, Dt as TeamReviewCompareFiles, E as SESSION_DOC_BODY_CAP_BYTES, Ft as decisionAsIfApprovalRequired, G as MyPrKeepMergeableRequest, H as MyPrAutoRebase, Ht as NODE_ID_RE, I as SshSignRequest, J as MyPrMergeMethod, L as TASK_TITLE_CONTROL_CHARACTERS, Lt as hasStandingViewerApproval, Mt as TeamReviewThreadResult, Nt as TeamReviewThreadsRepository, O as SESSION_NAME_PATTERN, Ot as TeamReviewResolveResponse, Pt as canApproveAfterComment, Q as MyPrMergeWhenReadyRequest, Qt as RESTARTABLE_STATES, R as TaskTitle, S as ConversationQuestion, St as ReviewResult, Tt as TeamReviewActionRequest, U as MyPrCheckContext, Ut as REPO_SLUG_RE, V as MyPr, Vt as GH_SEGMENT, W as MyPrCheckContextsResponse, Wt as repoParts, Xt as KILLABLE_STATES, Y as MyPrMergeRequest, Yt as BADGE_LABELS, Z as MyPrMergeStateResponse, Zt as MERGEABLE_LIVE_STATES, _ as CONVERSATION_TOOL_RESULT_HOOK_EVENT, _t as QueuedReview, bt as ReviewDraft, dt as EarlierReview, ft as FindingSeverity, g as CONVERSATION_PERMISSION_HOOK_EVENT, gt as PrDetail, h as CONVERSATION_APPROVAL_SUMMARY_MAX_CHARS, ht as PostingPlan, it as MyPrsState, j as SSH_SIGN_BODY_CAP_BYTES, jt as TeamReviewThreadCommentsResponse, k as SESSION_NAME_RULE_TEXT, kt as TeamReviewStateEntry, lt as CommitSha, m as COHERENCE_HOOKS_INACTIVE, mt as GithubReviewState, n as AGENT_URL_ENV, nt as MyPrThreadsResponse, q as MyPrLaneEntry, r as ASK_USER_QUESTION_TOOL_NAME, rt as MyPrsSnapshot, tt as MyPrThreadNode, ut as DECIDING_REVIEW_STATES, w as RefinedTaskTitle, wt as ReviewVerdict, xt as ReviewFinding, yt as ReviewComment, zt as HEX_SHA_RE } from "./session-
|
|
5
|
+
import { $t as STATES, A as SIGN_URL_ENV, At as TeamReviewStatus, B as LinkedPr, Bt as shortSha, Ct as ReviewThreadId, Dt as TeamReviewCompareFiles, E as SESSION_DOC_BODY_CAP_BYTES, Ft as decisionAsIfApprovalRequired, G as MyPrKeepMergeableRequest, H as MyPrAutoRebase, Ht as NODE_ID_RE, I as SshSignRequest, J as MyPrMergeMethod, L as TASK_TITLE_CONTROL_CHARACTERS, Lt as hasStandingViewerApproval, Mt as TeamReviewThreadResult, Nt as TeamReviewThreadsRepository, O as SESSION_NAME_PATTERN, Ot as TeamReviewResolveResponse, Pt as canApproveAfterComment, Q as MyPrMergeWhenReadyRequest, Qt as RESTARTABLE_STATES, R as TaskTitle, S as ConversationQuestion, St as ReviewResult, Tt as TeamReviewActionRequest, U as MyPrCheckContext, Ut as REPO_SLUG_RE, V as MyPr, Vt as GH_SEGMENT, W as MyPrCheckContextsResponse, Wt as repoParts, Xt as KILLABLE_STATES, Y as MyPrMergeRequest, Yt as BADGE_LABELS, Z as MyPrMergeStateResponse, Zt as MERGEABLE_LIVE_STATES, _ as CONVERSATION_TOOL_RESULT_HOOK_EVENT, _t as QueuedReview, bt as ReviewDraft, dt as EarlierReview, ft as FindingSeverity, g as CONVERSATION_PERMISSION_HOOK_EVENT, gt as PrDetail, h as CONVERSATION_APPROVAL_SUMMARY_MAX_CHARS, ht as PostingPlan, it as MyPrsState, j as SSH_SIGN_BODY_CAP_BYTES, jt as TeamReviewThreadCommentsResponse, k as SESSION_NAME_RULE_TEXT, kt as TeamReviewStateEntry, lt as CommitSha, m as COHERENCE_HOOKS_INACTIVE, mt as GithubReviewState, n as AGENT_URL_ENV, nt as MyPrThreadsResponse, q as MyPrLaneEntry, r as ASK_USER_QUESTION_TOOL_NAME, rt as MyPrsSnapshot, tt as MyPrThreadNode, ut as DECIDING_REVIEW_STATES, w as RefinedTaskTitle, wt as ReviewVerdict, xt as ReviewFinding, yt as ReviewComment, zt as HEX_SHA_RE } from "./session-BUcx9uJa.js";
|
|
6
6
|
import { n as execFileAsync, o as spawn, r as execFileSync, t as execFile } from "./child-process-safe-D4hyVIXj.js";
|
|
7
|
-
import { At as GithubIssueResponse, C as comparablePrKey, Ct as PLAN_RESULT_HOOK_EVENT, Dt as parseExitPlanModeHookPayload, E as WorkflowsSettings, Et as PlanRevision, F as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Ft as IssuesStatus, It as issuePullRequestStateFromGithub, Mt as IssueUpdate, Nt as IssuesSearchResponse, O as WorkflowsState, Ot as planTitle, Pt as IssuesState, S as GithubPrsFrame, St as PLAN_HOOK_OUTPUT_MAX_CHARS, T as WorkflowLabelName, Tt as PlanDecision, _ as MinedPrReviewData, _t as CoherenceJournalOutput, b as GithubPrKey, bt as PLAN_BODY_CAP_BYTES, c as BenchmarkActionRequest, d as BenchmarkJudgement, f as BenchmarkRun, ft as UpdateJournal, g as MergedPrListing, gt as TraceRecord, h as CommitComparison, ht as TraceCheckpoint, jt as IssueKey, kt as GithubIssuePullRequestsRepository, l as BenchmarkCase, m as BenchmarkSuite, mt as MAX_RAW_LINE_CHARS, n as ClientMessage, p as BenchmarkStatus, pt as SessionSetup, s as pullRequestKeyFromReference, t as CONTROL_FRAME_MAX_BYTES, tt as USAGE_INTEGER_RANGES, u as BenchmarkId, v as PrCheckoutCaseInput, vt as ChangeMap, w as WorkflowCommentBody, wt as PLAN_TOOL_NAME, x as GithubPrLightSearchNode, xt as PLAN_HOOK_EVENT, y as GithubPrDetailNode, yt as changeMapFactId } from "./control-messages-
|
|
8
|
-
import { a as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-
|
|
9
|
-
import { a as decideSpawnAllowance, c as describeKeptChildAttention, d as describeUnpushedCloseRefusal, h as projectReviews, i as decideAgentRequest, l as describePushLanding, m as projectMyPrs, n as REFUSAL_REASON, o as deriveChildSessionName, p as parseAgentVerb, s as describeChildEndNote, u as describeRoleRefusal } from "./agent-api-core-
|
|
7
|
+
import { At as GithubIssueResponse, C as comparablePrKey, Ct as PLAN_RESULT_HOOK_EVENT, Dt as parseExitPlanModeHookPayload, E as WorkflowsSettings, Et as PlanRevision, F as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Ft as IssuesStatus, It as issuePullRequestStateFromGithub, Mt as IssueUpdate, Nt as IssuesSearchResponse, O as WorkflowsState, Ot as planTitle, Pt as IssuesState, S as GithubPrsFrame, St as PLAN_HOOK_OUTPUT_MAX_CHARS, T as WorkflowLabelName, Tt as PlanDecision, _ as MinedPrReviewData, _t as CoherenceJournalOutput, b as GithubPrKey, bt as PLAN_BODY_CAP_BYTES, c as BenchmarkActionRequest, d as BenchmarkJudgement, f as BenchmarkRun, ft as UpdateJournal, g as MergedPrListing, gt as TraceRecord, h as CommitComparison, ht as TraceCheckpoint, jt as IssueKey, kt as GithubIssuePullRequestsRepository, l as BenchmarkCase, m as BenchmarkSuite, mt as MAX_RAW_LINE_CHARS, n as ClientMessage, p as BenchmarkStatus, pt as SessionSetup, s as pullRequestKeyFromReference, t as CONTROL_FRAME_MAX_BYTES, tt as USAGE_INTEGER_RANGES, u as BenchmarkId, v as PrCheckoutCaseInput, vt as ChangeMap, w as WorkflowCommentBody, wt as PLAN_TOOL_NAME, x as GithubPrLightSearchNode, xt as PLAN_HOOK_EVENT, y as GithubPrDetailNode, yt as changeMapFactId } from "./control-messages-CD99vE0V.js";
|
|
8
|
+
import { a as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-DdeSkLqC.js";
|
|
9
|
+
import { a as decideSpawnAllowance, c as describeKeptChildAttention, d as describeUnpushedCloseRefusal, h as projectReviews, i as decideAgentRequest, l as describePushLanding, m as projectMyPrs, n as REFUSAL_REASON, o as deriveChildSessionName, p as parseAgentVerb, s as describeChildEndNote, u as describeRoleRefusal } from "./agent-api-core-BeSecYrA.js";
|
|
10
10
|
import { n as glimmervoidHomeDir, t as decideConfigPath } from "./config-path-core-DFt-TemS.js";
|
|
11
|
-
import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-
|
|
11
|
+
import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-ByOV7a_h.js";
|
|
12
12
|
import { n as checkGuardrailsDependencies, t as allowGuardedSpawn } from "./guardrails-deps-BjPzk1rE.js";
|
|
13
13
|
import { t as HOOK_URL_ENV } from "./hook-relay-core-DA_bz2KA.js";
|
|
14
14
|
import { t as HOOK_VIA_GUARDRAILS_PROXY_ENV } from "./guardrails-proxy-core-BI3rdY-m.js";
|
|
@@ -21,13 +21,13 @@ import { n as DEFAULT_MAX_MS, r as nextBackoffMs, t as DEFAULT_BASE_MS } from ".
|
|
|
21
21
|
import { n as unrefTimer, t as DEFAULT_TIMER_FNS } from "./timer-deps-u2DAQLok.js";
|
|
22
22
|
import { n as shellQuote } from "./hook-command-core-qTNK-xMF.js";
|
|
23
23
|
import { C as parseCgroupBytes, S as memoryReserveBytes, T as parseMemAvailableBytes, _ as createSpawnGate, a as jsonStateLoadError, b as cgroupLimitBytes, c as loadedJsonValue, d as writeJsonAtomicSync, f as writeTextAtomic, g as createSerialQueue, h as createMemoryAdmission, i as createJsonStateWriter, m as createKeyedSerialQueue, n as appendJsonLines, o as loadJsonStateFile, p as writeTextAtomicSync, r as createJsonStateStore, s as loadJsonStateFileSync, t as appendJsonLine, u as writeJsonAtomic, v as isQueueAdmissionTimeout, w as parseCgroupV2Path, x as headroomFromReadings, y as cgroupAncestorPaths } from "./json-file-DFCTvPjj.js";
|
|
24
|
-
import { $ as DEFAULT_BRANCH_GC_PREFIXES, B as buildExceptionProperties, F as TELEMETRY_BATCH_URL, G as exceptionFingerprint, H as decideIsDevInstall, I as TELEMETRY_FLAGS_URL, J as resolveProjectToken, K as nodeMajorVersion, L as adapterBucket, P as FIRST_RUN_NOTICE, R as buildAiGenerationEvents, U as decideRemoteTelemetryState, V as classifySessionExit, W as decideTelemetryConsent, Z as stableConfigKey, a as generateProjectId, at as worktreeIntegrationTips, b as validateSettingsBlock, ct as isSameDirectoryPath, d as assetForPlatform, et as matchedPrefix, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as ensureProjectIds, it as usablePrefixes, m as installTargetPath, n as DEFAULT_CONFIG, nt as planWorktreeGc, o as glimmervoidHomeDir$1, ot as canonicalizePath, p as findEscapingArchiveMember, q as parsePosthogReleaseId, r as createConfigStore, rt as sessionIdFromBranch, st as comparableDirectoryPath, t as ABORT_CONFIG_SAVE, tt as planBranchGc, v as mergeSettingsBlock, y as settingsBlockSpecFor, z as buildBrowserExceptionProperties } from "./config-store-
|
|
25
|
-
import { T as USAGE_VENDOR_KEYS, _ as PersistedTaskTitle, a as parseDataClientMessage, b as configIssueMessage, c as PendingCrashReport, d as TelemetryState, g as ConfigUpdate, i as parseDataAttachHistory, l as TELEMETRY_EVENT_SCHEMAS, m as BrowserConfig, n as HookEnvelope, o as DashboardTelemetryReport, r as DEEP_HISTORY_SCROLLBACK_LINES, t as HTTP_URL_RE, v as RUNTIME_CONFIG_SCALAR_KEYS, w as USAGE_COST_MODES, y as TASK_TITLE_SOURCE_PRIORITY } from "./contracts-
|
|
26
|
-
import { A as dedupeCodexFiles, B as shouldPersistWarehouse, C as shortSha$1, D as codexSessionIdFromPath, E as codexRootCandidates, F as passOutcome, H as claudeCode, I as projectDirCandidates, L as resolveProjectsDirs, M as grokHomes, N as grokRootCandidates, O as continuationDelayMs, P as isUsageFile, R as scopedClaudeRoots, S as parseResolvedSha, T as codexHomes, V as splitLines$1, _ as isCheckFresh, a as describeAgentResolvability, b as parseRegistryDeprecation, c as isKnownAgentId, d as resolveAdapter, f as setCustomAgents, g as decideUpdateStatus, h as decideInstallFlavor, i as customAgentFingerprint, j as expandTilde, k as decideFileRead, l as listAgentIds, m as readHostCodexVersion, n as cachedAgentResolvability, o as getAdapter, p as codex, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resetCommandCache, v as normalizeSha, w as codexFallbackRoots, x as parseRegistryLatest, y as normalizeUpdateChannel, z as shouldEvaluateDespiteIoFailures } from "./adapters-
|
|
24
|
+
import { $ as DEFAULT_BRANCH_GC_PREFIXES, B as buildExceptionProperties, F as TELEMETRY_BATCH_URL, G as exceptionFingerprint, H as decideIsDevInstall, I as TELEMETRY_FLAGS_URL, J as resolveProjectToken, K as nodeMajorVersion, L as adapterBucket, P as FIRST_RUN_NOTICE, R as buildAiGenerationEvents, U as decideRemoteTelemetryState, V as classifySessionExit, W as decideTelemetryConsent, Z as stableConfigKey, a as generateProjectId, at as worktreeIntegrationTips, b as validateSettingsBlock, ct as isSameDirectoryPath, d as assetForPlatform, et as matchedPrefix, f as decideRtkInstall, g as verifyDigest, h as isRtkBinaryName, i as ensureProjectIds, it as usablePrefixes, m as installTargetPath, n as DEFAULT_CONFIG, nt as planWorktreeGc, o as glimmervoidHomeDir$1, ot as canonicalizePath, p as findEscapingArchiveMember, q as parsePosthogReleaseId, r as createConfigStore, rt as sessionIdFromBranch, st as comparableDirectoryPath, t as ABORT_CONFIG_SAVE, tt as planBranchGc, v as mergeSettingsBlock, y as settingsBlockSpecFor, z as buildBrowserExceptionProperties } from "./config-store-CBjnq-OI.js";
|
|
25
|
+
import { T as USAGE_VENDOR_KEYS, _ as PersistedTaskTitle, a as parseDataClientMessage, b as configIssueMessage, c as PendingCrashReport, d as TelemetryState, g as ConfigUpdate, i as parseDataAttachHistory, l as TELEMETRY_EVENT_SCHEMAS, m as BrowserConfig, n as HookEnvelope, o as DashboardTelemetryReport, r as DEEP_HISTORY_SCROLLBACK_LINES, t as HTTP_URL_RE, v as RUNTIME_CONFIG_SCALAR_KEYS, w as USAGE_COST_MODES, y as TASK_TITLE_SOURCE_PRIORITY } from "./contracts-BJGgl8et.js";
|
|
26
|
+
import { A as dedupeCodexFiles, B as shouldPersistWarehouse, C as shortSha$1, D as codexSessionIdFromPath, E as codexRootCandidates, F as passOutcome, H as claudeCode, I as projectDirCandidates, L as resolveProjectsDirs, M as grokHomes, N as grokRootCandidates, O as continuationDelayMs, P as isUsageFile, R as scopedClaudeRoots, S as parseResolvedSha, T as codexHomes, V as splitLines$1, _ as isCheckFresh, a as describeAgentResolvability, b as parseRegistryDeprecation, c as isKnownAgentId, d as resolveAdapter, f as setCustomAgents, g as decideUpdateStatus, h as decideInstallFlavor, i as customAgentFingerprint, j as expandTilde, k as decideFileRead, l as listAgentIds, m as readHostCodexVersion, n as cachedAgentResolvability, o as getAdapter, p as codex, r as commandFor, s as hookProfileOf, t as DEFAULT_AGENT_ID, u as resetCommandCache, v as normalizeSha, w as codexFallbackRoots, x as parseRegistryLatest, y as normalizeUpdateChannel, z as shouldEvaluateDespiteIoFailures } from "./adapters-jRUJYLhH.js";
|
|
27
27
|
import { t as HOOK_TOOLS } from "./hook-tools-Dl7-r2Eb.js";
|
|
28
28
|
import { a as formatSshPublicKey, c as orderAgentSockets, i as commitSigningShimText, l as parseSshPublicKey, n as NO_AGENT_HOLDS_KEY, o as isForwardedAgentDirName, r as agentListsKey, s as isSignableGitObject, t as COMMIT_SIGNING_SHIM_NAME } from "./ssh-signing-core-CRhVVWNW.js";
|
|
29
|
-
import { a as configSiblingPath, c as defaultPairingsPath, d as decideDeviceAuth, f as deviceNameFromUserAgent, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, p as hashSecret, r as validateRemoteConfig, s as createSeenStore, t as decideBindHost, u as DEFAULT_DEVICE_MAX_AGE_MS } from "./remote-config-
|
|
30
|
-
import { n as configFileViewOf, u as readSettingsForAgent } from "./settings-readout-core-
|
|
29
|
+
import { a as configSiblingPath, c as defaultPairingsPath, d as decideDeviceAuth, f as deviceNameFromUserAgent, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, p as hashSecret, r as validateRemoteConfig, s as createSeenStore, t as decideBindHost, u as DEFAULT_DEVICE_MAX_AGE_MS } from "./remote-config-BdCTswa1.js";
|
|
30
|
+
import { n as configFileViewOf, u as readSettingsForAgent } from "./settings-readout-core-CBqSjZiW.js";
|
|
31
31
|
import { _ as escapeMarkup, g as createLaneLog, n as createVisionsSetup } from "./visions-setup-DB2P8JUL.js";
|
|
32
32
|
import { t as getRtkPath } from "./rtk-resolver-BDHi6RMv.js";
|
|
33
33
|
import { createRequire } from "node:module";
|
|
@@ -39,7 +39,7 @@ import crypto, { createHash, randomBytes, randomInt, randomUUID } from "node:cry
|
|
|
39
39
|
import WebSocket$1, { WebSocket, WebSocketServer } from "ws";
|
|
40
40
|
import http from "node:http";
|
|
41
41
|
import net, { isIPv6 } from "node:net";
|
|
42
|
-
import
|
|
42
|
+
import fs$1, { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
43
43
|
import { pipeline } from "node:stream";
|
|
44
44
|
import express from "express";
|
|
45
45
|
import { EventEmitter } from "node:events";
|
|
@@ -49,6 +49,168 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
49
49
|
import https from "node:https";
|
|
50
50
|
import { StringDecoder } from "node:string_decoder";
|
|
51
51
|
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
52
|
+
//#region shared/contracts/owned-paths.ts
|
|
53
|
+
var OwnedPathOwner = z.string().regex(/^(session|lane):[A-Za-z0-9._:#-]+$/);
|
|
54
|
+
var OwnedPathEntry = z.object({
|
|
55
|
+
path: z.string().min(1),
|
|
56
|
+
owner: OwnedPathOwner,
|
|
57
|
+
kind: z.enum(["git-worktree", "folder"]),
|
|
58
|
+
projectPath: z.string().min(1).nullable(),
|
|
59
|
+
branch: z.string().min(1).nullable()
|
|
60
|
+
}).strict();
|
|
61
|
+
var OwnedPathsLedger = z.object({
|
|
62
|
+
version: z.literal(1),
|
|
63
|
+
entries: z.array(OwnedPathEntry)
|
|
64
|
+
}).strict();
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region server/core/owned-paths-core.ts
|
|
67
|
+
var SESSION_OWNER_PREFIX = "session:";
|
|
68
|
+
var RESTART_SURVIVING_LANE_OWNERS = /* @__PURE__ */ new Set(["lane:update-apply", "lane:team-review"]);
|
|
69
|
+
function isOwnerGone(owner, configuredSessionIds) {
|
|
70
|
+
if (!owner.startsWith(SESSION_OWNER_PREFIX)) return !RESTART_SURVIVING_LANE_OWNERS.has(owner);
|
|
71
|
+
return !configuredSessionIds.has(owner.slice(8));
|
|
72
|
+
}
|
|
73
|
+
function orphanedOwnedPaths(entries, configuredSessionIds) {
|
|
74
|
+
const orphans = entries.filter((entry) => isOwnerGone(entry.owner, configuredSessionIds));
|
|
75
|
+
return [...orphans.filter((entry) => entry.kind === "git-worktree"), ...orphans.filter((entry) => entry.kind === "folder")];
|
|
76
|
+
}
|
|
77
|
+
function holdsKeptPath(folderPath, keptPaths) {
|
|
78
|
+
return keptPaths.some((keptPath) => {
|
|
79
|
+
const relativePath = path.relative(folderPath, keptPath);
|
|
80
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region server/owned-paths-ledger.ts
|
|
85
|
+
function ownedPathsLedgerPath(glimmervoidHome = glimmervoidHomeDir$1()) {
|
|
86
|
+
return path.join(glimmervoidHome, "owned-paths.json");
|
|
87
|
+
}
|
|
88
|
+
function ledgerKey(ownedPath) {
|
|
89
|
+
return caseFoldedPathKey(path.resolve(ownedPath));
|
|
90
|
+
}
|
|
91
|
+
async function canonicalOwnedPath(ownedPath) {
|
|
92
|
+
const resolvedPath = path.resolve(ownedPath);
|
|
93
|
+
const missingSegments = [];
|
|
94
|
+
let existingAncestor = resolvedPath;
|
|
95
|
+
while (true) {
|
|
96
|
+
const realAncestor = await fs$1.realpath(existingAncestor).catch(() => null);
|
|
97
|
+
if (realAncestor !== null) return path.join(realAncestor, ...missingSegments);
|
|
98
|
+
const parent = path.dirname(existingAncestor);
|
|
99
|
+
if (parent === existingAncestor) return resolvedPath;
|
|
100
|
+
missingSegments.unshift(path.basename(existingAncestor));
|
|
101
|
+
existingAncestor = parent;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function createOwnedPathsLedger({ filePath, log = console }) {
|
|
105
|
+
const entriesByPath = /* @__PURE__ */ new Map();
|
|
106
|
+
const keysRecordedThisRun = /* @__PURE__ */ new Set();
|
|
107
|
+
let isLoaded = false;
|
|
108
|
+
const store = createJsonStateStore({
|
|
109
|
+
name: "owned paths ledger",
|
|
110
|
+
filePath,
|
|
111
|
+
parse: (raw) => OwnedPathsLedger.safeParse(raw).data ?? null,
|
|
112
|
+
adopt: (ledger) => {
|
|
113
|
+
entriesByPath.clear();
|
|
114
|
+
for (const entry of ledger?.entries ?? []) entriesByPath.set(ledgerKey(entry.path), entry);
|
|
115
|
+
isLoaded = true;
|
|
116
|
+
},
|
|
117
|
+
warn: (message, fields) => log.warn(`[owned-paths] ${message} ${JSON.stringify(fields)}`)
|
|
118
|
+
});
|
|
119
|
+
async function persist() {
|
|
120
|
+
const ledger = {
|
|
121
|
+
version: 1,
|
|
122
|
+
entries: [...entriesByPath.values()]
|
|
123
|
+
};
|
|
124
|
+
await store.write(ledger, () => `${JSON.stringify(ledger, null, 2)}\n`);
|
|
125
|
+
}
|
|
126
|
+
async function record(entry) {
|
|
127
|
+
await store.load();
|
|
128
|
+
if (!isLoaded) return;
|
|
129
|
+
const canonicalEntry = {
|
|
130
|
+
...entry,
|
|
131
|
+
path: await canonicalOwnedPath(entry.path)
|
|
132
|
+
};
|
|
133
|
+
const canonicalKey = ledgerKey(canonicalEntry.path);
|
|
134
|
+
keysRecordedThisRun.add(canonicalKey);
|
|
135
|
+
entriesByPath.set(canonicalKey, canonicalEntry);
|
|
136
|
+
await persist();
|
|
137
|
+
}
|
|
138
|
+
async function forget(ownedPath) {
|
|
139
|
+
await store.load();
|
|
140
|
+
if (!isLoaded || !entriesByPath.delete(ledgerKey(await canonicalOwnedPath(ownedPath)))) return;
|
|
141
|
+
await persist();
|
|
142
|
+
}
|
|
143
|
+
async function loadEarlierRunEntries() {
|
|
144
|
+
await store.load();
|
|
145
|
+
if (!isLoaded) return { ok: false };
|
|
146
|
+
return {
|
|
147
|
+
ok: true,
|
|
148
|
+
entries: [...entriesByPath.entries()].filter(([key]) => !keysRecordedThisRun.has(key)).map(([, entry]) => entry)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
loadEarlierRunEntries,
|
|
153
|
+
record,
|
|
154
|
+
forget,
|
|
155
|
+
idle: () => store.idle()
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async function pathExists$1(ownedPath) {
|
|
159
|
+
return fs$1.lstat(ownedPath).then(() => true, () => false);
|
|
160
|
+
}
|
|
161
|
+
async function holdsCheckout(folderPath) {
|
|
162
|
+
const children = await fs$1.readdir(folderPath).catch(() => []);
|
|
163
|
+
return (await Promise.all(children.map((child) => pathExists$1(path.join(folderPath, child, ".git"))))).includes(true);
|
|
164
|
+
}
|
|
165
|
+
async function removeOrphanedWorktree(entry, git, shareList) {
|
|
166
|
+
if (!entry.projectPath || !entry.branch) return "it names no repository and branch to check its work against";
|
|
167
|
+
const work = await git.describeWorkspaceMemberWork({
|
|
168
|
+
projectPath: entry.projectPath,
|
|
169
|
+
cwd: entry.path,
|
|
170
|
+
branch: entry.branch
|
|
171
|
+
});
|
|
172
|
+
if (work) return work;
|
|
173
|
+
const removed = await git.removeWorkspaceMember({
|
|
174
|
+
projectPath: entry.projectPath,
|
|
175
|
+
cwd: entry.path,
|
|
176
|
+
shareList
|
|
177
|
+
});
|
|
178
|
+
if (!removed.ok || await pathExists$1(entry.path)) return `git could not remove it: ${removed.err || "the folder is still there"}`;
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
async function removeOrphanedFolder(entry, ledger, keptPaths) {
|
|
182
|
+
if (holdsKeptPath(entry.path, keptPaths)) return "it holds a checkout that was kept";
|
|
183
|
+
if (await holdsCheckout(entry.path)) return "it still holds a checkout";
|
|
184
|
+
const removal = await fs$1.rm(entry.path, {
|
|
185
|
+
recursive: true,
|
|
186
|
+
force: true
|
|
187
|
+
}).then(() => null, (error) => `it could not be deleted: ${errorMessage(error)}`);
|
|
188
|
+
if (removal) return removal;
|
|
189
|
+
await ledger.forget(entry.path);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
async function reconcileOwnedPaths({ ledger, configuredSessionIds, git, shareList, log = console }) {
|
|
193
|
+
const loaded = await ledger.loadEarlierRunEntries();
|
|
194
|
+
if (!loaded.ok) {
|
|
195
|
+
log.warn("[owned-paths] deleted nothing at startup because the owned paths ledger could not be read");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const keptPaths = [];
|
|
199
|
+
for (const entry of orphanedOwnedPaths(loaded.entries, configuredSessionIds)) {
|
|
200
|
+
if (!await pathExists$1(entry.path)) {
|
|
201
|
+
await ledger.forget(entry.path);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const keepReason = entry.kind === "folder" ? await removeOrphanedFolder(entry, ledger, keptPaths) : await removeOrphanedWorktree(entry, git, shareList);
|
|
205
|
+
if (!keepReason) {
|
|
206
|
+
log.log(`[owned-paths] deleted ${entry.path}, left by ${entry.owner}`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
keptPaths.push(entry.path);
|
|
210
|
+
log.warn(`[owned-paths] kept ${entry.path}, left by ${entry.owner}, because ${keepReason}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
52
214
|
//#region server/core/restart-strategy.ts
|
|
53
215
|
function decideRestartStrategy(env) {
|
|
54
216
|
const invocationId = env ? env.INVOCATION_ID : void 0;
|
|
@@ -1363,7 +1525,7 @@ function decideByteRange(rangeHeader, sizeBytes) {
|
|
|
1363
1525
|
var CUSTOM_SOUNDS_ROUTE = "/custom-sounds";
|
|
1364
1526
|
async function listCustomSounds(soundsDir) {
|
|
1365
1527
|
try {
|
|
1366
|
-
return selectCustomSoundNames((await
|
|
1528
|
+
return selectCustomSoundNames((await fs$1.readdir(soundsDir, { withFileTypes: true })).map((entry) => ({
|
|
1367
1529
|
name: entry.name,
|
|
1368
1530
|
isFile: entry.isFile()
|
|
1369
1531
|
})));
|
|
@@ -1373,10 +1535,10 @@ async function listCustomSounds(soundsDir) {
|
|
|
1373
1535
|
}
|
|
1374
1536
|
async function resolveServableSound(soundsDir, name) {
|
|
1375
1537
|
try {
|
|
1376
|
-
const realDirectory = await
|
|
1377
|
-
const realFile = await
|
|
1538
|
+
const realDirectory = await fs$1.realpath(soundsDir);
|
|
1539
|
+
const realFile = await fs$1.realpath(path.join(soundsDir, name));
|
|
1378
1540
|
if (!isDirectChildOf$1(realDirectory, realFile)) return null;
|
|
1379
|
-
const linkStats = await
|
|
1541
|
+
const linkStats = await fs$1.lstat(path.join(soundsDir, name));
|
|
1380
1542
|
if (!linkStats.isFile()) return null;
|
|
1381
1543
|
return {
|
|
1382
1544
|
realFile,
|
|
@@ -1584,12 +1746,12 @@ function isStatuslineEvent(event) {
|
|
|
1584
1746
|
async function sweepSessionUploads(dir, justWritten) {
|
|
1585
1747
|
let entries = null;
|
|
1586
1748
|
try {
|
|
1587
|
-
entries = await
|
|
1749
|
+
entries = await fs$1.readdir(dir);
|
|
1588
1750
|
} catch {
|
|
1589
1751
|
return;
|
|
1590
1752
|
}
|
|
1591
1753
|
for (const name of planUploadRetention(entries, { justWritten })) try {
|
|
1592
|
-
await
|
|
1754
|
+
await fs$1.unlink(path.join(dir, name));
|
|
1593
1755
|
} catch {}
|
|
1594
1756
|
}
|
|
1595
1757
|
function receiveUpload({ req, res, sess, dir, filename, savedPath }) {
|
|
@@ -1604,7 +1766,7 @@ function receiveUpload({ req, res, sess, dir, filename, savedPath }) {
|
|
|
1604
1766
|
let discardWanted = false;
|
|
1605
1767
|
let streamClosed = false;
|
|
1606
1768
|
const unlinkPartial = () => {
|
|
1607
|
-
|
|
1769
|
+
fs$1.unlink(savedPath).catch(() => {});
|
|
1608
1770
|
};
|
|
1609
1771
|
const discardPartial = () => {
|
|
1610
1772
|
discardWanted = true;
|
|
@@ -1929,7 +2091,7 @@ function createBackendHttpApp(dependencies) {
|
|
|
1929
2091
|
abortedBeforeReceive = true;
|
|
1930
2092
|
};
|
|
1931
2093
|
req.on("error", markAbortedBeforeReceive);
|
|
1932
|
-
|
|
2094
|
+
fs$1.mkdir(dir, {
|
|
1933
2095
|
recursive: true,
|
|
1934
2096
|
mode: 448
|
|
1935
2097
|
}).then(() => {
|
|
@@ -1955,7 +2117,7 @@ function createBackendHttpApp(dependencies) {
|
|
|
1955
2117
|
return;
|
|
1956
2118
|
}
|
|
1957
2119
|
const savedPath = path.join(uploadsRoot, sessionId, filename);
|
|
1958
|
-
|
|
2120
|
+
fs$1.lstat(savedPath).then((savedStat) => {
|
|
1959
2121
|
if (!savedStat.isFile()) {
|
|
1960
2122
|
res.status(404).end();
|
|
1961
2123
|
return;
|
|
@@ -5819,6 +5981,1006 @@ function decideMarkerBase({ marker, fallbackBase, configuredIntegrationBranch, d
|
|
|
5819
5981
|
};
|
|
5820
5982
|
}
|
|
5821
5983
|
//#endregion
|
|
5984
|
+
//#region shared/contracts/guardrails-launch.ts
|
|
5985
|
+
var GuardrailsLaunchLogin = z.object({
|
|
5986
|
+
linkPath: z.string().min(1),
|
|
5987
|
+
targetPath: z.string().min(1)
|
|
5988
|
+
}).strict();
|
|
5989
|
+
var GuardrailsLaunchOwner = z.object({
|
|
5990
|
+
ownerPid: z.number().int().positive(),
|
|
5991
|
+
ownerHost: z.string(),
|
|
5992
|
+
ownerPidNamespace: z.string().nullable(),
|
|
5993
|
+
ownsTempDir: z.boolean(),
|
|
5994
|
+
keptTempFolderName: z.string().min(1).optional(),
|
|
5995
|
+
logins: z.array(GuardrailsLaunchLogin),
|
|
5996
|
+
placeholderPaths: z.array(z.string().min(1))
|
|
5997
|
+
}).strict();
|
|
5998
|
+
//#endregion
|
|
5999
|
+
//#region server/core/claude-trust-core.ts
|
|
6000
|
+
var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
|
|
6001
|
+
var CLAUDE_LEGACY_CONFIG_FILE_NAME = ".config.json";
|
|
6002
|
+
var CLAUDE_CONFIG_LOCK_SUFFIX = ".lock";
|
|
6003
|
+
var CLAUDE_CONFIG_LOCK_STALE_MS = 1e4;
|
|
6004
|
+
var CLAUDE_CONFIG_LOCK_RETRY_DELAYS_MS = Object.freeze([
|
|
6005
|
+
200,
|
|
6006
|
+
400,
|
|
6007
|
+
800,
|
|
6008
|
+
1600
|
|
6009
|
+
]);
|
|
6010
|
+
var GRANTED_KEYS = Object.freeze([
|
|
6011
|
+
"hasTrustDialogAccepted",
|
|
6012
|
+
"hasClaudeMdExternalIncludesApproved",
|
|
6013
|
+
"hasClaudeMdExternalIncludesWarningShown"
|
|
6014
|
+
]);
|
|
6015
|
+
var TRUST_ONLY_KEYS = Object.freeze(["hasTrustDialogAccepted"]);
|
|
6016
|
+
function isPlainRecord(value) {
|
|
6017
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6018
|
+
}
|
|
6019
|
+
function newProjectEntry() {
|
|
6020
|
+
return {
|
|
6021
|
+
allowedTools: [],
|
|
6022
|
+
mcpContextUris: [],
|
|
6023
|
+
mcpServers: {},
|
|
6024
|
+
enabledMcpjsonServers: [],
|
|
6025
|
+
disabledMcpjsonServers: []
|
|
6026
|
+
};
|
|
6027
|
+
}
|
|
6028
|
+
function listClaudeGlobalConfigPathCandidates(env, homeDir) {
|
|
6029
|
+
const configuredDir = env.CLAUDE_CONFIG_DIR?.trim();
|
|
6030
|
+
const legacyConfigHome = (configuredDir || path.join(homeDir, ".claude")).normalize("NFC");
|
|
6031
|
+
return [path.join(legacyConfigHome, CLAUDE_LEGACY_CONFIG_FILE_NAME), path.join(configuredDir || homeDir, CLAUDE_GLOBAL_CONFIG_FILE_NAME)];
|
|
6032
|
+
}
|
|
6033
|
+
function claudeProjectKeyOf(folderPath, platform) {
|
|
6034
|
+
if (platform !== "win32") return path.posix.normalize(folderPath);
|
|
6035
|
+
return path.win32.normalize(folderPath).replaceAll("\\", "/");
|
|
6036
|
+
}
|
|
6037
|
+
function repositoryRootOfCommonGitDir(commonGitDir) {
|
|
6038
|
+
if (!path.isAbsolute(commonGitDir) || path.basename(commonGitDir) !== ".git") return null;
|
|
6039
|
+
return path.dirname(commonGitDir);
|
|
6040
|
+
}
|
|
6041
|
+
function isConfigLockStale(lockModifiedMs, nowMs) {
|
|
6042
|
+
return nowMs - lockModifiedMs > CLAUDE_CONFIG_LOCK_STALE_MS;
|
|
6043
|
+
}
|
|
6044
|
+
function hasRecordedExternalImportsDecline(existingEntry) {
|
|
6045
|
+
return existingEntry?.hasClaudeMdExternalIncludesWarningShown === true && existingEntry.hasClaudeMdExternalIncludesApproved !== true;
|
|
6046
|
+
}
|
|
6047
|
+
function decideFolderTrust(configText, repositoryRoot, platform) {
|
|
6048
|
+
const projectKey = claudeProjectKeyOf(repositoryRoot, platform);
|
|
6049
|
+
let config;
|
|
6050
|
+
try {
|
|
6051
|
+
config = JSON.parse(configText);
|
|
6052
|
+
} catch {
|
|
6053
|
+
return {
|
|
6054
|
+
kind: "unreadable",
|
|
6055
|
+
reason: "it is not valid JSON"
|
|
6056
|
+
};
|
|
6057
|
+
}
|
|
6058
|
+
if (!isPlainRecord(config)) return {
|
|
6059
|
+
kind: "unreadable",
|
|
6060
|
+
reason: "it is not a JSON object"
|
|
6061
|
+
};
|
|
6062
|
+
const projects = config.projects ?? {};
|
|
6063
|
+
if (!isPlainRecord(projects)) return {
|
|
6064
|
+
kind: "unreadable",
|
|
6065
|
+
reason: "its projects key is not an object"
|
|
6066
|
+
};
|
|
6067
|
+
const existingEntry = projects[projectKey];
|
|
6068
|
+
if (existingEntry !== void 0 && !isPlainRecord(existingEntry)) return {
|
|
6069
|
+
kind: "unreadable",
|
|
6070
|
+
reason: "its entry for this folder is not an object"
|
|
6071
|
+
};
|
|
6072
|
+
const keysToGrant = hasRecordedExternalImportsDecline(existingEntry) ? TRUST_ONLY_KEYS : GRANTED_KEYS;
|
|
6073
|
+
if (keysToGrant.every((key) => existingEntry?.[key] === true)) return { kind: "trusted" };
|
|
6074
|
+
const trustedEntry = {
|
|
6075
|
+
...existingEntry ?? newProjectEntry(),
|
|
6076
|
+
...Object.fromEntries(keysToGrant.map((key) => [key, true]))
|
|
6077
|
+
};
|
|
6078
|
+
const nextConfigText = JSON.stringify({
|
|
6079
|
+
...config,
|
|
6080
|
+
projects: {
|
|
6081
|
+
...projects,
|
|
6082
|
+
[projectKey]: trustedEntry
|
|
6083
|
+
}
|
|
6084
|
+
}, null, 2);
|
|
6085
|
+
return {
|
|
6086
|
+
kind: "grant",
|
|
6087
|
+
nextConfigText: configText.endsWith("\n") ? `${nextConfigText}\n` : nextConfigText
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
//#endregion
|
|
6091
|
+
//#region server/guardrails/core/guardrails-bridge-socket-core.ts
|
|
6092
|
+
var LONGEST_BRIDGE_SOCKET_NAME = "claude-socks-0123456789abcdef.sock";
|
|
6093
|
+
var UNIX_SOCKET_PATH_BYTE_LIMIT = 107;
|
|
6094
|
+
var SHORT_BRIDGE_SOCKET_FOLDER = "/tmp";
|
|
6095
|
+
function bridgeSocketPathBytes(tempDirectory) {
|
|
6096
|
+
return Buffer.byteLength(path.join(tempDirectory, LONGEST_BRIDGE_SOCKET_NAME));
|
|
6097
|
+
}
|
|
6098
|
+
function hasRoomForBridgeSocket(tempDirectory, platform) {
|
|
6099
|
+
return platform !== "linux" || bridgeSocketPathBytes(tempDirectory) <= UNIX_SOCKET_PATH_BYTE_LIMIT;
|
|
6100
|
+
}
|
|
6101
|
+
function bridgeSocketLauncherEnv(inheritedTempDirectory, platform) {
|
|
6102
|
+
if (hasRoomForBridgeSocket(inheritedTempDirectory, platform)) return { TMPDIR: inheritedTempDirectory };
|
|
6103
|
+
return { TMPDIR: SHORT_BRIDGE_SOCKET_FOLDER };
|
|
6104
|
+
}
|
|
6105
|
+
//#endregion
|
|
6106
|
+
//#region server/guardrails/core/guardrails-policy-core.ts
|
|
6107
|
+
var VENDOR_PROFILES = Object.freeze({
|
|
6108
|
+
claude: {
|
|
6109
|
+
homeEntries: [".claude", ".claude.json"],
|
|
6110
|
+
launchWritableFolders: ({ cwd, agentSessionId }) => [path.join("projects", encodeProjectDir(cwd)), ...agentSessionId === null ? [] : [path.join("session-env", agentSessionId)]],
|
|
6111
|
+
launchPrivateFolders: ["shell-snapshots"],
|
|
6112
|
+
launchHomeEnvName: null,
|
|
6113
|
+
loginFiles: [".credentials.json"],
|
|
6114
|
+
configEntries: [
|
|
6115
|
+
"settings.json",
|
|
6116
|
+
"settings.local.json",
|
|
6117
|
+
"hooks",
|
|
6118
|
+
"skills",
|
|
6119
|
+
"plugins",
|
|
6120
|
+
"commands",
|
|
6121
|
+
"agents",
|
|
6122
|
+
"CLAUDE.md",
|
|
6123
|
+
"AGENTS.md"
|
|
6124
|
+
],
|
|
6125
|
+
apiDomains: ["api.anthropic.com", "platform.claude.com"],
|
|
6126
|
+
launchEnv: { DISABLE_AUTOUPDATER: "1" }
|
|
6127
|
+
},
|
|
6128
|
+
codex: {
|
|
6129
|
+
homeEntries: [".codex"],
|
|
6130
|
+
launchWritableFolders: () => [],
|
|
6131
|
+
launchPrivateFolders: [],
|
|
6132
|
+
launchHomeEnvName: "CODEX_HOME",
|
|
6133
|
+
loginFiles: ["auth.json"],
|
|
6134
|
+
configEntries: [
|
|
6135
|
+
"config.toml",
|
|
6136
|
+
"AGENTS.md",
|
|
6137
|
+
"skills",
|
|
6138
|
+
"rules",
|
|
6139
|
+
"prompts",
|
|
6140
|
+
"hooks.json"
|
|
6141
|
+
],
|
|
6142
|
+
apiDomains: [
|
|
6143
|
+
"chatgpt.com",
|
|
6144
|
+
"*.chatgpt.com",
|
|
6145
|
+
"auth.openai.com",
|
|
6146
|
+
"api.openai.com",
|
|
6147
|
+
"*.openai.com"
|
|
6148
|
+
],
|
|
6149
|
+
launchEnv: {}
|
|
6150
|
+
}
|
|
6151
|
+
});
|
|
6152
|
+
var GUARDRAILS_VENDORS = Object.freeze(["claude", "codex"]);
|
|
6153
|
+
var UNWRAPPED_VENDOR_HOME_ENTRIES = Object.freeze([".grok"]);
|
|
6154
|
+
var CREDENTIAL_HOME_ENTRIES = Object.freeze([
|
|
6155
|
+
".ssh",
|
|
6156
|
+
".config/gh",
|
|
6157
|
+
".config/coderv2",
|
|
6158
|
+
"Library/Application Support/coderv2",
|
|
6159
|
+
"Library/Keychains",
|
|
6160
|
+
".git-credentials"
|
|
6161
|
+
]);
|
|
6162
|
+
var CLOUD_CREDENTIAL_HOME_ENTRIES = Object.freeze([
|
|
6163
|
+
".aws",
|
|
6164
|
+
".azure",
|
|
6165
|
+
".config/gcloud",
|
|
6166
|
+
".kube",
|
|
6167
|
+
".docker",
|
|
6168
|
+
".gnupg",
|
|
6169
|
+
".netrc",
|
|
6170
|
+
".npmrc"
|
|
6171
|
+
]);
|
|
6172
|
+
var STANDARD_LEVEL_USABLE_CREDENTIAL_ENTRIES = Object.freeze(["Library/Keychains"]);
|
|
6173
|
+
var XDG_CONFIG_DEFAULT_HOME_PREFIX = ".config/";
|
|
6174
|
+
var SHARED_TEMP_DIRS = Object.freeze(["/tmp/claude", "/private/tmp/claude"]);
|
|
6175
|
+
var SHARED_HOME_WRITE_ENTRIES = Object.freeze([".npm/_logs"]);
|
|
6176
|
+
var GUARDRAILS_PLACEHOLDER_NAMES = Object.freeze([
|
|
6177
|
+
".bash_profile",
|
|
6178
|
+
".bashrc",
|
|
6179
|
+
".claude",
|
|
6180
|
+
".gitconfig",
|
|
6181
|
+
".gitmodules",
|
|
6182
|
+
".idea",
|
|
6183
|
+
".mcp.json",
|
|
6184
|
+
".profile",
|
|
6185
|
+
".ripgreprc",
|
|
6186
|
+
".vscode",
|
|
6187
|
+
".zprofile",
|
|
6188
|
+
".zshrc"
|
|
6189
|
+
]);
|
|
6190
|
+
var WHOLE_FILESYSTEM = "/";
|
|
6191
|
+
var GUARDRAILS_TEMP_FOLDER = "guardrails-temp";
|
|
6192
|
+
var AGENT_LOGINS_SIBLING_ENTRY = "agent-logins";
|
|
6193
|
+
var GLIMMERVOID_CONFIG_SIBLING_ENTRIES = Object.freeze([
|
|
6194
|
+
AGENT_LOGINS_SIBLING_ENTRY,
|
|
6195
|
+
"bin",
|
|
6196
|
+
"pairings.json",
|
|
6197
|
+
"pairings-seen.json",
|
|
6198
|
+
"plans",
|
|
6199
|
+
"telegram-outbox.json",
|
|
6200
|
+
"traces",
|
|
6201
|
+
"uploads",
|
|
6202
|
+
"usage-budget-state.json",
|
|
6203
|
+
"usage-lanes.json",
|
|
6204
|
+
"usage-warehouse.json",
|
|
6205
|
+
"visions-intent.json"
|
|
6206
|
+
]);
|
|
6207
|
+
var GLIMMERVOID_HOME_STATE_ENTRIES = Object.freeze([
|
|
6208
|
+
"benchmark-runs",
|
|
6209
|
+
"benchmarks",
|
|
6210
|
+
"config.json",
|
|
6211
|
+
"issues-cache.json",
|
|
6212
|
+
"litellm-pricing.json",
|
|
6213
|
+
"my-prs-state.json",
|
|
6214
|
+
"my-prs-work",
|
|
6215
|
+
"posthog-reports",
|
|
6216
|
+
"posthog-state.json",
|
|
6217
|
+
"posthog-work",
|
|
6218
|
+
"recordings",
|
|
6219
|
+
"guardrails-homes",
|
|
6220
|
+
"guardrails-policies",
|
|
6221
|
+
"sane-yolo",
|
|
6222
|
+
"scratch",
|
|
6223
|
+
"sounds",
|
|
6224
|
+
"team-review-repos",
|
|
6225
|
+
"team-review-state.json",
|
|
6226
|
+
"team-review-work",
|
|
6227
|
+
"team-review-worktrees",
|
|
6228
|
+
"telemetry-pending-crash.json",
|
|
6229
|
+
"telemetry.json",
|
|
6230
|
+
"tmp",
|
|
6231
|
+
"update-check.json",
|
|
6232
|
+
"update-journal.json",
|
|
6233
|
+
"workflows-state.json",
|
|
6234
|
+
"workflows-work"
|
|
6235
|
+
]);
|
|
6236
|
+
var RUNTIME_MANDATORY_CLAUDE_DENY_FOLDERS = Object.freeze(["commands", "agents"]);
|
|
6237
|
+
var GUARDED_PROXY_BYPASS = "localhost,::1,169.254.0.0/16,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16";
|
|
6238
|
+
var HOOK_LOOPBACK_HOST = "127.0.0.1";
|
|
6239
|
+
var AGENT_SESSION_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
6240
|
+
var STANDARD_OPEN_NETWORK = Object.freeze({
|
|
6241
|
+
allowAllUnixSockets: true,
|
|
6242
|
+
allowMachLookup: ["*"]
|
|
6243
|
+
});
|
|
6244
|
+
var STRICT_OPEN_NETWORK = Object.freeze({ allowAllUnixSockets: false });
|
|
6245
|
+
function unique(entries) {
|
|
6246
|
+
return [...new Set(entries)];
|
|
6247
|
+
}
|
|
6248
|
+
function literalEntries(entryPaths) {
|
|
6249
|
+
return unique(entryPaths).map((entryPath) => ({
|
|
6250
|
+
path: entryPath,
|
|
6251
|
+
literal: true
|
|
6252
|
+
}));
|
|
6253
|
+
}
|
|
6254
|
+
function homePaths(host, homeEntries) {
|
|
6255
|
+
return homeEntries.map((entry) => path.join(host.homeDir, entry));
|
|
6256
|
+
}
|
|
6257
|
+
function isInsideAny(parents, child) {
|
|
6258
|
+
return parents.some((parent) => isPathInside(parent, child));
|
|
6259
|
+
}
|
|
6260
|
+
function vendorStatePaths(host, vendor, stateFolderEntries) {
|
|
6261
|
+
return stateFolderEntries.map((entry) => path.join(host.vendorLocations[vendor].stateFolder, entry));
|
|
6262
|
+
}
|
|
6263
|
+
function vendorHeldPaths(host, vendor) {
|
|
6264
|
+
const { stateFolder, configFiles, otherHomes } = host.vendorLocations[vendor];
|
|
6265
|
+
return [
|
|
6266
|
+
stateFolder,
|
|
6267
|
+
...configFiles,
|
|
6268
|
+
...otherHomes
|
|
6269
|
+
];
|
|
6270
|
+
}
|
|
6271
|
+
function launchHomeOf(host, vendor) {
|
|
6272
|
+
return path.join(host.launchHomeDir, vendor);
|
|
6273
|
+
}
|
|
6274
|
+
function vendorsWithLaunchHome(role) {
|
|
6275
|
+
if (role.vendorHome.scope === "state") return [];
|
|
6276
|
+
return role.vendors.filter((vendor) => VENDOR_PROFILES[vendor].launchHomeEnvName !== null);
|
|
6277
|
+
}
|
|
6278
|
+
function vendorWritableFolders(role, host) {
|
|
6279
|
+
const { vendorHome } = role;
|
|
6280
|
+
if (vendorHome.scope === "state") return role.vendors.map((vendor) => host.vendorLocations[vendor].stateFolder);
|
|
6281
|
+
return role.vendors.flatMap((vendor) => {
|
|
6282
|
+
const profile = VENDOR_PROFILES[vendor];
|
|
6283
|
+
if (profile.launchHomeEnvName !== null) return [launchHomeOf(host, vendor)];
|
|
6284
|
+
return vendorStatePaths(host, vendor, profile.launchWritableFolders(vendorHome));
|
|
6285
|
+
});
|
|
6286
|
+
}
|
|
6287
|
+
function launchPrivateFolders(role, host) {
|
|
6288
|
+
if (role.vendorHome.scope === "state") return [];
|
|
6289
|
+
return role.vendors.flatMap((vendor) => vendorStatePaths(host, vendor, VENDOR_PROFILES[vendor].launchPrivateFolders));
|
|
6290
|
+
}
|
|
6291
|
+
function launchHomeLinkCandidates(role, host) {
|
|
6292
|
+
return vendorsWithLaunchHome(role).flatMap((vendor) => {
|
|
6293
|
+
const { configEntries, loginFiles } = VENDOR_PROFILES[vendor];
|
|
6294
|
+
const linkFor = (entry, isLogin) => ({
|
|
6295
|
+
linkPath: path.join(launchHomeOf(host, vendor), entry),
|
|
6296
|
+
targetPath: path.join(host.vendorLocations[vendor].stateFolder, entry),
|
|
6297
|
+
isLogin
|
|
6298
|
+
});
|
|
6299
|
+
return [...configEntries.map((entry) => linkFor(entry, false)), ...loginFiles.map((entry) => linkFor(entry, true))];
|
|
6300
|
+
});
|
|
6301
|
+
}
|
|
6302
|
+
function launchHomeEnv(role, host) {
|
|
6303
|
+
return Object.fromEntries(vendorsWithLaunchHome(role).flatMap((vendor) => {
|
|
6304
|
+
const envName = VENDOR_PROFILES[vendor].launchHomeEnvName;
|
|
6305
|
+
return envName === null ? [] : [[envName, launchHomeOf(host, vendor)]];
|
|
6306
|
+
}));
|
|
6307
|
+
}
|
|
6308
|
+
function vendorLoginFiles(role, host) {
|
|
6309
|
+
return role.vendors.flatMap((vendor) => vendorStatePaths(host, vendor, VENDOR_PROFILES[vendor].loginFiles));
|
|
6310
|
+
}
|
|
6311
|
+
function roleWritableRoots(role) {
|
|
6312
|
+
return [
|
|
6313
|
+
...role.writableRoots,
|
|
6314
|
+
...role.lockedCheckouts,
|
|
6315
|
+
...role.commitCheckouts.flatMap(({ checkout, gitDir }) => [checkout, gitDir])
|
|
6316
|
+
];
|
|
6317
|
+
}
|
|
6318
|
+
function alwaysWritablePaths(role, host) {
|
|
6319
|
+
return [
|
|
6320
|
+
host.tempDir,
|
|
6321
|
+
...roleWritableRoots(role),
|
|
6322
|
+
...vendorWritableFolders(role, host)
|
|
6323
|
+
];
|
|
6324
|
+
}
|
|
6325
|
+
function commitCheckoutWriteDenies({ checkout, gitDir }) {
|
|
6326
|
+
const gitPointer = path.join(checkout, ".git");
|
|
6327
|
+
const gitDirDenies = [path.join(gitDir, "hooks"), path.join(gitDir, "config")];
|
|
6328
|
+
if (path.resolve(gitPointer) === path.resolve(gitDir)) return gitDirDenies;
|
|
6329
|
+
return [gitPointer, ...gitDirDenies];
|
|
6330
|
+
}
|
|
6331
|
+
function writeDeniesWhenPresent(role, host) {
|
|
6332
|
+
const writablePaths = alwaysWritablePaths(role, host);
|
|
6333
|
+
const sharedHomeWrites = homePaths(host, SHARED_HOME_WRITE_ENTRIES).filter((sharedPath) => !isInsideAny(roleWritableRoots(role), sharedPath));
|
|
6334
|
+
const vendorConfig = role.vendors.flatMap((vendor) => [...vendorStatePaths(host, vendor, VENDOR_PROFILES[vendor].configEntries), ...host.vendorLocations[vendor].configFiles]).filter((configPath) => isInsideAny(writablePaths, configPath));
|
|
6335
|
+
return [
|
|
6336
|
+
...SHARED_TEMP_DIRS,
|
|
6337
|
+
...sharedHomeWrites,
|
|
6338
|
+
...vendorConfig,
|
|
6339
|
+
...role.commitCheckouts.flatMap(commitCheckoutWriteDenies)
|
|
6340
|
+
];
|
|
6341
|
+
}
|
|
6342
|
+
function lockedCheckoutWriteDenies(role) {
|
|
6343
|
+
return role.lockedCheckouts.flatMap((checkout) => [path.join(checkout, ".git"), path.join(checkout, ".claude")]);
|
|
6344
|
+
}
|
|
6345
|
+
function lockedClaudeMountPoints(role) {
|
|
6346
|
+
return role.lockedCheckouts.flatMap((checkout) => RUNTIME_MANDATORY_CLAUDE_DENY_FOLDERS.map((folder) => path.join(checkout, ".claude", folder)));
|
|
6347
|
+
}
|
|
6348
|
+
function guardrailsPlaceholderPaths(role) {
|
|
6349
|
+
return unique([...(role.vendorHome.scope === "launch" ? [role.vendorHome.cwd] : []).flatMap((workingFolder) => GUARDRAILS_PLACEHOLDER_NAMES.map((placeholderName) => path.join(workingFolder, placeholderName))), ...lockedCheckoutWriteDenies(role)]);
|
|
6350
|
+
}
|
|
6351
|
+
function homeEntriesNamedLike(host, homeEntries) {
|
|
6352
|
+
return host.homeEntryNames.filter((entryName) => homeEntries.some((homeEntry) => entryName.startsWith(homeEntry)));
|
|
6353
|
+
}
|
|
6354
|
+
function relocatedXdgConfigPaths(host, homeEntries) {
|
|
6355
|
+
const xdgConfigHome = host.relocatedXdgConfigHome;
|
|
6356
|
+
if (xdgConfigHome === null) return [];
|
|
6357
|
+
return homeEntries.filter((entry) => entry.startsWith(XDG_CONFIG_DEFAULT_HOME_PREFIX)).map((entry) => path.join(xdgConfigHome, entry.slice(8)));
|
|
6358
|
+
}
|
|
6359
|
+
function isStandardLevel(role) {
|
|
6360
|
+
return role.guardrailLevel === "standard";
|
|
6361
|
+
}
|
|
6362
|
+
function blockedCredentialHomeEntries(role) {
|
|
6363
|
+
const credentialHomeEntries = [...CREDENTIAL_HOME_ENTRIES, ...CLOUD_CREDENTIAL_HOME_ENTRIES];
|
|
6364
|
+
if (!isStandardLevel(role)) return credentialHomeEntries;
|
|
6365
|
+
return credentialHomeEntries.filter((entry) => !STANDARD_LEVEL_USABLE_CREDENTIAL_ENTRIES.includes(entry));
|
|
6366
|
+
}
|
|
6367
|
+
function credentialWriteBlocks(role, host) {
|
|
6368
|
+
const credentialHomeEntries = blockedCredentialHomeEntries(role);
|
|
6369
|
+
return [...homePaths(host, credentialHomeEntries), ...relocatedXdgConfigPaths(host, credentialHomeEntries)];
|
|
6370
|
+
}
|
|
6371
|
+
function glimmervoidStateWriteBlocks(role, host) {
|
|
6372
|
+
const keptWritable = alwaysWritablePaths(role, host);
|
|
6373
|
+
const configFolder = path.dirname(host.glimmervoidConfigFile);
|
|
6374
|
+
const homeEntries = [
|
|
6375
|
+
...GLIMMERVOID_HOME_STATE_ENTRIES,
|
|
6376
|
+
...GLIMMERVOID_CONFIG_SIBLING_ENTRIES,
|
|
6377
|
+
...host.glimmervoidHomeEntryNames
|
|
6378
|
+
].map((entryName) => path.join(host.glimmervoidHome, entryName));
|
|
6379
|
+
const configSiblings = GLIMMERVOID_CONFIG_SIBLING_ENTRIES.map((entryName) => path.join(configFolder, entryName));
|
|
6380
|
+
return unique([
|
|
6381
|
+
host.glimmervoidConfigFile,
|
|
6382
|
+
...homeEntries,
|
|
6383
|
+
...configSiblings
|
|
6384
|
+
]).filter((entryPath) => !keptWritable.some((writable) => isPathInside(entryPath, writable)));
|
|
6385
|
+
}
|
|
6386
|
+
function standardWriteBlockCandidates(role, host) {
|
|
6387
|
+
if (!isStandardLevel(role)) return [];
|
|
6388
|
+
return unique([
|
|
6389
|
+
...credentialWriteBlocks(role, host),
|
|
6390
|
+
...glimmervoidStateWriteBlocks(role, host),
|
|
6391
|
+
...role.blockedPaths ?? []
|
|
6392
|
+
]);
|
|
6393
|
+
}
|
|
6394
|
+
function readDeniedPaths(role, host) {
|
|
6395
|
+
const otherVendors = GUARDRAILS_VENDORS.filter((vendor) => !role.vendors.includes(vendor));
|
|
6396
|
+
const otherVendorHomeEntries = [...otherVendors.flatMap((vendor) => VENDOR_PROFILES[vendor].homeEntries), ...UNWRAPPED_VENDOR_HOME_ENTRIES];
|
|
6397
|
+
const credentialHomeEntries = blockedCredentialHomeEntries(role);
|
|
6398
|
+
return [
|
|
6399
|
+
...homePaths(host, [
|
|
6400
|
+
...credentialHomeEntries,
|
|
6401
|
+
...otherVendorHomeEntries,
|
|
6402
|
+
...homeEntriesNamedLike(host, otherVendorHomeEntries)
|
|
6403
|
+
]),
|
|
6404
|
+
...relocatedXdgConfigPaths(host, credentialHomeEntries),
|
|
6405
|
+
...otherVendors.flatMap((vendor) => vendorHeldPaths(host, vendor)),
|
|
6406
|
+
...host.unwrappedVendorHomes,
|
|
6407
|
+
host.glimmervoidHome,
|
|
6408
|
+
path.join(path.dirname(host.glimmervoidConfigFile), AGENT_LOGINS_SIBLING_ENTRY),
|
|
6409
|
+
...launchPrivateFolders(role, host),
|
|
6410
|
+
...role.readDenied
|
|
6411
|
+
];
|
|
6412
|
+
}
|
|
6413
|
+
function guardrailsPolicyProbePaths(role, host) {
|
|
6414
|
+
return unique([
|
|
6415
|
+
...writeDeniesWhenPresent(role, host),
|
|
6416
|
+
...vendorLoginFiles(role, host),
|
|
6417
|
+
...launchHomeLinkCandidates(role, host).map((link) => link.targetPath)
|
|
6418
|
+
]);
|
|
6419
|
+
}
|
|
6420
|
+
function isValidPort(port) {
|
|
6421
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
6422
|
+
}
|
|
6423
|
+
function hostNamedPaths(host) {
|
|
6424
|
+
const relocatedXdgConfigHomes = host.relocatedXdgConfigHome === null ? [] : [host.relocatedXdgConfigHome];
|
|
6425
|
+
return [
|
|
6426
|
+
host.homeDir,
|
|
6427
|
+
host.glimmervoidHome,
|
|
6428
|
+
host.glimmervoidConfigFile,
|
|
6429
|
+
host.tempDir,
|
|
6430
|
+
host.launchHomeDir,
|
|
6431
|
+
...relocatedXdgConfigHomes,
|
|
6432
|
+
...host.unwrappedVendorHomes,
|
|
6433
|
+
...GUARDRAILS_VENDORS.flatMap((vendor) => vendorHeldPaths(host, vendor))
|
|
6434
|
+
];
|
|
6435
|
+
}
|
|
6436
|
+
function guardrailsRoleRefusal(role, host) {
|
|
6437
|
+
const launchCwds = role.vendorHome.scope === "launch" ? [role.vendorHome.cwd] : [];
|
|
6438
|
+
const relativePath = [
|
|
6439
|
+
...hostNamedPaths(host),
|
|
6440
|
+
...roleWritableRoots(role),
|
|
6441
|
+
...role.readDenied,
|
|
6442
|
+
...role.readableWithinDenied,
|
|
6443
|
+
...launchCwds
|
|
6444
|
+
].find((namedPath) => !path.isAbsolute(namedPath));
|
|
6445
|
+
if (relativePath !== void 0) return `guardrails policy path is not absolute: ${relativePath}`;
|
|
6446
|
+
const rootOverProtectedHome = [...roleWritableRoots(role), ...vendorWritableFolders(role, host)].find((root) => isPathInside(root, host.homeDir) || isPathInside(root, host.glimmervoidHome));
|
|
6447
|
+
if (rootOverProtectedHome !== void 0) return `writable root ${rootOverProtectedHome} contains the home folder or the Glimmervoid home`;
|
|
6448
|
+
const writablePaths = alwaysWritablePaths(role, host);
|
|
6449
|
+
const reallowedAncestor = role.readableWithinDenied.find((readable) => writablePaths.some((writable) => isPathInside(readable, writable, { allowEqual: false })));
|
|
6450
|
+
if (reallowedAncestor !== void 0) return `readable path ${reallowedAncestor} is an ancestor of a writable root`;
|
|
6451
|
+
if (role.network.access === "allowlist" && role.network.hookPort !== null && !isValidPort(role.network.hookPort)) return "hook listener port is invalid";
|
|
6452
|
+
const agentSessionId = role.vendorHome.scope === "launch" ? role.vendorHome.agentSessionId : null;
|
|
6453
|
+
if (agentSessionId !== null && !AGENT_SESSION_ID_SHAPE.test(agentSessionId)) return "agent session id is invalid";
|
|
6454
|
+
return null;
|
|
6455
|
+
}
|
|
6456
|
+
function networkPolicy(role) {
|
|
6457
|
+
if (role.network.access === "open") return isStandardLevel(role) ? { ...STANDARD_OPEN_NETWORK } : { ...STRICT_OPEN_NETWORK };
|
|
6458
|
+
if (role.network.access === "none") return {
|
|
6459
|
+
allowedDomains: [],
|
|
6460
|
+
deniedDomains: [],
|
|
6461
|
+
allowLocalBinding: false,
|
|
6462
|
+
allowAllUnixSockets: false
|
|
6463
|
+
};
|
|
6464
|
+
const hookEndpoint = role.network.hookPort === null ? [] : [`${HOOK_LOOPBACK_HOST}:${role.network.hookPort}`];
|
|
6465
|
+
const vendorDomains = role.vendors.flatMap((vendor) => VENDOR_PROFILES[vendor].apiDomains);
|
|
6466
|
+
return {
|
|
6467
|
+
allowedDomains: unique([
|
|
6468
|
+
...hookEndpoint,
|
|
6469
|
+
...vendorDomains,
|
|
6470
|
+
...role.network.domains
|
|
6471
|
+
]),
|
|
6472
|
+
deniedDomains: [],
|
|
6473
|
+
strictAllowlist: true,
|
|
6474
|
+
allowLocalBinding: false,
|
|
6475
|
+
allowAllUnixSockets: false
|
|
6476
|
+
};
|
|
6477
|
+
}
|
|
6478
|
+
function guardedEnvFor(role, host) {
|
|
6479
|
+
if (role.network.access === "allowlist") return {
|
|
6480
|
+
NO_PROXY: GUARDED_PROXY_BYPASS,
|
|
6481
|
+
no_proxy: GUARDED_PROXY_BYPASS,
|
|
6482
|
+
[HOOK_VIA_GUARDRAILS_PROXY_ENV]: "1"
|
|
6483
|
+
};
|
|
6484
|
+
if (role.network.access === "open") return { TMPDIR: host.tempDir };
|
|
6485
|
+
return {};
|
|
6486
|
+
}
|
|
6487
|
+
function filesystemWrites(role, host, writablePaths) {
|
|
6488
|
+
if (isStandardLevel(role)) return {
|
|
6489
|
+
allowWrite: literalEntries([WHOLE_FILESYSTEM, ...writablePaths]),
|
|
6490
|
+
denyWrite: literalEntries(host.writeBlockedPaths)
|
|
6491
|
+
};
|
|
6492
|
+
const isPresent = (candidate) => host.existingPaths.has(candidate);
|
|
6493
|
+
return {
|
|
6494
|
+
allowWrite: literalEntries(writablePaths),
|
|
6495
|
+
denyWrite: literalEntries([...writeDeniesWhenPresent(role, host).filter(isPresent), ...lockedCheckoutWriteDenies(role)])
|
|
6496
|
+
};
|
|
6497
|
+
}
|
|
6498
|
+
function buildGuardrailsLaunchPlan(role, host) {
|
|
6499
|
+
const refusal = guardrailsRoleRefusal(role, host);
|
|
6500
|
+
if (refusal !== null) return {
|
|
6501
|
+
ok: false,
|
|
6502
|
+
reason: refusal
|
|
6503
|
+
};
|
|
6504
|
+
const isPresent = (candidate) => host.existingPaths.has(candidate);
|
|
6505
|
+
const writablePaths = [...alwaysWritablePaths(role, host), ...vendorLoginFiles(role, host).filter(isPresent)];
|
|
6506
|
+
const readDenied = readDeniedPaths(role, host);
|
|
6507
|
+
const writableWithinDenied = writablePaths.filter((writable) => isInsideAny(readDenied, writable));
|
|
6508
|
+
return {
|
|
6509
|
+
ok: true,
|
|
6510
|
+
plan: {
|
|
6511
|
+
launcher: role.network.access === "open" ? "open-network" : "runtime-cli",
|
|
6512
|
+
policy: {
|
|
6513
|
+
network: networkPolicy(role),
|
|
6514
|
+
filesystem: {
|
|
6515
|
+
denyRead: literalEntries(readDenied),
|
|
6516
|
+
allowRead: literalEntries([...writableWithinDenied, ...role.readableWithinDenied]),
|
|
6517
|
+
...filesystemWrites(role, host, writablePaths)
|
|
6518
|
+
},
|
|
6519
|
+
terminalScope: isStandardLevel(role) ? "any" : "own",
|
|
6520
|
+
allowAppleEvents: isStandardLevel(role)
|
|
6521
|
+
},
|
|
6522
|
+
launchEnv: Object.assign({ CLAUDE_CODE_TMPDIR: host.tempDir }, ...role.vendors.map((vendor) => VENDOR_PROFILES[vendor].launchEnv), launchHomeEnv(role, host)),
|
|
6523
|
+
guardedEnv: guardedEnvFor(role, host),
|
|
6524
|
+
requiredDirectories: unique([
|
|
6525
|
+
host.tempDir,
|
|
6526
|
+
...vendorWritableFolders(role, host),
|
|
6527
|
+
...launchPrivateFolders(role, host)
|
|
6528
|
+
]),
|
|
6529
|
+
launchHomeLinks: launchHomeLinkCandidates(role, host).filter((link) => isPresent(link.targetPath))
|
|
6530
|
+
}
|
|
6531
|
+
};
|
|
6532
|
+
}
|
|
6533
|
+
//#endregion
|
|
6534
|
+
//#region server/prune-files.ts
|
|
6535
|
+
var MS_PER_DAY = 864e5;
|
|
6536
|
+
function idForEntry(entry, suffixes) {
|
|
6537
|
+
for (const suffix of suffixes) {
|
|
6538
|
+
if (suffix.length === 0) return entry;
|
|
6539
|
+
if (entry.endsWith(suffix)) return entry.slice(0, -suffix.length);
|
|
6540
|
+
}
|
|
6541
|
+
return null;
|
|
6542
|
+
}
|
|
6543
|
+
async function pruneAgedFiles({ directory, suffixes, retainDays, now = Date.now(), isRetainedId = () => false, entryMode = "file", fsPromises = fs.promises }) {
|
|
6544
|
+
let entries;
|
|
6545
|
+
try {
|
|
6546
|
+
entries = await fsPromises.readdir(directory);
|
|
6547
|
+
} catch {
|
|
6548
|
+
return [];
|
|
6549
|
+
}
|
|
6550
|
+
const cutoff = now - retainDays * MS_PER_DAY;
|
|
6551
|
+
const removedIds = [];
|
|
6552
|
+
for (const entry of entries) {
|
|
6553
|
+
const id = idForEntry(entry, suffixes);
|
|
6554
|
+
if (!id || isRetainedId(id)) continue;
|
|
6555
|
+
const filePath = path.join(directory, entry);
|
|
6556
|
+
try {
|
|
6557
|
+
if ((await fsPromises.stat(filePath)).mtimeMs >= cutoff) continue;
|
|
6558
|
+
if (entryMode === "directory") await fsPromises.rm(filePath, {
|
|
6559
|
+
recursive: true,
|
|
6560
|
+
force: true
|
|
6561
|
+
});
|
|
6562
|
+
if (entryMode === "file") await fsPromises.unlink(filePath);
|
|
6563
|
+
removedIds.push(id);
|
|
6564
|
+
} catch {}
|
|
6565
|
+
}
|
|
6566
|
+
return removedIds;
|
|
6567
|
+
}
|
|
6568
|
+
//#endregion
|
|
6569
|
+
//#region server/guardrails/guardrails-launch.ts
|
|
6570
|
+
var LAUNCH_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
6571
|
+
var GUARDED_ENV_COMMAND = "/usr/bin/env";
|
|
6572
|
+
var POLICY_FOLDER = "guardrails-policies";
|
|
6573
|
+
var LAUNCH_HOME_FOLDER = "guardrails-homes";
|
|
6574
|
+
var START_FOLDER = "start";
|
|
6575
|
+
var OWNER_FILE_SUFFIX = ".owner.json";
|
|
6576
|
+
var ENTER_FOLDER_SCRIPT = "cd \"$1\" && shift && exec \"$@\"";
|
|
6577
|
+
var LOGIN_FILE_MODE = 384;
|
|
6578
|
+
var ABSENT_PATH_DENY_PLATFORM = "darwin";
|
|
6579
|
+
var OWN_PID_NAMESPACE_LINK = "/proc/self/ns/pid";
|
|
6580
|
+
var SESSION_TEMP_RETAIN_DAYS = 7;
|
|
6581
|
+
async function isPresent(candidate) {
|
|
6582
|
+
try {
|
|
6583
|
+
await access(candidate);
|
|
6584
|
+
return true;
|
|
6585
|
+
} catch (error) {
|
|
6586
|
+
if (!isMissingFileError(error)) throw error;
|
|
6587
|
+
return false;
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
async function presentPaths$1(candidates) {
|
|
6591
|
+
const presence = await Promise.all(candidates.map(isPresent));
|
|
6592
|
+
return new Set(candidates.filter((_candidate, index) => presence[index]));
|
|
6593
|
+
}
|
|
6594
|
+
async function realPathWhenPresent(candidate) {
|
|
6595
|
+
if (!path.isAbsolute(candidate)) return candidate;
|
|
6596
|
+
try {
|
|
6597
|
+
return await realpath(candidate);
|
|
6598
|
+
} catch (error) {
|
|
6599
|
+
if (!isMissingFileError(error)) throw error;
|
|
6600
|
+
return candidate;
|
|
6601
|
+
}
|
|
6602
|
+
}
|
|
6603
|
+
function realPathsWhenPresent(candidates) {
|
|
6604
|
+
return Promise.all(candidates.map(realPathWhenPresent));
|
|
6605
|
+
}
|
|
6606
|
+
async function symlinkTargetsInside(folder) {
|
|
6607
|
+
const symlinkEntries = (await readdir(folder, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isSymbolicLink());
|
|
6608
|
+
return (await Promise.all(symlinkEntries.map((entry) => realpath(path.join(folder, entry.name)).catch(() => null)))).filter((target) => target !== null);
|
|
6609
|
+
}
|
|
6610
|
+
async function blockedPathsWithRealTargets(candidates, operatorBlockedPaths) {
|
|
6611
|
+
const resolved = await Promise.all(candidates.map(async (candidate) => {
|
|
6612
|
+
const isPresentEntry = await lstat(candidate).then(() => true, (error) => !isMissingFileError(error));
|
|
6613
|
+
const isKeptWhenAbsent = process.platform === ABSENT_PATH_DENY_PLATFORM || operatorBlockedPaths.includes(candidate);
|
|
6614
|
+
if (!isPresentEntry) return isKeptWhenAbsent ? [candidate] : [];
|
|
6615
|
+
const realCandidate = await realpath(candidate).catch(() => candidate);
|
|
6616
|
+
return [
|
|
6617
|
+
candidate,
|
|
6618
|
+
realCandidate,
|
|
6619
|
+
...await symlinkTargetsInside(realCandidate)
|
|
6620
|
+
];
|
|
6621
|
+
}));
|
|
6622
|
+
return [...new Set(resolved.flat())];
|
|
6623
|
+
}
|
|
6624
|
+
async function configuredConfigFile(glimmervoidHome) {
|
|
6625
|
+
const { envPath, homePath } = decideConfigPath({
|
|
6626
|
+
env: process.env,
|
|
6627
|
+
homeDir: glimmervoidHome
|
|
6628
|
+
}, () => false);
|
|
6629
|
+
const configFile = envPath ?? homePath;
|
|
6630
|
+
return path.join(await realPathWhenPresent(path.dirname(configFile)), path.basename(configFile));
|
|
6631
|
+
}
|
|
6632
|
+
function scannedClaudeHomes(env, homeDir) {
|
|
6633
|
+
return [env, {
|
|
6634
|
+
...env,
|
|
6635
|
+
CLAUDE_CONFIG_DIR: void 0
|
|
6636
|
+
}].flatMap((scannedEnv) => projectDirCandidates(scannedEnv, [], homeDir)).map((projectsDir) => path.dirname(projectsDir));
|
|
6637
|
+
}
|
|
6638
|
+
async function resolveVendorLocations(env, homeDir) {
|
|
6639
|
+
const [legacyClaudeConfigPath, claudeConfigPath] = listClaudeGlobalConfigPathCandidates(env, homeDir);
|
|
6640
|
+
const [codexStateFolder, ...otherCodexHomes] = codexHomes(env, homeDir);
|
|
6641
|
+
if (codexStateFolder === void 0) return null;
|
|
6642
|
+
return {
|
|
6643
|
+
claude: {
|
|
6644
|
+
stateFolder: await realPathWhenPresent(path.dirname(legacyClaudeConfigPath)),
|
|
6645
|
+
configFiles: [await realPathWhenPresent(claudeConfigPath)],
|
|
6646
|
+
otherHomes: await realPathsWhenPresent(scannedClaudeHomes(env, homeDir))
|
|
6647
|
+
},
|
|
6648
|
+
codex: {
|
|
6649
|
+
stateFolder: await realPathWhenPresent(codexStateFolder),
|
|
6650
|
+
configFiles: [],
|
|
6651
|
+
otherHomes: await realPathsWhenPresent(otherCodexHomes)
|
|
6652
|
+
}
|
|
6653
|
+
};
|
|
6654
|
+
}
|
|
6655
|
+
async function withRealRoots(role) {
|
|
6656
|
+
const [writableRoots, lockedCheckouts, commitCheckouts] = await Promise.all([
|
|
6657
|
+
Promise.all(role.writableRoots.map((root) => realpath(root))),
|
|
6658
|
+
Promise.all(role.lockedCheckouts.map((checkout) => realpath(checkout))),
|
|
6659
|
+
Promise.all(role.commitCheckouts.map(async ({ checkout, gitDir }) => ({
|
|
6660
|
+
checkout: await realpath(checkout),
|
|
6661
|
+
gitDir: await realpath(gitDir)
|
|
6662
|
+
})))
|
|
6663
|
+
]);
|
|
6664
|
+
const vendorHome = role.vendorHome.scope === "launch" ? {
|
|
6665
|
+
...role.vendorHome,
|
|
6666
|
+
cwd: await realPathWhenPresent(role.vendorHome.cwd)
|
|
6667
|
+
} : role.vendorHome;
|
|
6668
|
+
return {
|
|
6669
|
+
...role,
|
|
6670
|
+
vendorHome,
|
|
6671
|
+
writableRoots,
|
|
6672
|
+
lockedCheckouts,
|
|
6673
|
+
commitCheckouts,
|
|
6674
|
+
readableWithinDenied: await realPathsWhenPresent(role.readableWithinDenied)
|
|
6675
|
+
};
|
|
6676
|
+
}
|
|
6677
|
+
async function lstatWhenPresent(candidate) {
|
|
6678
|
+
try {
|
|
6679
|
+
return await lstat(candidate);
|
|
6680
|
+
} catch (error) {
|
|
6681
|
+
if (!isMissingFileError(error)) throw error;
|
|
6682
|
+
return null;
|
|
6683
|
+
}
|
|
6684
|
+
}
|
|
6685
|
+
async function isRegularFile(candidate) {
|
|
6686
|
+
return (await lstatWhenPresent(candidate))?.isFile() ?? false;
|
|
6687
|
+
}
|
|
6688
|
+
async function isRealDirectory$1(candidate) {
|
|
6689
|
+
return (await lstatWhenPresent(candidate))?.isDirectory() ?? false;
|
|
6690
|
+
}
|
|
6691
|
+
async function absentPaths(candidates) {
|
|
6692
|
+
const presentCandidates = await presentPaths$1(candidates);
|
|
6693
|
+
return candidates.filter((candidate) => !presentCandidates.has(candidate));
|
|
6694
|
+
}
|
|
6695
|
+
async function removeLeftoverPlaceholders(placeholderPaths) {
|
|
6696
|
+
for (const placeholderPath of placeholderPaths) {
|
|
6697
|
+
const placeholderStats = await lstat(placeholderPath).catch(() => null);
|
|
6698
|
+
if (placeholderStats?.isFile() && placeholderStats.size === 0) await rm(placeholderPath, { force: true });
|
|
6699
|
+
}
|
|
6700
|
+
}
|
|
6701
|
+
async function createMissingMountPoints(mountPoints) {
|
|
6702
|
+
for (const mountPoint of mountPoints) {
|
|
6703
|
+
if (!await isRealDirectory$1(path.dirname(mountPoint))) continue;
|
|
6704
|
+
await mkdir(mountPoint).catch((error) => {
|
|
6705
|
+
if (errorCode(error) !== "EEXIST") throw error;
|
|
6706
|
+
});
|
|
6707
|
+
}
|
|
6708
|
+
}
|
|
6709
|
+
async function isNewerThanTarget(linkPath, targetPath) {
|
|
6710
|
+
const targetStats = await stat(targetPath).catch(() => null);
|
|
6711
|
+
if (targetStats === null) return true;
|
|
6712
|
+
return (await lstat(linkPath)).mtimeMs > targetStats.mtimeMs;
|
|
6713
|
+
}
|
|
6714
|
+
async function keepRefreshedLogins(logins, launchId, { onlyWhenNewer }) {
|
|
6715
|
+
for (const { linkPath, targetPath } of logins) {
|
|
6716
|
+
if (!await isRegularFile(linkPath)) continue;
|
|
6717
|
+
if (onlyWhenNewer && !await isNewerThanTarget(linkPath, targetPath)) continue;
|
|
6718
|
+
const stagedPath = `${targetPath}.${launchId}`;
|
|
6719
|
+
await copyFile(linkPath, stagedPath);
|
|
6720
|
+
await chmod(stagedPath, LOGIN_FILE_MODE);
|
|
6721
|
+
await rename(stagedPath, targetPath);
|
|
6722
|
+
}
|
|
6723
|
+
}
|
|
6724
|
+
async function removeLaunch(folders, owner, launchId, { onlyNewerLogins }) {
|
|
6725
|
+
await rm(folders.policyPath, { force: true });
|
|
6726
|
+
await removeLeftoverPlaceholders(owner.placeholderPaths);
|
|
6727
|
+
await keepRefreshedLogins(owner.logins.filter((login) => isPathInside(folders.launchHomeDir, login.linkPath, { allowEqual: false })), launchId, { onlyWhenNewer: onlyNewerLogins });
|
|
6728
|
+
await rm(folders.launchHomeDir, {
|
|
6729
|
+
recursive: true,
|
|
6730
|
+
force: true
|
|
6731
|
+
});
|
|
6732
|
+
if (folders.ownedTempDir !== null) await rm(folders.ownedTempDir, {
|
|
6733
|
+
recursive: true,
|
|
6734
|
+
force: true
|
|
6735
|
+
});
|
|
6736
|
+
await rm(folders.ownerPath, { force: true });
|
|
6737
|
+
}
|
|
6738
|
+
function isProcessAlive$1(pid) {
|
|
6739
|
+
try {
|
|
6740
|
+
process.kill(pid, 0);
|
|
6741
|
+
return true;
|
|
6742
|
+
} catch (error) {
|
|
6743
|
+
return errorCode(error) === "EPERM";
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
function readOwnPidNamespace() {
|
|
6747
|
+
return readlink(OWN_PID_NAMESPACE_LINK).catch(() => null);
|
|
6748
|
+
}
|
|
6749
|
+
async function readOwnerFile(ownerPath) {
|
|
6750
|
+
const outcome = await loadJsonStateFile({
|
|
6751
|
+
filePath: ownerPath,
|
|
6752
|
+
quarantine: false,
|
|
6753
|
+
parse: (raw) => GuardrailsLaunchOwner.safeParse(raw).data ?? null
|
|
6754
|
+
});
|
|
6755
|
+
return outcome.status === "loaded" ? outcome.value : null;
|
|
6756
|
+
}
|
|
6757
|
+
async function sweepOrphanedGuardrailsLaunches(log = console) {
|
|
6758
|
+
try {
|
|
6759
|
+
const policyDir = path.join(glimmervoidHomeDir$1(), POLICY_FOLDER);
|
|
6760
|
+
const entryNames = await readdir(policyDir).catch((error) => {
|
|
6761
|
+
if (isMissingFileError(error)) return [];
|
|
6762
|
+
throw error;
|
|
6763
|
+
});
|
|
6764
|
+
const glimmervoidHome = await realpath(glimmervoidHomeDir$1());
|
|
6765
|
+
const sweeperPidNamespace = await readOwnPidNamespace();
|
|
6766
|
+
let removedLaunchCount = 0;
|
|
6767
|
+
for (const entryName of entryNames) {
|
|
6768
|
+
if (!entryName.endsWith(OWNER_FILE_SUFFIX)) continue;
|
|
6769
|
+
const launchId = entryName.slice(0, -11);
|
|
6770
|
+
if (!LAUNCH_ID_SHAPE.test(launchId)) continue;
|
|
6771
|
+
const ownerPath = path.join(policyDir, entryName);
|
|
6772
|
+
const owner = await readOwnerFile(ownerPath);
|
|
6773
|
+
if (owner === null || owner.ownerHost !== os.hostname() || owner.ownerPidNamespace !== sweeperPidNamespace || isProcessAlive$1(owner.ownerPid)) continue;
|
|
6774
|
+
try {
|
|
6775
|
+
await removeLaunch({
|
|
6776
|
+
policyPath: path.join(policyDir, `${launchId}.json`),
|
|
6777
|
+
ownerPath,
|
|
6778
|
+
launchHomeDir: path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId),
|
|
6779
|
+
ownedTempDir: owner.ownsTempDir ? path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER, launchId) : null
|
|
6780
|
+
}, owner, launchId, { onlyNewerLogins: true });
|
|
6781
|
+
removedLaunchCount += 1;
|
|
6782
|
+
} catch (error) {
|
|
6783
|
+
log.warn(`[guardrails] could not remove the leftover launch ${launchId}: ${errorMessage(error)}`);
|
|
6784
|
+
}
|
|
6785
|
+
}
|
|
6786
|
+
if (removedLaunchCount > 0) log.log(`[guardrails] removed ${removedLaunchCount} launch folders left by sessions that ended without a clean shutdown`);
|
|
6787
|
+
} catch (error) {
|
|
6788
|
+
log.warn(`[guardrails] could not sweep leftover launch folders: ${errorMessage(error)}`);
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
async function pruneLeftoverSessionTempFolders(glimmervoidHome, liveSessionIds, log = console) {
|
|
6792
|
+
const policyEntryNames = await readdir(path.join(glimmervoidHome, POLICY_FOLDER)).catch((error) => {
|
|
6793
|
+
if (isMissingFileError(error)) return [];
|
|
6794
|
+
log.warn(`[guardrails] kept every session temp folder because the launch list could not be read: ${errorMessage(error)}`);
|
|
6795
|
+
return null;
|
|
6796
|
+
});
|
|
6797
|
+
if (policyEntryNames === null) return;
|
|
6798
|
+
const ownerEntryNames = policyEntryNames.filter((entryName) => entryName.endsWith(OWNER_FILE_SUFFIX));
|
|
6799
|
+
const owners = await Promise.all(ownerEntryNames.map((entryName) => readOwnerFile(path.join(glimmervoidHome, POLICY_FOLDER, entryName))));
|
|
6800
|
+
const tempFolderNamesWithOwners = /* @__PURE__ */ new Set([...ownerEntryNames.map((entryName) => entryName.slice(0, -11)), ...owners.flatMap((owner) => owner?.keptTempFolderName === void 0 ? [] : [owner.keptTempFolderName])]);
|
|
6801
|
+
await pruneAgedFiles({
|
|
6802
|
+
directory: path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER),
|
|
6803
|
+
suffixes: [""],
|
|
6804
|
+
retainDays: SESSION_TEMP_RETAIN_DAYS,
|
|
6805
|
+
entryMode: "directory",
|
|
6806
|
+
isRetainedId: (id) => liveSessionIds.has(id) || tempFolderNamesWithOwners.has(id)
|
|
6807
|
+
});
|
|
6808
|
+
}
|
|
6809
|
+
function launcherCommand(plan, policyPath) {
|
|
6810
|
+
if (plan.launcher === "open-network") return {
|
|
6811
|
+
file: process.execPath,
|
|
6812
|
+
args: [relayPath("guardrails-open-launcher"), policyPath]
|
|
6813
|
+
};
|
|
6814
|
+
const runtimeCliPath = resolvePackageBin("@anthropic-ai/sandbox-runtime", "srt");
|
|
6815
|
+
if (!runtimeCliPath) return null;
|
|
6816
|
+
return {
|
|
6817
|
+
file: process.execPath,
|
|
6818
|
+
args: [
|
|
6819
|
+
runtimeCliPath,
|
|
6820
|
+
"--settings",
|
|
6821
|
+
policyPath,
|
|
6822
|
+
"--"
|
|
6823
|
+
]
|
|
6824
|
+
};
|
|
6825
|
+
}
|
|
6826
|
+
function guardedEnvPrefix(plan) {
|
|
6827
|
+
const assignments = Object.entries(plan.guardedEnv).map(([name, value]) => `${name}=${value}`);
|
|
6828
|
+
return assignments.length === 0 ? [] : [GUARDED_ENV_COMMAND, ...assignments];
|
|
6829
|
+
}
|
|
6830
|
+
function enteringFolder(enterDir, file, args) {
|
|
6831
|
+
if (enterDir === void 0) return [file, ...args];
|
|
6832
|
+
return [
|
|
6833
|
+
"/bin/sh",
|
|
6834
|
+
"-c",
|
|
6835
|
+
ENTER_FOLDER_SCRIPT,
|
|
6836
|
+
"sh",
|
|
6837
|
+
enterDir,
|
|
6838
|
+
file,
|
|
6839
|
+
...args
|
|
6840
|
+
];
|
|
6841
|
+
}
|
|
6842
|
+
function sessionTempFolderFor(sessionId) {
|
|
6843
|
+
if (!LAUNCH_ID_SHAPE.test(sessionId)) return null;
|
|
6844
|
+
return path.join(glimmervoidHomeDir$1(), GUARDRAILS_TEMP_FOLDER, sessionId);
|
|
6845
|
+
}
|
|
6846
|
+
async function removeSessionTempFolder(sessionId, log = console) {
|
|
6847
|
+
const sessionTempFolder = sessionTempFolderFor(sessionId);
|
|
6848
|
+
if (!sessionTempFolder) return;
|
|
6849
|
+
await rm(sessionTempFolder, {
|
|
6850
|
+
recursive: true,
|
|
6851
|
+
force: true
|
|
6852
|
+
}).catch((error) => log.warn(`[guardrails] could not delete session temp folder ${sessionTempFolder}: ${errorMessage(error)}`));
|
|
6853
|
+
}
|
|
6854
|
+
function launchTempDirFor(glimmervoidHome, launchId, tempDir, keptTempFolderName) {
|
|
6855
|
+
if (tempDir !== void 0) return path.resolve(tempDir);
|
|
6856
|
+
return path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER, keptTempFolderName ?? launchId);
|
|
6857
|
+
}
|
|
6858
|
+
async function prepareGuardrailsLaunch({ role, launchId, tempDir, keptTempFolderName, enterDir }) {
|
|
6859
|
+
if (!LAUNCH_ID_SHAPE.test(launchId)) return {
|
|
6860
|
+
ok: false,
|
|
6861
|
+
reason: "guardrails launch id is invalid"
|
|
6862
|
+
};
|
|
6863
|
+
if (keptTempFolderName !== void 0 && !LAUNCH_ID_SHAPE.test(keptTempFolderName)) return {
|
|
6864
|
+
ok: false,
|
|
6865
|
+
reason: "guardrails temp folder name is invalid"
|
|
6866
|
+
};
|
|
6867
|
+
try {
|
|
6868
|
+
const policyDir = path.join(glimmervoidHomeDir$1(), POLICY_FOLDER);
|
|
6869
|
+
await mkdir(policyDir, {
|
|
6870
|
+
recursive: true,
|
|
6871
|
+
mode: 448
|
|
6872
|
+
});
|
|
6873
|
+
const [homeDir, glimmervoidHome, realRole, ownerPidNamespace] = await Promise.all([
|
|
6874
|
+
realpath(os.homedir()),
|
|
6875
|
+
realpath(glimmervoidHomeDir$1()),
|
|
6876
|
+
withRealRoots(role),
|
|
6877
|
+
readOwnPidNamespace()
|
|
6878
|
+
]);
|
|
6879
|
+
const ownsTempDir = tempDir === void 0 && keptTempFolderName === void 0;
|
|
6880
|
+
const launchTempDir = launchTempDirFor(glimmervoidHome, launchId, tempDir, keptTempFolderName);
|
|
6881
|
+
const launchHomeDir = path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId);
|
|
6882
|
+
const vendorLocations = await resolveVendorLocations(process.env, homeDir);
|
|
6883
|
+
if (vendorLocations === null) return {
|
|
6884
|
+
ok: false,
|
|
6885
|
+
reason: "CODEX_HOME names no folder"
|
|
6886
|
+
};
|
|
6887
|
+
const xdgConfigHomeOverride = rawTextOr(process.env.XDG_CONFIG_HOME, null);
|
|
6888
|
+
const stableHostPaths = {
|
|
6889
|
+
homeDir,
|
|
6890
|
+
glimmervoidHome,
|
|
6891
|
+
glimmervoidConfigFile: await configuredConfigFile(glimmervoidHome),
|
|
6892
|
+
tempDir: launchTempDir,
|
|
6893
|
+
launchHomeDir,
|
|
6894
|
+
vendorLocations,
|
|
6895
|
+
unwrappedVendorHomes: await realPathsWhenPresent(grokHomes(process.env, homeDir)),
|
|
6896
|
+
relocatedXdgConfigHome: xdgConfigHomeOverride === null ? null : await realPathWhenPresent(xdgConfigHomeOverride)
|
|
6897
|
+
};
|
|
6898
|
+
const buildPlanFromCurrentDisk = async () => {
|
|
6899
|
+
const [homeEntryNames, glimmervoidHomeEntryNames] = await Promise.all([readdir(homeDir), readdir(glimmervoidHome)]);
|
|
6900
|
+
const hostPaths = {
|
|
6901
|
+
...stableHostPaths,
|
|
6902
|
+
homeEntryNames,
|
|
6903
|
+
glimmervoidHomeEntryNames
|
|
6904
|
+
};
|
|
6905
|
+
const [existingPaths, writeBlockedPaths] = await Promise.all([presentPaths$1(guardrailsPolicyProbePaths(realRole, hostPaths)), blockedPathsWithRealTargets(standardWriteBlockCandidates(realRole, hostPaths), realRole.blockedPaths ?? [])]);
|
|
6906
|
+
return buildGuardrailsLaunchPlan(realRole, {
|
|
6907
|
+
...hostPaths,
|
|
6908
|
+
existingPaths,
|
|
6909
|
+
writeBlockedPaths
|
|
6910
|
+
});
|
|
6911
|
+
};
|
|
6912
|
+
const built = await buildPlanFromCurrentDisk();
|
|
6913
|
+
if (!built.ok) return built;
|
|
6914
|
+
const { plan } = built;
|
|
6915
|
+
const policyPath = path.join(policyDir, `${launchId}.json`);
|
|
6916
|
+
const launcher = launcherCommand(plan, policyPath);
|
|
6917
|
+
if (!launcher) return {
|
|
6918
|
+
ok: false,
|
|
6919
|
+
reason: "the sandbox runtime CLI could not be resolved"
|
|
6920
|
+
};
|
|
6921
|
+
await Promise.all(plan.requiredDirectories.map((directory) => mkdir(directory, { recursive: true })));
|
|
6922
|
+
const startDir = enterDir === void 0 ? void 0 : path.join(launchTempDir, START_FOLDER);
|
|
6923
|
+
if (startDir !== void 0) await mkdir(startDir, { recursive: true });
|
|
6924
|
+
await Promise.all(plan.launchHomeLinks.map((link) => symlink(link.targetPath, link.linkPath)));
|
|
6925
|
+
await writeJsonAtomic(policyPath, plan.policy);
|
|
6926
|
+
const folders = {
|
|
6927
|
+
policyPath,
|
|
6928
|
+
ownerPath: path.join(policyDir, `${launchId}${OWNER_FILE_SUFFIX}`),
|
|
6929
|
+
launchHomeDir,
|
|
6930
|
+
ownedTempDir: ownsTempDir ? launchTempDir : null
|
|
6931
|
+
};
|
|
6932
|
+
const owner = {
|
|
6933
|
+
ownerPid: process.pid,
|
|
6934
|
+
ownerHost: os.hostname(),
|
|
6935
|
+
ownerPidNamespace,
|
|
6936
|
+
ownsTempDir,
|
|
6937
|
+
...keptTempFolderName === void 0 ? {} : { keptTempFolderName },
|
|
6938
|
+
logins: plan.launchHomeLinks.filter((link) => link.isLogin).map(({ linkPath, targetPath }) => ({
|
|
6939
|
+
linkPath,
|
|
6940
|
+
targetPath
|
|
6941
|
+
})),
|
|
6942
|
+
placeholderPaths: []
|
|
6943
|
+
};
|
|
6944
|
+
await writeJsonAtomic(folders.ownerPath, owner);
|
|
6945
|
+
const envPrefix = guardedEnvPrefix(plan);
|
|
6946
|
+
return {
|
|
6947
|
+
ok: true,
|
|
6948
|
+
launch: {
|
|
6949
|
+
wrap: async (file, args, { hasTerminal }) => {
|
|
6950
|
+
const rebuilt = await buildPlanFromCurrentDisk();
|
|
6951
|
+
if (!rebuilt.ok) throw new Error(rebuilt.reason);
|
|
6952
|
+
await writeJsonAtomic(policyPath, {
|
|
6953
|
+
...rebuilt.plan.policy,
|
|
6954
|
+
allowPty: hasTerminal && rebuilt.plan.launcher === "open-network"
|
|
6955
|
+
});
|
|
6956
|
+
owner.placeholderPaths = await absentPaths(guardrailsPlaceholderPaths(realRole));
|
|
6957
|
+
await writeJsonAtomic(folders.ownerPath, owner);
|
|
6958
|
+
await createMissingMountPoints(lockedClaudeMountPoints(realRole));
|
|
6959
|
+
return {
|
|
6960
|
+
file: launcher.file,
|
|
6961
|
+
args: [
|
|
6962
|
+
...launcher.args,
|
|
6963
|
+
...envPrefix,
|
|
6964
|
+
...enteringFolder(enterDir, file, args)
|
|
6965
|
+
]
|
|
6966
|
+
};
|
|
6967
|
+
},
|
|
6968
|
+
env: {
|
|
6969
|
+
...plan.launchEnv,
|
|
6970
|
+
...bridgeSocketLauncherEnv(os.tmpdir(), process.platform)
|
|
6971
|
+
},
|
|
6972
|
+
...startDir === void 0 ? {} : { cwd: startDir },
|
|
6973
|
+
dispose: () => removeLaunch(folders, owner, launchId, { onlyNewerLogins: false })
|
|
6974
|
+
}
|
|
6975
|
+
};
|
|
6976
|
+
} catch (error) {
|
|
6977
|
+
return {
|
|
6978
|
+
ok: false,
|
|
6979
|
+
reason: errorMessage(error)
|
|
6980
|
+
};
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
//#endregion
|
|
5822
6984
|
//#region server/session/session-registry.ts
|
|
5823
6985
|
function runAutoResume(sessions, config, spawnGate, logger = console) {
|
|
5824
6986
|
const ids = pickAutoResume(config.projects, config, Date.now());
|
|
@@ -5968,7 +7130,7 @@ function createSessionRegistry(dependencies) {
|
|
|
5968
7130
|
if (ingestLane) ingestLane.detachSessionTap(session);
|
|
5969
7131
|
if (ingestLane) ingestLane.releaseSessionRoots(session);
|
|
5970
7132
|
session.destroy();
|
|
5971
|
-
Promise.resolve(session._killReap).catch(() => {}).then(() => session.discardWorktree?.()).catch(() => {});
|
|
7133
|
+
Promise.resolve(session._killReap).catch(() => {}).then(() => session.discardWorktree?.()).catch(() => {}).then(() => removeSessionTempFolder(id, dependencies.logger));
|
|
5972
7134
|
sessions.delete(id);
|
|
5973
7135
|
capturedAgentFingerprints.delete(id);
|
|
5974
7136
|
dependencies.broadcastControl({
|
|
@@ -9233,8 +10395,10 @@ var QUICK_RETRY_DELAYS_MS = Object.freeze([
|
|
|
9233
10395
|
3e4,
|
|
9234
10396
|
9e4
|
|
9235
10397
|
]);
|
|
10398
|
+
var TRANSIENT_BACKOFF_MAX_MS = 3e5;
|
|
9236
10399
|
function nextRetrySchedule({ failureStreak, quickRetryCount, quickRetries, retryAfterMs, baseMs, maxMs, random }) {
|
|
9237
|
-
const
|
|
10400
|
+
const hasRateLimitWait = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs > 0;
|
|
10401
|
+
const quickRetryDelayMs = quickRetries && !hasRateLimitWait ? QUICK_RETRY_DELAYS_MS[quickRetryCount] : void 0;
|
|
9238
10402
|
if (quickRetryDelayMs !== void 0) return {
|
|
9239
10403
|
waitMs: quickRetryDelayMs,
|
|
9240
10404
|
retry: {
|
|
@@ -9242,11 +10406,12 @@ function nextRetrySchedule({ failureStreak, quickRetryCount, quickRetries, retry
|
|
|
9242
10406
|
limit: QUICK_RETRY_DELAYS_MS.length
|
|
9243
10407
|
}
|
|
9244
10408
|
};
|
|
10409
|
+
const backoffCeilingMs = quickRetries && !hasRateLimitWait ? Math.min(maxMs, TRANSIENT_BACKOFF_MAX_MS) : maxMs;
|
|
9245
10410
|
return {
|
|
9246
10411
|
waitMs: nextBackoffMs({
|
|
9247
10412
|
attempt: Math.max(1, failureStreak - quickRetryCount),
|
|
9248
10413
|
baseMs,
|
|
9249
|
-
maxMs,
|
|
10414
|
+
maxMs: backoffCeilingMs,
|
|
9250
10415
|
retryAfterMs,
|
|
9251
10416
|
random
|
|
9252
10417
|
}),
|
|
@@ -9302,434 +10467,6 @@ function buildLanePermissions({ denyTools = [], allowTools = [] } = {}) {
|
|
|
9302
10467
|
};
|
|
9303
10468
|
}
|
|
9304
10469
|
//#endregion
|
|
9305
|
-
//#region server/guardrails/core/guardrails-policy-core.ts
|
|
9306
|
-
var VENDOR_PROFILES = Object.freeze({
|
|
9307
|
-
claude: {
|
|
9308
|
-
homeEntries: [".claude", ".claude.json"],
|
|
9309
|
-
launchWritableFolders: ({ cwd, agentSessionId }) => [path.join("projects", encodeProjectDir(cwd)), ...agentSessionId === null ? [] : [path.join("session-env", agentSessionId)]],
|
|
9310
|
-
launchPrivateFolders: ["shell-snapshots"],
|
|
9311
|
-
launchHomeEnvName: null,
|
|
9312
|
-
loginFiles: [".credentials.json"],
|
|
9313
|
-
configEntries: [
|
|
9314
|
-
"settings.json",
|
|
9315
|
-
"settings.local.json",
|
|
9316
|
-
"hooks",
|
|
9317
|
-
"skills",
|
|
9318
|
-
"plugins",
|
|
9319
|
-
"commands",
|
|
9320
|
-
"agents",
|
|
9321
|
-
"CLAUDE.md",
|
|
9322
|
-
"AGENTS.md"
|
|
9323
|
-
],
|
|
9324
|
-
apiDomains: ["api.anthropic.com", "platform.claude.com"],
|
|
9325
|
-
launchEnv: { DISABLE_AUTOUPDATER: "1" }
|
|
9326
|
-
},
|
|
9327
|
-
codex: {
|
|
9328
|
-
homeEntries: [".codex"],
|
|
9329
|
-
launchWritableFolders: () => [],
|
|
9330
|
-
launchPrivateFolders: [],
|
|
9331
|
-
launchHomeEnvName: "CODEX_HOME",
|
|
9332
|
-
loginFiles: ["auth.json"],
|
|
9333
|
-
configEntries: [
|
|
9334
|
-
"config.toml",
|
|
9335
|
-
"AGENTS.md",
|
|
9336
|
-
"skills",
|
|
9337
|
-
"rules",
|
|
9338
|
-
"prompts",
|
|
9339
|
-
"hooks.json"
|
|
9340
|
-
],
|
|
9341
|
-
apiDomains: [
|
|
9342
|
-
"chatgpt.com",
|
|
9343
|
-
"*.chatgpt.com",
|
|
9344
|
-
"auth.openai.com",
|
|
9345
|
-
"api.openai.com",
|
|
9346
|
-
"*.openai.com"
|
|
9347
|
-
],
|
|
9348
|
-
launchEnv: {}
|
|
9349
|
-
}
|
|
9350
|
-
});
|
|
9351
|
-
var GUARDRAILS_VENDORS = Object.freeze(["claude", "codex"]);
|
|
9352
|
-
var UNWRAPPED_VENDOR_HOME_ENTRIES = Object.freeze([".grok"]);
|
|
9353
|
-
var CREDENTIAL_HOME_ENTRIES = Object.freeze([
|
|
9354
|
-
".ssh",
|
|
9355
|
-
".config/gh",
|
|
9356
|
-
".config/coderv2",
|
|
9357
|
-
"Library/Application Support/coderv2",
|
|
9358
|
-
"Library/Keychains",
|
|
9359
|
-
".git-credentials"
|
|
9360
|
-
]);
|
|
9361
|
-
var CLOUD_CREDENTIAL_HOME_ENTRIES = Object.freeze([
|
|
9362
|
-
".aws",
|
|
9363
|
-
".azure",
|
|
9364
|
-
".config/gcloud",
|
|
9365
|
-
".kube",
|
|
9366
|
-
".docker",
|
|
9367
|
-
".gnupg",
|
|
9368
|
-
".netrc",
|
|
9369
|
-
".npmrc"
|
|
9370
|
-
]);
|
|
9371
|
-
var STANDARD_LEVEL_USABLE_CREDENTIAL_ENTRIES = Object.freeze(["Library/Keychains"]);
|
|
9372
|
-
var XDG_CONFIG_DEFAULT_HOME_PREFIX = ".config/";
|
|
9373
|
-
var SHARED_TEMP_DIRS = Object.freeze(["/tmp/claude", "/private/tmp/claude"]);
|
|
9374
|
-
var SHARED_HOME_WRITE_ENTRIES = Object.freeze([".npm/_logs"]);
|
|
9375
|
-
var GUARDRAILS_PLACEHOLDER_NAMES = Object.freeze([
|
|
9376
|
-
".bash_profile",
|
|
9377
|
-
".bashrc",
|
|
9378
|
-
".claude",
|
|
9379
|
-
".gitconfig",
|
|
9380
|
-
".gitmodules",
|
|
9381
|
-
".idea",
|
|
9382
|
-
".mcp.json",
|
|
9383
|
-
".profile",
|
|
9384
|
-
".ripgreprc",
|
|
9385
|
-
".vscode",
|
|
9386
|
-
".zprofile",
|
|
9387
|
-
".zshrc"
|
|
9388
|
-
]);
|
|
9389
|
-
var WHOLE_FILESYSTEM = "/";
|
|
9390
|
-
var GUARDRAILS_TEMP_FOLDER = "guardrails-temp";
|
|
9391
|
-
var AGENT_LOGINS_SIBLING_ENTRY = "agent-logins";
|
|
9392
|
-
var GLIMMERVOID_CONFIG_SIBLING_ENTRIES = Object.freeze([
|
|
9393
|
-
AGENT_LOGINS_SIBLING_ENTRY,
|
|
9394
|
-
"bin",
|
|
9395
|
-
"pairings.json",
|
|
9396
|
-
"pairings-seen.json",
|
|
9397
|
-
"plans",
|
|
9398
|
-
"telegram-outbox.json",
|
|
9399
|
-
"traces",
|
|
9400
|
-
"uploads",
|
|
9401
|
-
"usage-budget-state.json",
|
|
9402
|
-
"usage-lanes.json",
|
|
9403
|
-
"usage-warehouse.json",
|
|
9404
|
-
"visions-intent.json"
|
|
9405
|
-
]);
|
|
9406
|
-
var GLIMMERVOID_HOME_STATE_ENTRIES = Object.freeze([
|
|
9407
|
-
"benchmark-runs",
|
|
9408
|
-
"benchmarks",
|
|
9409
|
-
"config.json",
|
|
9410
|
-
"issues-cache.json",
|
|
9411
|
-
"litellm-pricing.json",
|
|
9412
|
-
"my-prs-state.json",
|
|
9413
|
-
"my-prs-work",
|
|
9414
|
-
"posthog-reports",
|
|
9415
|
-
"posthog-state.json",
|
|
9416
|
-
"posthog-work",
|
|
9417
|
-
"recordings",
|
|
9418
|
-
"guardrails-homes",
|
|
9419
|
-
"guardrails-policies",
|
|
9420
|
-
"sane-yolo",
|
|
9421
|
-
"scratch",
|
|
9422
|
-
"sounds",
|
|
9423
|
-
"team-review-repos",
|
|
9424
|
-
"team-review-state.json",
|
|
9425
|
-
"team-review-work",
|
|
9426
|
-
"team-review-worktrees",
|
|
9427
|
-
"telemetry-pending-crash.json",
|
|
9428
|
-
"telemetry.json",
|
|
9429
|
-
"tmp",
|
|
9430
|
-
"update-check.json",
|
|
9431
|
-
"update-journal.json",
|
|
9432
|
-
"workflows-state.json",
|
|
9433
|
-
"workflows-work"
|
|
9434
|
-
]);
|
|
9435
|
-
var RUNTIME_MANDATORY_CLAUDE_DENY_FOLDERS = Object.freeze(["commands", "agents"]);
|
|
9436
|
-
var GUARDED_PROXY_BYPASS = "localhost,::1,169.254.0.0/16,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16";
|
|
9437
|
-
var HOOK_LOOPBACK_HOST = "127.0.0.1";
|
|
9438
|
-
var AGENT_SESSION_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
9439
|
-
var STANDARD_OPEN_NETWORK = Object.freeze({
|
|
9440
|
-
allowAllUnixSockets: true,
|
|
9441
|
-
allowMachLookup: ["*"]
|
|
9442
|
-
});
|
|
9443
|
-
var STRICT_OPEN_NETWORK = Object.freeze({ allowAllUnixSockets: false });
|
|
9444
|
-
function unique(entries) {
|
|
9445
|
-
return [...new Set(entries)];
|
|
9446
|
-
}
|
|
9447
|
-
function literalEntries(entryPaths) {
|
|
9448
|
-
return unique(entryPaths).map((entryPath) => ({
|
|
9449
|
-
path: entryPath,
|
|
9450
|
-
literal: true
|
|
9451
|
-
}));
|
|
9452
|
-
}
|
|
9453
|
-
function homePaths(host, homeEntries) {
|
|
9454
|
-
return homeEntries.map((entry) => path.join(host.homeDir, entry));
|
|
9455
|
-
}
|
|
9456
|
-
function isInsideAny(parents, child) {
|
|
9457
|
-
return parents.some((parent) => isPathInside(parent, child));
|
|
9458
|
-
}
|
|
9459
|
-
function vendorStatePaths(host, vendor, stateFolderEntries) {
|
|
9460
|
-
return stateFolderEntries.map((entry) => path.join(host.vendorLocations[vendor].stateFolder, entry));
|
|
9461
|
-
}
|
|
9462
|
-
function vendorHeldPaths(host, vendor) {
|
|
9463
|
-
const { stateFolder, configFiles, otherHomes } = host.vendorLocations[vendor];
|
|
9464
|
-
return [
|
|
9465
|
-
stateFolder,
|
|
9466
|
-
...configFiles,
|
|
9467
|
-
...otherHomes
|
|
9468
|
-
];
|
|
9469
|
-
}
|
|
9470
|
-
function launchHomeOf(host, vendor) {
|
|
9471
|
-
return path.join(host.launchHomeDir, vendor);
|
|
9472
|
-
}
|
|
9473
|
-
function vendorsWithLaunchHome(role) {
|
|
9474
|
-
if (role.vendorHome.scope === "state") return [];
|
|
9475
|
-
return role.vendors.filter((vendor) => VENDOR_PROFILES[vendor].launchHomeEnvName !== null);
|
|
9476
|
-
}
|
|
9477
|
-
function vendorWritableFolders(role, host) {
|
|
9478
|
-
const { vendorHome } = role;
|
|
9479
|
-
if (vendorHome.scope === "state") return role.vendors.map((vendor) => host.vendorLocations[vendor].stateFolder);
|
|
9480
|
-
return role.vendors.flatMap((vendor) => {
|
|
9481
|
-
const profile = VENDOR_PROFILES[vendor];
|
|
9482
|
-
if (profile.launchHomeEnvName !== null) return [launchHomeOf(host, vendor)];
|
|
9483
|
-
return vendorStatePaths(host, vendor, profile.launchWritableFolders(vendorHome));
|
|
9484
|
-
});
|
|
9485
|
-
}
|
|
9486
|
-
function launchPrivateFolders(role, host) {
|
|
9487
|
-
if (role.vendorHome.scope === "state") return [];
|
|
9488
|
-
return role.vendors.flatMap((vendor) => vendorStatePaths(host, vendor, VENDOR_PROFILES[vendor].launchPrivateFolders));
|
|
9489
|
-
}
|
|
9490
|
-
function launchHomeLinkCandidates(role, host) {
|
|
9491
|
-
return vendorsWithLaunchHome(role).flatMap((vendor) => {
|
|
9492
|
-
const { configEntries, loginFiles } = VENDOR_PROFILES[vendor];
|
|
9493
|
-
const linkFor = (entry, isLogin) => ({
|
|
9494
|
-
linkPath: path.join(launchHomeOf(host, vendor), entry),
|
|
9495
|
-
targetPath: path.join(host.vendorLocations[vendor].stateFolder, entry),
|
|
9496
|
-
isLogin
|
|
9497
|
-
});
|
|
9498
|
-
return [...configEntries.map((entry) => linkFor(entry, false)), ...loginFiles.map((entry) => linkFor(entry, true))];
|
|
9499
|
-
});
|
|
9500
|
-
}
|
|
9501
|
-
function launchHomeEnv(role, host) {
|
|
9502
|
-
return Object.fromEntries(vendorsWithLaunchHome(role).flatMap((vendor) => {
|
|
9503
|
-
const envName = VENDOR_PROFILES[vendor].launchHomeEnvName;
|
|
9504
|
-
return envName === null ? [] : [[envName, launchHomeOf(host, vendor)]];
|
|
9505
|
-
}));
|
|
9506
|
-
}
|
|
9507
|
-
function vendorLoginFiles(role, host) {
|
|
9508
|
-
return role.vendors.flatMap((vendor) => vendorStatePaths(host, vendor, VENDOR_PROFILES[vendor].loginFiles));
|
|
9509
|
-
}
|
|
9510
|
-
function roleWritableRoots(role) {
|
|
9511
|
-
return [
|
|
9512
|
-
...role.writableRoots,
|
|
9513
|
-
...role.lockedCheckouts,
|
|
9514
|
-
...role.commitCheckouts.flatMap(({ checkout, gitDir }) => [checkout, gitDir])
|
|
9515
|
-
];
|
|
9516
|
-
}
|
|
9517
|
-
function alwaysWritablePaths(role, host) {
|
|
9518
|
-
return [
|
|
9519
|
-
host.tempDir,
|
|
9520
|
-
...roleWritableRoots(role),
|
|
9521
|
-
...vendorWritableFolders(role, host)
|
|
9522
|
-
];
|
|
9523
|
-
}
|
|
9524
|
-
function commitCheckoutWriteDenies({ checkout, gitDir }) {
|
|
9525
|
-
const gitPointer = path.join(checkout, ".git");
|
|
9526
|
-
const gitDirDenies = [path.join(gitDir, "hooks"), path.join(gitDir, "config")];
|
|
9527
|
-
if (path.resolve(gitPointer) === path.resolve(gitDir)) return gitDirDenies;
|
|
9528
|
-
return [gitPointer, ...gitDirDenies];
|
|
9529
|
-
}
|
|
9530
|
-
function writeDeniesWhenPresent(role, host) {
|
|
9531
|
-
const writablePaths = alwaysWritablePaths(role, host);
|
|
9532
|
-
const sharedHomeWrites = homePaths(host, SHARED_HOME_WRITE_ENTRIES).filter((sharedPath) => !isInsideAny(roleWritableRoots(role), sharedPath));
|
|
9533
|
-
const vendorConfig = role.vendors.flatMap((vendor) => [...vendorStatePaths(host, vendor, VENDOR_PROFILES[vendor].configEntries), ...host.vendorLocations[vendor].configFiles]).filter((configPath) => isInsideAny(writablePaths, configPath));
|
|
9534
|
-
return [
|
|
9535
|
-
...SHARED_TEMP_DIRS,
|
|
9536
|
-
...sharedHomeWrites,
|
|
9537
|
-
...vendorConfig,
|
|
9538
|
-
...role.commitCheckouts.flatMap(commitCheckoutWriteDenies)
|
|
9539
|
-
];
|
|
9540
|
-
}
|
|
9541
|
-
function lockedCheckoutWriteDenies(role) {
|
|
9542
|
-
return role.lockedCheckouts.flatMap((checkout) => [path.join(checkout, ".git"), path.join(checkout, ".claude")]);
|
|
9543
|
-
}
|
|
9544
|
-
function lockedClaudeMountPoints(role) {
|
|
9545
|
-
return role.lockedCheckouts.flatMap((checkout) => RUNTIME_MANDATORY_CLAUDE_DENY_FOLDERS.map((folder) => path.join(checkout, ".claude", folder)));
|
|
9546
|
-
}
|
|
9547
|
-
function guardrailsPlaceholderPaths(role) {
|
|
9548
|
-
return unique([...(role.vendorHome.scope === "launch" ? [role.vendorHome.cwd] : []).flatMap((workingFolder) => GUARDRAILS_PLACEHOLDER_NAMES.map((placeholderName) => path.join(workingFolder, placeholderName))), ...lockedCheckoutWriteDenies(role)]);
|
|
9549
|
-
}
|
|
9550
|
-
function homeEntriesNamedLike(host, homeEntries) {
|
|
9551
|
-
return host.homeEntryNames.filter((entryName) => homeEntries.some((homeEntry) => entryName.startsWith(homeEntry)));
|
|
9552
|
-
}
|
|
9553
|
-
function relocatedXdgConfigPaths(host, homeEntries) {
|
|
9554
|
-
const xdgConfigHome = host.relocatedXdgConfigHome;
|
|
9555
|
-
if (xdgConfigHome === null) return [];
|
|
9556
|
-
return homeEntries.filter((entry) => entry.startsWith(XDG_CONFIG_DEFAULT_HOME_PREFIX)).map((entry) => path.join(xdgConfigHome, entry.slice(8)));
|
|
9557
|
-
}
|
|
9558
|
-
function isStandardLevel(role) {
|
|
9559
|
-
return role.guardrailLevel === "standard";
|
|
9560
|
-
}
|
|
9561
|
-
function blockedCredentialHomeEntries(role) {
|
|
9562
|
-
const credentialHomeEntries = [...CREDENTIAL_HOME_ENTRIES, ...CLOUD_CREDENTIAL_HOME_ENTRIES];
|
|
9563
|
-
if (!isStandardLevel(role)) return credentialHomeEntries;
|
|
9564
|
-
return credentialHomeEntries.filter((entry) => !STANDARD_LEVEL_USABLE_CREDENTIAL_ENTRIES.includes(entry));
|
|
9565
|
-
}
|
|
9566
|
-
function credentialWriteBlocks(role, host) {
|
|
9567
|
-
const credentialHomeEntries = blockedCredentialHomeEntries(role);
|
|
9568
|
-
return [...homePaths(host, credentialHomeEntries), ...relocatedXdgConfigPaths(host, credentialHomeEntries)];
|
|
9569
|
-
}
|
|
9570
|
-
function glimmervoidStateWriteBlocks(role, host) {
|
|
9571
|
-
const keptWritable = alwaysWritablePaths(role, host);
|
|
9572
|
-
const configFolder = path.dirname(host.glimmervoidConfigFile);
|
|
9573
|
-
const homeEntries = [
|
|
9574
|
-
...GLIMMERVOID_HOME_STATE_ENTRIES,
|
|
9575
|
-
...GLIMMERVOID_CONFIG_SIBLING_ENTRIES,
|
|
9576
|
-
...host.glimmervoidHomeEntryNames
|
|
9577
|
-
].map((entryName) => path.join(host.glimmervoidHome, entryName));
|
|
9578
|
-
const configSiblings = GLIMMERVOID_CONFIG_SIBLING_ENTRIES.map((entryName) => path.join(configFolder, entryName));
|
|
9579
|
-
return unique([
|
|
9580
|
-
host.glimmervoidConfigFile,
|
|
9581
|
-
...homeEntries,
|
|
9582
|
-
...configSiblings
|
|
9583
|
-
]).filter((entryPath) => !keptWritable.some((writable) => isPathInside(entryPath, writable)));
|
|
9584
|
-
}
|
|
9585
|
-
function standardWriteBlockCandidates(role, host) {
|
|
9586
|
-
if (!isStandardLevel(role)) return [];
|
|
9587
|
-
return unique([
|
|
9588
|
-
...credentialWriteBlocks(role, host),
|
|
9589
|
-
...glimmervoidStateWriteBlocks(role, host),
|
|
9590
|
-
...role.blockedPaths ?? []
|
|
9591
|
-
]);
|
|
9592
|
-
}
|
|
9593
|
-
function readDeniedPaths(role, host) {
|
|
9594
|
-
const otherVendors = GUARDRAILS_VENDORS.filter((vendor) => !role.vendors.includes(vendor));
|
|
9595
|
-
const otherVendorHomeEntries = [...otherVendors.flatMap((vendor) => VENDOR_PROFILES[vendor].homeEntries), ...UNWRAPPED_VENDOR_HOME_ENTRIES];
|
|
9596
|
-
const credentialHomeEntries = blockedCredentialHomeEntries(role);
|
|
9597
|
-
return [
|
|
9598
|
-
...homePaths(host, [
|
|
9599
|
-
...credentialHomeEntries,
|
|
9600
|
-
...otherVendorHomeEntries,
|
|
9601
|
-
...homeEntriesNamedLike(host, otherVendorHomeEntries)
|
|
9602
|
-
]),
|
|
9603
|
-
...relocatedXdgConfigPaths(host, credentialHomeEntries),
|
|
9604
|
-
...otherVendors.flatMap((vendor) => vendorHeldPaths(host, vendor)),
|
|
9605
|
-
...host.unwrappedVendorHomes,
|
|
9606
|
-
host.glimmervoidHome,
|
|
9607
|
-
path.join(path.dirname(host.glimmervoidConfigFile), AGENT_LOGINS_SIBLING_ENTRY),
|
|
9608
|
-
...launchPrivateFolders(role, host),
|
|
9609
|
-
...role.readDenied
|
|
9610
|
-
];
|
|
9611
|
-
}
|
|
9612
|
-
function guardrailsPolicyProbePaths(role, host) {
|
|
9613
|
-
return unique([
|
|
9614
|
-
...writeDeniesWhenPresent(role, host),
|
|
9615
|
-
...vendorLoginFiles(role, host),
|
|
9616
|
-
...launchHomeLinkCandidates(role, host).map((link) => link.targetPath)
|
|
9617
|
-
]);
|
|
9618
|
-
}
|
|
9619
|
-
function isValidPort(port) {
|
|
9620
|
-
return Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
9621
|
-
}
|
|
9622
|
-
function hostNamedPaths(host) {
|
|
9623
|
-
const relocatedXdgConfigHomes = host.relocatedXdgConfigHome === null ? [] : [host.relocatedXdgConfigHome];
|
|
9624
|
-
return [
|
|
9625
|
-
host.homeDir,
|
|
9626
|
-
host.glimmervoidHome,
|
|
9627
|
-
host.glimmervoidConfigFile,
|
|
9628
|
-
host.tempDir,
|
|
9629
|
-
host.launchHomeDir,
|
|
9630
|
-
...relocatedXdgConfigHomes,
|
|
9631
|
-
...host.unwrappedVendorHomes,
|
|
9632
|
-
...GUARDRAILS_VENDORS.flatMap((vendor) => vendorHeldPaths(host, vendor))
|
|
9633
|
-
];
|
|
9634
|
-
}
|
|
9635
|
-
function guardrailsRoleRefusal(role, host) {
|
|
9636
|
-
const launchCwds = role.vendorHome.scope === "launch" ? [role.vendorHome.cwd] : [];
|
|
9637
|
-
const relativePath = [
|
|
9638
|
-
...hostNamedPaths(host),
|
|
9639
|
-
...roleWritableRoots(role),
|
|
9640
|
-
...role.readDenied,
|
|
9641
|
-
...role.readableWithinDenied,
|
|
9642
|
-
...launchCwds
|
|
9643
|
-
].find((namedPath) => !path.isAbsolute(namedPath));
|
|
9644
|
-
if (relativePath !== void 0) return `guardrails policy path is not absolute: ${relativePath}`;
|
|
9645
|
-
const rootOverProtectedHome = [...roleWritableRoots(role), ...vendorWritableFolders(role, host)].find((root) => isPathInside(root, host.homeDir) || isPathInside(root, host.glimmervoidHome));
|
|
9646
|
-
if (rootOverProtectedHome !== void 0) return `writable root ${rootOverProtectedHome} contains the home folder or the Glimmervoid home`;
|
|
9647
|
-
const writablePaths = alwaysWritablePaths(role, host);
|
|
9648
|
-
const reallowedAncestor = role.readableWithinDenied.find((readable) => writablePaths.some((writable) => isPathInside(readable, writable, { allowEqual: false })));
|
|
9649
|
-
if (reallowedAncestor !== void 0) return `readable path ${reallowedAncestor} is an ancestor of a writable root`;
|
|
9650
|
-
if (role.network.access === "allowlist" && role.network.hookPort !== null && !isValidPort(role.network.hookPort)) return "hook listener port is invalid";
|
|
9651
|
-
const agentSessionId = role.vendorHome.scope === "launch" ? role.vendorHome.agentSessionId : null;
|
|
9652
|
-
if (agentSessionId !== null && !AGENT_SESSION_ID_SHAPE.test(agentSessionId)) return "agent session id is invalid";
|
|
9653
|
-
return null;
|
|
9654
|
-
}
|
|
9655
|
-
function networkPolicy(role) {
|
|
9656
|
-
if (role.network.access === "open") return isStandardLevel(role) ? { ...STANDARD_OPEN_NETWORK } : { ...STRICT_OPEN_NETWORK };
|
|
9657
|
-
if (role.network.access === "none") return {
|
|
9658
|
-
allowedDomains: [],
|
|
9659
|
-
deniedDomains: [],
|
|
9660
|
-
allowLocalBinding: false,
|
|
9661
|
-
allowAllUnixSockets: false
|
|
9662
|
-
};
|
|
9663
|
-
const hookEndpoint = role.network.hookPort === null ? [] : [`${HOOK_LOOPBACK_HOST}:${role.network.hookPort}`];
|
|
9664
|
-
const vendorDomains = role.vendors.flatMap((vendor) => VENDOR_PROFILES[vendor].apiDomains);
|
|
9665
|
-
return {
|
|
9666
|
-
allowedDomains: unique([
|
|
9667
|
-
...hookEndpoint,
|
|
9668
|
-
...vendorDomains,
|
|
9669
|
-
...role.network.domains
|
|
9670
|
-
]),
|
|
9671
|
-
deniedDomains: [],
|
|
9672
|
-
strictAllowlist: true,
|
|
9673
|
-
allowLocalBinding: false,
|
|
9674
|
-
allowAllUnixSockets: false
|
|
9675
|
-
};
|
|
9676
|
-
}
|
|
9677
|
-
function guardedEnvFor(role, host) {
|
|
9678
|
-
if (role.network.access === "allowlist") return {
|
|
9679
|
-
NO_PROXY: GUARDED_PROXY_BYPASS,
|
|
9680
|
-
no_proxy: GUARDED_PROXY_BYPASS,
|
|
9681
|
-
[HOOK_VIA_GUARDRAILS_PROXY_ENV]: "1"
|
|
9682
|
-
};
|
|
9683
|
-
if (role.network.access === "open") return { TMPDIR: host.tempDir };
|
|
9684
|
-
return {};
|
|
9685
|
-
}
|
|
9686
|
-
function filesystemWrites(role, host, writablePaths) {
|
|
9687
|
-
if (isStandardLevel(role)) return {
|
|
9688
|
-
allowWrite: literalEntries([WHOLE_FILESYSTEM, ...writablePaths]),
|
|
9689
|
-
denyWrite: literalEntries(host.writeBlockedPaths)
|
|
9690
|
-
};
|
|
9691
|
-
const isPresent = (candidate) => host.existingPaths.has(candidate);
|
|
9692
|
-
return {
|
|
9693
|
-
allowWrite: literalEntries(writablePaths),
|
|
9694
|
-
denyWrite: literalEntries([...writeDeniesWhenPresent(role, host).filter(isPresent), ...lockedCheckoutWriteDenies(role)])
|
|
9695
|
-
};
|
|
9696
|
-
}
|
|
9697
|
-
function buildGuardrailsLaunchPlan(role, host) {
|
|
9698
|
-
const refusal = guardrailsRoleRefusal(role, host);
|
|
9699
|
-
if (refusal !== null) return {
|
|
9700
|
-
ok: false,
|
|
9701
|
-
reason: refusal
|
|
9702
|
-
};
|
|
9703
|
-
const isPresent = (candidate) => host.existingPaths.has(candidate);
|
|
9704
|
-
const writablePaths = [...alwaysWritablePaths(role, host), ...vendorLoginFiles(role, host).filter(isPresent)];
|
|
9705
|
-
const readDenied = readDeniedPaths(role, host);
|
|
9706
|
-
const writableWithinDenied = writablePaths.filter((writable) => isInsideAny(readDenied, writable));
|
|
9707
|
-
return {
|
|
9708
|
-
ok: true,
|
|
9709
|
-
plan: {
|
|
9710
|
-
launcher: role.network.access === "open" ? "open-network" : "runtime-cli",
|
|
9711
|
-
policy: {
|
|
9712
|
-
network: networkPolicy(role),
|
|
9713
|
-
filesystem: {
|
|
9714
|
-
denyRead: literalEntries(readDenied),
|
|
9715
|
-
allowRead: literalEntries([...writableWithinDenied, ...role.readableWithinDenied]),
|
|
9716
|
-
...filesystemWrites(role, host, writablePaths)
|
|
9717
|
-
},
|
|
9718
|
-
terminalScope: isStandardLevel(role) ? "any" : "own",
|
|
9719
|
-
allowAppleEvents: isStandardLevel(role)
|
|
9720
|
-
},
|
|
9721
|
-
launchEnv: Object.assign({ CLAUDE_CODE_TMPDIR: host.tempDir }, ...role.vendors.map((vendor) => VENDOR_PROFILES[vendor].launchEnv), launchHomeEnv(role, host)),
|
|
9722
|
-
guardedEnv: guardedEnvFor(role, host),
|
|
9723
|
-
requiredDirectories: unique([
|
|
9724
|
-
host.tempDir,
|
|
9725
|
-
...vendorWritableFolders(role, host),
|
|
9726
|
-
...launchPrivateFolders(role, host)
|
|
9727
|
-
]),
|
|
9728
|
-
launchHomeLinks: launchHomeLinkCandidates(role, host).filter((link) => isPresent(link.targetPath))
|
|
9729
|
-
}
|
|
9730
|
-
};
|
|
9731
|
-
}
|
|
9732
|
-
//#endregion
|
|
9733
10470
|
//#region shared/my-pr-merge.ts
|
|
9734
10471
|
var FAILING_CHECK_STATES$1 = /* @__PURE__ */ new Set(["FAILURE", "ERROR"]);
|
|
9735
10472
|
function myPrMergeBlocker(pr) {
|
|
@@ -10500,6 +11237,7 @@ function createGithubStorePoller({ github, now, isDashboardConnected, exclusive,
|
|
|
10500
11237
|
let backoffUntil = 0;
|
|
10501
11238
|
let rateLimitedUntil = 0;
|
|
10502
11239
|
let failureStreak = 0;
|
|
11240
|
+
let quickRetryCount = 0;
|
|
10503
11241
|
const unavailableKeys = /* @__PURE__ */ new Set();
|
|
10504
11242
|
function getSnapshot() {
|
|
10505
11243
|
const watchSnapshots = Object.fromEntries([...watches].map(([name, watch]) => [name, {
|
|
@@ -10568,19 +11306,21 @@ function createGithubStorePoller({ github, now, isDashboardConnected, exclusive,
|
|
|
10568
11306
|
const retryAfterMs = secondaryRateLimitWaitMs(pollingError) ?? void 0;
|
|
10569
11307
|
const schedule = nextRetrySchedule({
|
|
10570
11308
|
failureStreak,
|
|
10571
|
-
quickRetryCount
|
|
10572
|
-
quickRetries:
|
|
11309
|
+
quickRetryCount,
|
|
11310
|
+
quickRetries: true,
|
|
10573
11311
|
retryAfterMs,
|
|
10574
11312
|
baseMs: 6e4,
|
|
10575
11313
|
maxMs: GITHUB_RATE_LIMIT_WINDOW_MS,
|
|
10576
11314
|
random: () => 1
|
|
10577
11315
|
});
|
|
11316
|
+
if (schedule.retry) quickRetryCount += 1;
|
|
10578
11317
|
backoffUntil = now() + schedule.waitMs;
|
|
10579
11318
|
rateLimitedUntil = retryAfterMs === void 0 ? 0 : backoffUntil;
|
|
10580
11319
|
log.warn(`[github-store] ${pollingError}`);
|
|
10581
11320
|
return {
|
|
10582
11321
|
failed: true,
|
|
10583
|
-
retryAfterMs: schedule.waitMs
|
|
11322
|
+
retryAfterMs: schedule.waitMs,
|
|
11323
|
+
error: pollingError
|
|
10584
11324
|
};
|
|
10585
11325
|
}
|
|
10586
11326
|
function recordReadFailure(error) {
|
|
@@ -10589,6 +11329,7 @@ function createGithubStorePoller({ github, now, isDashboardConnected, exclusive,
|
|
|
10589
11329
|
function recordSuccess() {
|
|
10590
11330
|
pollingError = null;
|
|
10591
11331
|
failureStreak = 0;
|
|
11332
|
+
quickRetryCount = 0;
|
|
10592
11333
|
backoffUntil = 0;
|
|
10593
11334
|
rateLimitedUntil = 0;
|
|
10594
11335
|
}
|
|
@@ -10787,11 +11528,15 @@ function createGithubStorePoller({ github, now, isDashboardConnected, exclusive,
|
|
|
10787
11528
|
function tick(watchNames) {
|
|
10788
11529
|
return exclusive(async () => {
|
|
10789
11530
|
const tickStartedAt = now();
|
|
10790
|
-
if (
|
|
11531
|
+
if (watchNames !== void 0 && rateLimitedUntil > tickStartedAt) return {
|
|
11532
|
+
failed: true,
|
|
11533
|
+
retryAfterMs: rateLimitedUntil - tickStartedAt
|
|
11534
|
+
};
|
|
11535
|
+
if (watchNames === void 0 && shouldSkipTick({
|
|
10791
11536
|
now: tickStartedAt,
|
|
10792
11537
|
backoffUntil
|
|
10793
11538
|
})) return {
|
|
10794
|
-
failed:
|
|
11539
|
+
failed: false,
|
|
10795
11540
|
retryAfterMs: backoffUntil - tickStartedAt
|
|
10796
11541
|
};
|
|
10797
11542
|
const previousError = pollingError;
|
|
@@ -10825,7 +11570,7 @@ function createGithubStorePoller({ github, now, isDashboardConnected, exclusive,
|
|
|
10825
11570
|
} finally {
|
|
10826
11571
|
publishChanges([...details, ...behindChangedPrs], removedKeys, previousError, hasWatchChanges);
|
|
10827
11572
|
}
|
|
10828
|
-
});
|
|
11573
|
+
}, { isPriority: watchNames !== void 0 });
|
|
10829
11574
|
}
|
|
10830
11575
|
async function refreshWatches(names) {
|
|
10831
11576
|
if ((await tick(names)).failed) return {
|
|
@@ -11054,7 +11799,8 @@ function createGithubStorePoller({ github, now, isDashboardConnected, exclusive,
|
|
|
11054
11799
|
function storeReadingLaneClock(clock) {
|
|
11055
11800
|
return clock ? {
|
|
11056
11801
|
schedule: clock.schedule,
|
|
11057
|
-
exclusive: async (work) => work()
|
|
11802
|
+
exclusive: async (work) => work(),
|
|
11803
|
+
githubStatus: clock.githubStatus
|
|
11058
11804
|
} : void 0;
|
|
11059
11805
|
}
|
|
11060
11806
|
function createGithubStoreWiring({ github, clock, now = Date.now, isDashboardConnected, broadcast, log }) {
|
|
@@ -11137,6 +11883,8 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11137
11883
|
let rateLimitedUntil = 0;
|
|
11138
11884
|
let isRefreshing = false;
|
|
11139
11885
|
let refreshNotice = null;
|
|
11886
|
+
let lastRefreshedAt = null;
|
|
11887
|
+
let hasTransientFailure = false;
|
|
11140
11888
|
const running = /* @__PURE__ */ new Set();
|
|
11141
11889
|
function persist() {
|
|
11142
11890
|
return persistQueue.run(() => writeState()).catch((e) => {
|
|
@@ -11165,14 +11913,17 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11165
11913
|
retryTimer.unref?.();
|
|
11166
11914
|
}
|
|
11167
11915
|
function scheduleStatus() {
|
|
11916
|
+
const incident = hasTransientFailure ? clock?.githubStatus?.incident() ?? null : null;
|
|
11168
11917
|
return {
|
|
11169
11918
|
nextAttemptAt,
|
|
11170
11919
|
retry,
|
|
11171
11920
|
isRefreshing,
|
|
11172
|
-
refreshNotice
|
|
11921
|
+
refreshNotice,
|
|
11922
|
+
lastRefreshedAt,
|
|
11923
|
+
incident
|
|
11173
11924
|
};
|
|
11174
11925
|
}
|
|
11175
|
-
async function runTick(trigger) {
|
|
11926
|
+
async function runTick(trigger, beforeTick) {
|
|
11176
11927
|
const isManual = trigger === "manual";
|
|
11177
11928
|
if (stopped) return {
|
|
11178
11929
|
ok: false,
|
|
@@ -11220,6 +11971,15 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11220
11971
|
};
|
|
11221
11972
|
}
|
|
11222
11973
|
}
|
|
11974
|
+
if (isManual && beforeTick) {
|
|
11975
|
+
onScheduleChange();
|
|
11976
|
+
const refreshed = await beforeTick();
|
|
11977
|
+
if (stopped) return {
|
|
11978
|
+
ok: false,
|
|
11979
|
+
error: "Reviews polling is stopped."
|
|
11980
|
+
};
|
|
11981
|
+
if (!refreshed.ok) return refreshed;
|
|
11982
|
+
}
|
|
11223
11983
|
clearRetryTimer();
|
|
11224
11984
|
nextAttemptAt = null;
|
|
11225
11985
|
retry = null;
|
|
@@ -11230,6 +11990,8 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11230
11990
|
error: "Reviews polling is stopped."
|
|
11231
11991
|
};
|
|
11232
11992
|
if (outcome?.failed !== true) {
|
|
11993
|
+
lastRefreshedAt = now();
|
|
11994
|
+
hasTransientFailure = false;
|
|
11233
11995
|
failureStreak = 0;
|
|
11234
11996
|
quickRetryCount = 0;
|
|
11235
11997
|
backoffUntil = 0;
|
|
@@ -11238,6 +12000,8 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11238
12000
|
return { ok: true };
|
|
11239
12001
|
}
|
|
11240
12002
|
failureStreak += 1;
|
|
12003
|
+
hasTransientFailure = outcome.retryAfterMs === void 0;
|
|
12004
|
+
if (hasTransientFailure) clock?.githubStatus?.noteTransientFailure();
|
|
11241
12005
|
const scheduled = nextRetrySchedule({
|
|
11242
12006
|
failureStreak,
|
|
11243
12007
|
quickRetryCount,
|
|
@@ -11250,7 +12014,7 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11250
12014
|
if (scheduled.retry) quickRetryCount += 1;
|
|
11251
12015
|
if (outcome.retryAfterMs && outcome.retryAfterMs > 0) rateLimitedUntil = now() + outcome.retryAfterMs;
|
|
11252
12016
|
scheduleAttempt(scheduled.waitMs, scheduled.retry);
|
|
11253
|
-
log.warn(`[${tag}] poll failed (${failureStreak} in a row) - backing off ${Math.round(scheduled.waitMs / 1e3)}s`);
|
|
12017
|
+
log.warn(`[${tag}] poll failed (${failureStreak} in a row)${outcome.error ? `: ${outcome.error}` : ""} - backing off ${Math.round(scheduled.waitMs / 1e3)}s`);
|
|
11254
12018
|
return {
|
|
11255
12019
|
ok: false,
|
|
11256
12020
|
error: "Could not refresh from GitHub."
|
|
@@ -11270,8 +12034,8 @@ function createTickLoop({ tag, quickRetries = false, onScheduleChange = () => {}
|
|
|
11270
12034
|
if (tickRunning) return;
|
|
11271
12035
|
runTick("interval");
|
|
11272
12036
|
}
|
|
11273
|
-
function refresh() {
|
|
11274
|
-
return runTick("manual");
|
|
12037
|
+
function refresh(beforeTick) {
|
|
12038
|
+
return runTick("manual", beforeTick);
|
|
11275
12039
|
}
|
|
11276
12040
|
async function start(prelude = null) {
|
|
11277
12041
|
stopped = false;
|
|
@@ -11429,9 +12193,13 @@ function createIssuesPoller(dependencies) {
|
|
|
11429
12193
|
}
|
|
11430
12194
|
async function failedOutcome(error) {
|
|
11431
12195
|
const waitMs = await rateLimitWaitMs() ?? secondaryRateLimitWaitMs(error);
|
|
11432
|
-
return waitMs === null ? {
|
|
12196
|
+
return waitMs === null ? {
|
|
11433
12197
|
failed: true,
|
|
11434
|
-
|
|
12198
|
+
error
|
|
12199
|
+
} : {
|
|
12200
|
+
failed: true,
|
|
12201
|
+
retryAfterMs: waitMs,
|
|
12202
|
+
error
|
|
11435
12203
|
};
|
|
11436
12204
|
}
|
|
11437
12205
|
async function runTick() {
|
|
@@ -11877,7 +12645,7 @@ async function runCommandSafely(command, args, { env, timeoutMs, signal }) {
|
|
|
11877
12645
|
}
|
|
11878
12646
|
}
|
|
11879
12647
|
async function readTextFileOrNull$1(filePath) {
|
|
11880
|
-
return
|
|
12648
|
+
return fs$1.readFile(filePath, "utf8").catch(() => null);
|
|
11881
12649
|
}
|
|
11882
12650
|
function sleepUnlessAborted(durationMs, signal) {
|
|
11883
12651
|
return new Promise((resolve) => {
|
|
@@ -16609,11 +17377,6 @@ async function writeWhenAbsent(filePath, contents) {
|
|
|
16609
17377
|
var WORKSPACE_MAP_FILE = "AGENTS.md";
|
|
16610
17378
|
var WORKSPACE_POINTER_FILE = "CLAUDE.md";
|
|
16611
17379
|
var WORKSPACE_MAP_HASH_FILE = ".glimmervoid-map-hash";
|
|
16612
|
-
var GENERATED_WORKSPACE_FILES = /* @__PURE__ */ new Set([
|
|
16613
|
-
WORKSPACE_MAP_FILE,
|
|
16614
|
-
WORKSPACE_POINTER_FILE,
|
|
16615
|
-
WORKSPACE_MAP_HASH_FILE
|
|
16616
|
-
]);
|
|
16617
17380
|
function hashWorkspaceMap(contents) {
|
|
16618
17381
|
return crypto.createHash("sha256").update(contents).digest("hex");
|
|
16619
17382
|
}
|
|
@@ -16634,15 +17397,20 @@ async function writeWorkspaceMap(folder, contents) {
|
|
|
16634
17397
|
await fs.promises.writeFile(mapPath, contents);
|
|
16635
17398
|
await fs.promises.writeFile(hashPath, `${renderedHash}\n`);
|
|
16636
17399
|
}
|
|
16637
|
-
function createSessionWorkspaceMembers({ plan, sessionName, shareList, gitWorkspace }) {
|
|
17400
|
+
function createSessionWorkspaceMembers({ plan, owner, sessionName, shareList, gitWorkspace }) {
|
|
16638
17401
|
let activeProvision = null;
|
|
16639
17402
|
async function provisionBody() {
|
|
16640
17403
|
try {
|
|
16641
17404
|
await fs.promises.mkdir(plan.folder, { recursive: true });
|
|
17405
|
+
await gitWorkspace.recordOwnedFolder({
|
|
17406
|
+
folderPath: plan.folder,
|
|
17407
|
+
owner
|
|
17408
|
+
});
|
|
16642
17409
|
const members = [];
|
|
16643
17410
|
for (const member of plan.members) {
|
|
16644
17411
|
const ensured = await gitWorkspace.ensureWorkspaceMember({
|
|
16645
17412
|
projectPath: member.repoPath,
|
|
17413
|
+
owner,
|
|
16646
17414
|
wtDir: member.dir,
|
|
16647
17415
|
branch: plan.branch,
|
|
16648
17416
|
shareList
|
|
@@ -16682,28 +17450,68 @@ function createSessionWorkspaceMembers({ plan, sessionName, shareList, gitWorksp
|
|
|
16682
17450
|
});
|
|
16683
17451
|
return attempt;
|
|
16684
17452
|
}
|
|
16685
|
-
|
|
17453
|
+
const pathExists = (candidatePath) => fs.promises.stat(candidatePath).then(() => true, () => false);
|
|
17454
|
+
async function findMemberWork(member) {
|
|
17455
|
+
return gitWorkspace.describeWorkspaceMemberWork({
|
|
17456
|
+
projectPath: member.repoPath,
|
|
17457
|
+
cwd: member.dir,
|
|
17458
|
+
branch: plan.branch
|
|
17459
|
+
}).catch((error) => `its work could not be checked: ${errorMessage(error)}`);
|
|
17460
|
+
}
|
|
17461
|
+
async function findKeptWork() {
|
|
17462
|
+
if (activeProvision) await activeProvision;
|
|
17463
|
+
const kept = [];
|
|
17464
|
+
for (const member of plan.members) {
|
|
17465
|
+
if (!await pathExists(member.dir)) continue;
|
|
17466
|
+
const reason = await findMemberWork(member);
|
|
17467
|
+
if (reason) kept.push({
|
|
17468
|
+
path: member.dir,
|
|
17469
|
+
reason
|
|
17470
|
+
});
|
|
17471
|
+
}
|
|
17472
|
+
return kept;
|
|
17473
|
+
}
|
|
17474
|
+
async function releaseMember(member, discardWork) {
|
|
17475
|
+
if (!await pathExists(member.dir)) return null;
|
|
17476
|
+
const reason = discardWork ? null : await findMemberWork(member);
|
|
17477
|
+
if (reason) return {
|
|
17478
|
+
path: member.dir,
|
|
17479
|
+
reason
|
|
17480
|
+
};
|
|
17481
|
+
const removed = await gitWorkspace.removeWorkspaceMember({
|
|
17482
|
+
projectPath: member.repoPath,
|
|
17483
|
+
cwd: member.dir,
|
|
17484
|
+
shareList,
|
|
17485
|
+
discardWork
|
|
17486
|
+
});
|
|
17487
|
+
if (removed.ok) return null;
|
|
17488
|
+
return {
|
|
17489
|
+
path: member.dir,
|
|
17490
|
+
reason: removed.err?.split("\n")[0] || "git refused to remove it"
|
|
17491
|
+
};
|
|
17492
|
+
}
|
|
17493
|
+
async function release({ keepRepoPaths = [], discardWork = false } = {}) {
|
|
16686
17494
|
if (activeProvision) await activeProvision;
|
|
16687
17495
|
const keptRepoKeys = new Set(keepRepoPaths.map((repoPath) => caseFoldedPathKey(path.resolve(repoPath))));
|
|
16688
|
-
const
|
|
17496
|
+
const kept = [];
|
|
16689
17497
|
for (const member of plan.members) {
|
|
16690
17498
|
if (keptRepoKeys.has(caseFoldedPathKey(member.repoPath))) continue;
|
|
16691
|
-
|
|
16692
|
-
if (
|
|
16693
|
-
projectPath: member.repoPath,
|
|
16694
|
-
cwd: member.dir,
|
|
16695
|
-
shareList
|
|
16696
|
-
})).ok) keptDirs.push(member.dir);
|
|
17499
|
+
const keptMember = await releaseMember(member, discardWork);
|
|
17500
|
+
if (keptMember) kept.push(keptMember);
|
|
16697
17501
|
}
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
17502
|
+
if (keptRepoKeys.size > 0) return { kept };
|
|
17503
|
+
for (const member of plan.members) if (await pathExists(member.dir)) return { kept };
|
|
17504
|
+
await fs.promises.rm(plan.folder, {
|
|
17505
|
+
recursive: true,
|
|
17506
|
+
force: true
|
|
17507
|
+
});
|
|
17508
|
+
await gitWorkspace.forgetRemovedPath(plan.folder);
|
|
17509
|
+
return { kept };
|
|
16703
17510
|
}
|
|
16704
17511
|
return {
|
|
16705
17512
|
provision,
|
|
16706
17513
|
release,
|
|
17514
|
+
findKeptWork,
|
|
16707
17515
|
branch: plan.branch
|
|
16708
17516
|
};
|
|
16709
17517
|
}
|
|
@@ -16711,6 +17519,18 @@ function createSessionWorkspaceMembers({ plan, sessionName, shareList, gitWorksp
|
|
|
16711
17519
|
//#region session/session-worktree-lifecycle.ts
|
|
16712
17520
|
var WORKTREE_CHECK_DEBOUNCE_MS = 400;
|
|
16713
17521
|
var GIT_NOT_FOUND_EXIT_CODE = 1;
|
|
17522
|
+
function workspaceMemberGit(gitWorkspace) {
|
|
17523
|
+
if (!gitWorkspace) return null;
|
|
17524
|
+
const { ensureWorkspaceMember, removeWorkspaceMember, describeWorkspaceMemberWork, recordOwnedFolder, forgetRemovedPath } = gitWorkspace;
|
|
17525
|
+
if (!ensureWorkspaceMember || !removeWorkspaceMember || !describeWorkspaceMemberWork || !recordOwnedFolder || !forgetRemovedPath) return null;
|
|
17526
|
+
return {
|
|
17527
|
+
ensureWorkspaceMember,
|
|
17528
|
+
removeWorkspaceMember,
|
|
17529
|
+
describeWorkspaceMemberWork,
|
|
17530
|
+
recordOwnedFolder,
|
|
17531
|
+
forgetRemovedPath
|
|
17532
|
+
};
|
|
17533
|
+
}
|
|
16714
17534
|
async function readGitStrict(args, options) {
|
|
16715
17535
|
const command = await runGit(args, {
|
|
16716
17536
|
...options,
|
|
@@ -16750,9 +17570,8 @@ function setUpWorkspaceMembers({ folder, sessionId, sessionName, repoPaths, shar
|
|
|
16750
17570
|
repoPaths
|
|
16751
17571
|
});
|
|
16752
17572
|
if (!planned.ok) return planned;
|
|
16753
|
-
const
|
|
16754
|
-
|
|
16755
|
-
if (!ensureWorkspaceMember || !removeWorkspaceMember) return {
|
|
17573
|
+
const memberGit = workspaceMemberGit(gitWorkspace);
|
|
17574
|
+
if (!memberGit) return {
|
|
16756
17575
|
ok: false,
|
|
16757
17576
|
error: "Git workspace is unavailable"
|
|
16758
17577
|
};
|
|
@@ -16760,12 +17579,10 @@ function setUpWorkspaceMembers({ folder, sessionId, sessionName, repoPaths, shar
|
|
|
16760
17579
|
ok: true,
|
|
16761
17580
|
members: createSessionWorkspaceMembers({
|
|
16762
17581
|
plan: planned.plan,
|
|
17582
|
+
owner: `session:${sessionId}`,
|
|
16763
17583
|
sessionName,
|
|
16764
17584
|
shareList,
|
|
16765
|
-
gitWorkspace:
|
|
16766
|
-
ensureWorkspaceMember,
|
|
16767
|
-
removeWorkspaceMember
|
|
16768
|
-
}
|
|
17585
|
+
gitWorkspace: memberGit
|
|
16769
17586
|
})
|
|
16770
17587
|
};
|
|
16771
17588
|
}
|
|
@@ -17060,6 +17877,7 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17060
17877
|
try {
|
|
17061
17878
|
workspace = await gitWorkspace.create({
|
|
17062
17879
|
projectPath: currentProjectPath(),
|
|
17880
|
+
owner: `session:${id}`,
|
|
17063
17881
|
teamId: "session",
|
|
17064
17882
|
label: id,
|
|
17065
17883
|
baseBranch,
|
|
@@ -17162,9 +17980,21 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17162
17980
|
lifecycleState.isWorktree = false;
|
|
17163
17981
|
setMergeStatus(status);
|
|
17164
17982
|
}
|
|
17983
|
+
let isWorkspaceWorkDiscardConfirmed = false;
|
|
17984
|
+
function confirmWorkspaceWorkDiscard() {
|
|
17985
|
+
isWorkspaceWorkDiscardConfirmed = true;
|
|
17986
|
+
}
|
|
17987
|
+
async function findWorkspaceKeptWork() {
|
|
17988
|
+
if (!workspaceMembers?.ok) return [];
|
|
17989
|
+
return workspaceMembers.members.findKeptWork();
|
|
17990
|
+
}
|
|
17165
17991
|
async function releaseWorkspaceMembers(members, keepRepoPaths = []) {
|
|
17166
|
-
const released = await members.release({
|
|
17167
|
-
|
|
17992
|
+
const released = await members.release({
|
|
17993
|
+
keepRepoPaths,
|
|
17994
|
+
discardWork: isWorkspaceWorkDiscardConfirmed
|
|
17995
|
+
});
|
|
17996
|
+
if (released.kept.length === 0) return;
|
|
17997
|
+
console.warn(`[session ${id}] kept workspace members: ${released.kept.map((kept) => `${kept.path} (${kept.reason})`).join(", ")}`);
|
|
17168
17998
|
}
|
|
17169
17999
|
async function releaseDroppedWorkspaceMembers(keepRepoPaths) {
|
|
17170
18000
|
if (!workspaceMembers?.ok) return;
|
|
@@ -17947,6 +18777,8 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17947
18777
|
setMergeStatus,
|
|
17948
18778
|
pasteMergePrompt,
|
|
17949
18779
|
hasUnmergedWork,
|
|
18780
|
+
findWorkspaceKeptWork,
|
|
18781
|
+
confirmWorkspaceWorkDiscard,
|
|
17950
18782
|
getDiff,
|
|
17951
18783
|
getChangeScopes,
|
|
17952
18784
|
resolveEffectiveBase,
|
|
@@ -19318,6 +20150,12 @@ var Session = class extends EventEmitter {
|
|
|
19318
20150
|
discardWorktree() {
|
|
19319
20151
|
return this.worktreeLifecycle.discardWorktree();
|
|
19320
20152
|
}
|
|
20153
|
+
findWorkspaceKeptWork() {
|
|
20154
|
+
return this.worktreeLifecycle.findWorkspaceKeptWork();
|
|
20155
|
+
}
|
|
20156
|
+
confirmWorkspaceWorkDiscard() {
|
|
20157
|
+
this.worktreeLifecycle.confirmWorkspaceWorkDiscard();
|
|
20158
|
+
}
|
|
19321
20159
|
getDetectionStats() {
|
|
19322
20160
|
return {
|
|
19323
20161
|
lastSignal: this._lastSignal,
|
|
@@ -20868,760 +21706,200 @@ function boundedToolCallInput(input) {
|
|
|
20868
21706
|
return {
|
|
20869
21707
|
input: bounded.text,
|
|
20870
21708
|
truncated: true
|
|
20871
|
-
};
|
|
20872
|
-
}
|
|
20873
|
-
if (!input || typeof input !== "object") return { input };
|
|
20874
|
-
const bounded = Array.isArray(input) ? {
|
|
20875
|
-
input,
|
|
20876
|
-
hasCutProperty: false
|
|
20877
|
-
} : boundedInputProperties(input);
|
|
20878
|
-
const serialized = serializedInputText(bounded.input);
|
|
20879
|
-
if (serialized.length > MAX_TRACE_CALL_INPUT_CHARS) return {
|
|
20880
|
-
input: serialized.slice(0, MAX_TRACE_BODY_CHARS),
|
|
20881
|
-
truncated: true
|
|
20882
|
-
};
|
|
20883
|
-
if (!bounded.hasCutProperty) return { input };
|
|
20884
|
-
return {
|
|
20885
|
-
input: bounded.input,
|
|
20886
|
-
truncated: true
|
|
20887
|
-
};
|
|
20888
|
-
}
|
|
20889
|
-
function everyTextBlockJoined(blocks) {
|
|
20890
|
-
const blockTexts = blocks.flatMap((block) => firstTextBlock([block]) ?? []);
|
|
20891
|
-
if (blockTexts.length === 0) return null;
|
|
20892
|
-
return blockTexts.join("\n");
|
|
20893
|
-
}
|
|
20894
|
-
function isTaskNotificationLine(line) {
|
|
20895
|
-
return line.origin?.kind === "task-notification" || line.turnOrigin?.kind === "task_notification";
|
|
20896
|
-
}
|
|
20897
|
-
function mapUserLine(rawLine, line, context) {
|
|
20898
|
-
const base = baseRecord(line, context);
|
|
20899
|
-
const content = line.message?.content;
|
|
20900
|
-
const blocks = contentBlocks(line);
|
|
20901
|
-
const toolResult = blocks.find((block) => block.type === "tool_result");
|
|
20902
|
-
if (toolResult) {
|
|
20903
|
-
const toolUseId = rawTextOr(toolResult.tool_use_id, null);
|
|
20904
|
-
if (!toolUseId) return [rawRecord(rawLine, line, context)];
|
|
20905
|
-
const bounded = boundedText(textContent(toolResult.content));
|
|
20906
|
-
return [{
|
|
20907
|
-
...base,
|
|
20908
|
-
kind: "tool_result",
|
|
20909
|
-
toolUseId,
|
|
20910
|
-
content: bounded.text,
|
|
20911
|
-
isError: toolResult.is_error === true,
|
|
20912
|
-
truncated: bounded.truncated
|
|
20913
|
-
}];
|
|
20914
|
-
}
|
|
20915
|
-
if (line.isMeta === true) {
|
|
20916
|
-
const expansionText = typeof content === "string" ? content : everyTextBlockJoined(blocks);
|
|
20917
|
-
if (!expansionText) return [rawRecord(rawLine, line, context)];
|
|
20918
|
-
const sourceToolUseId = rawTextOr(line.sourceToolUseID, null);
|
|
20919
|
-
if (!sourceToolUseId) return [{
|
|
20920
|
-
...base,
|
|
20921
|
-
kind: "expansion",
|
|
20922
|
-
...boundedTextFields(expansionText)
|
|
20923
|
-
}];
|
|
20924
|
-
return [{
|
|
20925
|
-
...base,
|
|
20926
|
-
kind: "expansion",
|
|
20927
|
-
toolUseId: sourceToolUseId,
|
|
20928
|
-
...boundedTextFields(expansionText)
|
|
20929
|
-
}];
|
|
20930
|
-
}
|
|
20931
|
-
const text = typeof content === "string" ? content : firstTextBlock(blocks);
|
|
20932
|
-
if (!text) return [rawRecord(rawLine, line, context)];
|
|
20933
|
-
if (isTaskNotificationLine(line)) return [{
|
|
20934
|
-
...base,
|
|
20935
|
-
kind: "expansion",
|
|
20936
|
-
...boundedTextFields(text)
|
|
20937
|
-
}];
|
|
20938
|
-
if (typeof content === "string" && /<command-name>[\s\S]*?<\/command-name>/.test(content)) return [{
|
|
20939
|
-
...base,
|
|
20940
|
-
kind: "expansion",
|
|
20941
|
-
...boundedTextFields(content)
|
|
20942
|
-
}];
|
|
20943
|
-
return [{
|
|
20944
|
-
...base,
|
|
20945
|
-
kind: "prompt",
|
|
20946
|
-
...boundedTextFields(text)
|
|
20947
|
-
}];
|
|
20948
|
-
}
|
|
20949
|
-
function queuedCommandText(prompt) {
|
|
20950
|
-
if (typeof prompt === "string") return prompt || null;
|
|
20951
|
-
return firstTextBlock(objectBlocks(prompt));
|
|
20952
|
-
}
|
|
20953
|
-
function queuedCommandRecords(line, context) {
|
|
20954
|
-
const attachment = line.attachment;
|
|
20955
|
-
if (line.type !== "attachment" || attachment?.type !== "queued_command") return null;
|
|
20956
|
-
const text = queuedCommandText(attachment.prompt);
|
|
20957
|
-
if (!text) return null;
|
|
20958
|
-
if (attachment.commandMode === "prompt" && attachment.origin?.kind === "human") return [{
|
|
20959
|
-
...baseRecord(line, context),
|
|
20960
|
-
kind: "prompt",
|
|
20961
|
-
...boundedTextFields(text)
|
|
20962
|
-
}];
|
|
20963
|
-
if (attachment.commandMode === "task-notification") return [{
|
|
20964
|
-
...baseRecord(line, context),
|
|
20965
|
-
kind: "expansion",
|
|
20966
|
-
...boundedTextFields(text)
|
|
20967
|
-
}];
|
|
20968
|
-
return null;
|
|
20969
|
-
}
|
|
20970
|
-
function mapAssistantLine(rawLine, line, context) {
|
|
20971
|
-
const base = baseRecord(line, context);
|
|
20972
|
-
const records = [];
|
|
20973
|
-
let hasSkippedThinkingBlock = false;
|
|
20974
|
-
for (const block of contentBlocks(line)) {
|
|
20975
|
-
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
20976
|
-
if (block.thinking.length === 0) {
|
|
20977
|
-
hasSkippedThinkingBlock = true;
|
|
20978
|
-
continue;
|
|
20979
|
-
}
|
|
20980
|
-
records.push({
|
|
20981
|
-
...base,
|
|
20982
|
-
kind: "thinking",
|
|
20983
|
-
...boundedTextFields(block.thinking)
|
|
20984
|
-
});
|
|
20985
|
-
continue;
|
|
20986
|
-
}
|
|
20987
|
-
if (block.type === "text") {
|
|
20988
|
-
const text = firstTextBlock([block]);
|
|
20989
|
-
if (text) records.push({
|
|
20990
|
-
...base,
|
|
20991
|
-
kind: "assistant",
|
|
20992
|
-
...boundedTextFields(text)
|
|
20993
|
-
});
|
|
20994
|
-
continue;
|
|
20995
|
-
}
|
|
20996
|
-
if (block.type !== "tool_use") continue;
|
|
20997
|
-
const toolUseId = rawTextOr(block.id, null);
|
|
20998
|
-
const name = rawTextOr(block.name, null);
|
|
20999
|
-
if (!toolUseId || !name) return [rawRecord(rawLine, line, context)];
|
|
21000
|
-
records.push({
|
|
21001
|
-
...base,
|
|
21002
|
-
kind: "tool_call",
|
|
21003
|
-
toolUseId,
|
|
21004
|
-
name,
|
|
21005
|
-
...boundedToolCallInput(block.input)
|
|
21006
|
-
});
|
|
21007
|
-
}
|
|
21008
|
-
if (records.length === 0 && hasSkippedThinkingBlock) return [];
|
|
21009
|
-
if (records.length === 0) return [rawRecord(rawLine, line, context)];
|
|
21010
|
-
return records;
|
|
21011
|
-
}
|
|
21012
|
-
function traceRecordsFromTranscriptLine(rawLine, context) {
|
|
21013
|
-
const line = parseTranscriptLine(rawLine);
|
|
21014
|
-
if (!line) return [rawRecord(rawLine, null, context)];
|
|
21015
|
-
const queuedRecords = queuedCommandRecords(line, context);
|
|
21016
|
-
if (queuedRecords) return queuedRecords;
|
|
21017
|
-
if (isDroppedLine(line)) return [];
|
|
21018
|
-
if (line.type === "user") return mapUserLine(rawLine, line, context);
|
|
21019
|
-
if (line.type === "assistant") return mapAssistantLine(rawLine, line, context);
|
|
21020
|
-
return [rawRecord(rawLine, line, context)];
|
|
21021
|
-
}
|
|
21022
|
-
//#endregion
|
|
21023
|
-
//#region server/core/investigation-trail-core.ts
|
|
21024
|
-
var TRAIL_MAX_STEPS = 80;
|
|
21025
|
-
var TRAIL_TOOL_MAX_CHARS = 64;
|
|
21026
|
-
var TRAIL_NOTE_MAX_CHARS = 600;
|
|
21027
|
-
function describeToolStep(toolName, toolInput) {
|
|
21028
|
-
const trimmed = typeof toolName === "string" ? toolName.trim() : "";
|
|
21029
|
-
if (!trimmed) return null;
|
|
21030
|
-
const tool = trimmed.slice(0, TRAIL_TOOL_MAX_CHARS);
|
|
21031
|
-
return {
|
|
21032
|
-
tool,
|
|
21033
|
-
detail: toolDetailLine(tool, toolInput)
|
|
21034
|
-
};
|
|
21035
|
-
}
|
|
21036
|
-
function trailStepFromHook(event, payload) {
|
|
21037
|
-
if (String(event ?? "").toLowerCase() !== "pretooluse") return null;
|
|
21038
|
-
return describeToolStep(payload.tool_name, payload.tool_input);
|
|
21039
|
-
}
|
|
21040
|
-
function trailNotesFromTranscriptLines(lines, now) {
|
|
21041
|
-
return lines.flatMap((line) => traceRecordsFromTranscriptLine(line, {
|
|
21042
|
-
vendorSessionId: "",
|
|
21043
|
-
now
|
|
21044
|
-
})).flatMap((record) => record.kind === "assistant" ? [record.text.trim().slice(0, TRAIL_NOTE_MAX_CHARS)] : []).filter((note) => note !== "");
|
|
21045
|
-
}
|
|
21046
|
-
function createInvestigationTrail(startedAt) {
|
|
21047
|
-
return {
|
|
21048
|
-
startedAt,
|
|
21049
|
-
steps: []
|
|
21050
|
-
};
|
|
21051
|
-
}
|
|
21052
|
-
function appendTrailStep(trail, step, maxSteps = TRAIL_MAX_STEPS) {
|
|
21053
|
-
const isAppendingNote = isTrailNote(step);
|
|
21054
|
-
const sameKindEntries = trail.steps.filter((entry) => isTrailNote(entry) === isAppendingNote);
|
|
21055
|
-
const evictedCount = Math.max(0, sameKindEntries.length + 1 - maxSteps);
|
|
21056
|
-
const evictedEntries = new Set(sameKindEntries.slice(0, evictedCount));
|
|
21057
|
-
const keptEntries = trail.steps.filter((entry) => !evictedEntries.has(entry));
|
|
21058
|
-
return {
|
|
21059
|
-
startedAt: trail.startedAt,
|
|
21060
|
-
steps: [...keptEntries, step]
|
|
21061
|
-
};
|
|
21062
|
-
}
|
|
21063
|
-
function isTrailNote(entry) {
|
|
21064
|
-
return "note" in entry;
|
|
21065
|
-
}
|
|
21066
|
-
//#endregion
|
|
21067
|
-
//#region shared/contracts/guardrails-launch.ts
|
|
21068
|
-
var GuardrailsLaunchLogin = z.object({
|
|
21069
|
-
linkPath: z.string().min(1),
|
|
21070
|
-
targetPath: z.string().min(1)
|
|
21071
|
-
}).strict();
|
|
21072
|
-
var GuardrailsLaunchOwner = z.object({
|
|
21073
|
-
ownerPid: z.number().int().positive(),
|
|
21074
|
-
ownerHost: z.string(),
|
|
21075
|
-
ownerPidNamespace: z.string().nullable(),
|
|
21076
|
-
ownsTempDir: z.boolean(),
|
|
21077
|
-
keptTempFolderName: z.string().min(1).optional(),
|
|
21078
|
-
logins: z.array(GuardrailsLaunchLogin),
|
|
21079
|
-
placeholderPaths: z.array(z.string().min(1))
|
|
21080
|
-
}).strict();
|
|
21081
|
-
//#endregion
|
|
21082
|
-
//#region server/core/claude-trust-core.ts
|
|
21083
|
-
var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
|
|
21084
|
-
var CLAUDE_LEGACY_CONFIG_FILE_NAME = ".config.json";
|
|
21085
|
-
var CLAUDE_CONFIG_LOCK_SUFFIX = ".lock";
|
|
21086
|
-
var CLAUDE_CONFIG_LOCK_STALE_MS = 1e4;
|
|
21087
|
-
var CLAUDE_CONFIG_LOCK_RETRY_DELAYS_MS = Object.freeze([
|
|
21088
|
-
200,
|
|
21089
|
-
400,
|
|
21090
|
-
800,
|
|
21091
|
-
1600
|
|
21092
|
-
]);
|
|
21093
|
-
var GRANTED_KEYS = Object.freeze([
|
|
21094
|
-
"hasTrustDialogAccepted",
|
|
21095
|
-
"hasClaudeMdExternalIncludesApproved",
|
|
21096
|
-
"hasClaudeMdExternalIncludesWarningShown"
|
|
21097
|
-
]);
|
|
21098
|
-
var TRUST_ONLY_KEYS = Object.freeze(["hasTrustDialogAccepted"]);
|
|
21099
|
-
function isPlainRecord(value) {
|
|
21100
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21101
|
-
}
|
|
21102
|
-
function newProjectEntry() {
|
|
21103
|
-
return {
|
|
21104
|
-
allowedTools: [],
|
|
21105
|
-
mcpContextUris: [],
|
|
21106
|
-
mcpServers: {},
|
|
21107
|
-
enabledMcpjsonServers: [],
|
|
21108
|
-
disabledMcpjsonServers: []
|
|
21109
|
-
};
|
|
21110
|
-
}
|
|
21111
|
-
function listClaudeGlobalConfigPathCandidates(env, homeDir) {
|
|
21112
|
-
const configuredDir = env.CLAUDE_CONFIG_DIR?.trim();
|
|
21113
|
-
const legacyConfigHome = (configuredDir || path.join(homeDir, ".claude")).normalize("NFC");
|
|
21114
|
-
return [path.join(legacyConfigHome, CLAUDE_LEGACY_CONFIG_FILE_NAME), path.join(configuredDir || homeDir, CLAUDE_GLOBAL_CONFIG_FILE_NAME)];
|
|
21115
|
-
}
|
|
21116
|
-
function claudeProjectKeyOf(folderPath, platform) {
|
|
21117
|
-
if (platform !== "win32") return path.posix.normalize(folderPath);
|
|
21118
|
-
return path.win32.normalize(folderPath).replaceAll("\\", "/");
|
|
21119
|
-
}
|
|
21120
|
-
function repositoryRootOfCommonGitDir(commonGitDir) {
|
|
21121
|
-
if (!path.isAbsolute(commonGitDir) || path.basename(commonGitDir) !== ".git") return null;
|
|
21122
|
-
return path.dirname(commonGitDir);
|
|
21123
|
-
}
|
|
21124
|
-
function isConfigLockStale(lockModifiedMs, nowMs) {
|
|
21125
|
-
return nowMs - lockModifiedMs > CLAUDE_CONFIG_LOCK_STALE_MS;
|
|
21126
|
-
}
|
|
21127
|
-
function hasRecordedExternalImportsDecline(existingEntry) {
|
|
21128
|
-
return existingEntry?.hasClaudeMdExternalIncludesWarningShown === true && existingEntry.hasClaudeMdExternalIncludesApproved !== true;
|
|
21129
|
-
}
|
|
21130
|
-
function decideFolderTrust(configText, repositoryRoot, platform) {
|
|
21131
|
-
const projectKey = claudeProjectKeyOf(repositoryRoot, platform);
|
|
21132
|
-
let config;
|
|
21133
|
-
try {
|
|
21134
|
-
config = JSON.parse(configText);
|
|
21135
|
-
} catch {
|
|
21136
|
-
return {
|
|
21137
|
-
kind: "unreadable",
|
|
21138
|
-
reason: "it is not valid JSON"
|
|
21139
|
-
};
|
|
21140
|
-
}
|
|
21141
|
-
if (!isPlainRecord(config)) return {
|
|
21142
|
-
kind: "unreadable",
|
|
21143
|
-
reason: "it is not a JSON object"
|
|
21144
|
-
};
|
|
21145
|
-
const projects = config.projects ?? {};
|
|
21146
|
-
if (!isPlainRecord(projects)) return {
|
|
21147
|
-
kind: "unreadable",
|
|
21148
|
-
reason: "its projects key is not an object"
|
|
21149
|
-
};
|
|
21150
|
-
const existingEntry = projects[projectKey];
|
|
21151
|
-
if (existingEntry !== void 0 && !isPlainRecord(existingEntry)) return {
|
|
21152
|
-
kind: "unreadable",
|
|
21153
|
-
reason: "its entry for this folder is not an object"
|
|
21154
|
-
};
|
|
21155
|
-
const keysToGrant = hasRecordedExternalImportsDecline(existingEntry) ? TRUST_ONLY_KEYS : GRANTED_KEYS;
|
|
21156
|
-
if (keysToGrant.every((key) => existingEntry?.[key] === true)) return { kind: "trusted" };
|
|
21157
|
-
const trustedEntry = {
|
|
21158
|
-
...existingEntry ?? newProjectEntry(),
|
|
21159
|
-
...Object.fromEntries(keysToGrant.map((key) => [key, true]))
|
|
21160
|
-
};
|
|
21161
|
-
const nextConfigText = JSON.stringify({
|
|
21162
|
-
...config,
|
|
21163
|
-
projects: {
|
|
21164
|
-
...projects,
|
|
21165
|
-
[projectKey]: trustedEntry
|
|
21166
|
-
}
|
|
21167
|
-
}, null, 2);
|
|
21168
|
-
return {
|
|
21169
|
-
kind: "grant",
|
|
21170
|
-
nextConfigText: configText.endsWith("\n") ? `${nextConfigText}\n` : nextConfigText
|
|
21171
|
-
};
|
|
21172
|
-
}
|
|
21173
|
-
//#endregion
|
|
21174
|
-
//#region server/guardrails/core/guardrails-bridge-socket-core.ts
|
|
21175
|
-
var LONGEST_BRIDGE_SOCKET_NAME = "claude-socks-0123456789abcdef.sock";
|
|
21176
|
-
var UNIX_SOCKET_PATH_BYTE_LIMIT = 107;
|
|
21177
|
-
var SHORT_BRIDGE_SOCKET_FOLDER = "/tmp";
|
|
21178
|
-
function bridgeSocketPathBytes(tempDirectory) {
|
|
21179
|
-
return Buffer.byteLength(path.join(tempDirectory, LONGEST_BRIDGE_SOCKET_NAME));
|
|
21180
|
-
}
|
|
21181
|
-
function hasRoomForBridgeSocket(tempDirectory, platform) {
|
|
21182
|
-
return platform !== "linux" || bridgeSocketPathBytes(tempDirectory) <= UNIX_SOCKET_PATH_BYTE_LIMIT;
|
|
21183
|
-
}
|
|
21184
|
-
function bridgeSocketLauncherEnv(inheritedTempDirectory, platform) {
|
|
21185
|
-
if (hasRoomForBridgeSocket(inheritedTempDirectory, platform)) return { TMPDIR: inheritedTempDirectory };
|
|
21186
|
-
return { TMPDIR: SHORT_BRIDGE_SOCKET_FOLDER };
|
|
21187
|
-
}
|
|
21188
|
-
//#endregion
|
|
21189
|
-
//#region server/prune-files.ts
|
|
21190
|
-
var MS_PER_DAY = 864e5;
|
|
21191
|
-
function idForEntry(entry, suffixes) {
|
|
21192
|
-
for (const suffix of suffixes) {
|
|
21193
|
-
if (suffix.length === 0) return entry;
|
|
21194
|
-
if (entry.endsWith(suffix)) return entry.slice(0, -suffix.length);
|
|
21195
|
-
}
|
|
21196
|
-
return null;
|
|
21197
|
-
}
|
|
21198
|
-
async function pruneAgedFiles({ directory, suffixes, retainDays, now = Date.now(), isRetainedId = () => false, entryMode = "file", fsPromises = fs.promises }) {
|
|
21199
|
-
let entries;
|
|
21200
|
-
try {
|
|
21201
|
-
entries = await fsPromises.readdir(directory);
|
|
21202
|
-
} catch {
|
|
21203
|
-
return [];
|
|
21204
|
-
}
|
|
21205
|
-
const cutoff = now - retainDays * MS_PER_DAY;
|
|
21206
|
-
const removedIds = [];
|
|
21207
|
-
for (const entry of entries) {
|
|
21208
|
-
const id = idForEntry(entry, suffixes);
|
|
21209
|
-
if (!id || isRetainedId(id)) continue;
|
|
21210
|
-
const filePath = path.join(directory, entry);
|
|
21211
|
-
try {
|
|
21212
|
-
if ((await fsPromises.stat(filePath)).mtimeMs >= cutoff) continue;
|
|
21213
|
-
if (entryMode === "directory") await fsPromises.rm(filePath, {
|
|
21214
|
-
recursive: true,
|
|
21215
|
-
force: true
|
|
21216
|
-
});
|
|
21217
|
-
if (entryMode === "file") await fsPromises.unlink(filePath);
|
|
21218
|
-
removedIds.push(id);
|
|
21219
|
-
} catch {}
|
|
21220
|
-
}
|
|
21221
|
-
return removedIds;
|
|
21222
|
-
}
|
|
21223
|
-
//#endregion
|
|
21224
|
-
//#region server/guardrails/guardrails-launch.ts
|
|
21225
|
-
var LAUNCH_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
21226
|
-
var GUARDED_ENV_COMMAND = "/usr/bin/env";
|
|
21227
|
-
var POLICY_FOLDER = "guardrails-policies";
|
|
21228
|
-
var LAUNCH_HOME_FOLDER = "guardrails-homes";
|
|
21229
|
-
var START_FOLDER = "start";
|
|
21230
|
-
var OWNER_FILE_SUFFIX = ".owner.json";
|
|
21231
|
-
var ENTER_FOLDER_SCRIPT = "cd \"$1\" && shift && exec \"$@\"";
|
|
21232
|
-
var LOGIN_FILE_MODE = 384;
|
|
21233
|
-
var ABSENT_PATH_DENY_PLATFORM = "darwin";
|
|
21234
|
-
var OWN_PID_NAMESPACE_LINK = "/proc/self/ns/pid";
|
|
21235
|
-
var SESSION_TEMP_RETAIN_DAYS = 7;
|
|
21236
|
-
async function isPresent(candidate) {
|
|
21237
|
-
try {
|
|
21238
|
-
await access(candidate);
|
|
21239
|
-
return true;
|
|
21240
|
-
} catch (error) {
|
|
21241
|
-
if (!isMissingFileError(error)) throw error;
|
|
21242
|
-
return false;
|
|
21243
|
-
}
|
|
21244
|
-
}
|
|
21245
|
-
async function presentPaths$1(candidates) {
|
|
21246
|
-
const presence = await Promise.all(candidates.map(isPresent));
|
|
21247
|
-
return new Set(candidates.filter((_candidate, index) => presence[index]));
|
|
21248
|
-
}
|
|
21249
|
-
async function realPathWhenPresent(candidate) {
|
|
21250
|
-
if (!path.isAbsolute(candidate)) return candidate;
|
|
21251
|
-
try {
|
|
21252
|
-
return await realpath(candidate);
|
|
21253
|
-
} catch (error) {
|
|
21254
|
-
if (!isMissingFileError(error)) throw error;
|
|
21255
|
-
return candidate;
|
|
21256
|
-
}
|
|
21257
|
-
}
|
|
21258
|
-
function realPathsWhenPresent(candidates) {
|
|
21259
|
-
return Promise.all(candidates.map(realPathWhenPresent));
|
|
21260
|
-
}
|
|
21261
|
-
async function symlinkTargetsInside(folder) {
|
|
21262
|
-
const symlinkEntries = (await readdir(folder, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isSymbolicLink());
|
|
21263
|
-
return (await Promise.all(symlinkEntries.map((entry) => realpath(path.join(folder, entry.name)).catch(() => null)))).filter((target) => target !== null);
|
|
21264
|
-
}
|
|
21265
|
-
async function blockedPathsWithRealTargets(candidates, operatorBlockedPaths) {
|
|
21266
|
-
const resolved = await Promise.all(candidates.map(async (candidate) => {
|
|
21267
|
-
const isPresentEntry = await lstat(candidate).then(() => true, (error) => !isMissingFileError(error));
|
|
21268
|
-
const isKeptWhenAbsent = process.platform === ABSENT_PATH_DENY_PLATFORM || operatorBlockedPaths.includes(candidate);
|
|
21269
|
-
if (!isPresentEntry) return isKeptWhenAbsent ? [candidate] : [];
|
|
21270
|
-
const realCandidate = await realpath(candidate).catch(() => candidate);
|
|
21271
|
-
return [
|
|
21272
|
-
candidate,
|
|
21273
|
-
realCandidate,
|
|
21274
|
-
...await symlinkTargetsInside(realCandidate)
|
|
21275
|
-
];
|
|
21276
|
-
}));
|
|
21277
|
-
return [...new Set(resolved.flat())];
|
|
21278
|
-
}
|
|
21279
|
-
async function configuredConfigFile(glimmervoidHome) {
|
|
21280
|
-
const { envPath, homePath } = decideConfigPath({
|
|
21281
|
-
env: process.env,
|
|
21282
|
-
homeDir: glimmervoidHome
|
|
21283
|
-
}, () => false);
|
|
21284
|
-
const configFile = envPath ?? homePath;
|
|
21285
|
-
return path.join(await realPathWhenPresent(path.dirname(configFile)), path.basename(configFile));
|
|
21286
|
-
}
|
|
21287
|
-
function scannedClaudeHomes(env, homeDir) {
|
|
21288
|
-
return [env, {
|
|
21289
|
-
...env,
|
|
21290
|
-
CLAUDE_CONFIG_DIR: void 0
|
|
21291
|
-
}].flatMap((scannedEnv) => projectDirCandidates(scannedEnv, [], homeDir)).map((projectsDir) => path.dirname(projectsDir));
|
|
21292
|
-
}
|
|
21293
|
-
async function resolveVendorLocations(env, homeDir) {
|
|
21294
|
-
const [legacyClaudeConfigPath, claudeConfigPath] = listClaudeGlobalConfigPathCandidates(env, homeDir);
|
|
21295
|
-
const [codexStateFolder, ...otherCodexHomes] = codexHomes(env, homeDir);
|
|
21296
|
-
if (codexStateFolder === void 0) return null;
|
|
21297
|
-
return {
|
|
21298
|
-
claude: {
|
|
21299
|
-
stateFolder: await realPathWhenPresent(path.dirname(legacyClaudeConfigPath)),
|
|
21300
|
-
configFiles: [await realPathWhenPresent(claudeConfigPath)],
|
|
21301
|
-
otherHomes: await realPathsWhenPresent(scannedClaudeHomes(env, homeDir))
|
|
21302
|
-
},
|
|
21303
|
-
codex: {
|
|
21304
|
-
stateFolder: await realPathWhenPresent(codexStateFolder),
|
|
21305
|
-
configFiles: [],
|
|
21306
|
-
otherHomes: await realPathsWhenPresent(otherCodexHomes)
|
|
21307
|
-
}
|
|
21308
|
-
};
|
|
21309
|
-
}
|
|
21310
|
-
async function withRealRoots(role) {
|
|
21311
|
-
const [writableRoots, lockedCheckouts, commitCheckouts] = await Promise.all([
|
|
21312
|
-
Promise.all(role.writableRoots.map((root) => realpath(root))),
|
|
21313
|
-
Promise.all(role.lockedCheckouts.map((checkout) => realpath(checkout))),
|
|
21314
|
-
Promise.all(role.commitCheckouts.map(async ({ checkout, gitDir }) => ({
|
|
21315
|
-
checkout: await realpath(checkout),
|
|
21316
|
-
gitDir: await realpath(gitDir)
|
|
21317
|
-
})))
|
|
21318
|
-
]);
|
|
21319
|
-
const vendorHome = role.vendorHome.scope === "launch" ? {
|
|
21320
|
-
...role.vendorHome,
|
|
21321
|
-
cwd: await realPathWhenPresent(role.vendorHome.cwd)
|
|
21322
|
-
} : role.vendorHome;
|
|
21323
|
-
return {
|
|
21324
|
-
...role,
|
|
21325
|
-
vendorHome,
|
|
21326
|
-
writableRoots,
|
|
21327
|
-
lockedCheckouts,
|
|
21328
|
-
commitCheckouts,
|
|
21329
|
-
readableWithinDenied: await realPathsWhenPresent(role.readableWithinDenied)
|
|
21330
|
-
};
|
|
21331
|
-
}
|
|
21332
|
-
async function lstatWhenPresent(candidate) {
|
|
21333
|
-
try {
|
|
21334
|
-
return await lstat(candidate);
|
|
21335
|
-
} catch (error) {
|
|
21336
|
-
if (!isMissingFileError(error)) throw error;
|
|
21337
|
-
return null;
|
|
21338
|
-
}
|
|
21339
|
-
}
|
|
21340
|
-
async function isRegularFile(candidate) {
|
|
21341
|
-
return (await lstatWhenPresent(candidate))?.isFile() ?? false;
|
|
21342
|
-
}
|
|
21343
|
-
async function isRealDirectory$1(candidate) {
|
|
21344
|
-
return (await lstatWhenPresent(candidate))?.isDirectory() ?? false;
|
|
21345
|
-
}
|
|
21346
|
-
async function absentPaths(candidates) {
|
|
21347
|
-
const presentCandidates = await presentPaths$1(candidates);
|
|
21348
|
-
return candidates.filter((candidate) => !presentCandidates.has(candidate));
|
|
21349
|
-
}
|
|
21350
|
-
async function removeLeftoverPlaceholders(placeholderPaths) {
|
|
21351
|
-
for (const placeholderPath of placeholderPaths) {
|
|
21352
|
-
const placeholderStats = await lstat(placeholderPath).catch(() => null);
|
|
21353
|
-
if (placeholderStats?.isFile() && placeholderStats.size === 0) await rm(placeholderPath, { force: true });
|
|
21709
|
+
};
|
|
21354
21710
|
}
|
|
21711
|
+
if (!input || typeof input !== "object") return { input };
|
|
21712
|
+
const bounded = Array.isArray(input) ? {
|
|
21713
|
+
input,
|
|
21714
|
+
hasCutProperty: false
|
|
21715
|
+
} : boundedInputProperties(input);
|
|
21716
|
+
const serialized = serializedInputText(bounded.input);
|
|
21717
|
+
if (serialized.length > MAX_TRACE_CALL_INPUT_CHARS) return {
|
|
21718
|
+
input: serialized.slice(0, MAX_TRACE_BODY_CHARS),
|
|
21719
|
+
truncated: true
|
|
21720
|
+
};
|
|
21721
|
+
if (!bounded.hasCutProperty) return { input };
|
|
21722
|
+
return {
|
|
21723
|
+
input: bounded.input,
|
|
21724
|
+
truncated: true
|
|
21725
|
+
};
|
|
21355
21726
|
}
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
if (errorCode(error) !== "EEXIST") throw error;
|
|
21361
|
-
});
|
|
21362
|
-
}
|
|
21727
|
+
function everyTextBlockJoined(blocks) {
|
|
21728
|
+
const blockTexts = blocks.flatMap((block) => firstTextBlock([block]) ?? []);
|
|
21729
|
+
if (blockTexts.length === 0) return null;
|
|
21730
|
+
return blockTexts.join("\n");
|
|
21363
21731
|
}
|
|
21364
|
-
|
|
21365
|
-
|
|
21366
|
-
if (targetStats === null) return true;
|
|
21367
|
-
return (await lstat(linkPath)).mtimeMs > targetStats.mtimeMs;
|
|
21732
|
+
function isTaskNotificationLine(line) {
|
|
21733
|
+
return line.origin?.kind === "task-notification" || line.turnOrigin?.kind === "task_notification";
|
|
21368
21734
|
}
|
|
21369
|
-
|
|
21370
|
-
|
|
21371
|
-
|
|
21372
|
-
|
|
21373
|
-
|
|
21374
|
-
|
|
21375
|
-
|
|
21376
|
-
|
|
21735
|
+
function mapUserLine(rawLine, line, context) {
|
|
21736
|
+
const base = baseRecord(line, context);
|
|
21737
|
+
const content = line.message?.content;
|
|
21738
|
+
const blocks = contentBlocks(line);
|
|
21739
|
+
const toolResult = blocks.find((block) => block.type === "tool_result");
|
|
21740
|
+
if (toolResult) {
|
|
21741
|
+
const toolUseId = rawTextOr(toolResult.tool_use_id, null);
|
|
21742
|
+
if (!toolUseId) return [rawRecord(rawLine, line, context)];
|
|
21743
|
+
const bounded = boundedText(textContent(toolResult.content));
|
|
21744
|
+
return [{
|
|
21745
|
+
...base,
|
|
21746
|
+
kind: "tool_result",
|
|
21747
|
+
toolUseId,
|
|
21748
|
+
content: bounded.text,
|
|
21749
|
+
isError: toolResult.is_error === true,
|
|
21750
|
+
truncated: bounded.truncated
|
|
21751
|
+
}];
|
|
21377
21752
|
}
|
|
21378
|
-
|
|
21379
|
-
|
|
21380
|
-
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
|
|
21386
|
-
|
|
21387
|
-
|
|
21388
|
-
|
|
21389
|
-
|
|
21390
|
-
|
|
21391
|
-
|
|
21392
|
-
}
|
|
21393
|
-
function isProcessAlive$1(pid) {
|
|
21394
|
-
try {
|
|
21395
|
-
process.kill(pid, 0);
|
|
21396
|
-
return true;
|
|
21397
|
-
} catch (error) {
|
|
21398
|
-
return errorCode(error) === "EPERM";
|
|
21753
|
+
if (line.isMeta === true) {
|
|
21754
|
+
const expansionText = typeof content === "string" ? content : everyTextBlockJoined(blocks);
|
|
21755
|
+
if (!expansionText) return [rawRecord(rawLine, line, context)];
|
|
21756
|
+
const sourceToolUseId = rawTextOr(line.sourceToolUseID, null);
|
|
21757
|
+
if (!sourceToolUseId) return [{
|
|
21758
|
+
...base,
|
|
21759
|
+
kind: "expansion",
|
|
21760
|
+
...boundedTextFields(expansionText)
|
|
21761
|
+
}];
|
|
21762
|
+
return [{
|
|
21763
|
+
...base,
|
|
21764
|
+
kind: "expansion",
|
|
21765
|
+
toolUseId: sourceToolUseId,
|
|
21766
|
+
...boundedTextFields(expansionText)
|
|
21767
|
+
}];
|
|
21399
21768
|
}
|
|
21769
|
+
const text = typeof content === "string" ? content : firstTextBlock(blocks);
|
|
21770
|
+
if (!text) return [rawRecord(rawLine, line, context)];
|
|
21771
|
+
if (isTaskNotificationLine(line)) return [{
|
|
21772
|
+
...base,
|
|
21773
|
+
kind: "expansion",
|
|
21774
|
+
...boundedTextFields(text)
|
|
21775
|
+
}];
|
|
21776
|
+
if (typeof content === "string" && /<command-name>[\s\S]*?<\/command-name>/.test(content)) return [{
|
|
21777
|
+
...base,
|
|
21778
|
+
kind: "expansion",
|
|
21779
|
+
...boundedTextFields(content)
|
|
21780
|
+
}];
|
|
21781
|
+
return [{
|
|
21782
|
+
...base,
|
|
21783
|
+
kind: "prompt",
|
|
21784
|
+
...boundedTextFields(text)
|
|
21785
|
+
}];
|
|
21400
21786
|
}
|
|
21401
|
-
function
|
|
21402
|
-
|
|
21787
|
+
function queuedCommandText(prompt) {
|
|
21788
|
+
if (typeof prompt === "string") return prompt || null;
|
|
21789
|
+
return firstTextBlock(objectBlocks(prompt));
|
|
21403
21790
|
}
|
|
21404
|
-
|
|
21405
|
-
const
|
|
21406
|
-
|
|
21407
|
-
|
|
21408
|
-
|
|
21409
|
-
|
|
21410
|
-
|
|
21791
|
+
function queuedCommandRecords(line, context) {
|
|
21792
|
+
const attachment = line.attachment;
|
|
21793
|
+
if (line.type !== "attachment" || attachment?.type !== "queued_command") return null;
|
|
21794
|
+
const text = queuedCommandText(attachment.prompt);
|
|
21795
|
+
if (!text) return null;
|
|
21796
|
+
if (attachment.commandMode === "prompt" && attachment.origin?.kind === "human") return [{
|
|
21797
|
+
...baseRecord(line, context),
|
|
21798
|
+
kind: "prompt",
|
|
21799
|
+
...boundedTextFields(text)
|
|
21800
|
+
}];
|
|
21801
|
+
if (attachment.commandMode === "task-notification") return [{
|
|
21802
|
+
...baseRecord(line, context),
|
|
21803
|
+
kind: "expansion",
|
|
21804
|
+
...boundedTextFields(text)
|
|
21805
|
+
}];
|
|
21806
|
+
return null;
|
|
21411
21807
|
}
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
|
|
21416
|
-
|
|
21417
|
-
|
|
21418
|
-
|
|
21419
|
-
|
|
21420
|
-
|
|
21421
|
-
let removedLaunchCount = 0;
|
|
21422
|
-
for (const entryName of entryNames) {
|
|
21423
|
-
if (!entryName.endsWith(OWNER_FILE_SUFFIX)) continue;
|
|
21424
|
-
const launchId = entryName.slice(0, -11);
|
|
21425
|
-
if (!LAUNCH_ID_SHAPE.test(launchId)) continue;
|
|
21426
|
-
const ownerPath = path.join(policyDir, entryName);
|
|
21427
|
-
const owner = await readOwnerFile(ownerPath);
|
|
21428
|
-
if (owner === null || owner.ownerHost !== os.hostname() || owner.ownerPidNamespace !== sweeperPidNamespace || isProcessAlive$1(owner.ownerPid)) continue;
|
|
21429
|
-
try {
|
|
21430
|
-
await removeLaunch({
|
|
21431
|
-
policyPath: path.join(policyDir, `${launchId}.json`),
|
|
21432
|
-
ownerPath,
|
|
21433
|
-
launchHomeDir: path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId),
|
|
21434
|
-
ownedTempDir: owner.ownsTempDir ? path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER, launchId) : null
|
|
21435
|
-
}, owner, launchId, { onlyNewerLogins: true });
|
|
21436
|
-
removedLaunchCount += 1;
|
|
21437
|
-
} catch (error) {
|
|
21438
|
-
log.warn(`[guardrails] could not remove the leftover launch ${launchId}: ${errorMessage(error)}`);
|
|
21808
|
+
function mapAssistantLine(rawLine, line, context) {
|
|
21809
|
+
const base = baseRecord(line, context);
|
|
21810
|
+
const records = [];
|
|
21811
|
+
let hasSkippedThinkingBlock = false;
|
|
21812
|
+
for (const block of contentBlocks(line)) {
|
|
21813
|
+
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
21814
|
+
if (block.thinking.length === 0) {
|
|
21815
|
+
hasSkippedThinkingBlock = true;
|
|
21816
|
+
continue;
|
|
21439
21817
|
}
|
|
21818
|
+
records.push({
|
|
21819
|
+
...base,
|
|
21820
|
+
kind: "thinking",
|
|
21821
|
+
...boundedTextFields(block.thinking)
|
|
21822
|
+
});
|
|
21823
|
+
continue;
|
|
21440
21824
|
}
|
|
21441
|
-
if (
|
|
21442
|
-
|
|
21443
|
-
|
|
21825
|
+
if (block.type === "text") {
|
|
21826
|
+
const text = firstTextBlock([block]);
|
|
21827
|
+
if (text) records.push({
|
|
21828
|
+
...base,
|
|
21829
|
+
kind: "assistant",
|
|
21830
|
+
...boundedTextFields(text)
|
|
21831
|
+
});
|
|
21832
|
+
continue;
|
|
21833
|
+
}
|
|
21834
|
+
if (block.type !== "tool_use") continue;
|
|
21835
|
+
const toolUseId = rawTextOr(block.id, null);
|
|
21836
|
+
const name = rawTextOr(block.name, null);
|
|
21837
|
+
if (!toolUseId || !name) return [rawRecord(rawLine, line, context)];
|
|
21838
|
+
records.push({
|
|
21839
|
+
...base,
|
|
21840
|
+
kind: "tool_call",
|
|
21841
|
+
toolUseId,
|
|
21842
|
+
name,
|
|
21843
|
+
...boundedToolCallInput(block.input)
|
|
21844
|
+
});
|
|
21444
21845
|
}
|
|
21846
|
+
if (records.length === 0 && hasSkippedThinkingBlock) return [];
|
|
21847
|
+
if (records.length === 0) return [rawRecord(rawLine, line, context)];
|
|
21848
|
+
return records;
|
|
21445
21849
|
}
|
|
21446
|
-
|
|
21447
|
-
const
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21452
|
-
if (
|
|
21453
|
-
|
|
21454
|
-
|
|
21455
|
-
const tempFolderNamesWithOwners = /* @__PURE__ */ new Set([...ownerEntryNames.map((entryName) => entryName.slice(0, -11)), ...owners.flatMap((owner) => owner?.keptTempFolderName === void 0 ? [] : [owner.keptTempFolderName])]);
|
|
21456
|
-
await pruneAgedFiles({
|
|
21457
|
-
directory: path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER),
|
|
21458
|
-
suffixes: [""],
|
|
21459
|
-
retainDays: SESSION_TEMP_RETAIN_DAYS,
|
|
21460
|
-
entryMode: "directory",
|
|
21461
|
-
isRetainedId: (id) => liveSessionIds.has(id) || tempFolderNamesWithOwners.has(id)
|
|
21462
|
-
});
|
|
21850
|
+
function traceRecordsFromTranscriptLine(rawLine, context) {
|
|
21851
|
+
const line = parseTranscriptLine(rawLine);
|
|
21852
|
+
if (!line) return [rawRecord(rawLine, null, context)];
|
|
21853
|
+
const queuedRecords = queuedCommandRecords(line, context);
|
|
21854
|
+
if (queuedRecords) return queuedRecords;
|
|
21855
|
+
if (isDroppedLine(line)) return [];
|
|
21856
|
+
if (line.type === "user") return mapUserLine(rawLine, line, context);
|
|
21857
|
+
if (line.type === "assistant") return mapAssistantLine(rawLine, line, context);
|
|
21858
|
+
return [rawRecord(rawLine, line, context)];
|
|
21463
21859
|
}
|
|
21464
|
-
|
|
21465
|
-
|
|
21466
|
-
|
|
21467
|
-
|
|
21468
|
-
|
|
21469
|
-
|
|
21470
|
-
|
|
21860
|
+
//#endregion
|
|
21861
|
+
//#region server/core/investigation-trail-core.ts
|
|
21862
|
+
var TRAIL_MAX_STEPS = 80;
|
|
21863
|
+
var TRAIL_TOOL_MAX_CHARS = 64;
|
|
21864
|
+
var TRAIL_NOTE_MAX_CHARS = 600;
|
|
21865
|
+
function describeToolStep(toolName, toolInput) {
|
|
21866
|
+
const trimmed = typeof toolName === "string" ? toolName.trim() : "";
|
|
21867
|
+
if (!trimmed) return null;
|
|
21868
|
+
const tool = trimmed.slice(0, TRAIL_TOOL_MAX_CHARS);
|
|
21471
21869
|
return {
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
runtimeCliPath,
|
|
21475
|
-
"--settings",
|
|
21476
|
-
policyPath,
|
|
21477
|
-
"--"
|
|
21478
|
-
]
|
|
21870
|
+
tool,
|
|
21871
|
+
detail: toolDetailLine(tool, toolInput)
|
|
21479
21872
|
};
|
|
21480
21873
|
}
|
|
21481
|
-
function
|
|
21482
|
-
|
|
21483
|
-
return
|
|
21484
|
-
}
|
|
21485
|
-
function enteringFolder(enterDir, file, args) {
|
|
21486
|
-
if (enterDir === void 0) return [file, ...args];
|
|
21487
|
-
return [
|
|
21488
|
-
"/bin/sh",
|
|
21489
|
-
"-c",
|
|
21490
|
-
ENTER_FOLDER_SCRIPT,
|
|
21491
|
-
"sh",
|
|
21492
|
-
enterDir,
|
|
21493
|
-
file,
|
|
21494
|
-
...args
|
|
21495
|
-
];
|
|
21874
|
+
function trailStepFromHook(event, payload) {
|
|
21875
|
+
if (String(event ?? "").toLowerCase() !== "pretooluse") return null;
|
|
21876
|
+
return describeToolStep(payload.tool_name, payload.tool_input);
|
|
21496
21877
|
}
|
|
21497
|
-
function
|
|
21498
|
-
|
|
21499
|
-
|
|
21878
|
+
function trailNotesFromTranscriptLines(lines, now) {
|
|
21879
|
+
return lines.flatMap((line) => traceRecordsFromTranscriptLine(line, {
|
|
21880
|
+
vendorSessionId: "",
|
|
21881
|
+
now
|
|
21882
|
+
})).flatMap((record) => record.kind === "assistant" ? [record.text.trim().slice(0, TRAIL_NOTE_MAX_CHARS)] : []).filter((note) => note !== "");
|
|
21500
21883
|
}
|
|
21501
|
-
|
|
21502
|
-
|
|
21503
|
-
|
|
21504
|
-
|
|
21884
|
+
function createInvestigationTrail(startedAt) {
|
|
21885
|
+
return {
|
|
21886
|
+
startedAt,
|
|
21887
|
+
steps: []
|
|
21505
21888
|
};
|
|
21506
|
-
|
|
21507
|
-
|
|
21508
|
-
|
|
21889
|
+
}
|
|
21890
|
+
function appendTrailStep(trail, step, maxSteps = TRAIL_MAX_STEPS) {
|
|
21891
|
+
const isAppendingNote = isTrailNote(step);
|
|
21892
|
+
const sameKindEntries = trail.steps.filter((entry) => isTrailNote(entry) === isAppendingNote);
|
|
21893
|
+
const evictedCount = Math.max(0, sameKindEntries.length + 1 - maxSteps);
|
|
21894
|
+
const evictedEntries = new Set(sameKindEntries.slice(0, evictedCount));
|
|
21895
|
+
const keptEntries = trail.steps.filter((entry) => !evictedEntries.has(entry));
|
|
21896
|
+
return {
|
|
21897
|
+
startedAt: trail.startedAt,
|
|
21898
|
+
steps: [...keptEntries, step]
|
|
21509
21899
|
};
|
|
21510
|
-
|
|
21511
|
-
|
|
21512
|
-
|
|
21513
|
-
recursive: true,
|
|
21514
|
-
mode: 448
|
|
21515
|
-
});
|
|
21516
|
-
const [homeDir, glimmervoidHome, realRole, ownerPidNamespace] = await Promise.all([
|
|
21517
|
-
realpath(os.homedir()),
|
|
21518
|
-
realpath(glimmervoidHomeDir$1()),
|
|
21519
|
-
withRealRoots(role),
|
|
21520
|
-
readOwnPidNamespace()
|
|
21521
|
-
]);
|
|
21522
|
-
const ownsTempDir = tempDir === void 0 && keptTempFolderName === void 0;
|
|
21523
|
-
const launchTempDir = launchTempDirFor(glimmervoidHome, launchId, tempDir, keptTempFolderName);
|
|
21524
|
-
const launchHomeDir = path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId);
|
|
21525
|
-
const vendorLocations = await resolveVendorLocations(process.env, homeDir);
|
|
21526
|
-
if (vendorLocations === null) return {
|
|
21527
|
-
ok: false,
|
|
21528
|
-
reason: "CODEX_HOME names no folder"
|
|
21529
|
-
};
|
|
21530
|
-
const xdgConfigHomeOverride = rawTextOr(process.env.XDG_CONFIG_HOME, null);
|
|
21531
|
-
const stableHostPaths = {
|
|
21532
|
-
homeDir,
|
|
21533
|
-
glimmervoidHome,
|
|
21534
|
-
glimmervoidConfigFile: await configuredConfigFile(glimmervoidHome),
|
|
21535
|
-
tempDir: launchTempDir,
|
|
21536
|
-
launchHomeDir,
|
|
21537
|
-
vendorLocations,
|
|
21538
|
-
unwrappedVendorHomes: await realPathsWhenPresent(grokHomes(process.env, homeDir)),
|
|
21539
|
-
relocatedXdgConfigHome: xdgConfigHomeOverride === null ? null : await realPathWhenPresent(xdgConfigHomeOverride)
|
|
21540
|
-
};
|
|
21541
|
-
const buildPlanFromCurrentDisk = async () => {
|
|
21542
|
-
const [homeEntryNames, glimmervoidHomeEntryNames] = await Promise.all([readdir(homeDir), readdir(glimmervoidHome)]);
|
|
21543
|
-
const hostPaths = {
|
|
21544
|
-
...stableHostPaths,
|
|
21545
|
-
homeEntryNames,
|
|
21546
|
-
glimmervoidHomeEntryNames
|
|
21547
|
-
};
|
|
21548
|
-
const [existingPaths, writeBlockedPaths] = await Promise.all([presentPaths$1(guardrailsPolicyProbePaths(realRole, hostPaths)), blockedPathsWithRealTargets(standardWriteBlockCandidates(realRole, hostPaths), realRole.blockedPaths ?? [])]);
|
|
21549
|
-
return buildGuardrailsLaunchPlan(realRole, {
|
|
21550
|
-
...hostPaths,
|
|
21551
|
-
existingPaths,
|
|
21552
|
-
writeBlockedPaths
|
|
21553
|
-
});
|
|
21554
|
-
};
|
|
21555
|
-
const built = await buildPlanFromCurrentDisk();
|
|
21556
|
-
if (!built.ok) return built;
|
|
21557
|
-
const { plan } = built;
|
|
21558
|
-
const policyPath = path.join(policyDir, `${launchId}.json`);
|
|
21559
|
-
const launcher = launcherCommand(plan, policyPath);
|
|
21560
|
-
if (!launcher) return {
|
|
21561
|
-
ok: false,
|
|
21562
|
-
reason: "the sandbox runtime CLI could not be resolved"
|
|
21563
|
-
};
|
|
21564
|
-
await Promise.all(plan.requiredDirectories.map((directory) => mkdir(directory, { recursive: true })));
|
|
21565
|
-
const startDir = enterDir === void 0 ? void 0 : path.join(launchTempDir, START_FOLDER);
|
|
21566
|
-
if (startDir !== void 0) await mkdir(startDir, { recursive: true });
|
|
21567
|
-
await Promise.all(plan.launchHomeLinks.map((link) => symlink(link.targetPath, link.linkPath)));
|
|
21568
|
-
await writeJsonAtomic(policyPath, plan.policy);
|
|
21569
|
-
const folders = {
|
|
21570
|
-
policyPath,
|
|
21571
|
-
ownerPath: path.join(policyDir, `${launchId}${OWNER_FILE_SUFFIX}`),
|
|
21572
|
-
launchHomeDir,
|
|
21573
|
-
ownedTempDir: ownsTempDir ? launchTempDir : null
|
|
21574
|
-
};
|
|
21575
|
-
const owner = {
|
|
21576
|
-
ownerPid: process.pid,
|
|
21577
|
-
ownerHost: os.hostname(),
|
|
21578
|
-
ownerPidNamespace,
|
|
21579
|
-
ownsTempDir,
|
|
21580
|
-
...keptTempFolderName === void 0 ? {} : { keptTempFolderName },
|
|
21581
|
-
logins: plan.launchHomeLinks.filter((link) => link.isLogin).map(({ linkPath, targetPath }) => ({
|
|
21582
|
-
linkPath,
|
|
21583
|
-
targetPath
|
|
21584
|
-
})),
|
|
21585
|
-
placeholderPaths: []
|
|
21586
|
-
};
|
|
21587
|
-
await writeJsonAtomic(folders.ownerPath, owner);
|
|
21588
|
-
const envPrefix = guardedEnvPrefix(plan);
|
|
21589
|
-
return {
|
|
21590
|
-
ok: true,
|
|
21591
|
-
launch: {
|
|
21592
|
-
wrap: async (file, args, { hasTerminal }) => {
|
|
21593
|
-
const rebuilt = await buildPlanFromCurrentDisk();
|
|
21594
|
-
if (!rebuilt.ok) throw new Error(rebuilt.reason);
|
|
21595
|
-
await writeJsonAtomic(policyPath, {
|
|
21596
|
-
...rebuilt.plan.policy,
|
|
21597
|
-
allowPty: hasTerminal && rebuilt.plan.launcher === "open-network"
|
|
21598
|
-
});
|
|
21599
|
-
owner.placeholderPaths = await absentPaths(guardrailsPlaceholderPaths(realRole));
|
|
21600
|
-
await writeJsonAtomic(folders.ownerPath, owner);
|
|
21601
|
-
await createMissingMountPoints(lockedClaudeMountPoints(realRole));
|
|
21602
|
-
return {
|
|
21603
|
-
file: launcher.file,
|
|
21604
|
-
args: [
|
|
21605
|
-
...launcher.args,
|
|
21606
|
-
...envPrefix,
|
|
21607
|
-
...enteringFolder(enterDir, file, args)
|
|
21608
|
-
]
|
|
21609
|
-
};
|
|
21610
|
-
},
|
|
21611
|
-
env: {
|
|
21612
|
-
...plan.launchEnv,
|
|
21613
|
-
...bridgeSocketLauncherEnv(os.tmpdir(), process.platform)
|
|
21614
|
-
},
|
|
21615
|
-
...startDir === void 0 ? {} : { cwd: startDir },
|
|
21616
|
-
dispose: () => removeLaunch(folders, owner, launchId, { onlyNewerLogins: false })
|
|
21617
|
-
}
|
|
21618
|
-
};
|
|
21619
|
-
} catch (error) {
|
|
21620
|
-
return {
|
|
21621
|
-
ok: false,
|
|
21622
|
-
reason: errorMessage(error)
|
|
21623
|
-
};
|
|
21624
|
-
}
|
|
21900
|
+
}
|
|
21901
|
+
function isTrailNote(entry) {
|
|
21902
|
+
return "note" in entry;
|
|
21625
21903
|
}
|
|
21626
21904
|
//#endregion
|
|
21627
21905
|
//#region server/lanes/lane-spawn.ts
|
|
@@ -21642,7 +21920,7 @@ var LANE_SPAWN_DENY_TOOLS = Object.freeze([
|
|
|
21642
21920
|
async function writeStandaloneDenySettings(permissions, userHooks) {
|
|
21643
21921
|
const settingsDirectory = await makeHostScratchDirectory(SETTINGS_DIR_PREFIX);
|
|
21644
21922
|
const settingsPath = path.join(settingsDirectory, "settings.json");
|
|
21645
|
-
const removeSettingsDirectory = () =>
|
|
21923
|
+
const removeSettingsDirectory = () => fs$1.rm(settingsDirectory, {
|
|
21646
21924
|
recursive: true,
|
|
21647
21925
|
force: true
|
|
21648
21926
|
}).catch(() => {});
|
|
@@ -21742,7 +22020,7 @@ function createLaneSpawn({ sessions = /* @__PURE__ */ new Map(), closeSessionDat
|
|
|
21742
22020
|
}
|
|
21743
22021
|
async function readLaneResultFile(resultPath) {
|
|
21744
22022
|
try {
|
|
21745
|
-
return JSON.parse(await
|
|
22023
|
+
return JSON.parse(await fs$1.readFile(resultPath, "utf8"));
|
|
21746
22024
|
} catch {
|
|
21747
22025
|
return null;
|
|
21748
22026
|
}
|
|
@@ -21753,12 +22031,12 @@ var THREAD_JUDGE_TOOLS = Object.freeze(["Read", "Write"]);
|
|
|
21753
22031
|
var THREAD_JUDGE_TIMEOUT_MS = 12e4;
|
|
21754
22032
|
function createThreadJudge(spawn, shutdownSignal) {
|
|
21755
22033
|
return async (prompt) => {
|
|
21756
|
-
const workDir = await
|
|
22034
|
+
const workDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-thread-judge-"));
|
|
21757
22035
|
const controller = new AbortController();
|
|
21758
22036
|
const timeout = setTimeout(() => controller.abort(), THREAD_JUDGE_TIMEOUT_MS);
|
|
21759
22037
|
timeout.unref();
|
|
21760
22038
|
try {
|
|
21761
|
-
await
|
|
22039
|
+
await fs$1.writeFile(path.join(workDir, "thread-prompt.txt"), prompt, "utf8");
|
|
21762
22040
|
const signal = shutdownSignal ? AbortSignal.any([controller.signal, shutdownSignal]) : controller.signal;
|
|
21763
22041
|
if (signal.aborted) return null;
|
|
21764
22042
|
await spawn({
|
|
@@ -21773,7 +22051,7 @@ function createThreadJudge(spawn, shutdownSignal) {
|
|
|
21773
22051
|
return parseThreadJudgeResult(await readLaneResultFile(path.join(workDir, "thread-result.json")));
|
|
21774
22052
|
} finally {
|
|
21775
22053
|
clearTimeout(timeout);
|
|
21776
|
-
await
|
|
22054
|
+
await fs$1.rm(workDir, {
|
|
21777
22055
|
recursive: true,
|
|
21778
22056
|
force: true
|
|
21779
22057
|
});
|
|
@@ -21790,13 +22068,14 @@ var GH_MERGE_FLAGS = {
|
|
|
21790
22068
|
var MY_PR_MERGE_STATE_QUERY = `query($owner: String!, $name: String!, $number: Int!) {
|
|
21791
22069
|
repository(owner: $owner, name: $name) { pullRequest(number: $number) { state isInMergeQueue autoMergeRequest { enabledAt } } }
|
|
21792
22070
|
}`;
|
|
21793
|
-
var
|
|
22071
|
+
var GH_READ_TIMEOUT_MS = 15e3;
|
|
22072
|
+
var GH_WRITE_TIMEOUT_MS = 3e4;
|
|
21794
22073
|
var GH_MAX_BUFFER_BYTES = 2097153;
|
|
21795
|
-
function run(cmd, args, cwd, input, preserveOutput = false) {
|
|
22074
|
+
function run(cmd, args, cwd, input, preserveOutput = false, timeoutMs = GH_READ_TIMEOUT_MS) {
|
|
21796
22075
|
return runCommand(cmd, args, {
|
|
21797
22076
|
cwd,
|
|
21798
22077
|
input,
|
|
21799
|
-
timeoutMs
|
|
22078
|
+
timeoutMs,
|
|
21800
22079
|
maxBuffer: GH_MAX_BUFFER_BYTES,
|
|
21801
22080
|
trim: !preserveOutput,
|
|
21802
22081
|
keepStdoutOnFailure: true,
|
|
@@ -22101,9 +22380,9 @@ function conditionalGetResult(response) {
|
|
|
22101
22380
|
}
|
|
22102
22381
|
}
|
|
22103
22382
|
function createPrGh(cwd, commandRunner = run) {
|
|
22104
|
-
async function runGh(args, input, preserveOutput = false) {
|
|
22383
|
+
async function runGh(args, input, preserveOutput = false, timeoutMs = GH_READ_TIMEOUT_MS) {
|
|
22105
22384
|
try {
|
|
22106
|
-
return await commandRunner("gh", args, cwd, input, preserveOutput);
|
|
22385
|
+
return await commandRunner("gh", args, cwd, input, preserveOutput, timeoutMs);
|
|
22107
22386
|
} catch (error) {
|
|
22108
22387
|
return {
|
|
22109
22388
|
ok: false,
|
|
@@ -22433,7 +22712,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22433
22712
|
repo,
|
|
22434
22713
|
"--add-label",
|
|
22435
22714
|
label.data
|
|
22436
|
-
]);
|
|
22715
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22437
22716
|
return {
|
|
22438
22717
|
ok: response.ok,
|
|
22439
22718
|
err: response.ok ? "" : response.err.trim() || "gh pr edit failed"
|
|
@@ -22457,7 +22736,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22457
22736
|
repo,
|
|
22458
22737
|
"--body-file",
|
|
22459
22738
|
"-"
|
|
22460
|
-
], comment.data);
|
|
22739
|
+
], comment.data, false, GH_WRITE_TIMEOUT_MS);
|
|
22461
22740
|
return {
|
|
22462
22741
|
ok: response.ok,
|
|
22463
22742
|
err: response.ok ? "" : response.err.trim() || "gh pr comment failed"
|
|
@@ -22530,7 +22809,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22530
22809
|
`query=${RESOLVE_THREAD_MUTATION}`,
|
|
22531
22810
|
"-f",
|
|
22532
22811
|
`id=${threadId}`
|
|
22533
|
-
]);
|
|
22812
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22534
22813
|
const parsed = TeamReviewResolveResponse.safeParse(parseJsonOrNull(response.out));
|
|
22535
22814
|
const isResolved = response.ok && parsed.success && !parsed.data.errors?.length && parsed.data.data?.resolveReviewThread.thread.id === threadId;
|
|
22536
22815
|
return {
|
|
@@ -22691,7 +22970,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22691
22970
|
`id=${pullRequestId}`,
|
|
22692
22971
|
"-f",
|
|
22693
22972
|
`head=${expectedHeadSha}`
|
|
22694
|
-
]);
|
|
22973
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22695
22974
|
if (!response.ok) return {
|
|
22696
22975
|
ok: false,
|
|
22697
22976
|
err: response.err.trim() || "gh graphql rebase failed"
|
|
@@ -22730,7 +23009,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22730
23009
|
GH_MERGE_FLAGS[parsedMethod.data],
|
|
22731
23010
|
"--match-head-commit",
|
|
22732
23011
|
headSha
|
|
22733
|
-
]);
|
|
23012
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22734
23013
|
if (!response.ok) return {
|
|
22735
23014
|
ok: false,
|
|
22736
23015
|
err: response.err.trim() || "gh pr merge failed"
|
|
@@ -22966,7 +23245,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22966
23245
|
`repos/${parts[0]}/${parts[1]}/pulls/${number}/reviews`,
|
|
22967
23246
|
"--input",
|
|
22968
23247
|
"-"
|
|
22969
|
-
], input);
|
|
23248
|
+
], input, false, GH_WRITE_TIMEOUT_MS);
|
|
22970
23249
|
if (!response.ok) return {
|
|
22971
23250
|
ok: false,
|
|
22972
23251
|
err: response.err.trim() || "gh review post failed",
|
|
@@ -23004,7 +23283,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
23004
23283
|
`repos/${parts[0]}/${parts[1]}/pulls/${number}/reviews/${reviewId}/dismissals`,
|
|
23005
23284
|
"--input",
|
|
23006
23285
|
"-"
|
|
23007
|
-
], input);
|
|
23286
|
+
], input, false, GH_WRITE_TIMEOUT_MS);
|
|
23008
23287
|
return {
|
|
23009
23288
|
ok: response.ok,
|
|
23010
23289
|
err: response.ok ? "" : response.err.trim() || "gh review dismissal failed"
|
|
@@ -23358,7 +23637,7 @@ var PROCESS_LIST_TIMEOUT_MS = 5e3;
|
|
|
23358
23637
|
var TERMINATION_GRACE_MS = 2e3;
|
|
23359
23638
|
var EXIT_POLL_MS = 100;
|
|
23360
23639
|
async function resolvedPath(directory) {
|
|
23361
|
-
return
|
|
23640
|
+
return fs$1.realpath(directory).catch(() => path.resolve(directory));
|
|
23362
23641
|
}
|
|
23363
23642
|
async function listTeamReviewProcesses() {
|
|
23364
23643
|
if (process.platform !== "darwin" && process.platform !== "linux") return [];
|
|
@@ -23375,7 +23654,7 @@ async function listTeamReviewProcesses() {
|
|
|
23375
23654
|
const rows = parseProcessTable(stdout);
|
|
23376
23655
|
if (process.platform === "linux") return Promise.all(rows.map(async (row) => ({
|
|
23377
23656
|
...row,
|
|
23378
|
-
cwd: await
|
|
23657
|
+
cwd: await fs$1.readlink(`/proc/${row.pid}/cwd`).then((cwd) => path.resolve(cwd)).catch(() => null)
|
|
23379
23658
|
})));
|
|
23380
23659
|
const { stdout: cwdOutput } = await execFileAsync("lsof", [
|
|
23381
23660
|
"-a",
|
|
@@ -23390,7 +23669,7 @@ async function listTeamReviewProcesses() {
|
|
|
23390
23669
|
const cwdByPid = parseCwdTable(cwdOutput);
|
|
23391
23670
|
return Promise.all(rows.map(async (row) => ({
|
|
23392
23671
|
...row,
|
|
23393
|
-
cwd: await
|
|
23672
|
+
cwd: await fs$1.realpath(cwdByPid.get(row.pid) ?? "").catch(() => null)
|
|
23394
23673
|
})));
|
|
23395
23674
|
}
|
|
23396
23675
|
function isMissingProcess(error) {
|
|
@@ -24137,9 +24416,13 @@ function createTeamReviewPoller(deps) {
|
|
|
24137
24416
|
}
|
|
24138
24417
|
async function rateLimitedOutcome() {
|
|
24139
24418
|
const rateLimitWaitMs = await store.rateLimitWaitMs(now(), TEAM_REVIEW_RATE_LIMIT_RESOURCES) ?? secondaryRateLimitWaitMs(pollingError);
|
|
24140
|
-
return rateLimitWaitMs === null ? {
|
|
24419
|
+
return rateLimitWaitMs === null ? {
|
|
24141
24420
|
failed: true,
|
|
24142
|
-
|
|
24421
|
+
error: pollingError ?? void 0
|
|
24422
|
+
} : {
|
|
24423
|
+
failed: true,
|
|
24424
|
+
retryAfterMs: rateLimitWaitMs,
|
|
24425
|
+
error: pollingError ?? void 0
|
|
24143
24426
|
};
|
|
24144
24427
|
}
|
|
24145
24428
|
async function runTick() {
|
|
@@ -24154,7 +24437,8 @@ function createTeamReviewPoller(deps) {
|
|
|
24154
24437
|
pollingError = "GitHub rate limit asks to wait.";
|
|
24155
24438
|
return {
|
|
24156
24439
|
failed: true,
|
|
24157
|
-
retryAfterMs: tickRateLimitWaitMs
|
|
24440
|
+
retryAfterMs: tickRateLimitWaitMs,
|
|
24441
|
+
error: pollingError
|
|
24158
24442
|
};
|
|
24159
24443
|
}
|
|
24160
24444
|
if (collected === null) {
|
|
@@ -24172,7 +24456,8 @@ function createTeamReviewPoller(deps) {
|
|
|
24172
24456
|
pollingError = "GitHub rate limit asks to wait.";
|
|
24173
24457
|
return {
|
|
24174
24458
|
failed: true,
|
|
24175
|
-
retryAfterMs: rateLimitWaitMs
|
|
24459
|
+
retryAfterMs: rateLimitWaitMs,
|
|
24460
|
+
error: pollingError
|
|
24176
24461
|
};
|
|
24177
24462
|
}
|
|
24178
24463
|
const isPruned = await pruneDeparted(new Set(candidates.map((candidate) => candidate.key)), isComplete);
|
|
@@ -24185,7 +24470,8 @@ function createTeamReviewPoller(deps) {
|
|
|
24185
24470
|
pollingError = "GitHub rate limit asks to wait.";
|
|
24186
24471
|
return {
|
|
24187
24472
|
failed: true,
|
|
24188
|
-
retryAfterMs: tickRateLimitWaitMs
|
|
24473
|
+
retryAfterMs: tickRateLimitWaitMs,
|
|
24474
|
+
error: pollingError
|
|
24189
24475
|
};
|
|
24190
24476
|
}
|
|
24191
24477
|
if (hasSlotFreedSinceSlotCount && waitingForSlot.length > 0 && !loop.isStopped()) setTimeoutFn(() => {
|
|
@@ -24438,10 +24724,11 @@ function createTeamReviewPoller(deps) {
|
|
|
24438
24724
|
error: "Reviews polling is stopped."
|
|
24439
24725
|
};
|
|
24440
24726
|
if (watches.size === 0) await collectCandidates();
|
|
24441
|
-
|
|
24442
|
-
|
|
24443
|
-
|
|
24444
|
-
|
|
24727
|
+
return loop.refresh(async () => {
|
|
24728
|
+
const refreshed = await store.refreshWatches([...watches.keys()]);
|
|
24729
|
+
rebuild();
|
|
24730
|
+
return refreshed;
|
|
24731
|
+
});
|
|
24445
24732
|
}
|
|
24446
24733
|
async function stop() {
|
|
24447
24734
|
hasLoadedState = false;
|
|
@@ -24836,7 +25123,7 @@ function listHomeWorktreePathsMissingLock(porcelain, glimmervoidHomes) {
|
|
|
24836
25123
|
}
|
|
24837
25124
|
//#endregion
|
|
24838
25125
|
//#region server/git/git-workspace.ts
|
|
24839
|
-
var fsp
|
|
25126
|
+
var fsp = fs.promises;
|
|
24840
25127
|
var MERGE_PROBE_TIMEOUT_MS = 3e4;
|
|
24841
25128
|
var REMOTE_REF_PREFIX = "refs/remotes/origin/";
|
|
24842
25129
|
var REMOTE_BRANCH_LISTING_MAX_BUFFER = 67108864;
|
|
@@ -24908,7 +25195,7 @@ function comparableGlimmervoidHomes(glimmervoidHome) {
|
|
|
24908
25195
|
}
|
|
24909
25196
|
async function directoryExists$1(candidatePath) {
|
|
24910
25197
|
try {
|
|
24911
|
-
await fsp
|
|
25198
|
+
await fsp.access(candidatePath);
|
|
24912
25199
|
return true;
|
|
24913
25200
|
} catch {
|
|
24914
25201
|
return false;
|
|
@@ -25068,13 +25355,19 @@ function createGitWorkspace(opts = {}) {
|
|
|
25068
25355
|
}
|
|
25069
25356
|
}
|
|
25070
25357
|
const runInProject = (projectPath) => (args) => run(args, projectPath);
|
|
25071
|
-
async function addWorktreeEntry(runGitInProject, worktreePath, addArgs) {
|
|
25358
|
+
async function addWorktreeEntry(runGitInProject, worktreePath, addArgs, ownership) {
|
|
25072
25359
|
const added = await runGitInProject([
|
|
25073
25360
|
"worktree",
|
|
25074
25361
|
"add",
|
|
25075
25362
|
...addArgs
|
|
25076
25363
|
]);
|
|
25077
|
-
if (!added.ok
|
|
25364
|
+
if (!added.ok) return added;
|
|
25365
|
+
await opts.ownedPaths?.record({
|
|
25366
|
+
path: worktreePath,
|
|
25367
|
+
kind: "git-worktree",
|
|
25368
|
+
...ownership
|
|
25369
|
+
});
|
|
25370
|
+
if (!isWorktreeHiddenFromGuardedSessions(worktreePath, glimmervoidHomes)) return added;
|
|
25078
25371
|
await lockWorktreeEntry(runGitInProject, worktreePath);
|
|
25079
25372
|
return added;
|
|
25080
25373
|
}
|
|
@@ -25100,12 +25393,27 @@ function createGitWorkspace(opts = {}) {
|
|
|
25100
25393
|
"unlock",
|
|
25101
25394
|
worktreePath
|
|
25102
25395
|
]);
|
|
25103
|
-
|
|
25396
|
+
const removed = await runGitInProject([
|
|
25104
25397
|
"worktree",
|
|
25105
25398
|
"remove",
|
|
25106
25399
|
...removeFlags,
|
|
25107
25400
|
worktreePath
|
|
25108
25401
|
]);
|
|
25402
|
+
await forgetRemovedPath(worktreePath);
|
|
25403
|
+
return removed;
|
|
25404
|
+
}
|
|
25405
|
+
async function forgetRemovedPath(ownedPath) {
|
|
25406
|
+
if (await directoryExists$1(ownedPath)) return;
|
|
25407
|
+
await opts.ownedPaths?.forget(ownedPath);
|
|
25408
|
+
}
|
|
25409
|
+
async function recordOwnedFolder({ folderPath, owner }) {
|
|
25410
|
+
await opts.ownedPaths?.record({
|
|
25411
|
+
path: folderPath,
|
|
25412
|
+
owner,
|
|
25413
|
+
kind: "folder",
|
|
25414
|
+
projectPath: null,
|
|
25415
|
+
branch: null
|
|
25416
|
+
});
|
|
25109
25417
|
}
|
|
25110
25418
|
async function pruneWorktreeEntries(runGitInProject) {
|
|
25111
25419
|
const listed = await runGitInProject([
|
|
@@ -25343,7 +25651,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25343
25651
|
}
|
|
25344
25652
|
return null;
|
|
25345
25653
|
}
|
|
25346
|
-
async function createBody({ projectPath, teamId, label, baseBranch = null, configuredIntegrationBranch = null, forkFromHead = false, worktreeBase, shareList }) {
|
|
25654
|
+
async function createBody({ projectPath, owner, teamId, label, baseBranch = null, configuredIntegrationBranch = null, forkFromHead = false, worktreeBase, shareList }) {
|
|
25347
25655
|
const inside = await run(["rev-parse", "--is-inside-work-tree"], projectPath);
|
|
25348
25656
|
if (!inside.ok || inside.out !== "true") return {
|
|
25349
25657
|
cwd: projectPath,
|
|
@@ -25422,7 +25730,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25422
25730
|
branch,
|
|
25423
25731
|
wtDir,
|
|
25424
25732
|
baseSha
|
|
25425
|
-
]
|
|
25733
|
+
], {
|
|
25734
|
+
owner,
|
|
25735
|
+
projectPath,
|
|
25736
|
+
branch
|
|
25737
|
+
});
|
|
25426
25738
|
if (!add.ok) {
|
|
25427
25739
|
try {
|
|
25428
25740
|
fs.rmSync(wtDir, {
|
|
@@ -25470,7 +25782,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25470
25782
|
if (keepTrackableLinks) return;
|
|
25471
25783
|
await refuseTrackableLinks(wtDir, ignored);
|
|
25472
25784
|
}
|
|
25473
|
-
async function ensureWorkspaceMemberBody({ projectPath, wtDir, branch, shareList }) {
|
|
25785
|
+
async function ensureWorkspaceMemberBody({ projectPath, owner, wtDir, branch, shareList }) {
|
|
25474
25786
|
if (!wtDir || !branch) return {
|
|
25475
25787
|
cwd: projectPath,
|
|
25476
25788
|
isGit: false,
|
|
@@ -25521,7 +25833,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25521
25833
|
"--quiet",
|
|
25522
25834
|
`refs/heads/${branch}`
|
|
25523
25835
|
], projectPath)).ok) {
|
|
25524
|
-
const attached = await addWorktreeEntry(runInProject(projectPath), wtDir, [wtDir, branch]
|
|
25836
|
+
const attached = await addWorktreeEntry(runInProject(projectPath), wtDir, [wtDir, branch], {
|
|
25837
|
+
owner,
|
|
25838
|
+
projectPath,
|
|
25839
|
+
branch
|
|
25840
|
+
});
|
|
25525
25841
|
if (!attached.ok) return {
|
|
25526
25842
|
cwd: projectPath,
|
|
25527
25843
|
isGit: false,
|
|
@@ -25572,7 +25888,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25572
25888
|
branch,
|
|
25573
25889
|
wtDir,
|
|
25574
25890
|
baseSha
|
|
25575
|
-
]
|
|
25891
|
+
], {
|
|
25892
|
+
owner,
|
|
25893
|
+
projectPath,
|
|
25894
|
+
branch
|
|
25895
|
+
});
|
|
25576
25896
|
if (!added.ok) return {
|
|
25577
25897
|
cwd: projectPath,
|
|
25578
25898
|
isGit: false,
|
|
@@ -25601,11 +25921,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25601
25921
|
rel
|
|
25602
25922
|
], wtDir)).ok) continue;
|
|
25603
25923
|
const target = path.join(wtDir, rel);
|
|
25604
|
-
const stat = await fsp
|
|
25924
|
+
const stat = await fsp.lstat(target).catch(() => null);
|
|
25605
25925
|
if (!stat || !stat.isSymbolicLink()) continue;
|
|
25606
25926
|
console.warn(`[git-workspace] not sharing ${rel}: git does not ignore it as a link, so a commit in the worktree would carry it (use a slash-free .gitignore entry)`);
|
|
25607
25927
|
try {
|
|
25608
|
-
await fsp
|
|
25928
|
+
await fsp.rm(target, { force: true });
|
|
25609
25929
|
} catch {}
|
|
25610
25930
|
}
|
|
25611
25931
|
}
|
|
@@ -25866,7 +26186,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25866
26186
|
for (const [index, segment] of pathSegments.entries()) {
|
|
25867
26187
|
if (!segment || segment === "." || segment === "..") return false;
|
|
25868
26188
|
checkedPath = path.join(checkedPath, segment);
|
|
25869
|
-
const entryStats = await fsp
|
|
26189
|
+
const entryStats = await fsp.lstat(checkedPath).catch(() => null);
|
|
25870
26190
|
if (!entryStats) return false;
|
|
25871
26191
|
if (index === pathSegments.length - 1) return entryStats.isFile();
|
|
25872
26192
|
if (!entryStats.isDirectory()) return false;
|
|
@@ -26096,7 +26416,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26096
26416
|
], projectPath);
|
|
26097
26417
|
await pruneWorktreeEntries(runInProject(projectPath));
|
|
26098
26418
|
}
|
|
26099
|
-
async function probeWorktreeWork({ projectPath, cwd, branch, integrationBranch, configuredIntegrationBranch }) {
|
|
26419
|
+
async function probeWorktreeWork({ projectPath, cwd, branch, measuredTip = branch, integrationBranch, configuredIntegrationBranch }) {
|
|
26100
26420
|
const { base: resolvedIntegrationBranch, measureRef } = await migrateMarkerBase(projectPath, branch, integrationBranch, configuredIntegrationBranch);
|
|
26101
26421
|
const dirty = await run(WORK_PRESENCE_STATUS_ARGS, cwd);
|
|
26102
26422
|
return {
|
|
@@ -26104,11 +26424,32 @@ function createGitWorkspace(opts = {}) {
|
|
|
26104
26424
|
ahead: !isDirtyResult(dirty) && resolvedIntegrationBranch ? await run([
|
|
26105
26425
|
"rev-list",
|
|
26106
26426
|
"--count",
|
|
26107
|
-
`${measureRef || resolvedIntegrationBranch}..${
|
|
26427
|
+
`${measureRef || resolvedIntegrationBranch}..${measuredTip}`
|
|
26108
26428
|
], projectPath) : null,
|
|
26109
26429
|
integrationBranch: resolvedIntegrationBranch ?? null
|
|
26110
26430
|
};
|
|
26111
26431
|
}
|
|
26432
|
+
async function describeWorkspaceMemberWorkBody({ projectPath, cwd, branch }) {
|
|
26433
|
+
const memberHead = await run([
|
|
26434
|
+
"rev-parse",
|
|
26435
|
+
"--verify",
|
|
26436
|
+
"HEAD"
|
|
26437
|
+
], cwd);
|
|
26438
|
+
if (!memberHead.ok || !memberHead.out) return "its checked-out commit could not be read";
|
|
26439
|
+
const integrationBranch = await detectDefaultBranch({ projectPath });
|
|
26440
|
+
const { dirty, ahead, integrationBranch: comparedBranch } = await probeWorktreeWork({
|
|
26441
|
+
projectPath,
|
|
26442
|
+
cwd,
|
|
26443
|
+
branch,
|
|
26444
|
+
measuredTip: memberHead.out,
|
|
26445
|
+
integrationBranch
|
|
26446
|
+
});
|
|
26447
|
+
if (!dirty.ok) return "its git status could not be read";
|
|
26448
|
+
if (isDirtyResult(dirty)) return "uncommitted changes";
|
|
26449
|
+
if (!ahead?.ok) return "its commits could not be compared with the base branch";
|
|
26450
|
+
if (!ahead.out || ahead.out === "0") return null;
|
|
26451
|
+
return `${ahead.out} ${ahead.out === "1" ? "commit" : "commits"} not on ${comparedBranch}`;
|
|
26452
|
+
}
|
|
26112
26453
|
async function hasUnmergedWorkBody({ projectPath, workspace, integrationBranch, configuredIntegrationBranch }) {
|
|
26113
26454
|
if (!workspace || !workspace.isGit || !workspace.cwd || !workspace.branch) return true;
|
|
26114
26455
|
const { dirty, ahead } = await probeWorktreeWork({
|
|
@@ -26314,7 +26655,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26314
26655
|
for (const rel of shareList ?? []) {
|
|
26315
26656
|
if (!rel || String(rel).includes("..")) continue;
|
|
26316
26657
|
const linkPath = path.join(wtDir, rel);
|
|
26317
|
-
const stat = await fsp
|
|
26658
|
+
const stat = await fsp.lstat(linkPath).catch(() => null);
|
|
26318
26659
|
if (!stat || !stat.isSymbolicLink()) continue;
|
|
26319
26660
|
if (!(await run([
|
|
26320
26661
|
"check-ignore",
|
|
@@ -26322,21 +26663,30 @@ function createGitWorkspace(opts = {}) {
|
|
|
26322
26663
|
"--",
|
|
26323
26664
|
rel
|
|
26324
26665
|
], wtDir)).ok) continue;
|
|
26325
|
-
await fsp
|
|
26666
|
+
await fsp.rm(linkPath, {
|
|
26326
26667
|
recursive: false,
|
|
26327
26668
|
force: true
|
|
26328
|
-
}).catch(() => fsp
|
|
26669
|
+
}).catch(() => fsp.rmdir(linkPath)).catch(() => {});
|
|
26329
26670
|
}
|
|
26330
26671
|
}
|
|
26331
|
-
async function removeWorkspaceMemberBody({ projectPath, cwd, shareList }) {
|
|
26672
|
+
async function removeWorkspaceMemberBody({ projectPath, cwd, shareList, discardWork = false }) {
|
|
26332
26673
|
if (!cwd) return {
|
|
26333
26674
|
ok: false,
|
|
26334
26675
|
out: "",
|
|
26335
26676
|
err: "a workspace member removal needs a path"
|
|
26336
26677
|
};
|
|
26337
26678
|
await removeSharedLinks(cwd, shareList);
|
|
26338
|
-
const removed = await removeWorktreeEntry(runInProject(projectPath), cwd, []);
|
|
26679
|
+
const removed = await removeWorktreeEntry(runInProject(projectPath), cwd, discardWork ? ["--force", "--force"] : []);
|
|
26680
|
+
if (discardWork && await directoryExists$1(cwd)) await fsp.rm(cwd, {
|
|
26681
|
+
recursive: true,
|
|
26682
|
+
force: true
|
|
26683
|
+
});
|
|
26339
26684
|
await pruneWorktreeEntries(runInProject(projectPath));
|
|
26685
|
+
await forgetRemovedPath(cwd);
|
|
26686
|
+
if (discardWork && !await directoryExists$1(cwd)) return {
|
|
26687
|
+
ok: true,
|
|
26688
|
+
out: removed.out
|
|
26689
|
+
};
|
|
26340
26690
|
return removed;
|
|
26341
26691
|
}
|
|
26342
26692
|
async function listWorktreeBranches({ projectPath }) {
|
|
@@ -26457,7 +26807,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26457
26807
|
if (!updated.ok) return updated;
|
|
26458
26808
|
return okResult(originSha);
|
|
26459
26809
|
}
|
|
26460
|
-
async function stageDetachedWorktreeBody({ projectPath, worktreePath, sha, disableHooks, replaceEnv }) {
|
|
26810
|
+
async function stageDetachedWorktreeBody({ projectPath, worktreePath, sha, owner, disableHooks, replaceEnv }) {
|
|
26461
26811
|
if (!worktreePath) return {
|
|
26462
26812
|
ok: false,
|
|
26463
26813
|
out: "",
|
|
@@ -26475,7 +26825,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
26475
26825
|
"--detach",
|
|
26476
26826
|
worktreePath,
|
|
26477
26827
|
sha
|
|
26478
|
-
]
|
|
26828
|
+
], {
|
|
26829
|
+
owner,
|
|
26830
|
+
projectPath,
|
|
26831
|
+
branch: null
|
|
26832
|
+
});
|
|
26479
26833
|
}
|
|
26480
26834
|
async function checkoutDetachedBody({ worktreePath, sha, ...isolation }) {
|
|
26481
26835
|
if (!worktreePath) return {
|
|
@@ -26534,8 +26888,8 @@ function createGitWorkspace(opts = {}) {
|
|
|
26534
26888
|
err: commonDir.err || "the source repository has no git dir"
|
|
26535
26889
|
};
|
|
26536
26890
|
const sourceObjectsDir = path.join(path.resolve(projectPath, commonDir.out), "objects");
|
|
26537
|
-
await fsp
|
|
26538
|
-
await fsp
|
|
26891
|
+
await fsp.mkdir(path.dirname(checkoutPath), { recursive: true });
|
|
26892
|
+
await fsp.mkdir(checkoutPath);
|
|
26539
26893
|
const initialized = await run([
|
|
26540
26894
|
"init",
|
|
26541
26895
|
"--quiet",
|
|
@@ -26543,8 +26897,8 @@ function createGitWorkspace(opts = {}) {
|
|
|
26543
26897
|
], checkoutPath);
|
|
26544
26898
|
if (!initialized.ok) return initialized;
|
|
26545
26899
|
const alternatesPath = path.join(checkoutPath, ".git", "objects", "info", "alternates");
|
|
26546
|
-
await fsp
|
|
26547
|
-
await fsp
|
|
26900
|
+
await fsp.mkdir(path.dirname(alternatesPath), { recursive: true });
|
|
26901
|
+
await fsp.writeFile(alternatesPath, `${sourceObjectsDir}\n`, "utf8");
|
|
26548
26902
|
for (const [ref, refSha] of [[ISOLATED_CHECKOUT_REVIEW_REF, sha], [ISOLATED_CHECKOUT_BASE_REF, baseSha]]) {
|
|
26549
26903
|
const updated = await run([
|
|
26550
26904
|
"update-ref",
|
|
@@ -26730,7 +27084,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26730
27084
|
};
|
|
26731
27085
|
const addedPaths = added.out.split("\0").filter(Boolean);
|
|
26732
27086
|
const collidingPaths = [];
|
|
26733
|
-
for (const addedPath of addedPaths) if (await fsp
|
|
27087
|
+
for (const addedPath of addedPaths) if (await fsp.lstat(path.join(checkoutPath, addedPath)).then(() => true, (error) => !isMissingFileError(error, { includeNotDir: false }))) collidingPaths.push(addedPath);
|
|
26734
27088
|
return {
|
|
26735
27089
|
ok: true,
|
|
26736
27090
|
collidingPaths
|
|
@@ -27066,6 +27420,9 @@ function createGitWorkspace(opts = {}) {
|
|
|
27066
27420
|
pruneWorktrees: serialized(pruneWorktreesBody),
|
|
27067
27421
|
discardSessionBranch: serialized(discardSessionBranchBody),
|
|
27068
27422
|
stageDetachedWorktree: serialized(stageDetachedWorktreeBody),
|
|
27423
|
+
forgetRemovedPath,
|
|
27424
|
+
recordOwnedFolder,
|
|
27425
|
+
describeWorkspaceMemberWork: serialized(describeWorkspaceMemberWorkBody),
|
|
27069
27426
|
checkoutDetached: serialized(checkoutDetachedBody),
|
|
27070
27427
|
stageIsolatedCheckout,
|
|
27071
27428
|
mergeFastForwardTo,
|
|
@@ -27231,6 +27588,7 @@ function createGitWorkspaceSync(opts = {}) {
|
|
|
27231
27588
|
"--force",
|
|
27232
27589
|
cwd
|
|
27233
27590
|
], projectPath);
|
|
27591
|
+
if (!fs.existsSync(cwd)) opts.ownedPaths?.forget(cwd);
|
|
27234
27592
|
}
|
|
27235
27593
|
if (branch) run([
|
|
27236
27594
|
"branch",
|
|
@@ -27289,10 +27647,10 @@ async function populateWorktree(projectPath, wtDir, shareList) {
|
|
|
27289
27647
|
const src = path.join(projectPath, rel);
|
|
27290
27648
|
const dst = path.join(wtDir, rel);
|
|
27291
27649
|
try {
|
|
27292
|
-
const srcStat = await fsp
|
|
27650
|
+
const srcStat = await fsp.stat(src).catch(() => null);
|
|
27293
27651
|
if (!srcStat) continue;
|
|
27294
|
-
if (await fsp
|
|
27295
|
-
await fsp
|
|
27652
|
+
if (await fsp.access(dst).then(() => true, (error) => !isMissingFileError(error, { includeNotDir: false }))) continue;
|
|
27653
|
+
await fsp.mkdir(path.dirname(dst), { recursive: true });
|
|
27296
27654
|
if (srcStat.isDirectory()) {
|
|
27297
27655
|
if (process.platform === "win32") {
|
|
27298
27656
|
await execFileAsync("cmd", [
|
|
@@ -27304,10 +27662,10 @@ async function populateWorktree(projectPath, wtDir, shareList) {
|
|
|
27304
27662
|
]);
|
|
27305
27663
|
continue;
|
|
27306
27664
|
}
|
|
27307
|
-
await fsp
|
|
27665
|
+
await fsp.symlink(src, dst, "dir");
|
|
27308
27666
|
continue;
|
|
27309
27667
|
}
|
|
27310
|
-
await fsp
|
|
27668
|
+
await fsp.copyFile(src, dst);
|
|
27311
27669
|
} catch {}
|
|
27312
27670
|
}
|
|
27313
27671
|
}
|
|
@@ -27460,7 +27818,7 @@ async function describeTeamReviewRole({ checkoutPath, cachedClone, linkedCheckou
|
|
|
27460
27818
|
}
|
|
27461
27819
|
async function linkedCheckoutUnreadablePaths(linkedCheckout) {
|
|
27462
27820
|
if (linkedCheckout === null) return [];
|
|
27463
|
-
return (await
|
|
27821
|
+
return (await fs$1.readdir(linkedCheckout)).filter((entryName) => LINKED_CHECKOUT_UNREADABLE_ENTRY.test(entryName)).map((entryName) => path.join(linkedCheckout, entryName));
|
|
27464
27822
|
}
|
|
27465
27823
|
function teamReviewClaudeArgs(tier, readable) {
|
|
27466
27824
|
const model = tier === "full" ? FULL_MODEL : STAMP_MODEL;
|
|
@@ -27511,11 +27869,11 @@ function hooksPathPinnedSpawnEnv(workDir) {
|
|
|
27511
27869
|
}
|
|
27512
27870
|
async function makeTeamReviewWorkDir(root, prefix) {
|
|
27513
27871
|
const safePrefix = `glimmervoid-wt-${TEAM_REVIEW_LANE_ID}-${prefix}`.replace(/[^\w.-]+/g, "-");
|
|
27514
|
-
await
|
|
27515
|
-
const dir = await
|
|
27872
|
+
await fs$1.mkdir(root, { recursive: true });
|
|
27873
|
+
const dir = await fs$1.mkdtemp(path.join(root, `${safePrefix}-`));
|
|
27516
27874
|
return {
|
|
27517
27875
|
dir,
|
|
27518
|
-
cleanup: () =>
|
|
27876
|
+
cleanup: () => fs$1.rm(dir, {
|
|
27519
27877
|
recursive: true,
|
|
27520
27878
|
force: true
|
|
27521
27879
|
}).catch(() => {})
|
|
@@ -27524,11 +27882,11 @@ async function makeTeamReviewWorkDir(root, prefix) {
|
|
|
27524
27882
|
async function readReviewReport(reportPath, expectedHead, isReReview) {
|
|
27525
27883
|
let report;
|
|
27526
27884
|
try {
|
|
27527
|
-
if ((await
|
|
27885
|
+
if ((await fs$1.stat(reportPath)).size > RESULT_MAX_BYTES$1) return {
|
|
27528
27886
|
ok: false,
|
|
27529
27887
|
reason: "the review report is too large"
|
|
27530
27888
|
};
|
|
27531
|
-
report = await
|
|
27889
|
+
report = await fs$1.readFile(reportPath, "utf8");
|
|
27532
27890
|
} catch {
|
|
27533
27891
|
return {
|
|
27534
27892
|
ok: false,
|
|
@@ -27549,11 +27907,11 @@ async function readReviewReport(reportPath, expectedHead, isReReview) {
|
|
|
27549
27907
|
async function readPostingPlan(postingPath, expectedHead, log) {
|
|
27550
27908
|
let json;
|
|
27551
27909
|
try {
|
|
27552
|
-
if ((await
|
|
27910
|
+
if ((await fs$1.stat(postingPath)).size > RESULT_MAX_BYTES$1) {
|
|
27553
27911
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] the posting plan is too large, rendering from the findings instead`);
|
|
27554
27912
|
return null;
|
|
27555
27913
|
}
|
|
27556
|
-
json = await
|
|
27914
|
+
json = await fs$1.readFile(postingPath, "utf8");
|
|
27557
27915
|
} catch {
|
|
27558
27916
|
return null;
|
|
27559
27917
|
}
|
|
@@ -27566,7 +27924,7 @@ function worktreeDirName(repo, number, suffix) {
|
|
|
27566
27924
|
return `glimmervoid-wt-${TEAM_REVIEW_LANE_ID}-${repo.replace(/[^\w.-]+/g, "-")}-${number}-${suffix}`;
|
|
27567
27925
|
}
|
|
27568
27926
|
async function pathExists(candidatePath) {
|
|
27569
|
-
return
|
|
27927
|
+
return fs$1.lstat(candidatePath).then(() => true, () => false);
|
|
27570
27928
|
}
|
|
27571
27929
|
function isDirectChildOf(root, candidatePath) {
|
|
27572
27930
|
return path.dirname(path.resolve(candidatePath)) === path.resolve(root);
|
|
@@ -27576,10 +27934,10 @@ function isOwnedResumable(record, roots) {
|
|
|
27576
27934
|
}
|
|
27577
27935
|
async function isRealChildDirectoryOrMissing(root, candidatePath) {
|
|
27578
27936
|
if (!isDirectChildOf(root, candidatePath)) return false;
|
|
27579
|
-
const candidateStat = await
|
|
27937
|
+
const candidateStat = await fs$1.lstat(candidatePath).catch((error) => isMissingFileError(error, { includeNotDir: false }) ? "missing" : null);
|
|
27580
27938
|
if (candidateStat === "missing") return true;
|
|
27581
27939
|
if (!candidateStat?.isDirectory()) return false;
|
|
27582
|
-
const realPaths = await Promise.all([
|
|
27940
|
+
const realPaths = await Promise.all([fs$1.realpath(candidatePath), fs$1.realpath(root)]).catch(() => null);
|
|
27583
27941
|
if (!realPaths) return false;
|
|
27584
27942
|
const [realCandidatePath, realRootPath] = realPaths;
|
|
27585
27943
|
return path.dirname(realCandidatePath) === realRootPath;
|
|
@@ -27596,13 +27954,17 @@ async function pruneCachedClone(gitWorkspace, projectPath, log) {
|
|
|
27596
27954
|
if (!pruned.ok) log.warn(`[${TEAM_REVIEW_LANE_ID}] worktree prune failed in ${projectPath}: ${firstLine$1(pruned.err ?? "")}`);
|
|
27597
27955
|
}
|
|
27598
27956
|
async function deleteDirectory(directory, log) {
|
|
27599
|
-
await
|
|
27957
|
+
await fs$1.rm(directory, {
|
|
27600
27958
|
recursive: true,
|
|
27601
27959
|
force: true
|
|
27602
27960
|
}).catch((error) => log.warn(`[${TEAM_REVIEW_LANE_ID}] could not delete ${directory}: ${errorMessage(error)}`));
|
|
27603
27961
|
}
|
|
27962
|
+
async function deleteCheckout(gitWorkspace, worktreePath, log) {
|
|
27963
|
+
await deleteDirectory(worktreePath, log);
|
|
27964
|
+
await gitWorkspace.forgetRemovedPath?.(worktreePath);
|
|
27965
|
+
}
|
|
27604
27966
|
async function isDirectory(candidatePath) {
|
|
27605
|
-
return (await
|
|
27967
|
+
return (await fs$1.stat(candidatePath).catch(() => null))?.isDirectory() === true;
|
|
27606
27968
|
}
|
|
27607
27969
|
async function mainCheckoutOf(checkoutPath) {
|
|
27608
27970
|
const gitDirs = await readGitDirsOfOwnCheckout(checkoutPath).catch(() => null);
|
|
@@ -27614,17 +27976,17 @@ async function mainCheckoutOf(checkoutPath) {
|
|
|
27614
27976
|
async function configuredProjectPathsByRealPath(projects) {
|
|
27615
27977
|
const configuredPathByRealPath = /* @__PURE__ */ new Map();
|
|
27616
27978
|
for (const project of projects) {
|
|
27617
|
-
const projectRealPath = await
|
|
27979
|
+
const projectRealPath = await fs$1.realpath(project.path).catch(() => null);
|
|
27618
27980
|
if (projectRealPath === null || configuredPathByRealPath.has(projectRealPath)) continue;
|
|
27619
27981
|
configuredPathByRealPath.set(projectRealPath, project.path);
|
|
27620
27982
|
}
|
|
27621
27983
|
return configuredPathByRealPath;
|
|
27622
27984
|
}
|
|
27623
27985
|
async function findLocalCheckout({ repo, config, excludedRoots, gitWorkspace, log, requireInstalledModules = true, resolveCheckout }) {
|
|
27624
|
-
const resolvedExcludedRoots = await Promise.all(excludedRoots.map(async (root) =>
|
|
27986
|
+
const resolvedExcludedRoots = await Promise.all(excludedRoots.map(async (root) => fs$1.realpath(root).catch(() => path.resolve(root))));
|
|
27625
27987
|
const candidatePaths = (config.projects ?? []).map((project) => project.path);
|
|
27626
27988
|
for (const root of config.repoRoots ?? []) {
|
|
27627
|
-
const childNames = (await
|
|
27989
|
+
const childNames = (await fs$1.readdir(root, { withFileTypes: true }).catch((error) => {
|
|
27628
27990
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] could not list local repositories in ${root}: ${errorMessage(error)}`);
|
|
27629
27991
|
return [];
|
|
27630
27992
|
})).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
@@ -27632,7 +27994,7 @@ async function findLocalCheckout({ repo, config, excludedRoots, gitWorkspace, lo
|
|
|
27632
27994
|
}
|
|
27633
27995
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
27634
27996
|
for (const candidatePath of candidatePaths) {
|
|
27635
|
-
const checkoutPath = await
|
|
27997
|
+
const checkoutPath = await fs$1.realpath(candidatePath).catch(() => null);
|
|
27636
27998
|
if (!checkoutPath || seenPaths.has(checkoutPath)) continue;
|
|
27637
27999
|
seenPaths.add(checkoutPath);
|
|
27638
28000
|
if (resolvedExcludedRoots.some((root) => isPathInside(root, checkoutPath))) continue;
|
|
@@ -27646,9 +28008,9 @@ async function findLocalCheckout({ repo, config, excludedRoots, gitWorkspace, lo
|
|
|
27646
28008
|
}
|
|
27647
28009
|
async function isLinkedToCheckout(worktreePath, localCheckout) {
|
|
27648
28010
|
const modulesPath = path.join(worktreePath, "node_modules");
|
|
27649
|
-
if (!(await
|
|
27650
|
-
const linkedModules = await
|
|
27651
|
-
const checkoutModules = await
|
|
28011
|
+
if (!(await fs$1.lstat(modulesPath).catch(() => null))?.isSymbolicLink()) return false;
|
|
28012
|
+
const linkedModules = await fs$1.realpath(modulesPath).catch(() => null);
|
|
28013
|
+
const checkoutModules = await fs$1.realpath(path.join(localCheckout, "node_modules")).catch(() => null);
|
|
27652
28014
|
return linkedModules !== null && linkedModules === checkoutModules;
|
|
27653
28015
|
}
|
|
27654
28016
|
async function sweepLeftoverCheckouts({ worktreeRoot, workRoot, keepPaths, repoCache, gitWorkspace, reapProcesses = reapTeamReviewProcesses, log }) {
|
|
@@ -27659,11 +28021,11 @@ async function sweepLeftoverCheckouts({ worktreeRoot, workRoot, keepPaths, repoC
|
|
|
27659
28021
|
log
|
|
27660
28022
|
}).catch((error) => log.warn(`[${TEAM_REVIEW_LANE_ID}] process reap failed: ${errorMessage(error)}`));
|
|
27661
28023
|
for (const root of [worktreeRoot, workRoot]) {
|
|
27662
|
-
const leftoverNames = await
|
|
28024
|
+
const leftoverNames = await fs$1.readdir(root).catch(() => []);
|
|
27663
28025
|
for (const leftoverName of leftoverNames) {
|
|
27664
28026
|
const directory = path.join(root, leftoverName);
|
|
27665
28027
|
if (keepPaths.has(directory)) continue;
|
|
27666
|
-
await
|
|
28028
|
+
await deleteCheckout(gitWorkspace, directory, log);
|
|
27667
28029
|
}
|
|
27668
28030
|
}
|
|
27669
28031
|
const cachedClones = await repoCache.listRepos().catch((error) => {
|
|
@@ -27683,7 +28045,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27683
28045
|
}));
|
|
27684
28046
|
if (removal.ok && !await pathExists(worktreePath)) return;
|
|
27685
28047
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] worktree removal failed for ${worktreePath}, deleting it directly: ${firstLine$1(removal.err ?? "") || "the directory is still present"}`);
|
|
27686
|
-
await
|
|
28048
|
+
await deleteCheckout(gitWorkspace, worktreePath, log);
|
|
27687
28049
|
await pruneCachedClone(gitWorkspace, projectPath, log);
|
|
27688
28050
|
}
|
|
27689
28051
|
async function discoverLocalCheckout(repo) {
|
|
@@ -27730,7 +28092,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27730
28092
|
return null;
|
|
27731
28093
|
}
|
|
27732
28094
|
const scratchObjectsDir = path.join(workDir, REMERGE_OBJECTS_DIRNAME);
|
|
27733
|
-
await
|
|
28095
|
+
await fs$1.mkdir(scratchObjectsDir, { recursive: true });
|
|
27734
28096
|
return {
|
|
27735
28097
|
...isolation.range,
|
|
27736
28098
|
scratchObjectsDir
|
|
@@ -27840,7 +28202,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27840
28202
|
}
|
|
27841
28203
|
};
|
|
27842
28204
|
if (hasWorkDir) await deleteDirectory(args.resume.workDir, log);
|
|
27843
|
-
if (hasWorktree) await
|
|
28205
|
+
if (hasWorktree) await deleteCheckout(gitWorkspace, args.resume.worktreePath, log);
|
|
27844
28206
|
return {
|
|
27845
28207
|
kind: "stopped",
|
|
27846
28208
|
resumable: null
|
|
@@ -27931,7 +28293,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27931
28293
|
projectPath,
|
|
27932
28294
|
worktreePath: args.resume.worktreePath
|
|
27933
28295
|
});
|
|
27934
|
-
if (!projectPath) await
|
|
28296
|
+
if (!projectPath) await deleteCheckout(gitWorkspace, args.resume.worktreePath, log);
|
|
27935
28297
|
}
|
|
27936
28298
|
if (hasWorkDir) await deleteDirectory(args.resume.workDir, log);
|
|
27937
28299
|
sessionId = null;
|
|
@@ -27955,9 +28317,10 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27955
28317
|
projectPath: staged.projectPath,
|
|
27956
28318
|
worktreePath
|
|
27957
28319
|
};
|
|
27958
|
-
await
|
|
28320
|
+
await fs$1.mkdir(worktreeRoot, { recursive: true });
|
|
27959
28321
|
const created = await gitWorkspace.stageDetachedWorktree({
|
|
27960
28322
|
projectPath: staged.projectPath,
|
|
28323
|
+
owner: "lane:team-review",
|
|
27961
28324
|
worktreePath,
|
|
27962
28325
|
sha: detail.headRefOid
|
|
27963
28326
|
});
|
|
@@ -27998,8 +28361,8 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27998
28361
|
priorReview,
|
|
27999
28362
|
newWork
|
|
28000
28363
|
});
|
|
28001
|
-
await
|
|
28002
|
-
await
|
|
28364
|
+
await fs$1.writeFile(path.join(workDir, REVIEW_PROMPT_FILENAME), prompt, "utf8");
|
|
28365
|
+
await fs$1.mkdir(emptyGhConfigDir(workDir), { recursive: true });
|
|
28003
28366
|
reportProgress({
|
|
28004
28367
|
kind: "phase",
|
|
28005
28368
|
phase: "reviewing",
|
|
@@ -28517,7 +28880,7 @@ function createTeamReviewWiring({ config, store, reviewSessions, closeSessionDat
|
|
|
28517
28880
|
scope: "run",
|
|
28518
28881
|
log
|
|
28519
28882
|
}).catch((error) => log.warn(`[${TEAM_REVIEW_LANE_ID}] process reap failed: ${errorMessage(error)}`));
|
|
28520
|
-
await
|
|
28883
|
+
await deleteCheckout(gitWorkspace, record.worktreePath, log);
|
|
28521
28884
|
await deleteDirectory(record.workDir, log);
|
|
28522
28885
|
const cachedClones = await repoCache.listRepos();
|
|
28523
28886
|
for (const projectPath of cachedClones) await pruneCachedClone(gitWorkspace, projectPath, log);
|
|
@@ -28584,7 +28947,7 @@ function createTeamReviewWiring({ config, store, reviewSessions, closeSessionDat
|
|
|
28584
28947
|
worktreeRoot,
|
|
28585
28948
|
workRoot
|
|
28586
28949
|
];
|
|
28587
|
-
const resolvedExcludedRoots = await Promise.all(excludedRoots.map((root) =>
|
|
28950
|
+
const resolvedExcludedRoots = await Promise.all(excludedRoots.map((root) => fs$1.realpath(root).catch(() => path.resolve(root))));
|
|
28588
28951
|
const configuredPathByRealPath = await configuredProjectPathsByRealPath(config.projects ?? []);
|
|
28589
28952
|
return findLocalCheckout({
|
|
28590
28953
|
repo,
|
|
@@ -28878,18 +29241,18 @@ function worktreeOwnerRepo(gitFileText) {
|
|
|
28878
29241
|
async function readJsonFile$1(filePath) {
|
|
28879
29242
|
return loadedJsonValue(await loadJsonStateFile({
|
|
28880
29243
|
filePath,
|
|
28881
|
-
fsPromises:
|
|
29244
|
+
fsPromises: fs$1,
|
|
28882
29245
|
parse: (raw) => raw,
|
|
28883
29246
|
quarantine: false
|
|
28884
29247
|
}));
|
|
28885
29248
|
}
|
|
28886
29249
|
async function jsonFileNames(directory) {
|
|
28887
|
-
return (await
|
|
29250
|
+
return (await fs$1.readdir(directory, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort();
|
|
28888
29251
|
}
|
|
28889
29252
|
async function readBoundedText(filePath) {
|
|
28890
|
-
const stat = await
|
|
29253
|
+
const stat = await fs$1.stat(filePath).catch(() => null);
|
|
28891
29254
|
if (!stat?.isFile() || stat.size > RESULT_MAX_BYTES) return null;
|
|
28892
|
-
return
|
|
29255
|
+
return fs$1.readFile(filePath, "utf8").catch(() => null);
|
|
28893
29256
|
}
|
|
28894
29257
|
function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast, store, repoCache, repoCacheRoot, gitWorkspace, spawnSubject, spawnJudge, credentials, claudeCommand, runArmCommand = runArmCommandSafely, guardrailsRefusal = allowGuardedSpawn, baseEnv = process.env, now = () => Date.now(), randomSuffix = () => randomBytes(4).toString("hex"), log = console }) {
|
|
28895
29258
|
let activeRun = null;
|
|
@@ -28898,7 +29261,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
28898
29261
|
let leftoverSweep = Promise.resolve();
|
|
28899
29262
|
const suiteDir = (suiteId) => path.join(benchmarksRoot, suiteId);
|
|
28900
29263
|
async function removeDirectoryOrWarn(directory) {
|
|
28901
|
-
await
|
|
29264
|
+
await fs$1.rm(directory, {
|
|
28902
29265
|
recursive: true,
|
|
28903
29266
|
force: true
|
|
28904
29267
|
}).catch((error) => log.warn(`[${BENCHMARK_LANE_ID}] could not remove ${directory}: ${firstLine(errorMessage(error))}`));
|
|
@@ -28997,7 +29360,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
28997
29360
|
return refreshed;
|
|
28998
29361
|
}
|
|
28999
29362
|
async function summarizeAndBroadcast() {
|
|
29000
|
-
const suiteIds = (await
|
|
29363
|
+
const suiteIds = (await fs$1.readdir(benchmarksRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && BenchmarkId.safeParse(entry.name).success).map((entry) => entry.name).sort();
|
|
29001
29364
|
const summaries = [];
|
|
29002
29365
|
for (const suiteId of suiteIds) summaries.push(await summarizeSuite(suiteId).catch((error) => unreadableSuiteSummary(suiteId, errorMessage(error))));
|
|
29003
29366
|
suites = summaries;
|
|
@@ -29054,7 +29417,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29054
29417
|
error: mined.reason
|
|
29055
29418
|
};
|
|
29056
29419
|
const candidatesDir = path.join(suiteDir(suiteId), "candidates");
|
|
29057
|
-
await
|
|
29420
|
+
await fs$1.mkdir(candidatesDir, { recursive: true });
|
|
29058
29421
|
for (const outcome of mined.outcomes) {
|
|
29059
29422
|
if (!outcome.candidate) {
|
|
29060
29423
|
log.warn(`[${BENCHMARK_LANE_ID}] ${caseSource.repo}#${outcome.number} not mined: ${outcome.refusal}`);
|
|
@@ -29150,7 +29513,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29150
29513
|
reason: "the reviewed commit could not be fetched, or the base is not its ancestor"
|
|
29151
29514
|
};
|
|
29152
29515
|
const checkoutPath = path.join(staged.caseDir, "checkout");
|
|
29153
|
-
const cleared = await
|
|
29516
|
+
const cleared = await fs$1.rm(checkoutPath, {
|
|
29154
29517
|
recursive: true,
|
|
29155
29518
|
force: true
|
|
29156
29519
|
}).then(() => null, (error) => errorMessage(error));
|
|
@@ -29171,7 +29534,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29171
29534
|
reason: `could not stage a checkout: ${firstLine(created.err ?? "") || "the isolated checkout failed"}`
|
|
29172
29535
|
};
|
|
29173
29536
|
}
|
|
29174
|
-
const sharedObjectsPath = firstLine(await
|
|
29537
|
+
const sharedObjectsPath = firstLine(await fs$1.readFile(path.join(checkoutPath, ".git", "objects", "info", "alternates"), "utf8").catch(() => ""));
|
|
29175
29538
|
if (!sharedObjectsPath) {
|
|
29176
29539
|
await removeDirectoryOrWarn(checkoutPath);
|
|
29177
29540
|
return {
|
|
@@ -29189,7 +29552,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29189
29552
|
if (existing) return existing;
|
|
29190
29553
|
const caseDir = path.join(runDir, benchmarkCase.id);
|
|
29191
29554
|
const workDir = path.join(caseDir, "work");
|
|
29192
|
-
await
|
|
29555
|
+
await fs$1.mkdir(workDir, { recursive: true });
|
|
29193
29556
|
const staged = {
|
|
29194
29557
|
checkoutPath: null,
|
|
29195
29558
|
sharedObjectsPath: null,
|
|
@@ -29272,7 +29635,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29272
29635
|
},
|
|
29273
29636
|
judgeSeed: (cell) => cell.index + 1,
|
|
29274
29637
|
async persist(run) {
|
|
29275
|
-
await
|
|
29638
|
+
await fs$1.mkdir(runsDir, { recursive: true });
|
|
29276
29639
|
await writeJsonAtomic(path.join(runsDir, `${runId}.json`), redactedRun(run, usedTokens));
|
|
29277
29640
|
},
|
|
29278
29641
|
reportProgress
|
|
@@ -29299,10 +29662,10 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29299
29662
|
};
|
|
29300
29663
|
usedTokens.add(token.token);
|
|
29301
29664
|
const resultPath = path.join(staged.workDir, SUBJECT_RESULT_FILENAME);
|
|
29302
|
-
await
|
|
29303
|
-
await
|
|
29665
|
+
await fs$1.rm(resultPath, { force: true });
|
|
29666
|
+
await fs$1.writeFile(path.join(staged.workDir, SUBJECT_PROMPT_FILENAME), prompt, "utf8");
|
|
29304
29667
|
const baseSpawnEnv = hooksPathPinnedSpawnEnv(staged.workDir);
|
|
29305
|
-
await
|
|
29668
|
+
await fs$1.mkdir(baseSpawnEnv.GH_CONFIG_DIR, { recursive: true });
|
|
29306
29669
|
const subjectController = new AbortController();
|
|
29307
29670
|
const stopSubject = () => subjectController.abort();
|
|
29308
29671
|
let hasTimedOut = false;
|
|
@@ -29364,8 +29727,8 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29364
29727
|
};
|
|
29365
29728
|
}
|
|
29366
29729
|
async function runJudgeSession({ cell, prompt, model, signal, runDir }) {
|
|
29367
|
-
await
|
|
29368
|
-
const judgeDir = await
|
|
29730
|
+
await fs$1.mkdir(runDir, { recursive: true });
|
|
29731
|
+
const judgeDir = await fs$1.mkdtemp(path.join(runDir, `judge-${cell.index}-`));
|
|
29369
29732
|
const resultPath = path.join(judgeDir, JUDGE_RESULT_FILENAME);
|
|
29370
29733
|
try {
|
|
29371
29734
|
await spawnJudge({
|
|
@@ -29394,7 +29757,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29394
29757
|
costUsd: null
|
|
29395
29758
|
};
|
|
29396
29759
|
} finally {
|
|
29397
|
-
await
|
|
29760
|
+
await fs$1.rm(judgeDir, {
|
|
29398
29761
|
recursive: true,
|
|
29399
29762
|
force: true
|
|
29400
29763
|
}).catch(() => {});
|
|
@@ -29480,7 +29843,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29480
29843
|
return respond(outcome);
|
|
29481
29844
|
}
|
|
29482
29845
|
async function removeLeftoverWorktree(worktreePath) {
|
|
29483
|
-
const projectPath = worktreeOwnerRepo(await
|
|
29846
|
+
const projectPath = worktreeOwnerRepo(await fs$1.readFile(path.join(worktreePath, ".git"), "utf8").catch(() => ""));
|
|
29484
29847
|
if (projectPath) {
|
|
29485
29848
|
const removal = await gitWorkspace.removeWorktreeByPath({
|
|
29486
29849
|
projectPath,
|
|
@@ -29507,7 +29870,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29507
29870
|
}
|
|
29508
29871
|
async function sweepSuiteLeftovers(suiteId) {
|
|
29509
29872
|
const worktreesDir = path.join(suiteDir(suiteId), "worktrees");
|
|
29510
|
-
const worktreeEntries = await
|
|
29873
|
+
const worktreeEntries = await fs$1.readdir(worktreesDir, { withFileTypes: true }).catch(() => []);
|
|
29511
29874
|
for (const entry of worktreeEntries) if (entry.isDirectory()) await removeLeftoverWorktree(path.join(worktreesDir, entry.name));
|
|
29512
29875
|
await removeDirectoryOrWarn(worktreesDir);
|
|
29513
29876
|
await removeDirectoryOrWarn(path.join(suiteDir(suiteId), "work"));
|
|
@@ -29516,7 +29879,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29516
29879
|
async function sweepAllLeftovers() {
|
|
29517
29880
|
if (activeRun) return;
|
|
29518
29881
|
await removeDirectoryOrWarn(runsRoot);
|
|
29519
|
-
const suiteIds = (await
|
|
29882
|
+
const suiteIds = (await fs$1.readdir(benchmarksRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && BenchmarkId.safeParse(entry.name).success).map((entry) => entry.name);
|
|
29520
29883
|
for (const suiteId of suiteIds) await sweepSuiteLeftovers(suiteId).catch((error) => log.warn(`[${BENCHMARK_LANE_ID}] could not sweep ${suiteId}: ${errorMessage(error)}`));
|
|
29521
29884
|
}
|
|
29522
29885
|
function sweepLeftovers() {
|
|
@@ -30662,7 +31025,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30662
31025
|
var DEFAULT_INTERVAL_MS$1 = 216e5;
|
|
30663
31026
|
var BRANCH_GC_FETCH_TIMEOUT_MS = 6e4;
|
|
30664
31027
|
function createBranchGcPoller(deps) {
|
|
30665
|
-
const { gitWorkspace, getConfig, liveSessionIds, liveWorktreePaths, staleDays = 14, deleteUnmerged = false, prefixes = DEFAULT_BRANCH_GC_PREFIXES, pruneWorktrees = true, dryRun = false, intervalMs = DEFAULT_INTERVAL_MS$1, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, log = console, decisionTrace = () => {}, onTickComplete = () => {}, now = () => Date.now(), statProjectPath = (projectPath) =>
|
|
31028
|
+
const { gitWorkspace, getConfig, liveSessionIds, liveWorktreePaths, staleDays = 14, deleteUnmerged = false, prefixes = DEFAULT_BRANCH_GC_PREFIXES, pruneWorktrees = true, dryRun = false, intervalMs = DEFAULT_INTERVAL_MS$1, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, log = console, decisionTrace = () => {}, onTickComplete = () => {}, now = () => Date.now(), statProjectPath = (projectPath) => fs$1.stat(projectPath) } = deps;
|
|
30666
31029
|
const skippedProjectPaths = /* @__PURE__ */ new Set();
|
|
30667
31030
|
async function callGit(method, args) {
|
|
30668
31031
|
try {
|
|
@@ -33403,7 +33766,7 @@ function classifyGitStatusFailure(error) {
|
|
|
33403
33766
|
var DEFAULT_MAX_REPOS = 16;
|
|
33404
33767
|
var DEFAULT_GIT_TIMEOUT_MS = 15e3;
|
|
33405
33768
|
var MAX_GIT_BUFFER_BYTES = 8388608;
|
|
33406
|
-
function createGitIngest({ publish, sourceConfig = {}, reposProvider = null, logger = console, execFileFn = execFileAsync, createWatch = createWatchDebounce, canonicalize = canonicalizePath, statRepoDir = (dir) =>
|
|
33769
|
+
function createGitIngest({ publish, sourceConfig = {}, reposProvider = null, logger = console, execFileFn = execFileAsync, createWatch = createWatchDebounce, canonicalize = canonicalizePath, statRepoDir = (dir) => fs$1.stat(dir), nowFn = Date.now, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, maxRepos = DEFAULT_MAX_REPOS, gitTimeoutMs = DEFAULT_GIT_TIMEOUT_MS, gitPath = "git" } = {}) {
|
|
33407
33770
|
if (typeof publish !== "function") throw new Error("createGitIngest requires publish");
|
|
33408
33771
|
const publishEvent = publish;
|
|
33409
33772
|
const settleMs = positiveIntOr(sourceConfig.debounceMs, DEFAULT_DEBOUNCE_MS);
|
|
@@ -34826,10 +35189,10 @@ function hasAutoSessionName(session) {
|
|
|
34826
35189
|
return isAutoNameOf(session.name, path.basename(session.path));
|
|
34827
35190
|
}
|
|
34828
35191
|
async function refineTaskTitle(request, model, spawnLane, signal) {
|
|
34829
|
-
const workDir = await
|
|
35192
|
+
const workDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-task-title-"));
|
|
34830
35193
|
const resultPath = path.join(workDir, RESULT_FILE$1);
|
|
34831
35194
|
try {
|
|
34832
|
-
await
|
|
35195
|
+
await fs$1.writeFile(path.join(workDir, PROMPT_FILE$1), buildTaskTitleRefinementPrompt({
|
|
34833
35196
|
currentTitle: request.currentTitle,
|
|
34834
35197
|
recentPrompts: request.recentPrompts,
|
|
34835
35198
|
resultPath,
|
|
@@ -34852,7 +35215,7 @@ async function refineTaskTitle(request, model, spawnLane, signal) {
|
|
|
34852
35215
|
proposedSessionName: request.wantsSessionName ? parseRefinedSessionName(laneResult) : null
|
|
34853
35216
|
};
|
|
34854
35217
|
} finally {
|
|
34855
|
-
await
|
|
35218
|
+
await fs$1.rm(workDir, {
|
|
34856
35219
|
recursive: true,
|
|
34857
35220
|
force: true
|
|
34858
35221
|
});
|
|
@@ -37939,6 +38302,7 @@ function createRadarWiring({ config, investigationSessions, closeSessionDataClie
|
|
|
37939
38302
|
const issueId = safeIssueId(issue.issueId);
|
|
37940
38303
|
const workspace = await Promise.resolve(gitWorkspace.create({
|
|
37941
38304
|
projectPath: repoPath,
|
|
38305
|
+
owner: "lane:radar-fix",
|
|
37942
38306
|
teamId: "radar-fix",
|
|
37943
38307
|
label: `${safeIssueId(projectId)}-${issueId}-${newFixDiscriminator()}`,
|
|
37944
38308
|
worktreeBase: worktreeBaseFor(repoPath),
|
|
@@ -38446,9 +38810,13 @@ function createMyPrsPoller(dependencies) {
|
|
|
38446
38810
|
});
|
|
38447
38811
|
async function failedOutcome(errorText) {
|
|
38448
38812
|
const rateLimitWaitMs = await store.rateLimitWaitMs(now(), MY_PRS_RATE_LIMIT_RESOURCES) ?? secondaryRateLimitWaitMs(errorText);
|
|
38449
|
-
return rateLimitWaitMs === null ? {
|
|
38813
|
+
return rateLimitWaitMs === null ? {
|
|
38450
38814
|
failed: true,
|
|
38451
|
-
|
|
38815
|
+
error: errorText
|
|
38816
|
+
} : {
|
|
38817
|
+
failed: true,
|
|
38818
|
+
retryAfterMs: rateLimitWaitMs,
|
|
38819
|
+
error: errorText
|
|
38452
38820
|
};
|
|
38453
38821
|
}
|
|
38454
38822
|
async function runTick() {
|
|
@@ -38775,10 +39143,11 @@ function createMyPrsPoller(dependencies) {
|
|
|
38775
39143
|
error: "Reviews polling is stopped."
|
|
38776
39144
|
};
|
|
38777
39145
|
registerWatches();
|
|
38778
|
-
|
|
38779
|
-
|
|
38780
|
-
|
|
38781
|
-
|
|
39146
|
+
return loop.refresh(async () => {
|
|
39147
|
+
const refreshed = await store.refreshWatches(watchNames);
|
|
39148
|
+
rebuild();
|
|
39149
|
+
return refreshed;
|
|
39150
|
+
});
|
|
38782
39151
|
}
|
|
38783
39152
|
return {
|
|
38784
39153
|
start,
|
|
@@ -38801,10 +39170,10 @@ function defaultAgentSearchRoots() {
|
|
|
38801
39170
|
return roots.filter((root, index) => roots.indexOf(root) === index);
|
|
38802
39171
|
}
|
|
38803
39172
|
async function readEntryNames(directory) {
|
|
38804
|
-
return
|
|
39173
|
+
return fs$1.readdir(directory).catch(() => []);
|
|
38805
39174
|
}
|
|
38806
39175
|
async function ownedSocketAt(socketPath, ownerUid) {
|
|
38807
|
-
const stats = await
|
|
39176
|
+
const stats = await fs$1.lstat(socketPath).catch(() => null);
|
|
38808
39177
|
if (!stats?.isSocket()) return null;
|
|
38809
39178
|
if (ownerUid !== null && stats.uid !== ownerUid) return null;
|
|
38810
39179
|
return {
|
|
@@ -38852,12 +39221,12 @@ function createSshSigner({ env = process.env, agentSearchRoots = defaultAgentSea
|
|
|
38852
39221
|
ok: false,
|
|
38853
39222
|
error: NO_AGENT_HOLDS_KEY
|
|
38854
39223
|
};
|
|
38855
|
-
const workDir = await
|
|
39224
|
+
const workDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-sign-"));
|
|
38856
39225
|
try {
|
|
38857
39226
|
const keyPath = path.join(workDir, "signing-key.pub");
|
|
38858
39227
|
const payloadPath = path.join(workDir, "payload");
|
|
38859
|
-
await
|
|
38860
|
-
await
|
|
39228
|
+
await fs$1.writeFile(keyPath, `${formatSshPublicKey(publicKey)}\n`, { mode: 384 });
|
|
39229
|
+
await fs$1.writeFile(payloadPath, payload, { mode: 384 });
|
|
38861
39230
|
await execFileAsync("ssh-keygen", [
|
|
38862
39231
|
"-Y",
|
|
38863
39232
|
"sign",
|
|
@@ -38877,7 +39246,7 @@ function createSshSigner({ env = process.env, agentSearchRoots = defaultAgentSea
|
|
|
38877
39246
|
});
|
|
38878
39247
|
return {
|
|
38879
39248
|
ok: true,
|
|
38880
|
-
signature: await
|
|
39249
|
+
signature: await fs$1.readFile(`${payloadPath}.sig`, "utf8")
|
|
38881
39250
|
};
|
|
38882
39251
|
} catch (error) {
|
|
38883
39252
|
const stderr = error?.stderr;
|
|
@@ -38886,7 +39255,7 @@ function createSshSigner({ env = process.env, agentSearchRoots = defaultAgentSea
|
|
|
38886
39255
|
error: `ssh-keygen could not sign: ${typeof stderr === "string" && stderr.trim() ? stderr.trim() : errorMessage(error)}`
|
|
38887
39256
|
};
|
|
38888
39257
|
} finally {
|
|
38889
|
-
await
|
|
39258
|
+
await fs$1.rm(workDir, {
|
|
38890
39259
|
recursive: true,
|
|
38891
39260
|
force: true
|
|
38892
39261
|
});
|
|
@@ -38901,7 +39270,7 @@ async function readSigningKeySetting(setting, cwd) {
|
|
|
38901
39270
|
const literalKey = parseSshPublicKey(setting);
|
|
38902
39271
|
if (literalKey) return literalKey;
|
|
38903
39272
|
const keyPath = path.resolve(cwd, setting.replace(/^~(?=\/|$)/, os.homedir()));
|
|
38904
|
-
return parseSshPublicKey(await
|
|
39273
|
+
return parseSshPublicKey(await fs$1.readFile(keyPath, "utf8").catch(() => ""));
|
|
38905
39274
|
}
|
|
38906
39275
|
function readTextOrNull(filePath) {
|
|
38907
39276
|
try {
|
|
@@ -39089,8 +39458,8 @@ function createGuardedPrStaging({ spawnSession, repoCache, describeRole = guarde
|
|
|
39089
39458
|
if (!staged.ok) return { error: `could not stage the checkout ${staged.err}` };
|
|
39090
39459
|
}
|
|
39091
39460
|
const excludePath = path.join(checkoutPath, ".git", "info", "exclude");
|
|
39092
|
-
await
|
|
39093
|
-
await
|
|
39461
|
+
await fs$1.mkdir(path.dirname(excludePath), { recursive: true });
|
|
39462
|
+
await fs$1.appendFile(excludePath, keepMergeableExcludeLines(), "utf8");
|
|
39094
39463
|
return {
|
|
39095
39464
|
projectPath,
|
|
39096
39465
|
baseSha: baseSha.data
|
|
@@ -39283,7 +39652,7 @@ function createMyPrMergeabilityFix({ spawnSession, repoCache, workRoot, makeWork
|
|
|
39283
39652
|
};
|
|
39284
39653
|
}
|
|
39285
39654
|
async function fetchSessionCommits(staged, checkoutPath, headSha, handoffRef, signal) {
|
|
39286
|
-
const bundleDir = await
|
|
39655
|
+
const bundleDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-keep-mergeable-"));
|
|
39287
39656
|
try {
|
|
39288
39657
|
const bundlePath = path.join(bundleDir, "session.bundle");
|
|
39289
39658
|
const bundled = await runGit([
|
|
@@ -39305,7 +39674,7 @@ function createMyPrMergeabilityFix({ spawnSession, repoCache, workRoot, makeWork
|
|
|
39305
39674
|
`+${KEEP_MERGEABLE_WORK_BRANCH_REF}:${handoffRef}`
|
|
39306
39675
|
], staged.projectPath, signal);
|
|
39307
39676
|
} finally {
|
|
39308
|
-
await
|
|
39677
|
+
await fs$1.rm(bundleDir, {
|
|
39309
39678
|
recursive: true,
|
|
39310
39679
|
force: true
|
|
39311
39680
|
});
|
|
@@ -39390,8 +39759,8 @@ function createMyPrMergeabilityFix({ spawnSession, repoCache, workRoot, makeWork
|
|
|
39390
39759
|
if ("error" in staged) return reportRepairFailure(pr, `not started: ${staged.error}`);
|
|
39391
39760
|
projectPath = staged.projectPath;
|
|
39392
39761
|
if (signal.aborted) return stopped;
|
|
39393
|
-
await
|
|
39394
|
-
await
|
|
39762
|
+
await fs$1.writeFile(path.join(workDir.dir, MY_PRS_FIX_PROMPT_FILENAME), keepMergeablePrompt(pr), "utf8");
|
|
39763
|
+
await fs$1.mkdir(emptyGhConfigDir(workDir.dir), { recursive: true });
|
|
39395
39764
|
const outcome = await runSession({
|
|
39396
39765
|
idPrefix: MY_PRS_LANE_ID,
|
|
39397
39766
|
name: `Keep mergeable ${pr.key}`,
|
|
@@ -40230,12 +40599,16 @@ function createWorkflowsPoller(dependencies) {
|
|
|
40230
40599
|
return stateLoad;
|
|
40231
40600
|
}
|
|
40232
40601
|
function publishStatus() {
|
|
40602
|
+
const { nextAttemptAt, retry, isRefreshing, refreshNotice } = loop.scheduleStatus();
|
|
40233
40603
|
onTickComplete({
|
|
40234
40604
|
type: "workflows-status",
|
|
40235
40605
|
ts: now(),
|
|
40236
40606
|
configured: true,
|
|
40237
40607
|
error: pollingError,
|
|
40238
|
-
|
|
40608
|
+
nextAttemptAt,
|
|
40609
|
+
retry,
|
|
40610
|
+
isRefreshing,
|
|
40611
|
+
refreshNotice
|
|
40239
40612
|
});
|
|
40240
40613
|
}
|
|
40241
40614
|
const loop = createTickLoop({
|
|
@@ -40415,8 +40788,8 @@ function createWorkflowSpawn({ staging, workRoot, makeWorkDir = makeTeamReviewWo
|
|
|
40415
40788
|
const staged = await staging.stageCheckout(pr, workDir.dir, signal);
|
|
40416
40789
|
if (signal.aborted) return;
|
|
40417
40790
|
if ("error" in staged) return warn(`not started: ${staged.error}`);
|
|
40418
|
-
await
|
|
40419
|
-
await
|
|
40791
|
+
await fs$1.writeFile(path.join(workDir.dir, WORKFLOW_PROMPT_FILENAME), workflowSpawnPrompt(action.promptTemplate, event, MY_PRS_FIX_CHECKOUT_DIRNAME), "utf8");
|
|
40792
|
+
await fs$1.mkdir(emptyGhConfigDir(workDir.dir), { recursive: true });
|
|
40420
40793
|
if (await staging.runSession({
|
|
40421
40794
|
idPrefix: "workflows",
|
|
40422
40795
|
name: workflowSessionName({
|
|
@@ -40507,15 +40880,28 @@ function createWorkflowsWiring({ config, notificationManager, spawnSession, log
|
|
|
40507
40880
|
}
|
|
40508
40881
|
//#endregion
|
|
40509
40882
|
//#region server/github/github-clock.ts
|
|
40510
|
-
function createGithubClock({ baseIntervalMs, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, log = console }) {
|
|
40883
|
+
function createGithubClock({ baseIntervalMs, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, log = console, githubStatus }) {
|
|
40511
40884
|
const subscriptions = /* @__PURE__ */ new Set();
|
|
40512
40885
|
let timer = null;
|
|
40513
40886
|
let isSweeping = false;
|
|
40514
|
-
let
|
|
40515
|
-
|
|
40516
|
-
|
|
40517
|
-
|
|
40518
|
-
return
|
|
40887
|
+
let isExclusiveRunning = false;
|
|
40888
|
+
const priorityJobs = [];
|
|
40889
|
+
const backgroundJobs = [];
|
|
40890
|
+
function runNextExclusiveJob() {
|
|
40891
|
+
if (isExclusiveRunning) return;
|
|
40892
|
+
const job = priorityJobs.shift() ?? backgroundJobs.shift();
|
|
40893
|
+
if (!job) return;
|
|
40894
|
+
isExclusiveRunning = true;
|
|
40895
|
+
job().finally(() => {
|
|
40896
|
+
isExclusiveRunning = false;
|
|
40897
|
+
runNextExclusiveJob();
|
|
40898
|
+
});
|
|
40899
|
+
}
|
|
40900
|
+
function exclusive(work, { isPriority = false } = {}) {
|
|
40901
|
+
return new Promise((resolve, reject) => {
|
|
40902
|
+
(isPriority ? priorityJobs : backgroundJobs).push(() => Promise.resolve().then(work).then(resolve, reject));
|
|
40903
|
+
runNextExclusiveJob();
|
|
40904
|
+
});
|
|
40519
40905
|
}
|
|
40520
40906
|
async function sweep() {
|
|
40521
40907
|
if (isSweeping) return;
|
|
@@ -40557,7 +40943,56 @@ function createGithubClock({ baseIntervalMs, setIntervalFn = DEFAULT_TIMER_FNS.s
|
|
|
40557
40943
|
return {
|
|
40558
40944
|
schedule,
|
|
40559
40945
|
exclusive,
|
|
40560
|
-
sweep
|
|
40946
|
+
sweep,
|
|
40947
|
+
githubStatus
|
|
40948
|
+
};
|
|
40949
|
+
}
|
|
40950
|
+
//#endregion
|
|
40951
|
+
//#region server/github/core/github-status-core.ts
|
|
40952
|
+
var GITHUB_STATUS_CACHE_MS = 3e5;
|
|
40953
|
+
var GithubStatusSummary = z.object({ incidents: z.array(z.object({
|
|
40954
|
+
name: z.string(),
|
|
40955
|
+
status: z.string(),
|
|
40956
|
+
updated_at: z.iso.datetime({ offset: true })
|
|
40957
|
+
})) });
|
|
40958
|
+
function openIncidentName(body) {
|
|
40959
|
+
const summary = GithubStatusSummary.safeParse(body);
|
|
40960
|
+
if (!summary.success) return null;
|
|
40961
|
+
const openIncidents = summary.data.incidents.filter((incident) => incident.status !== "resolved" && incident.status !== "postmortem");
|
|
40962
|
+
openIncidents.sort((first, second) => Date.parse(second.updated_at) - Date.parse(first.updated_at));
|
|
40963
|
+
return openIncidents[0]?.name ?? null;
|
|
40964
|
+
}
|
|
40965
|
+
function isStatusReadDue(lastReadAtMs, nowMs) {
|
|
40966
|
+
return lastReadAtMs === null || nowMs - lastReadAtMs >= GITHUB_STATUS_CACHE_MS;
|
|
40967
|
+
}
|
|
40968
|
+
//#endregion
|
|
40969
|
+
//#region server/github/github-status.ts
|
|
40970
|
+
function createGithubStatus({ fetchFn = fetch, now = Date.now, log }) {
|
|
40971
|
+
let lastReadAtMs = null;
|
|
40972
|
+
let cachedIncident = null;
|
|
40973
|
+
let hasWarned = false;
|
|
40974
|
+
async function readStatus() {
|
|
40975
|
+
try {
|
|
40976
|
+
const response = await fetchFn("https://www.githubstatus.com/api/v2/summary.json", { signal: AbortSignal.timeout(5e3) });
|
|
40977
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
40978
|
+
cachedIncident = openIncidentName(GithubStatusSummary.parse(await response.json()));
|
|
40979
|
+
hasWarned = false;
|
|
40980
|
+
} catch (error) {
|
|
40981
|
+
cachedIncident = null;
|
|
40982
|
+
if (hasWarned) return;
|
|
40983
|
+
hasWarned = true;
|
|
40984
|
+
log.warn(`[github-status] read failed: ${errorMessage(error)}`);
|
|
40985
|
+
}
|
|
40986
|
+
}
|
|
40987
|
+
function noteTransientFailure() {
|
|
40988
|
+
const nowMs = now();
|
|
40989
|
+
if (!isStatusReadDue(lastReadAtMs, nowMs)) return;
|
|
40990
|
+
lastReadAtMs = nowMs;
|
|
40991
|
+
readStatus();
|
|
40992
|
+
}
|
|
40993
|
+
return {
|
|
40994
|
+
noteTransientFailure,
|
|
40995
|
+
incident: () => cachedIncident
|
|
40561
40996
|
};
|
|
40562
40997
|
}
|
|
40563
40998
|
//#endregion
|
|
@@ -41611,7 +42046,7 @@ function isFresh(fetchedAt, nowMs) {
|
|
|
41611
42046
|
if (!Number.isFinite(fetchedMs)) return false;
|
|
41612
42047
|
return nowMs - fetchedMs < CACHE_TTL_MS;
|
|
41613
42048
|
}
|
|
41614
|
-
async function loadPricing({ fetchEnabled, fsPromises =
|
|
42049
|
+
async function loadPricing({ fetchEnabled, fsPromises = fs$1, fetchFn = globalThis.fetch, cachePath = path.join(glimmervoidHomeDir$1(), "litellm-pricing.json"), nowFn = Date.now, timeoutMs = 15e3, logger = null } = {}) {
|
|
41615
42050
|
if (!fetchEnabled) return {
|
|
41616
42051
|
table: normalizePricingTable(claude_pricing_default),
|
|
41617
42052
|
source: "snapshot",
|
|
@@ -42310,7 +42745,7 @@ async function walkSourceFiles(roots, fsPromises, laneLog) {
|
|
|
42310
42745
|
};
|
|
42311
42746
|
}
|
|
42312
42747
|
function createUsageScanner(deps = {}) {
|
|
42313
|
-
const { fsPromises =
|
|
42748
|
+
const { fsPromises = fs$1, env = process.env, homeDir = os.homedir(), nowFn = Date.now, pricingTable, aliases = {}, costMode = "auto", blockHours = 5, retainDays = 90, extraProjectsDirs = [], vendors = {
|
|
42314
42749
|
codex: true,
|
|
42315
42750
|
grok: true
|
|
42316
42751
|
}, warehousePath = null, warehouseRetainDays = 365, budget: budgetDep = null, laneMap = null, logger = noopLogger, byteBudget = DEFAULT_BYTE_BUDGET, chunkSize = DEFAULT_CHUNK_SIZE, yieldNowFn = yieldNow } = deps;
|
|
@@ -43144,7 +43579,7 @@ function formatBudgetUsd(value) {
|
|
|
43144
43579
|
function budgetAlertText({ scope, threshold, spentUsd, budgetUsd }) {
|
|
43145
43580
|
return `Usage budget: ${scope === "monthly" ? "monthly" : "daily"} spend ${formatBudgetUsd(spentUsd)} reached ${threshold}% of ${formatBudgetUsd(budgetUsd)}`;
|
|
43146
43581
|
}
|
|
43147
|
-
function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broadcast = () => {}, controlClientCount = () => 0, warehousePath = null, budgetStatePath = null, laneMap = null, telemetry = null, sendTelegram = sendTelegramMessage, telegramTimeoutMs = TELEGRAM_TIMEOUT_MS, fsPromises =
|
|
43582
|
+
function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broadcast = () => {}, controlClientCount = () => 0, warehousePath = null, budgetStatePath = null, laneMap = null, telemetry = null, sendTelegram = sendTelegramMessage, telegramTimeoutMs = TELEGRAM_TIMEOUT_MS, fsPromises = fs$1, createScanner = createUsageScanner, loadPricingFn = loadPricing, execFileAsync: execFileAsync$1 = execFileAsync, rtkPathFn = defaultRtkPath, scannerDeps = {}, nowFn = Date.now, partialContinueMs = PARTIAL_CONTINUE_MS, setIntervalFn = DEFAULT_TIMER_FNS.setIntervalFn, clearIntervalFn = DEFAULT_TIMER_FNS.clearIntervalFn, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, logger = console, debug = false }) {
|
|
43148
43583
|
const laneLog = createLaneLog({
|
|
43149
43584
|
prefix: "[usage]",
|
|
43150
43585
|
logger,
|
|
@@ -43684,7 +44119,7 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
|
|
|
43684
44119
|
}
|
|
43685
44120
|
//#endregion
|
|
43686
44121
|
//#region server/usage/usage-lane-ledger.ts
|
|
43687
|
-
function createLaneLedger({ ledgerPath = null, fsPromises =
|
|
44122
|
+
function createLaneLedger({ ledgerPath = null, fsPromises = fs$1, nowFn = Date.now, retainDays = 365, logger = null } = {}) {
|
|
43688
44123
|
const laneLog = createLaneLog({
|
|
43689
44124
|
prefix: "[usage]",
|
|
43690
44125
|
logger
|
|
@@ -44716,7 +45151,7 @@ function errorResult(reason, errorSource = ERROR_SOURCE_TRANSPORT) {
|
|
|
44716
45151
|
async function readCommentsResult(resultPath, { lineCount = 0, onBytesRead = null } = {}) {
|
|
44717
45152
|
let raw = "";
|
|
44718
45153
|
try {
|
|
44719
|
-
raw = await
|
|
45154
|
+
raw = await fs$1.readFile(resultPath, "utf8");
|
|
44720
45155
|
} catch {
|
|
44721
45156
|
return errorResult("no readable result file");
|
|
44722
45157
|
}
|
|
@@ -44812,11 +45247,11 @@ function createVisionsSpawn({ sessions = /* @__PURE__ */ new Map(), closeSession
|
|
|
44812
45247
|
};
|
|
44813
45248
|
}
|
|
44814
45249
|
function makeVisionsWorkDir() {
|
|
44815
|
-
return
|
|
45250
|
+
return fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-visions-"));
|
|
44816
45251
|
}
|
|
44817
45252
|
function createVisionsDispatcher({ spawnSession, timeoutSeconds = 180, model = null, logger = console, nowFn = Date.now, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, makeWorkDir = makeVisionsWorkDir, removeWorkDir = async (dir) => {
|
|
44818
45253
|
try {
|
|
44819
|
-
await
|
|
45254
|
+
await fs$1.rm(dir, {
|
|
44820
45255
|
recursive: true,
|
|
44821
45256
|
force: true
|
|
44822
45257
|
});
|
|
@@ -44886,7 +45321,7 @@ function createVisionsDispatcher({ spawnSession, timeoutSeconds = 180, model = n
|
|
|
44886
45321
|
});
|
|
44887
45322
|
const sizeDecision = decidePromptSize(generatedPrompt);
|
|
44888
45323
|
if (!sizeDecision.dispatch) return errorResult(sizeDecision.gate);
|
|
44889
|
-
await
|
|
45324
|
+
await fs$1.writeFile(path.join(workDir, PROMPT_FILE), generatedPrompt, "utf8");
|
|
44890
45325
|
return await spawnWithTimeout({
|
|
44891
45326
|
id: idFor(uri),
|
|
44892
45327
|
name: `visions ${uri}`,
|
|
@@ -45293,7 +45728,7 @@ function changeFailureReason(uri, version, result) {
|
|
|
45293
45728
|
if (result.reason === "stale-version") return `stale-version (uri=${uri} incoming=${result.version} current=${result.currentVersion})`;
|
|
45294
45729
|
return `${result.reason} (uri=${uri} version=${version})`;
|
|
45295
45730
|
}
|
|
45296
|
-
function createVisionsWiring({ debounceMs = 300, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, nowFn = Date.now, sweep = sweepMarkdownWithFixes, maxPayload = MAX_FRAME_BYTES, autoFix = false, fixLogMax = 20, applyEditTimeoutMs = APPLY_EDIT_TIMEOUT_MS, logger = console, broadcast = null, dispatchConfig = null, dispatchHistory = [], dispatch = null, contextDigest = null, contextSeq = null, scopeProjects = [], pathsFoldCase = process.platform === "darwin", knownProjectIds = null, onEditorEvent = null, intentStatePath = null, intentThreadTtlMs = DEFAULT_THREAD_TTL_MS, fsFns = fs, fsPromises =
|
|
45731
|
+
function createVisionsWiring({ debounceMs = 300, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, nowFn = Date.now, sweep = sweepMarkdownWithFixes, maxPayload = MAX_FRAME_BYTES, autoFix = false, fixLogMax = 20, applyEditTimeoutMs = APPLY_EDIT_TIMEOUT_MS, logger = console, broadcast = null, dispatchConfig = null, dispatchHistory = [], dispatch = null, contextDigest = null, contextSeq = null, scopeProjects = [], pathsFoldCase = process.platform === "darwin", knownProjectIds = null, onEditorEvent = null, intentStatePath = null, intentThreadTtlMs = DEFAULT_THREAD_TTL_MS, fsFns = fs, fsPromises = fs$1, digestBudgetChars = DIGEST_BUDGET_CHARS, hashFn = hashText$1, buildPrompt = buildVisionsPrompt, debug = false } = {}) {
|
|
45297
45732
|
const wss = new WebSocketServer({
|
|
45298
45733
|
noServer: true,
|
|
45299
45734
|
maxPayload
|
|
@@ -46164,8 +46599,15 @@ function createBackendLanes(dependencies) {
|
|
|
46164
46599
|
warn: (message) => logger.warn(message)
|
|
46165
46600
|
});
|
|
46166
46601
|
const spawnGate = createSpawnGate({ memoryAdmission });
|
|
46167
|
-
const
|
|
46168
|
-
|
|
46602
|
+
const ownedPaths = createOwnedPathsLedger({
|
|
46603
|
+
filePath: ownedPathsLedgerPath(),
|
|
46604
|
+
log: logger
|
|
46605
|
+
});
|
|
46606
|
+
const gitWorkspace = createGitWorkspace({
|
|
46607
|
+
rerere: config.worktreeRerere !== false,
|
|
46608
|
+
ownedPaths
|
|
46609
|
+
});
|
|
46610
|
+
const gitWorkspaceSync = createGitWorkspaceSync({ ownedPaths });
|
|
46169
46611
|
const laneLedger = createLaneLedger({
|
|
46170
46612
|
ledgerPath: configSiblingPath(configStore.configPath, "usage-lanes.json"),
|
|
46171
46613
|
retainDays: resolveUsageConfig(config.usage).warehouseRetainDays,
|
|
@@ -46259,7 +46701,8 @@ function createBackendLanes(dependencies) {
|
|
|
46259
46701
|
});
|
|
46260
46702
|
const githubClock = createGithubClock({
|
|
46261
46703
|
baseIntervalMs: 6e4,
|
|
46262
|
-
log: logger
|
|
46704
|
+
log: logger,
|
|
46705
|
+
githubStatus: createGithubStatus({ log: logger })
|
|
46263
46706
|
});
|
|
46264
46707
|
const glimmervoidHome = glimmervoidHomeDir$1();
|
|
46265
46708
|
const github = createPrGh(glimmervoidHome);
|
|
@@ -46653,6 +47096,7 @@ function createBackendLanes(dependencies) {
|
|
|
46653
47096
|
currentVisions,
|
|
46654
47097
|
gitWorkspace,
|
|
46655
47098
|
gitWorkspaceSync,
|
|
47099
|
+
ownedPaths,
|
|
46656
47100
|
githubStore,
|
|
46657
47101
|
investigationSessions,
|
|
46658
47102
|
planReview,
|
|
@@ -48851,7 +49295,7 @@ var SessionRecorder = class {
|
|
|
48851
49295
|
if (this._retainDays <= 0 && this._retainFiles <= 0 && this._retainBytes <= 0) return;
|
|
48852
49296
|
let entries;
|
|
48853
49297
|
try {
|
|
48854
|
-
entries = await
|
|
49298
|
+
entries = await fs$1.readdir(this._baseDir);
|
|
48855
49299
|
} catch {
|
|
48856
49300
|
return;
|
|
48857
49301
|
}
|
|
@@ -48869,7 +49313,7 @@ var SessionRecorder = class {
|
|
|
48869
49313
|
for (const entry of recordings) {
|
|
48870
49314
|
if (doomed.has(entry)) continue;
|
|
48871
49315
|
try {
|
|
48872
|
-
if ((await
|
|
49316
|
+
if ((await fs$1.stat(path.join(this._baseDir, entry))).mtimeMs < cutoff) doomed.add(entry);
|
|
48873
49317
|
} catch {}
|
|
48874
49318
|
}
|
|
48875
49319
|
}
|
|
@@ -48877,14 +49321,14 @@ var SessionRecorder = class {
|
|
|
48877
49321
|
const filepath = path.join(this._baseDir, entry);
|
|
48878
49322
|
if (filepath === this._filepath || openRecordingPaths.has(filepath)) continue;
|
|
48879
49323
|
try {
|
|
48880
|
-
await
|
|
49324
|
+
await fs$1.unlink(filepath);
|
|
48881
49325
|
} catch {}
|
|
48882
49326
|
}
|
|
48883
49327
|
if (this._retainBytes <= 0) return;
|
|
48884
49328
|
const current = this._filepath ? path.basename(this._filepath) : null;
|
|
48885
49329
|
const measured = (await Promise.all(recordings.filter((entry) => !doomed.has(entry) && entry !== current).map(async (entry) => {
|
|
48886
49330
|
try {
|
|
48887
|
-
const stat = await
|
|
49331
|
+
const stat = await fs$1.stat(path.join(this._baseDir, entry));
|
|
48888
49332
|
return {
|
|
48889
49333
|
entry,
|
|
48890
49334
|
size: stat.size,
|
|
@@ -48899,7 +49343,7 @@ var SessionRecorder = class {
|
|
|
48899
49343
|
for (const entry of evictable) {
|
|
48900
49344
|
if (totalBytes <= this._retainBytes) return;
|
|
48901
49345
|
try {
|
|
48902
|
-
await
|
|
49346
|
+
await fs$1.unlink(path.join(this._baseDir, entry.entry));
|
|
48903
49347
|
totalBytes -= entry.size;
|
|
48904
49348
|
} catch (err) {
|
|
48905
49349
|
if (isMissingFileError(err, { includeNotDir: false })) totalBytes -= entry.size;
|
|
@@ -49027,6 +49471,8 @@ function createSessionFactory(dependencies) {
|
|
|
49027
49471
|
isSettingOn: (dependencies.getConfig?.() ?? config).coherence?.enabled === true
|
|
49028
49472
|
});
|
|
49029
49473
|
const builtMapping = project.environment;
|
|
49474
|
+
const sessionTempFolder = builtMapping ? null : sessionTempFolderFor(project.id);
|
|
49475
|
+
if (sessionTempFolder) fs.mkdirSync(sessionTempFolder, { recursive: true });
|
|
49030
49476
|
const session = new Session({
|
|
49031
49477
|
id: project.id,
|
|
49032
49478
|
name: project.name,
|
|
@@ -49064,6 +49510,10 @@ function createSessionFactory(dependencies) {
|
|
|
49064
49510
|
commitSigningProgram: dependencies.commitSigningProgram ?? null,
|
|
49065
49511
|
getRemoteLaunch: builtMapping ? () => remoteLaunchFor(project, builtMapping, dependencies.getConfig?.() ?? config) : null,
|
|
49066
49512
|
...overrides,
|
|
49513
|
+
spawnEnv: sessionTempFolder ? {
|
|
49514
|
+
CLAUDE_CODE_TMPDIR: sessionTempFolder,
|
|
49515
|
+
...overrides.spawnEnv ?? {}
|
|
49516
|
+
} : overrides.spawnEnv,
|
|
49067
49517
|
prepareGuardrails: "prepareGuardrails" in overrides ? overrides.prepareGuardrails : defaultGuardrailsFor(project, config, overrides)
|
|
49068
49518
|
});
|
|
49069
49519
|
const captureConfig = {
|
|
@@ -50923,22 +51373,37 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
50923
51373
|
console.log(`[control] Removed spawned session via UI: ${sess.name}`);
|
|
50924
51374
|
return { ok: true };
|
|
50925
51375
|
}
|
|
51376
|
+
const isDiscardingWorkspaceWork = sess.isWorkspace && msg.discardWork === true;
|
|
51377
|
+
if (isDiscardingWorkspaceWork) sess.killSession();
|
|
51378
|
+
const keptPaths = sess.isWorkspace ? await sess.findWorkspaceKeptWork() : [];
|
|
51379
|
+
const confirmedKeptPaths = new Set(isDiscardingWorkspaceWork ? msg.confirmedKeptPaths ?? [] : []);
|
|
51380
|
+
if (keptPaths.some((kept) => !confirmedKeptPaths.has(kept.path))) return {
|
|
51381
|
+
ok: false,
|
|
51382
|
+
error: "Workspace members hold work that is not saved",
|
|
51383
|
+
canDiscardWork: true,
|
|
51384
|
+
keptPaths
|
|
51385
|
+
};
|
|
50926
51386
|
if (config.projects.some((project) => project.id === sess.id && project.environment) && coderEnv) {
|
|
50927
51387
|
sess.killSession();
|
|
50928
51388
|
const settlement = await coderEnv.settleRemoval({
|
|
50929
51389
|
projectId: sess.id,
|
|
50930
|
-
discardRemoteWork: msg.
|
|
51390
|
+
discardRemoteWork: msg.discardWork === true
|
|
50931
51391
|
});
|
|
50932
51392
|
if (!settlement.ok) return {
|
|
50933
51393
|
ok: false,
|
|
50934
51394
|
error: settlement.reason,
|
|
50935
|
-
|
|
51395
|
+
canDiscardWork: settlement.canDiscardRemoteWork
|
|
50936
51396
|
};
|
|
50937
51397
|
}
|
|
50938
51398
|
const freshConfig = configStore.save((cfg) => {
|
|
50939
51399
|
cfg.projects = cfg.projects.filter((p) => p.id !== sess.id);
|
|
50940
51400
|
});
|
|
50941
|
-
if (freshConfig)
|
|
51401
|
+
if (!freshConfig) return {
|
|
51402
|
+
ok: false,
|
|
51403
|
+
error: `Could not save the removal of session "${sess.name}"`
|
|
51404
|
+
};
|
|
51405
|
+
if (isDiscardingWorkspaceWork) sess.confirmWorkspaceWorkDiscard();
|
|
51406
|
+
applyConfigReload(freshConfig);
|
|
50942
51407
|
console.log(`[control] Removed session via UI: ${sess.name}`);
|
|
50943
51408
|
return { ok: true };
|
|
50944
51409
|
}
|
|
@@ -53799,7 +54264,7 @@ async function downloadCapped(url, { fetchImpl, timeoutMs = DOWNLOAD_TIMEOUT_MS
|
|
|
53799
54264
|
}
|
|
53800
54265
|
}
|
|
53801
54266
|
async function findBinary(dir, platform) {
|
|
53802
|
-
const entries = await
|
|
54267
|
+
const entries = await fs$1.readdir(dir, { withFileTypes: true });
|
|
53803
54268
|
const nested = [];
|
|
53804
54269
|
for (const entry of entries) {
|
|
53805
54270
|
const full = path.join(dir, entry.name);
|
|
@@ -53817,13 +54282,13 @@ async function findBinary(dir, platform) {
|
|
|
53817
54282
|
}
|
|
53818
54283
|
async function removeQuietly(target) {
|
|
53819
54284
|
try {
|
|
53820
|
-
await
|
|
54285
|
+
await fs$1.rm(target, {
|
|
53821
54286
|
recursive: true,
|
|
53822
54287
|
force: true
|
|
53823
54288
|
});
|
|
53824
54289
|
} catch {}
|
|
53825
54290
|
}
|
|
53826
|
-
async function moveIntoPlace(source, target, renameImpl =
|
|
54291
|
+
async function moveIntoPlace(source, target, renameImpl = fs$1.rename) {
|
|
53827
54292
|
try {
|
|
53828
54293
|
await renameImpl(source, target);
|
|
53829
54294
|
return;
|
|
@@ -53832,15 +54297,15 @@ async function moveIntoPlace(source, target, renameImpl = fsp.rename) {
|
|
|
53832
54297
|
}
|
|
53833
54298
|
const partial = `${target}.partial`;
|
|
53834
54299
|
try {
|
|
53835
|
-
await
|
|
54300
|
+
await fs$1.copyFile(source, partial);
|
|
53836
54301
|
await renameImpl(partial, target);
|
|
53837
54302
|
} catch (err) {
|
|
53838
54303
|
await removeQuietly(partial);
|
|
53839
54304
|
throw err;
|
|
53840
54305
|
}
|
|
53841
|
-
await
|
|
54306
|
+
await fs$1.unlink(source);
|
|
53842
54307
|
}
|
|
53843
|
-
async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoidHomeDir(homeDir, process.env), platform = process.platform, arch = process.arch, fetchImpl = globalThis.fetch, execFileImpl = execFileAsync, renameImpl =
|
|
54308
|
+
async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoidHomeDir(homeDir, process.env), platform = process.platform, arch = process.arch, fetchImpl = globalThis.fetch, execFileImpl = execFileAsync, renameImpl = fs$1.rename, timeoutMs = DOWNLOAD_TIMEOUT_MS, log = console, asset = assetForPlatform(platform, arch) } = {}) {
|
|
53844
54309
|
if (!asset) return {
|
|
53845
54310
|
ok: false,
|
|
53846
54311
|
reason: `unsupported platform ${platform}-${arch}`
|
|
@@ -53851,7 +54316,7 @@ async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoi
|
|
|
53851
54316
|
};
|
|
53852
54317
|
const stagingDir = path.join(glimmervoidHome, "tmp", `rtk-${crypto.randomBytes(6).toString("hex")}`);
|
|
53853
54318
|
try {
|
|
53854
|
-
await
|
|
54319
|
+
await fs$1.mkdir(stagingDir, { recursive: true });
|
|
53855
54320
|
} catch (err) {
|
|
53856
54321
|
return {
|
|
53857
54322
|
ok: false,
|
|
@@ -53874,9 +54339,9 @@ async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoi
|
|
|
53874
54339
|
reason: `sha256 mismatch for ${asset.file}: expected ${asset.sha256}, got ${actualHex}`
|
|
53875
54340
|
};
|
|
53876
54341
|
const archivePath = path.join(stagingDir, asset.file);
|
|
53877
|
-
await
|
|
54342
|
+
await fs$1.writeFile(archivePath, downloaded.bytes);
|
|
53878
54343
|
const extractDir = path.join(stagingDir, "extract");
|
|
53879
|
-
await
|
|
54344
|
+
await fs$1.mkdir(extractDir, { recursive: true });
|
|
53880
54345
|
try {
|
|
53881
54346
|
const listing = await execFileImpl("tar", ["-tf", archivePath]);
|
|
53882
54347
|
const escaping = findEscapingArchiveMember(listing.stdout);
|
|
@@ -53901,13 +54366,13 @@ async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoi
|
|
|
53901
54366
|
ok: false,
|
|
53902
54367
|
reason: `extract failed: no rtk binary inside ${asset.file}`
|
|
53903
54368
|
};
|
|
53904
|
-
if (!(await
|
|
54369
|
+
if (!(await fs$1.lstat(extracted)).isFile()) return {
|
|
53905
54370
|
ok: false,
|
|
53906
54371
|
reason: `extract failed: rtk inside ${asset.file} is not a regular file`
|
|
53907
54372
|
};
|
|
53908
|
-
if (platform !== "win32") await
|
|
54373
|
+
if (platform !== "win32") await fs$1.chmod(extracted, 493);
|
|
53909
54374
|
const target = installTargetPath(glimmervoidHome, platform);
|
|
53910
|
-
await
|
|
54375
|
+
await fs$1.mkdir(path.dirname(target), { recursive: true });
|
|
53911
54376
|
await removeQuietly(target);
|
|
53912
54377
|
await moveIntoPlace(extracted, target, renameImpl);
|
|
53913
54378
|
log?.log?.(`[rtk] installed rtk ${asset.version} at ${target}`);
|
|
@@ -53998,13 +54463,13 @@ function sleepFor(durationMs) {
|
|
|
53998
54463
|
function createClaudeFolderTrust({ env = process.env, homeDir = os.homedir(), now = Date.now, platform = process.platform, sleep = sleepFor } = {}) {
|
|
53999
54464
|
async function resolveConfiguredPath() {
|
|
54000
54465
|
const [legacyConfigPath, currentConfigPath] = listClaudeGlobalConfigPathCandidates(env, homeDir);
|
|
54001
|
-
return await
|
|
54466
|
+
return await fs$1.access(legacyConfigPath).then(() => true, () => false) ? legacyConfigPath : currentConfigPath;
|
|
54002
54467
|
}
|
|
54003
54468
|
function refusal(repositoryRoot, configPath, reason) {
|
|
54004
54469
|
return /* @__PURE__ */ new Error(`Could not trust ${repositoryRoot} for Claude Code: ${configPath} was left unchanged because ${reason}`);
|
|
54005
54470
|
}
|
|
54006
54471
|
async function readTrustDecision(repositoryRoot, configPath) {
|
|
54007
|
-
const decision = decideFolderTrust(await
|
|
54472
|
+
const decision = decideFolderTrust(await fs$1.readFile(configPath, "utf8").catch((error) => {
|
|
54008
54473
|
throw refusal(repositoryRoot, configPath, `it could not be read (${errorCode(error) ?? "unknown error"})`);
|
|
54009
54474
|
}), repositoryRoot, platform);
|
|
54010
54475
|
if (decision.kind === "unreadable") throw refusal(repositoryRoot, configPath, decision.reason);
|
|
@@ -54012,13 +54477,13 @@ function createClaudeFolderTrust({ env = process.env, homeDir = os.homedir(), no
|
|
|
54012
54477
|
}
|
|
54013
54478
|
async function tryTakeConfigLock(lockPath) {
|
|
54014
54479
|
try {
|
|
54015
|
-
await
|
|
54480
|
+
await fs$1.mkdir(lockPath);
|
|
54016
54481
|
return true;
|
|
54017
54482
|
} catch (error) {
|
|
54018
54483
|
if (errorCode(error) !== "EEXIST") throw error;
|
|
54019
54484
|
}
|
|
54020
|
-
const heldLock = await
|
|
54021
|
-
if (heldLock && isConfigLockStale(heldLock.mtimeMs, now())) await
|
|
54485
|
+
const heldLock = await fs$1.stat(lockPath).catch(() => null);
|
|
54486
|
+
if (heldLock && isConfigLockStale(heldLock.mtimeMs, now())) await fs$1.rmdir(lockPath).catch(() => {});
|
|
54022
54487
|
return false;
|
|
54023
54488
|
}
|
|
54024
54489
|
async function takeConfigLock(repositoryRoot, configuredPath) {
|
|
@@ -54031,17 +54496,17 @@ function createClaudeFolderTrust({ env = process.env, homeDir = os.homedir(), no
|
|
|
54031
54496
|
}
|
|
54032
54497
|
async function ensureTrusted(repositoryRoot) {
|
|
54033
54498
|
const configuredPath = await resolveConfiguredPath();
|
|
54034
|
-
const configPath = await
|
|
54499
|
+
const configPath = await fs$1.realpath(configuredPath).catch(() => configuredPath);
|
|
54035
54500
|
if ((await readTrustDecision(repositoryRoot, configPath)).kind === "trusted") return "already-trusted";
|
|
54036
54501
|
const lockPath = await takeConfigLock(repositoryRoot, configuredPath);
|
|
54037
54502
|
try {
|
|
54038
54503
|
const decision = await readTrustDecision(repositoryRoot, configPath);
|
|
54039
54504
|
if (decision.kind === "trusted") return "already-trusted";
|
|
54040
|
-
const { mode } = await
|
|
54505
|
+
const { mode } = await fs$1.stat(configPath);
|
|
54041
54506
|
await writeTextAtomic(configPath, decision.nextConfigText, { mode: mode & 511 });
|
|
54042
54507
|
return "granted";
|
|
54043
54508
|
} finally {
|
|
54044
|
-
await
|
|
54509
|
+
await fs$1.rmdir(lockPath).catch(() => {});
|
|
54045
54510
|
}
|
|
54046
54511
|
}
|
|
54047
54512
|
return { ensureTrusted };
|
|
@@ -54283,11 +54748,11 @@ function spawnConnectPty(file, args, options) {
|
|
|
54283
54748
|
return pty.spawn(file, args, options);
|
|
54284
54749
|
}
|
|
54285
54750
|
async function makePrivateDir(directory) {
|
|
54286
|
-
await
|
|
54751
|
+
await fs$1.mkdir(directory, {
|
|
54287
54752
|
recursive: true,
|
|
54288
54753
|
mode: 448
|
|
54289
54754
|
});
|
|
54290
|
-
await
|
|
54755
|
+
await fs$1.chmod(directory, 448);
|
|
54291
54756
|
}
|
|
54292
54757
|
function createAgentConnect({ loginsRoot, loginSource, launchFor, onChange, ptySpawn = spawnConnectPty, makeHomeDir = makePrivateDir, setTimeoutFn = setTimeout, clearTimeoutFn = clearTimeout }) {
|
|
54293
54758
|
const statusByAgent = new Map(CONNECTABLE_AGENTS.map((agent) => [agent, idleLoginStatus(agent, null)]));
|
|
@@ -54492,7 +54957,7 @@ function chatgptAuthTokensRefreshResult(login) {
|
|
|
54492
54957
|
//#region server/codex-credentials.ts
|
|
54493
54958
|
var HOST_REFRESH_TIMEOUT_MS = 6e4;
|
|
54494
54959
|
async function readTextFileOrNull(filePath) {
|
|
54495
|
-
return
|
|
54960
|
+
return fs$1.readFile(filePath, "utf8").catch(() => null);
|
|
54496
54961
|
}
|
|
54497
54962
|
function codexHomeDir(env, homeDir) {
|
|
54498
54963
|
const override = env.CODEX_HOME?.trim();
|
|
@@ -55101,7 +55566,8 @@ function createUpdateApplyLane(dependencies) {
|
|
|
55101
55566
|
await runWorkspaceStep("stage", () => dependencies.gitWorkspace.stageDetachedWorktree({
|
|
55102
55567
|
projectPath: dependencies.packageRoot,
|
|
55103
55568
|
worktreePath: stagingPath,
|
|
55104
|
-
sha: status.latestSha || void 0
|
|
55569
|
+
sha: status.latestSha || void 0,
|
|
55570
|
+
owner: "lane:update-apply"
|
|
55105
55571
|
}));
|
|
55106
55572
|
const lockfileDiff = await gitCommand([
|
|
55107
55573
|
"diff",
|
|
@@ -55472,19 +55938,19 @@ async function collectWorktreePaths(projects) {
|
|
|
55472
55938
|
}
|
|
55473
55939
|
async function readFileNames(folder) {
|
|
55474
55940
|
try {
|
|
55475
|
-
return (await
|
|
55941
|
+
return (await fs$1.readdir(folder, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name);
|
|
55476
55942
|
} catch (error) {
|
|
55477
55943
|
if (isMissingFileError(error)) return null;
|
|
55478
55944
|
throw error;
|
|
55479
55945
|
}
|
|
55480
55946
|
}
|
|
55481
55947
|
async function removeFolderIfEmpty(folder) {
|
|
55482
|
-
if ((await
|
|
55483
|
-
await
|
|
55948
|
+
if ((await fs$1.readdir(folder)).length > 0) return;
|
|
55949
|
+
await fs$1.rmdir(folder);
|
|
55484
55950
|
}
|
|
55485
55951
|
async function isRealDirectory(folder) {
|
|
55486
55952
|
try {
|
|
55487
|
-
return (await
|
|
55953
|
+
return (await fs$1.lstat(folder)).isDirectory();
|
|
55488
55954
|
} catch (error) {
|
|
55489
55955
|
if (isMissingFileError(error)) return false;
|
|
55490
55956
|
throw error;
|
|
@@ -55492,10 +55958,10 @@ async function isRealDirectory(folder) {
|
|
|
55492
55958
|
}
|
|
55493
55959
|
async function isReportFolderOwnedByWorktree(worktreePath, reportFolder) {
|
|
55494
55960
|
if (!await isRealDirectory(path.dirname(reportFolder)) || !await isRealDirectory(reportFolder)) return false;
|
|
55495
|
-
const realWorktreePath = await
|
|
55496
|
-
if (!isPathInside(realWorktreePath, await
|
|
55961
|
+
const realWorktreePath = await fs$1.realpath(worktreePath);
|
|
55962
|
+
if (!isPathInside(realWorktreePath, await fs$1.realpath(reportFolder), { allowEqual: false })) return false;
|
|
55497
55963
|
const reportFolderTopLevel = (await gitOutput(["rev-parse", "--show-toplevel"], reportFolder)).trim();
|
|
55498
|
-
return equalsIgnoringCaseOnWindows(await
|
|
55964
|
+
return equalsIgnoringCaseOnWindows(await fs$1.realpath(reportFolderTopLevel), realWorktreePath);
|
|
55499
55965
|
}
|
|
55500
55966
|
async function listTrackedReportGitPaths(worktreePath) {
|
|
55501
55967
|
return (await gitOutput([
|
|
@@ -55512,7 +55978,7 @@ async function cleanWorktree(worktreePath) {
|
|
|
55512
55978
|
if (fileNames === null) return NOTHING_CLEANED;
|
|
55513
55979
|
const trackedReportGitPaths = await listTrackedReportGitPaths(worktreePath);
|
|
55514
55980
|
const deletableFileNames = untrackedReportFileNames(fileNames, trackedReportGitPaths);
|
|
55515
|
-
for (const fileName of deletableFileNames) await
|
|
55981
|
+
for (const fileName of deletableFileNames) await fs$1.rm(path.join(reportFolder, fileName), { force: true });
|
|
55516
55982
|
await removeFolderIfEmpty(reportFolder);
|
|
55517
55983
|
await removeFolderIfEmpty(path.dirname(reportFolder));
|
|
55518
55984
|
return {
|
|
@@ -56566,6 +57032,12 @@ function createBackend(httpServer, options = {}) {
|
|
|
56566
57032
|
const reconcileProjectsOnReload = sessionRegistry.applyConfigReload;
|
|
56567
57033
|
rtkInstall.maybeInstall();
|
|
56568
57034
|
sweepOrphanedGuardrailsLaunches();
|
|
57035
|
+
reconcileOwnedPaths({
|
|
57036
|
+
ledger: laneAssembly.ownedPaths,
|
|
57037
|
+
configuredSessionIds: new Set(config.projects.map((project) => project.id)),
|
|
57038
|
+
git: laneAssembly.gitWorkspace,
|
|
57039
|
+
shareList: config.worktreeShare || DEFAULT_CONFIG.worktreeShare
|
|
57040
|
+
});
|
|
56569
57041
|
laneAssembly.startRuntimeLanes();
|
|
56570
57042
|
function applyCustomAgents(sourceConfig) {
|
|
56571
57043
|
setCustomAgents(sourceConfig.customAgents ?? []);
|