glimmervoid 0.42.2 → 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-Ywst1s6Z.js → main-_78bVmTr.js} +1917 -1428
- 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-D9A5nTKg.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 ? {
|
|
12197
|
+
failed: true,
|
|
12198
|
+
error
|
|
12199
|
+
} : {
|
|
11433
12200
|
failed: true,
|
|
11434
|
-
retryAfterMs: waitMs
|
|
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;
|
|
@@ -17242,7 +18072,7 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17242
18072
|
"--quiet",
|
|
17243
18073
|
candidate
|
|
17244
18074
|
], opts) !== null) return {
|
|
17245
|
-
ref: candidate,
|
|
18075
|
+
ref: await freshestBaseRef(candidate, opts),
|
|
17246
18076
|
verified: true
|
|
17247
18077
|
};
|
|
17248
18078
|
return {
|
|
@@ -17250,6 +18080,23 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17250
18080
|
verified: false
|
|
17251
18081
|
};
|
|
17252
18082
|
}
|
|
18083
|
+
async function freshestBaseRef(baseBranch, opts) {
|
|
18084
|
+
const localRef = `refs/heads/${baseBranch}`;
|
|
18085
|
+
const originRef = `refs/remotes/origin/${baseBranch}`;
|
|
18086
|
+
const existingRefs = (await gitOut([
|
|
18087
|
+
"for-each-ref",
|
|
18088
|
+
"--format=%(refname)",
|
|
18089
|
+
localRef,
|
|
18090
|
+
originRef
|
|
18091
|
+
], opts)).split("\n");
|
|
18092
|
+
if (!existingRefs.includes(localRef) || !existingRefs.includes(originRef)) return baseBranch;
|
|
18093
|
+
return await gitLookup([
|
|
18094
|
+
"merge-base",
|
|
18095
|
+
"--is-ancestor",
|
|
18096
|
+
localRef,
|
|
18097
|
+
originRef
|
|
18098
|
+
], opts) !== null ? `origin/${baseBranch}` : baseBranch;
|
|
18099
|
+
}
|
|
17253
18100
|
function diffGitOptions(cwd) {
|
|
17254
18101
|
return {
|
|
17255
18102
|
cwd,
|
|
@@ -17318,7 +18165,7 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17318
18165
|
async function memberMergeBase(member) {
|
|
17319
18166
|
const opts = diffGitOptions(member.dir);
|
|
17320
18167
|
const candidates = member.base ? [
|
|
17321
|
-
member.base,
|
|
18168
|
+
await freshestBaseRef(member.base, opts),
|
|
17322
18169
|
`origin/${member.base}`,
|
|
17323
18170
|
"origin/HEAD"
|
|
17324
18171
|
] : ["origin/HEAD"];
|
|
@@ -17930,6 +18777,8 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
17930
18777
|
setMergeStatus,
|
|
17931
18778
|
pasteMergePrompt,
|
|
17932
18779
|
hasUnmergedWork,
|
|
18780
|
+
findWorkspaceKeptWork,
|
|
18781
|
+
confirmWorkspaceWorkDiscard,
|
|
17933
18782
|
getDiff,
|
|
17934
18783
|
getChangeScopes,
|
|
17935
18784
|
resolveEffectiveBase,
|
|
@@ -19301,6 +20150,12 @@ var Session = class extends EventEmitter {
|
|
|
19301
20150
|
discardWorktree() {
|
|
19302
20151
|
return this.worktreeLifecycle.discardWorktree();
|
|
19303
20152
|
}
|
|
20153
|
+
findWorkspaceKeptWork() {
|
|
20154
|
+
return this.worktreeLifecycle.findWorkspaceKeptWork();
|
|
20155
|
+
}
|
|
20156
|
+
confirmWorkspaceWorkDiscard() {
|
|
20157
|
+
this.worktreeLifecycle.confirmWorkspaceWorkDiscard();
|
|
20158
|
+
}
|
|
19304
20159
|
getDetectionStats() {
|
|
19305
20160
|
return {
|
|
19306
20161
|
lastSignal: this._lastSignal,
|
|
@@ -20851,760 +21706,200 @@ function boundedToolCallInput(input) {
|
|
|
20851
21706
|
return {
|
|
20852
21707
|
input: bounded.text,
|
|
20853
21708
|
truncated: true
|
|
20854
|
-
};
|
|
20855
|
-
}
|
|
20856
|
-
if (!input || typeof input !== "object") return { input };
|
|
20857
|
-
const bounded = Array.isArray(input) ? {
|
|
20858
|
-
input,
|
|
20859
|
-
hasCutProperty: false
|
|
20860
|
-
} : boundedInputProperties(input);
|
|
20861
|
-
const serialized = serializedInputText(bounded.input);
|
|
20862
|
-
if (serialized.length > MAX_TRACE_CALL_INPUT_CHARS) return {
|
|
20863
|
-
input: serialized.slice(0, MAX_TRACE_BODY_CHARS),
|
|
20864
|
-
truncated: true
|
|
20865
|
-
};
|
|
20866
|
-
if (!bounded.hasCutProperty) return { input };
|
|
20867
|
-
return {
|
|
20868
|
-
input: bounded.input,
|
|
20869
|
-
truncated: true
|
|
20870
|
-
};
|
|
20871
|
-
}
|
|
20872
|
-
function everyTextBlockJoined(blocks) {
|
|
20873
|
-
const blockTexts = blocks.flatMap((block) => firstTextBlock([block]) ?? []);
|
|
20874
|
-
if (blockTexts.length === 0) return null;
|
|
20875
|
-
return blockTexts.join("\n");
|
|
20876
|
-
}
|
|
20877
|
-
function isTaskNotificationLine(line) {
|
|
20878
|
-
return line.origin?.kind === "task-notification" || line.turnOrigin?.kind === "task_notification";
|
|
20879
|
-
}
|
|
20880
|
-
function mapUserLine(rawLine, line, context) {
|
|
20881
|
-
const base = baseRecord(line, context);
|
|
20882
|
-
const content = line.message?.content;
|
|
20883
|
-
const blocks = contentBlocks(line);
|
|
20884
|
-
const toolResult = blocks.find((block) => block.type === "tool_result");
|
|
20885
|
-
if (toolResult) {
|
|
20886
|
-
const toolUseId = rawTextOr(toolResult.tool_use_id, null);
|
|
20887
|
-
if (!toolUseId) return [rawRecord(rawLine, line, context)];
|
|
20888
|
-
const bounded = boundedText(textContent(toolResult.content));
|
|
20889
|
-
return [{
|
|
20890
|
-
...base,
|
|
20891
|
-
kind: "tool_result",
|
|
20892
|
-
toolUseId,
|
|
20893
|
-
content: bounded.text,
|
|
20894
|
-
isError: toolResult.is_error === true,
|
|
20895
|
-
truncated: bounded.truncated
|
|
20896
|
-
}];
|
|
20897
|
-
}
|
|
20898
|
-
if (line.isMeta === true) {
|
|
20899
|
-
const expansionText = typeof content === "string" ? content : everyTextBlockJoined(blocks);
|
|
20900
|
-
if (!expansionText) return [rawRecord(rawLine, line, context)];
|
|
20901
|
-
const sourceToolUseId = rawTextOr(line.sourceToolUseID, null);
|
|
20902
|
-
if (!sourceToolUseId) return [{
|
|
20903
|
-
...base,
|
|
20904
|
-
kind: "expansion",
|
|
20905
|
-
...boundedTextFields(expansionText)
|
|
20906
|
-
}];
|
|
20907
|
-
return [{
|
|
20908
|
-
...base,
|
|
20909
|
-
kind: "expansion",
|
|
20910
|
-
toolUseId: sourceToolUseId,
|
|
20911
|
-
...boundedTextFields(expansionText)
|
|
20912
|
-
}];
|
|
20913
|
-
}
|
|
20914
|
-
const text = typeof content === "string" ? content : firstTextBlock(blocks);
|
|
20915
|
-
if (!text) return [rawRecord(rawLine, line, context)];
|
|
20916
|
-
if (isTaskNotificationLine(line)) return [{
|
|
20917
|
-
...base,
|
|
20918
|
-
kind: "expansion",
|
|
20919
|
-
...boundedTextFields(text)
|
|
20920
|
-
}];
|
|
20921
|
-
if (typeof content === "string" && /<command-name>[\s\S]*?<\/command-name>/.test(content)) return [{
|
|
20922
|
-
...base,
|
|
20923
|
-
kind: "expansion",
|
|
20924
|
-
...boundedTextFields(content)
|
|
20925
|
-
}];
|
|
20926
|
-
return [{
|
|
20927
|
-
...base,
|
|
20928
|
-
kind: "prompt",
|
|
20929
|
-
...boundedTextFields(text)
|
|
20930
|
-
}];
|
|
20931
|
-
}
|
|
20932
|
-
function queuedCommandText(prompt) {
|
|
20933
|
-
if (typeof prompt === "string") return prompt || null;
|
|
20934
|
-
return firstTextBlock(objectBlocks(prompt));
|
|
20935
|
-
}
|
|
20936
|
-
function queuedCommandRecords(line, context) {
|
|
20937
|
-
const attachment = line.attachment;
|
|
20938
|
-
if (line.type !== "attachment" || attachment?.type !== "queued_command") return null;
|
|
20939
|
-
const text = queuedCommandText(attachment.prompt);
|
|
20940
|
-
if (!text) return null;
|
|
20941
|
-
if (attachment.commandMode === "prompt" && attachment.origin?.kind === "human") return [{
|
|
20942
|
-
...baseRecord(line, context),
|
|
20943
|
-
kind: "prompt",
|
|
20944
|
-
...boundedTextFields(text)
|
|
20945
|
-
}];
|
|
20946
|
-
if (attachment.commandMode === "task-notification") return [{
|
|
20947
|
-
...baseRecord(line, context),
|
|
20948
|
-
kind: "expansion",
|
|
20949
|
-
...boundedTextFields(text)
|
|
20950
|
-
}];
|
|
20951
|
-
return null;
|
|
20952
|
-
}
|
|
20953
|
-
function mapAssistantLine(rawLine, line, context) {
|
|
20954
|
-
const base = baseRecord(line, context);
|
|
20955
|
-
const records = [];
|
|
20956
|
-
let hasSkippedThinkingBlock = false;
|
|
20957
|
-
for (const block of contentBlocks(line)) {
|
|
20958
|
-
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
20959
|
-
if (block.thinking.length === 0) {
|
|
20960
|
-
hasSkippedThinkingBlock = true;
|
|
20961
|
-
continue;
|
|
20962
|
-
}
|
|
20963
|
-
records.push({
|
|
20964
|
-
...base,
|
|
20965
|
-
kind: "thinking",
|
|
20966
|
-
...boundedTextFields(block.thinking)
|
|
20967
|
-
});
|
|
20968
|
-
continue;
|
|
20969
|
-
}
|
|
20970
|
-
if (block.type === "text") {
|
|
20971
|
-
const text = firstTextBlock([block]);
|
|
20972
|
-
if (text) records.push({
|
|
20973
|
-
...base,
|
|
20974
|
-
kind: "assistant",
|
|
20975
|
-
...boundedTextFields(text)
|
|
20976
|
-
});
|
|
20977
|
-
continue;
|
|
20978
|
-
}
|
|
20979
|
-
if (block.type !== "tool_use") continue;
|
|
20980
|
-
const toolUseId = rawTextOr(block.id, null);
|
|
20981
|
-
const name = rawTextOr(block.name, null);
|
|
20982
|
-
if (!toolUseId || !name) return [rawRecord(rawLine, line, context)];
|
|
20983
|
-
records.push({
|
|
20984
|
-
...base,
|
|
20985
|
-
kind: "tool_call",
|
|
20986
|
-
toolUseId,
|
|
20987
|
-
name,
|
|
20988
|
-
...boundedToolCallInput(block.input)
|
|
20989
|
-
});
|
|
20990
|
-
}
|
|
20991
|
-
if (records.length === 0 && hasSkippedThinkingBlock) return [];
|
|
20992
|
-
if (records.length === 0) return [rawRecord(rawLine, line, context)];
|
|
20993
|
-
return records;
|
|
20994
|
-
}
|
|
20995
|
-
function traceRecordsFromTranscriptLine(rawLine, context) {
|
|
20996
|
-
const line = parseTranscriptLine(rawLine);
|
|
20997
|
-
if (!line) return [rawRecord(rawLine, null, context)];
|
|
20998
|
-
const queuedRecords = queuedCommandRecords(line, context);
|
|
20999
|
-
if (queuedRecords) return queuedRecords;
|
|
21000
|
-
if (isDroppedLine(line)) return [];
|
|
21001
|
-
if (line.type === "user") return mapUserLine(rawLine, line, context);
|
|
21002
|
-
if (line.type === "assistant") return mapAssistantLine(rawLine, line, context);
|
|
21003
|
-
return [rawRecord(rawLine, line, context)];
|
|
21004
|
-
}
|
|
21005
|
-
//#endregion
|
|
21006
|
-
//#region server/core/investigation-trail-core.ts
|
|
21007
|
-
var TRAIL_MAX_STEPS = 80;
|
|
21008
|
-
var TRAIL_TOOL_MAX_CHARS = 64;
|
|
21009
|
-
var TRAIL_NOTE_MAX_CHARS = 600;
|
|
21010
|
-
function describeToolStep(toolName, toolInput) {
|
|
21011
|
-
const trimmed = typeof toolName === "string" ? toolName.trim() : "";
|
|
21012
|
-
if (!trimmed) return null;
|
|
21013
|
-
const tool = trimmed.slice(0, TRAIL_TOOL_MAX_CHARS);
|
|
21014
|
-
return {
|
|
21015
|
-
tool,
|
|
21016
|
-
detail: toolDetailLine(tool, toolInput)
|
|
21017
|
-
};
|
|
21018
|
-
}
|
|
21019
|
-
function trailStepFromHook(event, payload) {
|
|
21020
|
-
if (String(event ?? "").toLowerCase() !== "pretooluse") return null;
|
|
21021
|
-
return describeToolStep(payload.tool_name, payload.tool_input);
|
|
21022
|
-
}
|
|
21023
|
-
function trailNotesFromTranscriptLines(lines, now) {
|
|
21024
|
-
return lines.flatMap((line) => traceRecordsFromTranscriptLine(line, {
|
|
21025
|
-
vendorSessionId: "",
|
|
21026
|
-
now
|
|
21027
|
-
})).flatMap((record) => record.kind === "assistant" ? [record.text.trim().slice(0, TRAIL_NOTE_MAX_CHARS)] : []).filter((note) => note !== "");
|
|
21028
|
-
}
|
|
21029
|
-
function createInvestigationTrail(startedAt) {
|
|
21030
|
-
return {
|
|
21031
|
-
startedAt,
|
|
21032
|
-
steps: []
|
|
21033
|
-
};
|
|
21034
|
-
}
|
|
21035
|
-
function appendTrailStep(trail, step, maxSteps = TRAIL_MAX_STEPS) {
|
|
21036
|
-
const isAppendingNote = isTrailNote(step);
|
|
21037
|
-
const sameKindEntries = trail.steps.filter((entry) => isTrailNote(entry) === isAppendingNote);
|
|
21038
|
-
const evictedCount = Math.max(0, sameKindEntries.length + 1 - maxSteps);
|
|
21039
|
-
const evictedEntries = new Set(sameKindEntries.slice(0, evictedCount));
|
|
21040
|
-
const keptEntries = trail.steps.filter((entry) => !evictedEntries.has(entry));
|
|
21041
|
-
return {
|
|
21042
|
-
startedAt: trail.startedAt,
|
|
21043
|
-
steps: [...keptEntries, step]
|
|
21044
|
-
};
|
|
21045
|
-
}
|
|
21046
|
-
function isTrailNote(entry) {
|
|
21047
|
-
return "note" in entry;
|
|
21048
|
-
}
|
|
21049
|
-
//#endregion
|
|
21050
|
-
//#region shared/contracts/guardrails-launch.ts
|
|
21051
|
-
var GuardrailsLaunchLogin = z.object({
|
|
21052
|
-
linkPath: z.string().min(1),
|
|
21053
|
-
targetPath: z.string().min(1)
|
|
21054
|
-
}).strict();
|
|
21055
|
-
var GuardrailsLaunchOwner = z.object({
|
|
21056
|
-
ownerPid: z.number().int().positive(),
|
|
21057
|
-
ownerHost: z.string(),
|
|
21058
|
-
ownerPidNamespace: z.string().nullable(),
|
|
21059
|
-
ownsTempDir: z.boolean(),
|
|
21060
|
-
keptTempFolderName: z.string().min(1).optional(),
|
|
21061
|
-
logins: z.array(GuardrailsLaunchLogin),
|
|
21062
|
-
placeholderPaths: z.array(z.string().min(1))
|
|
21063
|
-
}).strict();
|
|
21064
|
-
//#endregion
|
|
21065
|
-
//#region server/core/claude-trust-core.ts
|
|
21066
|
-
var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
|
|
21067
|
-
var CLAUDE_LEGACY_CONFIG_FILE_NAME = ".config.json";
|
|
21068
|
-
var CLAUDE_CONFIG_LOCK_SUFFIX = ".lock";
|
|
21069
|
-
var CLAUDE_CONFIG_LOCK_STALE_MS = 1e4;
|
|
21070
|
-
var CLAUDE_CONFIG_LOCK_RETRY_DELAYS_MS = Object.freeze([
|
|
21071
|
-
200,
|
|
21072
|
-
400,
|
|
21073
|
-
800,
|
|
21074
|
-
1600
|
|
21075
|
-
]);
|
|
21076
|
-
var GRANTED_KEYS = Object.freeze([
|
|
21077
|
-
"hasTrustDialogAccepted",
|
|
21078
|
-
"hasClaudeMdExternalIncludesApproved",
|
|
21079
|
-
"hasClaudeMdExternalIncludesWarningShown"
|
|
21080
|
-
]);
|
|
21081
|
-
var TRUST_ONLY_KEYS = Object.freeze(["hasTrustDialogAccepted"]);
|
|
21082
|
-
function isPlainRecord(value) {
|
|
21083
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21084
|
-
}
|
|
21085
|
-
function newProjectEntry() {
|
|
21086
|
-
return {
|
|
21087
|
-
allowedTools: [],
|
|
21088
|
-
mcpContextUris: [],
|
|
21089
|
-
mcpServers: {},
|
|
21090
|
-
enabledMcpjsonServers: [],
|
|
21091
|
-
disabledMcpjsonServers: []
|
|
21092
|
-
};
|
|
21093
|
-
}
|
|
21094
|
-
function listClaudeGlobalConfigPathCandidates(env, homeDir) {
|
|
21095
|
-
const configuredDir = env.CLAUDE_CONFIG_DIR?.trim();
|
|
21096
|
-
const legacyConfigHome = (configuredDir || path.join(homeDir, ".claude")).normalize("NFC");
|
|
21097
|
-
return [path.join(legacyConfigHome, CLAUDE_LEGACY_CONFIG_FILE_NAME), path.join(configuredDir || homeDir, CLAUDE_GLOBAL_CONFIG_FILE_NAME)];
|
|
21098
|
-
}
|
|
21099
|
-
function claudeProjectKeyOf(folderPath, platform) {
|
|
21100
|
-
if (platform !== "win32") return path.posix.normalize(folderPath);
|
|
21101
|
-
return path.win32.normalize(folderPath).replaceAll("\\", "/");
|
|
21102
|
-
}
|
|
21103
|
-
function repositoryRootOfCommonGitDir(commonGitDir) {
|
|
21104
|
-
if (!path.isAbsolute(commonGitDir) || path.basename(commonGitDir) !== ".git") return null;
|
|
21105
|
-
return path.dirname(commonGitDir);
|
|
21106
|
-
}
|
|
21107
|
-
function isConfigLockStale(lockModifiedMs, nowMs) {
|
|
21108
|
-
return nowMs - lockModifiedMs > CLAUDE_CONFIG_LOCK_STALE_MS;
|
|
21109
|
-
}
|
|
21110
|
-
function hasRecordedExternalImportsDecline(existingEntry) {
|
|
21111
|
-
return existingEntry?.hasClaudeMdExternalIncludesWarningShown === true && existingEntry.hasClaudeMdExternalIncludesApproved !== true;
|
|
21112
|
-
}
|
|
21113
|
-
function decideFolderTrust(configText, repositoryRoot, platform) {
|
|
21114
|
-
const projectKey = claudeProjectKeyOf(repositoryRoot, platform);
|
|
21115
|
-
let config;
|
|
21116
|
-
try {
|
|
21117
|
-
config = JSON.parse(configText);
|
|
21118
|
-
} catch {
|
|
21119
|
-
return {
|
|
21120
|
-
kind: "unreadable",
|
|
21121
|
-
reason: "it is not valid JSON"
|
|
21122
|
-
};
|
|
21123
|
-
}
|
|
21124
|
-
if (!isPlainRecord(config)) return {
|
|
21125
|
-
kind: "unreadable",
|
|
21126
|
-
reason: "it is not a JSON object"
|
|
21127
|
-
};
|
|
21128
|
-
const projects = config.projects ?? {};
|
|
21129
|
-
if (!isPlainRecord(projects)) return {
|
|
21130
|
-
kind: "unreadable",
|
|
21131
|
-
reason: "its projects key is not an object"
|
|
21132
|
-
};
|
|
21133
|
-
const existingEntry = projects[projectKey];
|
|
21134
|
-
if (existingEntry !== void 0 && !isPlainRecord(existingEntry)) return {
|
|
21135
|
-
kind: "unreadable",
|
|
21136
|
-
reason: "its entry for this folder is not an object"
|
|
21137
|
-
};
|
|
21138
|
-
const keysToGrant = hasRecordedExternalImportsDecline(existingEntry) ? TRUST_ONLY_KEYS : GRANTED_KEYS;
|
|
21139
|
-
if (keysToGrant.every((key) => existingEntry?.[key] === true)) return { kind: "trusted" };
|
|
21140
|
-
const trustedEntry = {
|
|
21141
|
-
...existingEntry ?? newProjectEntry(),
|
|
21142
|
-
...Object.fromEntries(keysToGrant.map((key) => [key, true]))
|
|
21143
|
-
};
|
|
21144
|
-
const nextConfigText = JSON.stringify({
|
|
21145
|
-
...config,
|
|
21146
|
-
projects: {
|
|
21147
|
-
...projects,
|
|
21148
|
-
[projectKey]: trustedEntry
|
|
21149
|
-
}
|
|
21150
|
-
}, null, 2);
|
|
21151
|
-
return {
|
|
21152
|
-
kind: "grant",
|
|
21153
|
-
nextConfigText: configText.endsWith("\n") ? `${nextConfigText}\n` : nextConfigText
|
|
21154
|
-
};
|
|
21155
|
-
}
|
|
21156
|
-
//#endregion
|
|
21157
|
-
//#region server/guardrails/core/guardrails-bridge-socket-core.ts
|
|
21158
|
-
var LONGEST_BRIDGE_SOCKET_NAME = "claude-socks-0123456789abcdef.sock";
|
|
21159
|
-
var UNIX_SOCKET_PATH_BYTE_LIMIT = 107;
|
|
21160
|
-
var SHORT_BRIDGE_SOCKET_FOLDER = "/tmp";
|
|
21161
|
-
function bridgeSocketPathBytes(tempDirectory) {
|
|
21162
|
-
return Buffer.byteLength(path.join(tempDirectory, LONGEST_BRIDGE_SOCKET_NAME));
|
|
21163
|
-
}
|
|
21164
|
-
function hasRoomForBridgeSocket(tempDirectory, platform) {
|
|
21165
|
-
return platform !== "linux" || bridgeSocketPathBytes(tempDirectory) <= UNIX_SOCKET_PATH_BYTE_LIMIT;
|
|
21166
|
-
}
|
|
21167
|
-
function bridgeSocketLauncherEnv(inheritedTempDirectory, platform) {
|
|
21168
|
-
if (hasRoomForBridgeSocket(inheritedTempDirectory, platform)) return { TMPDIR: inheritedTempDirectory };
|
|
21169
|
-
return { TMPDIR: SHORT_BRIDGE_SOCKET_FOLDER };
|
|
21170
|
-
}
|
|
21171
|
-
//#endregion
|
|
21172
|
-
//#region server/prune-files.ts
|
|
21173
|
-
var MS_PER_DAY = 864e5;
|
|
21174
|
-
function idForEntry(entry, suffixes) {
|
|
21175
|
-
for (const suffix of suffixes) {
|
|
21176
|
-
if (suffix.length === 0) return entry;
|
|
21177
|
-
if (entry.endsWith(suffix)) return entry.slice(0, -suffix.length);
|
|
21178
|
-
}
|
|
21179
|
-
return null;
|
|
21180
|
-
}
|
|
21181
|
-
async function pruneAgedFiles({ directory, suffixes, retainDays, now = Date.now(), isRetainedId = () => false, entryMode = "file", fsPromises = fs.promises }) {
|
|
21182
|
-
let entries;
|
|
21183
|
-
try {
|
|
21184
|
-
entries = await fsPromises.readdir(directory);
|
|
21185
|
-
} catch {
|
|
21186
|
-
return [];
|
|
21187
|
-
}
|
|
21188
|
-
const cutoff = now - retainDays * MS_PER_DAY;
|
|
21189
|
-
const removedIds = [];
|
|
21190
|
-
for (const entry of entries) {
|
|
21191
|
-
const id = idForEntry(entry, suffixes);
|
|
21192
|
-
if (!id || isRetainedId(id)) continue;
|
|
21193
|
-
const filePath = path.join(directory, entry);
|
|
21194
|
-
try {
|
|
21195
|
-
if ((await fsPromises.stat(filePath)).mtimeMs >= cutoff) continue;
|
|
21196
|
-
if (entryMode === "directory") await fsPromises.rm(filePath, {
|
|
21197
|
-
recursive: true,
|
|
21198
|
-
force: true
|
|
21199
|
-
});
|
|
21200
|
-
if (entryMode === "file") await fsPromises.unlink(filePath);
|
|
21201
|
-
removedIds.push(id);
|
|
21202
|
-
} catch {}
|
|
21203
|
-
}
|
|
21204
|
-
return removedIds;
|
|
21205
|
-
}
|
|
21206
|
-
//#endregion
|
|
21207
|
-
//#region server/guardrails/guardrails-launch.ts
|
|
21208
|
-
var LAUNCH_ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
21209
|
-
var GUARDED_ENV_COMMAND = "/usr/bin/env";
|
|
21210
|
-
var POLICY_FOLDER = "guardrails-policies";
|
|
21211
|
-
var LAUNCH_HOME_FOLDER = "guardrails-homes";
|
|
21212
|
-
var START_FOLDER = "start";
|
|
21213
|
-
var OWNER_FILE_SUFFIX = ".owner.json";
|
|
21214
|
-
var ENTER_FOLDER_SCRIPT = "cd \"$1\" && shift && exec \"$@\"";
|
|
21215
|
-
var LOGIN_FILE_MODE = 384;
|
|
21216
|
-
var ABSENT_PATH_DENY_PLATFORM = "darwin";
|
|
21217
|
-
var OWN_PID_NAMESPACE_LINK = "/proc/self/ns/pid";
|
|
21218
|
-
var SESSION_TEMP_RETAIN_DAYS = 7;
|
|
21219
|
-
async function isPresent(candidate) {
|
|
21220
|
-
try {
|
|
21221
|
-
await access(candidate);
|
|
21222
|
-
return true;
|
|
21223
|
-
} catch (error) {
|
|
21224
|
-
if (!isMissingFileError(error)) throw error;
|
|
21225
|
-
return false;
|
|
21226
|
-
}
|
|
21227
|
-
}
|
|
21228
|
-
async function presentPaths$1(candidates) {
|
|
21229
|
-
const presence = await Promise.all(candidates.map(isPresent));
|
|
21230
|
-
return new Set(candidates.filter((_candidate, index) => presence[index]));
|
|
21231
|
-
}
|
|
21232
|
-
async function realPathWhenPresent(candidate) {
|
|
21233
|
-
if (!path.isAbsolute(candidate)) return candidate;
|
|
21234
|
-
try {
|
|
21235
|
-
return await realpath(candidate);
|
|
21236
|
-
} catch (error) {
|
|
21237
|
-
if (!isMissingFileError(error)) throw error;
|
|
21238
|
-
return candidate;
|
|
21239
|
-
}
|
|
21240
|
-
}
|
|
21241
|
-
function realPathsWhenPresent(candidates) {
|
|
21242
|
-
return Promise.all(candidates.map(realPathWhenPresent));
|
|
21243
|
-
}
|
|
21244
|
-
async function symlinkTargetsInside(folder) {
|
|
21245
|
-
const symlinkEntries = (await readdir(folder, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isSymbolicLink());
|
|
21246
|
-
return (await Promise.all(symlinkEntries.map((entry) => realpath(path.join(folder, entry.name)).catch(() => null)))).filter((target) => target !== null);
|
|
21247
|
-
}
|
|
21248
|
-
async function blockedPathsWithRealTargets(candidates, operatorBlockedPaths) {
|
|
21249
|
-
const resolved = await Promise.all(candidates.map(async (candidate) => {
|
|
21250
|
-
const isPresentEntry = await lstat(candidate).then(() => true, (error) => !isMissingFileError(error));
|
|
21251
|
-
const isKeptWhenAbsent = process.platform === ABSENT_PATH_DENY_PLATFORM || operatorBlockedPaths.includes(candidate);
|
|
21252
|
-
if (!isPresentEntry) return isKeptWhenAbsent ? [candidate] : [];
|
|
21253
|
-
const realCandidate = await realpath(candidate).catch(() => candidate);
|
|
21254
|
-
return [
|
|
21255
|
-
candidate,
|
|
21256
|
-
realCandidate,
|
|
21257
|
-
...await symlinkTargetsInside(realCandidate)
|
|
21258
|
-
];
|
|
21259
|
-
}));
|
|
21260
|
-
return [...new Set(resolved.flat())];
|
|
21261
|
-
}
|
|
21262
|
-
async function configuredConfigFile(glimmervoidHome) {
|
|
21263
|
-
const { envPath, homePath } = decideConfigPath({
|
|
21264
|
-
env: process.env,
|
|
21265
|
-
homeDir: glimmervoidHome
|
|
21266
|
-
}, () => false);
|
|
21267
|
-
const configFile = envPath ?? homePath;
|
|
21268
|
-
return path.join(await realPathWhenPresent(path.dirname(configFile)), path.basename(configFile));
|
|
21269
|
-
}
|
|
21270
|
-
function scannedClaudeHomes(env, homeDir) {
|
|
21271
|
-
return [env, {
|
|
21272
|
-
...env,
|
|
21273
|
-
CLAUDE_CONFIG_DIR: void 0
|
|
21274
|
-
}].flatMap((scannedEnv) => projectDirCandidates(scannedEnv, [], homeDir)).map((projectsDir) => path.dirname(projectsDir));
|
|
21275
|
-
}
|
|
21276
|
-
async function resolveVendorLocations(env, homeDir) {
|
|
21277
|
-
const [legacyClaudeConfigPath, claudeConfigPath] = listClaudeGlobalConfigPathCandidates(env, homeDir);
|
|
21278
|
-
const [codexStateFolder, ...otherCodexHomes] = codexHomes(env, homeDir);
|
|
21279
|
-
if (codexStateFolder === void 0) return null;
|
|
21280
|
-
return {
|
|
21281
|
-
claude: {
|
|
21282
|
-
stateFolder: await realPathWhenPresent(path.dirname(legacyClaudeConfigPath)),
|
|
21283
|
-
configFiles: [await realPathWhenPresent(claudeConfigPath)],
|
|
21284
|
-
otherHomes: await realPathsWhenPresent(scannedClaudeHomes(env, homeDir))
|
|
21285
|
-
},
|
|
21286
|
-
codex: {
|
|
21287
|
-
stateFolder: await realPathWhenPresent(codexStateFolder),
|
|
21288
|
-
configFiles: [],
|
|
21289
|
-
otherHomes: await realPathsWhenPresent(otherCodexHomes)
|
|
21290
|
-
}
|
|
21291
|
-
};
|
|
21292
|
-
}
|
|
21293
|
-
async function withRealRoots(role) {
|
|
21294
|
-
const [writableRoots, lockedCheckouts, commitCheckouts] = await Promise.all([
|
|
21295
|
-
Promise.all(role.writableRoots.map((root) => realpath(root))),
|
|
21296
|
-
Promise.all(role.lockedCheckouts.map((checkout) => realpath(checkout))),
|
|
21297
|
-
Promise.all(role.commitCheckouts.map(async ({ checkout, gitDir }) => ({
|
|
21298
|
-
checkout: await realpath(checkout),
|
|
21299
|
-
gitDir: await realpath(gitDir)
|
|
21300
|
-
})))
|
|
21301
|
-
]);
|
|
21302
|
-
const vendorHome = role.vendorHome.scope === "launch" ? {
|
|
21303
|
-
...role.vendorHome,
|
|
21304
|
-
cwd: await realPathWhenPresent(role.vendorHome.cwd)
|
|
21305
|
-
} : role.vendorHome;
|
|
21306
|
-
return {
|
|
21307
|
-
...role,
|
|
21308
|
-
vendorHome,
|
|
21309
|
-
writableRoots,
|
|
21310
|
-
lockedCheckouts,
|
|
21311
|
-
commitCheckouts,
|
|
21312
|
-
readableWithinDenied: await realPathsWhenPresent(role.readableWithinDenied)
|
|
21313
|
-
};
|
|
21314
|
-
}
|
|
21315
|
-
async function lstatWhenPresent(candidate) {
|
|
21316
|
-
try {
|
|
21317
|
-
return await lstat(candidate);
|
|
21318
|
-
} catch (error) {
|
|
21319
|
-
if (!isMissingFileError(error)) throw error;
|
|
21320
|
-
return null;
|
|
21321
|
-
}
|
|
21322
|
-
}
|
|
21323
|
-
async function isRegularFile(candidate) {
|
|
21324
|
-
return (await lstatWhenPresent(candidate))?.isFile() ?? false;
|
|
21325
|
-
}
|
|
21326
|
-
async function isRealDirectory$1(candidate) {
|
|
21327
|
-
return (await lstatWhenPresent(candidate))?.isDirectory() ?? false;
|
|
21328
|
-
}
|
|
21329
|
-
async function absentPaths(candidates) {
|
|
21330
|
-
const presentCandidates = await presentPaths$1(candidates);
|
|
21331
|
-
return candidates.filter((candidate) => !presentCandidates.has(candidate));
|
|
21332
|
-
}
|
|
21333
|
-
async function removeLeftoverPlaceholders(placeholderPaths) {
|
|
21334
|
-
for (const placeholderPath of placeholderPaths) {
|
|
21335
|
-
const placeholderStats = await lstat(placeholderPath).catch(() => null);
|
|
21336
|
-
if (placeholderStats?.isFile() && placeholderStats.size === 0) await rm(placeholderPath, { force: true });
|
|
21709
|
+
};
|
|
21337
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
|
+
};
|
|
21338
21726
|
}
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
if (errorCode(error) !== "EEXIST") throw error;
|
|
21344
|
-
});
|
|
21345
|
-
}
|
|
21727
|
+
function everyTextBlockJoined(blocks) {
|
|
21728
|
+
const blockTexts = blocks.flatMap((block) => firstTextBlock([block]) ?? []);
|
|
21729
|
+
if (blockTexts.length === 0) return null;
|
|
21730
|
+
return blockTexts.join("\n");
|
|
21346
21731
|
}
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
if (targetStats === null) return true;
|
|
21350
|
-
return (await lstat(linkPath)).mtimeMs > targetStats.mtimeMs;
|
|
21732
|
+
function isTaskNotificationLine(line) {
|
|
21733
|
+
return line.origin?.kind === "task-notification" || line.turnOrigin?.kind === "task_notification";
|
|
21351
21734
|
}
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
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
|
+
}];
|
|
21360
21752
|
}
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
|
|
21366
|
-
|
|
21367
|
-
|
|
21368
|
-
|
|
21369
|
-
|
|
21370
|
-
|
|
21371
|
-
|
|
21372
|
-
|
|
21373
|
-
|
|
21374
|
-
|
|
21375
|
-
}
|
|
21376
|
-
function isProcessAlive$1(pid) {
|
|
21377
|
-
try {
|
|
21378
|
-
process.kill(pid, 0);
|
|
21379
|
-
return true;
|
|
21380
|
-
} catch (error) {
|
|
21381
|
-
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
|
+
}];
|
|
21382
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
|
+
}];
|
|
21383
21786
|
}
|
|
21384
|
-
function
|
|
21385
|
-
|
|
21787
|
+
function queuedCommandText(prompt) {
|
|
21788
|
+
if (typeof prompt === "string") return prompt || null;
|
|
21789
|
+
return firstTextBlock(objectBlocks(prompt));
|
|
21386
21790
|
}
|
|
21387
|
-
|
|
21388
|
-
const
|
|
21389
|
-
|
|
21390
|
-
|
|
21391
|
-
|
|
21392
|
-
|
|
21393
|
-
|
|
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;
|
|
21394
21807
|
}
|
|
21395
|
-
|
|
21396
|
-
|
|
21397
|
-
|
|
21398
|
-
|
|
21399
|
-
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
|
|
21403
|
-
|
|
21404
|
-
let removedLaunchCount = 0;
|
|
21405
|
-
for (const entryName of entryNames) {
|
|
21406
|
-
if (!entryName.endsWith(OWNER_FILE_SUFFIX)) continue;
|
|
21407
|
-
const launchId = entryName.slice(0, -11);
|
|
21408
|
-
if (!LAUNCH_ID_SHAPE.test(launchId)) continue;
|
|
21409
|
-
const ownerPath = path.join(policyDir, entryName);
|
|
21410
|
-
const owner = await readOwnerFile(ownerPath);
|
|
21411
|
-
if (owner === null || owner.ownerHost !== os.hostname() || owner.ownerPidNamespace !== sweeperPidNamespace || isProcessAlive$1(owner.ownerPid)) continue;
|
|
21412
|
-
try {
|
|
21413
|
-
await removeLaunch({
|
|
21414
|
-
policyPath: path.join(policyDir, `${launchId}.json`),
|
|
21415
|
-
ownerPath,
|
|
21416
|
-
launchHomeDir: path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId),
|
|
21417
|
-
ownedTempDir: owner.ownsTempDir ? path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER, launchId) : null
|
|
21418
|
-
}, owner, launchId, { onlyNewerLogins: true });
|
|
21419
|
-
removedLaunchCount += 1;
|
|
21420
|
-
} catch (error) {
|
|
21421
|
-
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;
|
|
21422
21817
|
}
|
|
21818
|
+
records.push({
|
|
21819
|
+
...base,
|
|
21820
|
+
kind: "thinking",
|
|
21821
|
+
...boundedTextFields(block.thinking)
|
|
21822
|
+
});
|
|
21823
|
+
continue;
|
|
21423
21824
|
}
|
|
21424
|
-
if (
|
|
21425
|
-
|
|
21426
|
-
|
|
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
|
+
});
|
|
21427
21845
|
}
|
|
21846
|
+
if (records.length === 0 && hasSkippedThinkingBlock) return [];
|
|
21847
|
+
if (records.length === 0) return [rawRecord(rawLine, line, context)];
|
|
21848
|
+
return records;
|
|
21428
21849
|
}
|
|
21429
|
-
|
|
21430
|
-
const
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
if (
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
const tempFolderNamesWithOwners = /* @__PURE__ */ new Set([...ownerEntryNames.map((entryName) => entryName.slice(0, -11)), ...owners.flatMap((owner) => owner?.keptTempFolderName === void 0 ? [] : [owner.keptTempFolderName])]);
|
|
21439
|
-
await pruneAgedFiles({
|
|
21440
|
-
directory: path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER),
|
|
21441
|
-
suffixes: [""],
|
|
21442
|
-
retainDays: SESSION_TEMP_RETAIN_DAYS,
|
|
21443
|
-
entryMode: "directory",
|
|
21444
|
-
isRetainedId: (id) => liveSessionIds.has(id) || tempFolderNamesWithOwners.has(id)
|
|
21445
|
-
});
|
|
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)];
|
|
21446
21859
|
}
|
|
21447
|
-
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
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);
|
|
21454
21869
|
return {
|
|
21455
|
-
|
|
21456
|
-
|
|
21457
|
-
runtimeCliPath,
|
|
21458
|
-
"--settings",
|
|
21459
|
-
policyPath,
|
|
21460
|
-
"--"
|
|
21461
|
-
]
|
|
21870
|
+
tool,
|
|
21871
|
+
detail: toolDetailLine(tool, toolInput)
|
|
21462
21872
|
};
|
|
21463
21873
|
}
|
|
21464
|
-
function
|
|
21465
|
-
|
|
21466
|
-
return
|
|
21467
|
-
}
|
|
21468
|
-
function enteringFolder(enterDir, file, args) {
|
|
21469
|
-
if (enterDir === void 0) return [file, ...args];
|
|
21470
|
-
return [
|
|
21471
|
-
"/bin/sh",
|
|
21472
|
-
"-c",
|
|
21473
|
-
ENTER_FOLDER_SCRIPT,
|
|
21474
|
-
"sh",
|
|
21475
|
-
enterDir,
|
|
21476
|
-
file,
|
|
21477
|
-
...args
|
|
21478
|
-
];
|
|
21874
|
+
function trailStepFromHook(event, payload) {
|
|
21875
|
+
if (String(event ?? "").toLowerCase() !== "pretooluse") return null;
|
|
21876
|
+
return describeToolStep(payload.tool_name, payload.tool_input);
|
|
21479
21877
|
}
|
|
21480
|
-
function
|
|
21481
|
-
|
|
21482
|
-
|
|
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 !== "");
|
|
21483
21883
|
}
|
|
21484
|
-
|
|
21485
|
-
|
|
21486
|
-
|
|
21487
|
-
|
|
21884
|
+
function createInvestigationTrail(startedAt) {
|
|
21885
|
+
return {
|
|
21886
|
+
startedAt,
|
|
21887
|
+
steps: []
|
|
21488
21888
|
};
|
|
21489
|
-
|
|
21490
|
-
|
|
21491
|
-
|
|
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]
|
|
21492
21899
|
};
|
|
21493
|
-
|
|
21494
|
-
|
|
21495
|
-
|
|
21496
|
-
recursive: true,
|
|
21497
|
-
mode: 448
|
|
21498
|
-
});
|
|
21499
|
-
const [homeDir, glimmervoidHome, realRole, ownerPidNamespace] = await Promise.all([
|
|
21500
|
-
realpath(os.homedir()),
|
|
21501
|
-
realpath(glimmervoidHomeDir$1()),
|
|
21502
|
-
withRealRoots(role),
|
|
21503
|
-
readOwnPidNamespace()
|
|
21504
|
-
]);
|
|
21505
|
-
const ownsTempDir = tempDir === void 0 && keptTempFolderName === void 0;
|
|
21506
|
-
const launchTempDir = launchTempDirFor(glimmervoidHome, launchId, tempDir, keptTempFolderName);
|
|
21507
|
-
const launchHomeDir = path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId);
|
|
21508
|
-
const vendorLocations = await resolveVendorLocations(process.env, homeDir);
|
|
21509
|
-
if (vendorLocations === null) return {
|
|
21510
|
-
ok: false,
|
|
21511
|
-
reason: "CODEX_HOME names no folder"
|
|
21512
|
-
};
|
|
21513
|
-
const xdgConfigHomeOverride = rawTextOr(process.env.XDG_CONFIG_HOME, null);
|
|
21514
|
-
const stableHostPaths = {
|
|
21515
|
-
homeDir,
|
|
21516
|
-
glimmervoidHome,
|
|
21517
|
-
glimmervoidConfigFile: await configuredConfigFile(glimmervoidHome),
|
|
21518
|
-
tempDir: launchTempDir,
|
|
21519
|
-
launchHomeDir,
|
|
21520
|
-
vendorLocations,
|
|
21521
|
-
unwrappedVendorHomes: await realPathsWhenPresent(grokHomes(process.env, homeDir)),
|
|
21522
|
-
relocatedXdgConfigHome: xdgConfigHomeOverride === null ? null : await realPathWhenPresent(xdgConfigHomeOverride)
|
|
21523
|
-
};
|
|
21524
|
-
const buildPlanFromCurrentDisk = async () => {
|
|
21525
|
-
const [homeEntryNames, glimmervoidHomeEntryNames] = await Promise.all([readdir(homeDir), readdir(glimmervoidHome)]);
|
|
21526
|
-
const hostPaths = {
|
|
21527
|
-
...stableHostPaths,
|
|
21528
|
-
homeEntryNames,
|
|
21529
|
-
glimmervoidHomeEntryNames
|
|
21530
|
-
};
|
|
21531
|
-
const [existingPaths, writeBlockedPaths] = await Promise.all([presentPaths$1(guardrailsPolicyProbePaths(realRole, hostPaths)), blockedPathsWithRealTargets(standardWriteBlockCandidates(realRole, hostPaths), realRole.blockedPaths ?? [])]);
|
|
21532
|
-
return buildGuardrailsLaunchPlan(realRole, {
|
|
21533
|
-
...hostPaths,
|
|
21534
|
-
existingPaths,
|
|
21535
|
-
writeBlockedPaths
|
|
21536
|
-
});
|
|
21537
|
-
};
|
|
21538
|
-
const built = await buildPlanFromCurrentDisk();
|
|
21539
|
-
if (!built.ok) return built;
|
|
21540
|
-
const { plan } = built;
|
|
21541
|
-
const policyPath = path.join(policyDir, `${launchId}.json`);
|
|
21542
|
-
const launcher = launcherCommand(plan, policyPath);
|
|
21543
|
-
if (!launcher) return {
|
|
21544
|
-
ok: false,
|
|
21545
|
-
reason: "the sandbox runtime CLI could not be resolved"
|
|
21546
|
-
};
|
|
21547
|
-
await Promise.all(plan.requiredDirectories.map((directory) => mkdir(directory, { recursive: true })));
|
|
21548
|
-
const startDir = enterDir === void 0 ? void 0 : path.join(launchTempDir, START_FOLDER);
|
|
21549
|
-
if (startDir !== void 0) await mkdir(startDir, { recursive: true });
|
|
21550
|
-
await Promise.all(plan.launchHomeLinks.map((link) => symlink(link.targetPath, link.linkPath)));
|
|
21551
|
-
await writeJsonAtomic(policyPath, plan.policy);
|
|
21552
|
-
const folders = {
|
|
21553
|
-
policyPath,
|
|
21554
|
-
ownerPath: path.join(policyDir, `${launchId}${OWNER_FILE_SUFFIX}`),
|
|
21555
|
-
launchHomeDir,
|
|
21556
|
-
ownedTempDir: ownsTempDir ? launchTempDir : null
|
|
21557
|
-
};
|
|
21558
|
-
const owner = {
|
|
21559
|
-
ownerPid: process.pid,
|
|
21560
|
-
ownerHost: os.hostname(),
|
|
21561
|
-
ownerPidNamespace,
|
|
21562
|
-
ownsTempDir,
|
|
21563
|
-
...keptTempFolderName === void 0 ? {} : { keptTempFolderName },
|
|
21564
|
-
logins: plan.launchHomeLinks.filter((link) => link.isLogin).map(({ linkPath, targetPath }) => ({
|
|
21565
|
-
linkPath,
|
|
21566
|
-
targetPath
|
|
21567
|
-
})),
|
|
21568
|
-
placeholderPaths: []
|
|
21569
|
-
};
|
|
21570
|
-
await writeJsonAtomic(folders.ownerPath, owner);
|
|
21571
|
-
const envPrefix = guardedEnvPrefix(plan);
|
|
21572
|
-
return {
|
|
21573
|
-
ok: true,
|
|
21574
|
-
launch: {
|
|
21575
|
-
wrap: async (file, args, { hasTerminal }) => {
|
|
21576
|
-
const rebuilt = await buildPlanFromCurrentDisk();
|
|
21577
|
-
if (!rebuilt.ok) throw new Error(rebuilt.reason);
|
|
21578
|
-
await writeJsonAtomic(policyPath, {
|
|
21579
|
-
...rebuilt.plan.policy,
|
|
21580
|
-
allowPty: hasTerminal && rebuilt.plan.launcher === "open-network"
|
|
21581
|
-
});
|
|
21582
|
-
owner.placeholderPaths = await absentPaths(guardrailsPlaceholderPaths(realRole));
|
|
21583
|
-
await writeJsonAtomic(folders.ownerPath, owner);
|
|
21584
|
-
await createMissingMountPoints(lockedClaudeMountPoints(realRole));
|
|
21585
|
-
return {
|
|
21586
|
-
file: launcher.file,
|
|
21587
|
-
args: [
|
|
21588
|
-
...launcher.args,
|
|
21589
|
-
...envPrefix,
|
|
21590
|
-
...enteringFolder(enterDir, file, args)
|
|
21591
|
-
]
|
|
21592
|
-
};
|
|
21593
|
-
},
|
|
21594
|
-
env: {
|
|
21595
|
-
...plan.launchEnv,
|
|
21596
|
-
...bridgeSocketLauncherEnv(os.tmpdir(), process.platform)
|
|
21597
|
-
},
|
|
21598
|
-
...startDir === void 0 ? {} : { cwd: startDir },
|
|
21599
|
-
dispose: () => removeLaunch(folders, owner, launchId, { onlyNewerLogins: false })
|
|
21600
|
-
}
|
|
21601
|
-
};
|
|
21602
|
-
} catch (error) {
|
|
21603
|
-
return {
|
|
21604
|
-
ok: false,
|
|
21605
|
-
reason: errorMessage(error)
|
|
21606
|
-
};
|
|
21607
|
-
}
|
|
21900
|
+
}
|
|
21901
|
+
function isTrailNote(entry) {
|
|
21902
|
+
return "note" in entry;
|
|
21608
21903
|
}
|
|
21609
21904
|
//#endregion
|
|
21610
21905
|
//#region server/lanes/lane-spawn.ts
|
|
@@ -21625,7 +21920,7 @@ var LANE_SPAWN_DENY_TOOLS = Object.freeze([
|
|
|
21625
21920
|
async function writeStandaloneDenySettings(permissions, userHooks) {
|
|
21626
21921
|
const settingsDirectory = await makeHostScratchDirectory(SETTINGS_DIR_PREFIX);
|
|
21627
21922
|
const settingsPath = path.join(settingsDirectory, "settings.json");
|
|
21628
|
-
const removeSettingsDirectory = () =>
|
|
21923
|
+
const removeSettingsDirectory = () => fs$1.rm(settingsDirectory, {
|
|
21629
21924
|
recursive: true,
|
|
21630
21925
|
force: true
|
|
21631
21926
|
}).catch(() => {});
|
|
@@ -21725,7 +22020,7 @@ function createLaneSpawn({ sessions = /* @__PURE__ */ new Map(), closeSessionDat
|
|
|
21725
22020
|
}
|
|
21726
22021
|
async function readLaneResultFile(resultPath) {
|
|
21727
22022
|
try {
|
|
21728
|
-
return JSON.parse(await
|
|
22023
|
+
return JSON.parse(await fs$1.readFile(resultPath, "utf8"));
|
|
21729
22024
|
} catch {
|
|
21730
22025
|
return null;
|
|
21731
22026
|
}
|
|
@@ -21736,12 +22031,12 @@ var THREAD_JUDGE_TOOLS = Object.freeze(["Read", "Write"]);
|
|
|
21736
22031
|
var THREAD_JUDGE_TIMEOUT_MS = 12e4;
|
|
21737
22032
|
function createThreadJudge(spawn, shutdownSignal) {
|
|
21738
22033
|
return async (prompt) => {
|
|
21739
|
-
const workDir = await
|
|
22034
|
+
const workDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-thread-judge-"));
|
|
21740
22035
|
const controller = new AbortController();
|
|
21741
22036
|
const timeout = setTimeout(() => controller.abort(), THREAD_JUDGE_TIMEOUT_MS);
|
|
21742
22037
|
timeout.unref();
|
|
21743
22038
|
try {
|
|
21744
|
-
await
|
|
22039
|
+
await fs$1.writeFile(path.join(workDir, "thread-prompt.txt"), prompt, "utf8");
|
|
21745
22040
|
const signal = shutdownSignal ? AbortSignal.any([controller.signal, shutdownSignal]) : controller.signal;
|
|
21746
22041
|
if (signal.aborted) return null;
|
|
21747
22042
|
await spawn({
|
|
@@ -21756,7 +22051,7 @@ function createThreadJudge(spawn, shutdownSignal) {
|
|
|
21756
22051
|
return parseThreadJudgeResult(await readLaneResultFile(path.join(workDir, "thread-result.json")));
|
|
21757
22052
|
} finally {
|
|
21758
22053
|
clearTimeout(timeout);
|
|
21759
|
-
await
|
|
22054
|
+
await fs$1.rm(workDir, {
|
|
21760
22055
|
recursive: true,
|
|
21761
22056
|
force: true
|
|
21762
22057
|
});
|
|
@@ -21773,13 +22068,14 @@ var GH_MERGE_FLAGS = {
|
|
|
21773
22068
|
var MY_PR_MERGE_STATE_QUERY = `query($owner: String!, $name: String!, $number: Int!) {
|
|
21774
22069
|
repository(owner: $owner, name: $name) { pullRequest(number: $number) { state isInMergeQueue autoMergeRequest { enabledAt } } }
|
|
21775
22070
|
}`;
|
|
21776
|
-
var
|
|
22071
|
+
var GH_READ_TIMEOUT_MS = 15e3;
|
|
22072
|
+
var GH_WRITE_TIMEOUT_MS = 3e4;
|
|
21777
22073
|
var GH_MAX_BUFFER_BYTES = 2097153;
|
|
21778
|
-
function run(cmd, args, cwd, input, preserveOutput = false) {
|
|
22074
|
+
function run(cmd, args, cwd, input, preserveOutput = false, timeoutMs = GH_READ_TIMEOUT_MS) {
|
|
21779
22075
|
return runCommand(cmd, args, {
|
|
21780
22076
|
cwd,
|
|
21781
22077
|
input,
|
|
21782
|
-
timeoutMs
|
|
22078
|
+
timeoutMs,
|
|
21783
22079
|
maxBuffer: GH_MAX_BUFFER_BYTES,
|
|
21784
22080
|
trim: !preserveOutput,
|
|
21785
22081
|
keepStdoutOnFailure: true,
|
|
@@ -22084,9 +22380,9 @@ function conditionalGetResult(response) {
|
|
|
22084
22380
|
}
|
|
22085
22381
|
}
|
|
22086
22382
|
function createPrGh(cwd, commandRunner = run) {
|
|
22087
|
-
async function runGh(args, input, preserveOutput = false) {
|
|
22383
|
+
async function runGh(args, input, preserveOutput = false, timeoutMs = GH_READ_TIMEOUT_MS) {
|
|
22088
22384
|
try {
|
|
22089
|
-
return await commandRunner("gh", args, cwd, input, preserveOutput);
|
|
22385
|
+
return await commandRunner("gh", args, cwd, input, preserveOutput, timeoutMs);
|
|
22090
22386
|
} catch (error) {
|
|
22091
22387
|
return {
|
|
22092
22388
|
ok: false,
|
|
@@ -22416,7 +22712,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22416
22712
|
repo,
|
|
22417
22713
|
"--add-label",
|
|
22418
22714
|
label.data
|
|
22419
|
-
]);
|
|
22715
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22420
22716
|
return {
|
|
22421
22717
|
ok: response.ok,
|
|
22422
22718
|
err: response.ok ? "" : response.err.trim() || "gh pr edit failed"
|
|
@@ -22440,7 +22736,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22440
22736
|
repo,
|
|
22441
22737
|
"--body-file",
|
|
22442
22738
|
"-"
|
|
22443
|
-
], comment.data);
|
|
22739
|
+
], comment.data, false, GH_WRITE_TIMEOUT_MS);
|
|
22444
22740
|
return {
|
|
22445
22741
|
ok: response.ok,
|
|
22446
22742
|
err: response.ok ? "" : response.err.trim() || "gh pr comment failed"
|
|
@@ -22513,7 +22809,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22513
22809
|
`query=${RESOLVE_THREAD_MUTATION}`,
|
|
22514
22810
|
"-f",
|
|
22515
22811
|
`id=${threadId}`
|
|
22516
|
-
]);
|
|
22812
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22517
22813
|
const parsed = TeamReviewResolveResponse.safeParse(parseJsonOrNull(response.out));
|
|
22518
22814
|
const isResolved = response.ok && parsed.success && !parsed.data.errors?.length && parsed.data.data?.resolveReviewThread.thread.id === threadId;
|
|
22519
22815
|
return {
|
|
@@ -22674,7 +22970,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22674
22970
|
`id=${pullRequestId}`,
|
|
22675
22971
|
"-f",
|
|
22676
22972
|
`head=${expectedHeadSha}`
|
|
22677
|
-
]);
|
|
22973
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22678
22974
|
if (!response.ok) return {
|
|
22679
22975
|
ok: false,
|
|
22680
22976
|
err: response.err.trim() || "gh graphql rebase failed"
|
|
@@ -22713,7 +23009,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22713
23009
|
GH_MERGE_FLAGS[parsedMethod.data],
|
|
22714
23010
|
"--match-head-commit",
|
|
22715
23011
|
headSha
|
|
22716
|
-
]);
|
|
23012
|
+
], void 0, false, GH_WRITE_TIMEOUT_MS);
|
|
22717
23013
|
if (!response.ok) return {
|
|
22718
23014
|
ok: false,
|
|
22719
23015
|
err: response.err.trim() || "gh pr merge failed"
|
|
@@ -22949,7 +23245,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22949
23245
|
`repos/${parts[0]}/${parts[1]}/pulls/${number}/reviews`,
|
|
22950
23246
|
"--input",
|
|
22951
23247
|
"-"
|
|
22952
|
-
], input);
|
|
23248
|
+
], input, false, GH_WRITE_TIMEOUT_MS);
|
|
22953
23249
|
if (!response.ok) return {
|
|
22954
23250
|
ok: false,
|
|
22955
23251
|
err: response.err.trim() || "gh review post failed",
|
|
@@ -22987,7 +23283,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
22987
23283
|
`repos/${parts[0]}/${parts[1]}/pulls/${number}/reviews/${reviewId}/dismissals`,
|
|
22988
23284
|
"--input",
|
|
22989
23285
|
"-"
|
|
22990
|
-
], input);
|
|
23286
|
+
], input, false, GH_WRITE_TIMEOUT_MS);
|
|
22991
23287
|
return {
|
|
22992
23288
|
ok: response.ok,
|
|
22993
23289
|
err: response.ok ? "" : response.err.trim() || "gh review dismissal failed"
|
|
@@ -23341,7 +23637,7 @@ var PROCESS_LIST_TIMEOUT_MS = 5e3;
|
|
|
23341
23637
|
var TERMINATION_GRACE_MS = 2e3;
|
|
23342
23638
|
var EXIT_POLL_MS = 100;
|
|
23343
23639
|
async function resolvedPath(directory) {
|
|
23344
|
-
return
|
|
23640
|
+
return fs$1.realpath(directory).catch(() => path.resolve(directory));
|
|
23345
23641
|
}
|
|
23346
23642
|
async function listTeamReviewProcesses() {
|
|
23347
23643
|
if (process.platform !== "darwin" && process.platform !== "linux") return [];
|
|
@@ -23358,7 +23654,7 @@ async function listTeamReviewProcesses() {
|
|
|
23358
23654
|
const rows = parseProcessTable(stdout);
|
|
23359
23655
|
if (process.platform === "linux") return Promise.all(rows.map(async (row) => ({
|
|
23360
23656
|
...row,
|
|
23361
|
-
cwd: await
|
|
23657
|
+
cwd: await fs$1.readlink(`/proc/${row.pid}/cwd`).then((cwd) => path.resolve(cwd)).catch(() => null)
|
|
23362
23658
|
})));
|
|
23363
23659
|
const { stdout: cwdOutput } = await execFileAsync("lsof", [
|
|
23364
23660
|
"-a",
|
|
@@ -23373,7 +23669,7 @@ async function listTeamReviewProcesses() {
|
|
|
23373
23669
|
const cwdByPid = parseCwdTable(cwdOutput);
|
|
23374
23670
|
return Promise.all(rows.map(async (row) => ({
|
|
23375
23671
|
...row,
|
|
23376
|
-
cwd: await
|
|
23672
|
+
cwd: await fs$1.realpath(cwdByPid.get(row.pid) ?? "").catch(() => null)
|
|
23377
23673
|
})));
|
|
23378
23674
|
}
|
|
23379
23675
|
function isMissingProcess(error) {
|
|
@@ -24120,9 +24416,13 @@ function createTeamReviewPoller(deps) {
|
|
|
24120
24416
|
}
|
|
24121
24417
|
async function rateLimitedOutcome() {
|
|
24122
24418
|
const rateLimitWaitMs = await store.rateLimitWaitMs(now(), TEAM_REVIEW_RATE_LIMIT_RESOURCES) ?? secondaryRateLimitWaitMs(pollingError);
|
|
24123
|
-
return rateLimitWaitMs === null ? {
|
|
24419
|
+
return rateLimitWaitMs === null ? {
|
|
24124
24420
|
failed: true,
|
|
24125
|
-
|
|
24421
|
+
error: pollingError ?? void 0
|
|
24422
|
+
} : {
|
|
24423
|
+
failed: true,
|
|
24424
|
+
retryAfterMs: rateLimitWaitMs,
|
|
24425
|
+
error: pollingError ?? void 0
|
|
24126
24426
|
};
|
|
24127
24427
|
}
|
|
24128
24428
|
async function runTick() {
|
|
@@ -24137,7 +24437,8 @@ function createTeamReviewPoller(deps) {
|
|
|
24137
24437
|
pollingError = "GitHub rate limit asks to wait.";
|
|
24138
24438
|
return {
|
|
24139
24439
|
failed: true,
|
|
24140
|
-
retryAfterMs: tickRateLimitWaitMs
|
|
24440
|
+
retryAfterMs: tickRateLimitWaitMs,
|
|
24441
|
+
error: pollingError
|
|
24141
24442
|
};
|
|
24142
24443
|
}
|
|
24143
24444
|
if (collected === null) {
|
|
@@ -24155,7 +24456,8 @@ function createTeamReviewPoller(deps) {
|
|
|
24155
24456
|
pollingError = "GitHub rate limit asks to wait.";
|
|
24156
24457
|
return {
|
|
24157
24458
|
failed: true,
|
|
24158
|
-
retryAfterMs: rateLimitWaitMs
|
|
24459
|
+
retryAfterMs: rateLimitWaitMs,
|
|
24460
|
+
error: pollingError
|
|
24159
24461
|
};
|
|
24160
24462
|
}
|
|
24161
24463
|
const isPruned = await pruneDeparted(new Set(candidates.map((candidate) => candidate.key)), isComplete);
|
|
@@ -24168,7 +24470,8 @@ function createTeamReviewPoller(deps) {
|
|
|
24168
24470
|
pollingError = "GitHub rate limit asks to wait.";
|
|
24169
24471
|
return {
|
|
24170
24472
|
failed: true,
|
|
24171
|
-
retryAfterMs: tickRateLimitWaitMs
|
|
24473
|
+
retryAfterMs: tickRateLimitWaitMs,
|
|
24474
|
+
error: pollingError
|
|
24172
24475
|
};
|
|
24173
24476
|
}
|
|
24174
24477
|
if (hasSlotFreedSinceSlotCount && waitingForSlot.length > 0 && !loop.isStopped()) setTimeoutFn(() => {
|
|
@@ -24421,10 +24724,11 @@ function createTeamReviewPoller(deps) {
|
|
|
24421
24724
|
error: "Reviews polling is stopped."
|
|
24422
24725
|
};
|
|
24423
24726
|
if (watches.size === 0) await collectCandidates();
|
|
24424
|
-
|
|
24425
|
-
|
|
24426
|
-
|
|
24427
|
-
|
|
24727
|
+
return loop.refresh(async () => {
|
|
24728
|
+
const refreshed = await store.refreshWatches([...watches.keys()]);
|
|
24729
|
+
rebuild();
|
|
24730
|
+
return refreshed;
|
|
24731
|
+
});
|
|
24428
24732
|
}
|
|
24429
24733
|
async function stop() {
|
|
24430
24734
|
hasLoadedState = false;
|
|
@@ -24819,7 +25123,7 @@ function listHomeWorktreePathsMissingLock(porcelain, glimmervoidHomes) {
|
|
|
24819
25123
|
}
|
|
24820
25124
|
//#endregion
|
|
24821
25125
|
//#region server/git/git-workspace.ts
|
|
24822
|
-
var fsp
|
|
25126
|
+
var fsp = fs.promises;
|
|
24823
25127
|
var MERGE_PROBE_TIMEOUT_MS = 3e4;
|
|
24824
25128
|
var REMOTE_REF_PREFIX = "refs/remotes/origin/";
|
|
24825
25129
|
var REMOTE_BRANCH_LISTING_MAX_BUFFER = 67108864;
|
|
@@ -24891,7 +25195,7 @@ function comparableGlimmervoidHomes(glimmervoidHome) {
|
|
|
24891
25195
|
}
|
|
24892
25196
|
async function directoryExists$1(candidatePath) {
|
|
24893
25197
|
try {
|
|
24894
|
-
await fsp
|
|
25198
|
+
await fsp.access(candidatePath);
|
|
24895
25199
|
return true;
|
|
24896
25200
|
} catch {
|
|
24897
25201
|
return false;
|
|
@@ -25051,13 +25355,19 @@ function createGitWorkspace(opts = {}) {
|
|
|
25051
25355
|
}
|
|
25052
25356
|
}
|
|
25053
25357
|
const runInProject = (projectPath) => (args) => run(args, projectPath);
|
|
25054
|
-
async function addWorktreeEntry(runGitInProject, worktreePath, addArgs) {
|
|
25358
|
+
async function addWorktreeEntry(runGitInProject, worktreePath, addArgs, ownership) {
|
|
25055
25359
|
const added = await runGitInProject([
|
|
25056
25360
|
"worktree",
|
|
25057
25361
|
"add",
|
|
25058
25362
|
...addArgs
|
|
25059
25363
|
]);
|
|
25060
|
-
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;
|
|
25061
25371
|
await lockWorktreeEntry(runGitInProject, worktreePath);
|
|
25062
25372
|
return added;
|
|
25063
25373
|
}
|
|
@@ -25083,12 +25393,27 @@ function createGitWorkspace(opts = {}) {
|
|
|
25083
25393
|
"unlock",
|
|
25084
25394
|
worktreePath
|
|
25085
25395
|
]);
|
|
25086
|
-
|
|
25396
|
+
const removed = await runGitInProject([
|
|
25087
25397
|
"worktree",
|
|
25088
25398
|
"remove",
|
|
25089
25399
|
...removeFlags,
|
|
25090
25400
|
worktreePath
|
|
25091
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
|
+
});
|
|
25092
25417
|
}
|
|
25093
25418
|
async function pruneWorktreeEntries(runGitInProject) {
|
|
25094
25419
|
const listed = await runGitInProject([
|
|
@@ -25326,7 +25651,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25326
25651
|
}
|
|
25327
25652
|
return null;
|
|
25328
25653
|
}
|
|
25329
|
-
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 }) {
|
|
25330
25655
|
const inside = await run(["rev-parse", "--is-inside-work-tree"], projectPath);
|
|
25331
25656
|
if (!inside.ok || inside.out !== "true") return {
|
|
25332
25657
|
cwd: projectPath,
|
|
@@ -25405,7 +25730,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25405
25730
|
branch,
|
|
25406
25731
|
wtDir,
|
|
25407
25732
|
baseSha
|
|
25408
|
-
]
|
|
25733
|
+
], {
|
|
25734
|
+
owner,
|
|
25735
|
+
projectPath,
|
|
25736
|
+
branch
|
|
25737
|
+
});
|
|
25409
25738
|
if (!add.ok) {
|
|
25410
25739
|
try {
|
|
25411
25740
|
fs.rmSync(wtDir, {
|
|
@@ -25453,7 +25782,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25453
25782
|
if (keepTrackableLinks) return;
|
|
25454
25783
|
await refuseTrackableLinks(wtDir, ignored);
|
|
25455
25784
|
}
|
|
25456
|
-
async function ensureWorkspaceMemberBody({ projectPath, wtDir, branch, shareList }) {
|
|
25785
|
+
async function ensureWorkspaceMemberBody({ projectPath, owner, wtDir, branch, shareList }) {
|
|
25457
25786
|
if (!wtDir || !branch) return {
|
|
25458
25787
|
cwd: projectPath,
|
|
25459
25788
|
isGit: false,
|
|
@@ -25504,7 +25833,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25504
25833
|
"--quiet",
|
|
25505
25834
|
`refs/heads/${branch}`
|
|
25506
25835
|
], projectPath)).ok) {
|
|
25507
|
-
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
|
+
});
|
|
25508
25841
|
if (!attached.ok) return {
|
|
25509
25842
|
cwd: projectPath,
|
|
25510
25843
|
isGit: false,
|
|
@@ -25555,7 +25888,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25555
25888
|
branch,
|
|
25556
25889
|
wtDir,
|
|
25557
25890
|
baseSha
|
|
25558
|
-
]
|
|
25891
|
+
], {
|
|
25892
|
+
owner,
|
|
25893
|
+
projectPath,
|
|
25894
|
+
branch
|
|
25895
|
+
});
|
|
25559
25896
|
if (!added.ok) return {
|
|
25560
25897
|
cwd: projectPath,
|
|
25561
25898
|
isGit: false,
|
|
@@ -25584,11 +25921,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
25584
25921
|
rel
|
|
25585
25922
|
], wtDir)).ok) continue;
|
|
25586
25923
|
const target = path.join(wtDir, rel);
|
|
25587
|
-
const stat = await fsp
|
|
25924
|
+
const stat = await fsp.lstat(target).catch(() => null);
|
|
25588
25925
|
if (!stat || !stat.isSymbolicLink()) continue;
|
|
25589
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)`);
|
|
25590
25927
|
try {
|
|
25591
|
-
await fsp
|
|
25928
|
+
await fsp.rm(target, { force: true });
|
|
25592
25929
|
} catch {}
|
|
25593
25930
|
}
|
|
25594
25931
|
}
|
|
@@ -25849,7 +26186,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25849
26186
|
for (const [index, segment] of pathSegments.entries()) {
|
|
25850
26187
|
if (!segment || segment === "." || segment === "..") return false;
|
|
25851
26188
|
checkedPath = path.join(checkedPath, segment);
|
|
25852
|
-
const entryStats = await fsp
|
|
26189
|
+
const entryStats = await fsp.lstat(checkedPath).catch(() => null);
|
|
25853
26190
|
if (!entryStats) return false;
|
|
25854
26191
|
if (index === pathSegments.length - 1) return entryStats.isFile();
|
|
25855
26192
|
if (!entryStats.isDirectory()) return false;
|
|
@@ -26079,7 +26416,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26079
26416
|
], projectPath);
|
|
26080
26417
|
await pruneWorktreeEntries(runInProject(projectPath));
|
|
26081
26418
|
}
|
|
26082
|
-
async function probeWorktreeWork({ projectPath, cwd, branch, integrationBranch, configuredIntegrationBranch }) {
|
|
26419
|
+
async function probeWorktreeWork({ projectPath, cwd, branch, measuredTip = branch, integrationBranch, configuredIntegrationBranch }) {
|
|
26083
26420
|
const { base: resolvedIntegrationBranch, measureRef } = await migrateMarkerBase(projectPath, branch, integrationBranch, configuredIntegrationBranch);
|
|
26084
26421
|
const dirty = await run(WORK_PRESENCE_STATUS_ARGS, cwd);
|
|
26085
26422
|
return {
|
|
@@ -26087,11 +26424,32 @@ function createGitWorkspace(opts = {}) {
|
|
|
26087
26424
|
ahead: !isDirtyResult(dirty) && resolvedIntegrationBranch ? await run([
|
|
26088
26425
|
"rev-list",
|
|
26089
26426
|
"--count",
|
|
26090
|
-
`${measureRef || resolvedIntegrationBranch}..${
|
|
26427
|
+
`${measureRef || resolvedIntegrationBranch}..${measuredTip}`
|
|
26091
26428
|
], projectPath) : null,
|
|
26092
26429
|
integrationBranch: resolvedIntegrationBranch ?? null
|
|
26093
26430
|
};
|
|
26094
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
|
+
}
|
|
26095
26453
|
async function hasUnmergedWorkBody({ projectPath, workspace, integrationBranch, configuredIntegrationBranch }) {
|
|
26096
26454
|
if (!workspace || !workspace.isGit || !workspace.cwd || !workspace.branch) return true;
|
|
26097
26455
|
const { dirty, ahead } = await probeWorktreeWork({
|
|
@@ -26297,7 +26655,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26297
26655
|
for (const rel of shareList ?? []) {
|
|
26298
26656
|
if (!rel || String(rel).includes("..")) continue;
|
|
26299
26657
|
const linkPath = path.join(wtDir, rel);
|
|
26300
|
-
const stat = await fsp
|
|
26658
|
+
const stat = await fsp.lstat(linkPath).catch(() => null);
|
|
26301
26659
|
if (!stat || !stat.isSymbolicLink()) continue;
|
|
26302
26660
|
if (!(await run([
|
|
26303
26661
|
"check-ignore",
|
|
@@ -26305,21 +26663,30 @@ function createGitWorkspace(opts = {}) {
|
|
|
26305
26663
|
"--",
|
|
26306
26664
|
rel
|
|
26307
26665
|
], wtDir)).ok) continue;
|
|
26308
|
-
await fsp
|
|
26666
|
+
await fsp.rm(linkPath, {
|
|
26309
26667
|
recursive: false,
|
|
26310
26668
|
force: true
|
|
26311
|
-
}).catch(() => fsp
|
|
26669
|
+
}).catch(() => fsp.rmdir(linkPath)).catch(() => {});
|
|
26312
26670
|
}
|
|
26313
26671
|
}
|
|
26314
|
-
async function removeWorkspaceMemberBody({ projectPath, cwd, shareList }) {
|
|
26672
|
+
async function removeWorkspaceMemberBody({ projectPath, cwd, shareList, discardWork = false }) {
|
|
26315
26673
|
if (!cwd) return {
|
|
26316
26674
|
ok: false,
|
|
26317
26675
|
out: "",
|
|
26318
26676
|
err: "a workspace member removal needs a path"
|
|
26319
26677
|
};
|
|
26320
26678
|
await removeSharedLinks(cwd, shareList);
|
|
26321
|
-
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
|
+
});
|
|
26322
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
|
+
};
|
|
26323
26690
|
return removed;
|
|
26324
26691
|
}
|
|
26325
26692
|
async function listWorktreeBranches({ projectPath }) {
|
|
@@ -26440,7 +26807,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26440
26807
|
if (!updated.ok) return updated;
|
|
26441
26808
|
return okResult(originSha);
|
|
26442
26809
|
}
|
|
26443
|
-
async function stageDetachedWorktreeBody({ projectPath, worktreePath, sha, disableHooks, replaceEnv }) {
|
|
26810
|
+
async function stageDetachedWorktreeBody({ projectPath, worktreePath, sha, owner, disableHooks, replaceEnv }) {
|
|
26444
26811
|
if (!worktreePath) return {
|
|
26445
26812
|
ok: false,
|
|
26446
26813
|
out: "",
|
|
@@ -26458,7 +26825,11 @@ function createGitWorkspace(opts = {}) {
|
|
|
26458
26825
|
"--detach",
|
|
26459
26826
|
worktreePath,
|
|
26460
26827
|
sha
|
|
26461
|
-
]
|
|
26828
|
+
], {
|
|
26829
|
+
owner,
|
|
26830
|
+
projectPath,
|
|
26831
|
+
branch: null
|
|
26832
|
+
});
|
|
26462
26833
|
}
|
|
26463
26834
|
async function checkoutDetachedBody({ worktreePath, sha, ...isolation }) {
|
|
26464
26835
|
if (!worktreePath) return {
|
|
@@ -26517,8 +26888,8 @@ function createGitWorkspace(opts = {}) {
|
|
|
26517
26888
|
err: commonDir.err || "the source repository has no git dir"
|
|
26518
26889
|
};
|
|
26519
26890
|
const sourceObjectsDir = path.join(path.resolve(projectPath, commonDir.out), "objects");
|
|
26520
|
-
await fsp
|
|
26521
|
-
await fsp
|
|
26891
|
+
await fsp.mkdir(path.dirname(checkoutPath), { recursive: true });
|
|
26892
|
+
await fsp.mkdir(checkoutPath);
|
|
26522
26893
|
const initialized = await run([
|
|
26523
26894
|
"init",
|
|
26524
26895
|
"--quiet",
|
|
@@ -26526,8 +26897,8 @@ function createGitWorkspace(opts = {}) {
|
|
|
26526
26897
|
], checkoutPath);
|
|
26527
26898
|
if (!initialized.ok) return initialized;
|
|
26528
26899
|
const alternatesPath = path.join(checkoutPath, ".git", "objects", "info", "alternates");
|
|
26529
|
-
await fsp
|
|
26530
|
-
await fsp
|
|
26900
|
+
await fsp.mkdir(path.dirname(alternatesPath), { recursive: true });
|
|
26901
|
+
await fsp.writeFile(alternatesPath, `${sourceObjectsDir}\n`, "utf8");
|
|
26531
26902
|
for (const [ref, refSha] of [[ISOLATED_CHECKOUT_REVIEW_REF, sha], [ISOLATED_CHECKOUT_BASE_REF, baseSha]]) {
|
|
26532
26903
|
const updated = await run([
|
|
26533
26904
|
"update-ref",
|
|
@@ -26713,7 +27084,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26713
27084
|
};
|
|
26714
27085
|
const addedPaths = added.out.split("\0").filter(Boolean);
|
|
26715
27086
|
const collidingPaths = [];
|
|
26716
|
-
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);
|
|
26717
27088
|
return {
|
|
26718
27089
|
ok: true,
|
|
26719
27090
|
collidingPaths
|
|
@@ -27049,6 +27420,9 @@ function createGitWorkspace(opts = {}) {
|
|
|
27049
27420
|
pruneWorktrees: serialized(pruneWorktreesBody),
|
|
27050
27421
|
discardSessionBranch: serialized(discardSessionBranchBody),
|
|
27051
27422
|
stageDetachedWorktree: serialized(stageDetachedWorktreeBody),
|
|
27423
|
+
forgetRemovedPath,
|
|
27424
|
+
recordOwnedFolder,
|
|
27425
|
+
describeWorkspaceMemberWork: serialized(describeWorkspaceMemberWorkBody),
|
|
27052
27426
|
checkoutDetached: serialized(checkoutDetachedBody),
|
|
27053
27427
|
stageIsolatedCheckout,
|
|
27054
27428
|
mergeFastForwardTo,
|
|
@@ -27214,6 +27588,7 @@ function createGitWorkspaceSync(opts = {}) {
|
|
|
27214
27588
|
"--force",
|
|
27215
27589
|
cwd
|
|
27216
27590
|
], projectPath);
|
|
27591
|
+
if (!fs.existsSync(cwd)) opts.ownedPaths?.forget(cwd);
|
|
27217
27592
|
}
|
|
27218
27593
|
if (branch) run([
|
|
27219
27594
|
"branch",
|
|
@@ -27272,10 +27647,10 @@ async function populateWorktree(projectPath, wtDir, shareList) {
|
|
|
27272
27647
|
const src = path.join(projectPath, rel);
|
|
27273
27648
|
const dst = path.join(wtDir, rel);
|
|
27274
27649
|
try {
|
|
27275
|
-
const srcStat = await fsp
|
|
27650
|
+
const srcStat = await fsp.stat(src).catch(() => null);
|
|
27276
27651
|
if (!srcStat) continue;
|
|
27277
|
-
if (await fsp
|
|
27278
|
-
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 });
|
|
27279
27654
|
if (srcStat.isDirectory()) {
|
|
27280
27655
|
if (process.platform === "win32") {
|
|
27281
27656
|
await execFileAsync("cmd", [
|
|
@@ -27287,10 +27662,10 @@ async function populateWorktree(projectPath, wtDir, shareList) {
|
|
|
27287
27662
|
]);
|
|
27288
27663
|
continue;
|
|
27289
27664
|
}
|
|
27290
|
-
await fsp
|
|
27665
|
+
await fsp.symlink(src, dst, "dir");
|
|
27291
27666
|
continue;
|
|
27292
27667
|
}
|
|
27293
|
-
await fsp
|
|
27668
|
+
await fsp.copyFile(src, dst);
|
|
27294
27669
|
} catch {}
|
|
27295
27670
|
}
|
|
27296
27671
|
}
|
|
@@ -27443,7 +27818,7 @@ async function describeTeamReviewRole({ checkoutPath, cachedClone, linkedCheckou
|
|
|
27443
27818
|
}
|
|
27444
27819
|
async function linkedCheckoutUnreadablePaths(linkedCheckout) {
|
|
27445
27820
|
if (linkedCheckout === null) return [];
|
|
27446
|
-
return (await
|
|
27821
|
+
return (await fs$1.readdir(linkedCheckout)).filter((entryName) => LINKED_CHECKOUT_UNREADABLE_ENTRY.test(entryName)).map((entryName) => path.join(linkedCheckout, entryName));
|
|
27447
27822
|
}
|
|
27448
27823
|
function teamReviewClaudeArgs(tier, readable) {
|
|
27449
27824
|
const model = tier === "full" ? FULL_MODEL : STAMP_MODEL;
|
|
@@ -27494,11 +27869,11 @@ function hooksPathPinnedSpawnEnv(workDir) {
|
|
|
27494
27869
|
}
|
|
27495
27870
|
async function makeTeamReviewWorkDir(root, prefix) {
|
|
27496
27871
|
const safePrefix = `glimmervoid-wt-${TEAM_REVIEW_LANE_ID}-${prefix}`.replace(/[^\w.-]+/g, "-");
|
|
27497
|
-
await
|
|
27498
|
-
const dir = await
|
|
27872
|
+
await fs$1.mkdir(root, { recursive: true });
|
|
27873
|
+
const dir = await fs$1.mkdtemp(path.join(root, `${safePrefix}-`));
|
|
27499
27874
|
return {
|
|
27500
27875
|
dir,
|
|
27501
|
-
cleanup: () =>
|
|
27876
|
+
cleanup: () => fs$1.rm(dir, {
|
|
27502
27877
|
recursive: true,
|
|
27503
27878
|
force: true
|
|
27504
27879
|
}).catch(() => {})
|
|
@@ -27507,11 +27882,11 @@ async function makeTeamReviewWorkDir(root, prefix) {
|
|
|
27507
27882
|
async function readReviewReport(reportPath, expectedHead, isReReview) {
|
|
27508
27883
|
let report;
|
|
27509
27884
|
try {
|
|
27510
|
-
if ((await
|
|
27885
|
+
if ((await fs$1.stat(reportPath)).size > RESULT_MAX_BYTES$1) return {
|
|
27511
27886
|
ok: false,
|
|
27512
27887
|
reason: "the review report is too large"
|
|
27513
27888
|
};
|
|
27514
|
-
report = await
|
|
27889
|
+
report = await fs$1.readFile(reportPath, "utf8");
|
|
27515
27890
|
} catch {
|
|
27516
27891
|
return {
|
|
27517
27892
|
ok: false,
|
|
@@ -27532,11 +27907,11 @@ async function readReviewReport(reportPath, expectedHead, isReReview) {
|
|
|
27532
27907
|
async function readPostingPlan(postingPath, expectedHead, log) {
|
|
27533
27908
|
let json;
|
|
27534
27909
|
try {
|
|
27535
|
-
if ((await
|
|
27910
|
+
if ((await fs$1.stat(postingPath)).size > RESULT_MAX_BYTES$1) {
|
|
27536
27911
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] the posting plan is too large, rendering from the findings instead`);
|
|
27537
27912
|
return null;
|
|
27538
27913
|
}
|
|
27539
|
-
json = await
|
|
27914
|
+
json = await fs$1.readFile(postingPath, "utf8");
|
|
27540
27915
|
} catch {
|
|
27541
27916
|
return null;
|
|
27542
27917
|
}
|
|
@@ -27549,7 +27924,7 @@ function worktreeDirName(repo, number, suffix) {
|
|
|
27549
27924
|
return `glimmervoid-wt-${TEAM_REVIEW_LANE_ID}-${repo.replace(/[^\w.-]+/g, "-")}-${number}-${suffix}`;
|
|
27550
27925
|
}
|
|
27551
27926
|
async function pathExists(candidatePath) {
|
|
27552
|
-
return
|
|
27927
|
+
return fs$1.lstat(candidatePath).then(() => true, () => false);
|
|
27553
27928
|
}
|
|
27554
27929
|
function isDirectChildOf(root, candidatePath) {
|
|
27555
27930
|
return path.dirname(path.resolve(candidatePath)) === path.resolve(root);
|
|
@@ -27559,10 +27934,10 @@ function isOwnedResumable(record, roots) {
|
|
|
27559
27934
|
}
|
|
27560
27935
|
async function isRealChildDirectoryOrMissing(root, candidatePath) {
|
|
27561
27936
|
if (!isDirectChildOf(root, candidatePath)) return false;
|
|
27562
|
-
const candidateStat = await
|
|
27937
|
+
const candidateStat = await fs$1.lstat(candidatePath).catch((error) => isMissingFileError(error, { includeNotDir: false }) ? "missing" : null);
|
|
27563
27938
|
if (candidateStat === "missing") return true;
|
|
27564
27939
|
if (!candidateStat?.isDirectory()) return false;
|
|
27565
|
-
const realPaths = await Promise.all([
|
|
27940
|
+
const realPaths = await Promise.all([fs$1.realpath(candidatePath), fs$1.realpath(root)]).catch(() => null);
|
|
27566
27941
|
if (!realPaths) return false;
|
|
27567
27942
|
const [realCandidatePath, realRootPath] = realPaths;
|
|
27568
27943
|
return path.dirname(realCandidatePath) === realRootPath;
|
|
@@ -27579,13 +27954,17 @@ async function pruneCachedClone(gitWorkspace, projectPath, log) {
|
|
|
27579
27954
|
if (!pruned.ok) log.warn(`[${TEAM_REVIEW_LANE_ID}] worktree prune failed in ${projectPath}: ${firstLine$1(pruned.err ?? "")}`);
|
|
27580
27955
|
}
|
|
27581
27956
|
async function deleteDirectory(directory, log) {
|
|
27582
|
-
await
|
|
27957
|
+
await fs$1.rm(directory, {
|
|
27583
27958
|
recursive: true,
|
|
27584
27959
|
force: true
|
|
27585
27960
|
}).catch((error) => log.warn(`[${TEAM_REVIEW_LANE_ID}] could not delete ${directory}: ${errorMessage(error)}`));
|
|
27586
27961
|
}
|
|
27962
|
+
async function deleteCheckout(gitWorkspace, worktreePath, log) {
|
|
27963
|
+
await deleteDirectory(worktreePath, log);
|
|
27964
|
+
await gitWorkspace.forgetRemovedPath?.(worktreePath);
|
|
27965
|
+
}
|
|
27587
27966
|
async function isDirectory(candidatePath) {
|
|
27588
|
-
return (await
|
|
27967
|
+
return (await fs$1.stat(candidatePath).catch(() => null))?.isDirectory() === true;
|
|
27589
27968
|
}
|
|
27590
27969
|
async function mainCheckoutOf(checkoutPath) {
|
|
27591
27970
|
const gitDirs = await readGitDirsOfOwnCheckout(checkoutPath).catch(() => null);
|
|
@@ -27597,17 +27976,17 @@ async function mainCheckoutOf(checkoutPath) {
|
|
|
27597
27976
|
async function configuredProjectPathsByRealPath(projects) {
|
|
27598
27977
|
const configuredPathByRealPath = /* @__PURE__ */ new Map();
|
|
27599
27978
|
for (const project of projects) {
|
|
27600
|
-
const projectRealPath = await
|
|
27979
|
+
const projectRealPath = await fs$1.realpath(project.path).catch(() => null);
|
|
27601
27980
|
if (projectRealPath === null || configuredPathByRealPath.has(projectRealPath)) continue;
|
|
27602
27981
|
configuredPathByRealPath.set(projectRealPath, project.path);
|
|
27603
27982
|
}
|
|
27604
27983
|
return configuredPathByRealPath;
|
|
27605
27984
|
}
|
|
27606
27985
|
async function findLocalCheckout({ repo, config, excludedRoots, gitWorkspace, log, requireInstalledModules = true, resolveCheckout }) {
|
|
27607
|
-
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))));
|
|
27608
27987
|
const candidatePaths = (config.projects ?? []).map((project) => project.path);
|
|
27609
27988
|
for (const root of config.repoRoots ?? []) {
|
|
27610
|
-
const childNames = (await
|
|
27989
|
+
const childNames = (await fs$1.readdir(root, { withFileTypes: true }).catch((error) => {
|
|
27611
27990
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] could not list local repositories in ${root}: ${errorMessage(error)}`);
|
|
27612
27991
|
return [];
|
|
27613
27992
|
})).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
@@ -27615,7 +27994,7 @@ async function findLocalCheckout({ repo, config, excludedRoots, gitWorkspace, lo
|
|
|
27615
27994
|
}
|
|
27616
27995
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
27617
27996
|
for (const candidatePath of candidatePaths) {
|
|
27618
|
-
const checkoutPath = await
|
|
27997
|
+
const checkoutPath = await fs$1.realpath(candidatePath).catch(() => null);
|
|
27619
27998
|
if (!checkoutPath || seenPaths.has(checkoutPath)) continue;
|
|
27620
27999
|
seenPaths.add(checkoutPath);
|
|
27621
28000
|
if (resolvedExcludedRoots.some((root) => isPathInside(root, checkoutPath))) continue;
|
|
@@ -27629,9 +28008,9 @@ async function findLocalCheckout({ repo, config, excludedRoots, gitWorkspace, lo
|
|
|
27629
28008
|
}
|
|
27630
28009
|
async function isLinkedToCheckout(worktreePath, localCheckout) {
|
|
27631
28010
|
const modulesPath = path.join(worktreePath, "node_modules");
|
|
27632
|
-
if (!(await
|
|
27633
|
-
const linkedModules = await
|
|
27634
|
-
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);
|
|
27635
28014
|
return linkedModules !== null && linkedModules === checkoutModules;
|
|
27636
28015
|
}
|
|
27637
28016
|
async function sweepLeftoverCheckouts({ worktreeRoot, workRoot, keepPaths, repoCache, gitWorkspace, reapProcesses = reapTeamReviewProcesses, log }) {
|
|
@@ -27642,11 +28021,11 @@ async function sweepLeftoverCheckouts({ worktreeRoot, workRoot, keepPaths, repoC
|
|
|
27642
28021
|
log
|
|
27643
28022
|
}).catch((error) => log.warn(`[${TEAM_REVIEW_LANE_ID}] process reap failed: ${errorMessage(error)}`));
|
|
27644
28023
|
for (const root of [worktreeRoot, workRoot]) {
|
|
27645
|
-
const leftoverNames = await
|
|
28024
|
+
const leftoverNames = await fs$1.readdir(root).catch(() => []);
|
|
27646
28025
|
for (const leftoverName of leftoverNames) {
|
|
27647
28026
|
const directory = path.join(root, leftoverName);
|
|
27648
28027
|
if (keepPaths.has(directory)) continue;
|
|
27649
|
-
await
|
|
28028
|
+
await deleteCheckout(gitWorkspace, directory, log);
|
|
27650
28029
|
}
|
|
27651
28030
|
}
|
|
27652
28031
|
const cachedClones = await repoCache.listRepos().catch((error) => {
|
|
@@ -27666,7 +28045,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27666
28045
|
}));
|
|
27667
28046
|
if (removal.ok && !await pathExists(worktreePath)) return;
|
|
27668
28047
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] worktree removal failed for ${worktreePath}, deleting it directly: ${firstLine$1(removal.err ?? "") || "the directory is still present"}`);
|
|
27669
|
-
await
|
|
28048
|
+
await deleteCheckout(gitWorkspace, worktreePath, log);
|
|
27670
28049
|
await pruneCachedClone(gitWorkspace, projectPath, log);
|
|
27671
28050
|
}
|
|
27672
28051
|
async function discoverLocalCheckout(repo) {
|
|
@@ -27713,7 +28092,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27713
28092
|
return null;
|
|
27714
28093
|
}
|
|
27715
28094
|
const scratchObjectsDir = path.join(workDir, REMERGE_OBJECTS_DIRNAME);
|
|
27716
|
-
await
|
|
28095
|
+
await fs$1.mkdir(scratchObjectsDir, { recursive: true });
|
|
27717
28096
|
return {
|
|
27718
28097
|
...isolation.range,
|
|
27719
28098
|
scratchObjectsDir
|
|
@@ -27823,7 +28202,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27823
28202
|
}
|
|
27824
28203
|
};
|
|
27825
28204
|
if (hasWorkDir) await deleteDirectory(args.resume.workDir, log);
|
|
27826
|
-
if (hasWorktree) await
|
|
28205
|
+
if (hasWorktree) await deleteCheckout(gitWorkspace, args.resume.worktreePath, log);
|
|
27827
28206
|
return {
|
|
27828
28207
|
kind: "stopped",
|
|
27829
28208
|
resumable: null
|
|
@@ -27914,7 +28293,7 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27914
28293
|
projectPath,
|
|
27915
28294
|
worktreePath: args.resume.worktreePath
|
|
27916
28295
|
});
|
|
27917
|
-
if (!projectPath) await
|
|
28296
|
+
if (!projectPath) await deleteCheckout(gitWorkspace, args.resume.worktreePath, log);
|
|
27918
28297
|
}
|
|
27919
28298
|
if (hasWorkDir) await deleteDirectory(args.resume.workDir, log);
|
|
27920
28299
|
sessionId = null;
|
|
@@ -27938,9 +28317,10 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27938
28317
|
projectPath: staged.projectPath,
|
|
27939
28318
|
worktreePath
|
|
27940
28319
|
};
|
|
27941
|
-
await
|
|
28320
|
+
await fs$1.mkdir(worktreeRoot, { recursive: true });
|
|
27942
28321
|
const created = await gitWorkspace.stageDetachedWorktree({
|
|
27943
28322
|
projectPath: staged.projectPath,
|
|
28323
|
+
owner: "lane:team-review",
|
|
27944
28324
|
worktreePath,
|
|
27945
28325
|
sha: detail.headRefOid
|
|
27946
28326
|
});
|
|
@@ -27981,8 +28361,8 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
27981
28361
|
priorReview,
|
|
27982
28362
|
newWork
|
|
27983
28363
|
});
|
|
27984
|
-
await
|
|
27985
|
-
await
|
|
28364
|
+
await fs$1.writeFile(path.join(workDir, REVIEW_PROMPT_FILENAME), prompt, "utf8");
|
|
28365
|
+
await fs$1.mkdir(emptyGhConfigDir(workDir), { recursive: true });
|
|
27986
28366
|
reportProgress({
|
|
27987
28367
|
kind: "phase",
|
|
27988
28368
|
phase: "reviewing",
|
|
@@ -28500,7 +28880,7 @@ function createTeamReviewWiring({ config, store, reviewSessions, closeSessionDat
|
|
|
28500
28880
|
scope: "run",
|
|
28501
28881
|
log
|
|
28502
28882
|
}).catch((error) => log.warn(`[${TEAM_REVIEW_LANE_ID}] process reap failed: ${errorMessage(error)}`));
|
|
28503
|
-
await
|
|
28883
|
+
await deleteCheckout(gitWorkspace, record.worktreePath, log);
|
|
28504
28884
|
await deleteDirectory(record.workDir, log);
|
|
28505
28885
|
const cachedClones = await repoCache.listRepos();
|
|
28506
28886
|
for (const projectPath of cachedClones) await pruneCachedClone(gitWorkspace, projectPath, log);
|
|
@@ -28567,7 +28947,7 @@ function createTeamReviewWiring({ config, store, reviewSessions, closeSessionDat
|
|
|
28567
28947
|
worktreeRoot,
|
|
28568
28948
|
workRoot
|
|
28569
28949
|
];
|
|
28570
|
-
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))));
|
|
28571
28951
|
const configuredPathByRealPath = await configuredProjectPathsByRealPath(config.projects ?? []);
|
|
28572
28952
|
return findLocalCheckout({
|
|
28573
28953
|
repo,
|
|
@@ -28861,18 +29241,18 @@ function worktreeOwnerRepo(gitFileText) {
|
|
|
28861
29241
|
async function readJsonFile$1(filePath) {
|
|
28862
29242
|
return loadedJsonValue(await loadJsonStateFile({
|
|
28863
29243
|
filePath,
|
|
28864
|
-
fsPromises:
|
|
29244
|
+
fsPromises: fs$1,
|
|
28865
29245
|
parse: (raw) => raw,
|
|
28866
29246
|
quarantine: false
|
|
28867
29247
|
}));
|
|
28868
29248
|
}
|
|
28869
29249
|
async function jsonFileNames(directory) {
|
|
28870
|
-
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();
|
|
28871
29251
|
}
|
|
28872
29252
|
async function readBoundedText(filePath) {
|
|
28873
|
-
const stat = await
|
|
29253
|
+
const stat = await fs$1.stat(filePath).catch(() => null);
|
|
28874
29254
|
if (!stat?.isFile() || stat.size > RESULT_MAX_BYTES) return null;
|
|
28875
|
-
return
|
|
29255
|
+
return fs$1.readFile(filePath, "utf8").catch(() => null);
|
|
28876
29256
|
}
|
|
28877
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 }) {
|
|
28878
29258
|
let activeRun = null;
|
|
@@ -28881,7 +29261,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
28881
29261
|
let leftoverSweep = Promise.resolve();
|
|
28882
29262
|
const suiteDir = (suiteId) => path.join(benchmarksRoot, suiteId);
|
|
28883
29263
|
async function removeDirectoryOrWarn(directory) {
|
|
28884
|
-
await
|
|
29264
|
+
await fs$1.rm(directory, {
|
|
28885
29265
|
recursive: true,
|
|
28886
29266
|
force: true
|
|
28887
29267
|
}).catch((error) => log.warn(`[${BENCHMARK_LANE_ID}] could not remove ${directory}: ${firstLine(errorMessage(error))}`));
|
|
@@ -28980,7 +29360,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
28980
29360
|
return refreshed;
|
|
28981
29361
|
}
|
|
28982
29362
|
async function summarizeAndBroadcast() {
|
|
28983
|
-
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();
|
|
28984
29364
|
const summaries = [];
|
|
28985
29365
|
for (const suiteId of suiteIds) summaries.push(await summarizeSuite(suiteId).catch((error) => unreadableSuiteSummary(suiteId, errorMessage(error))));
|
|
28986
29366
|
suites = summaries;
|
|
@@ -29037,7 +29417,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29037
29417
|
error: mined.reason
|
|
29038
29418
|
};
|
|
29039
29419
|
const candidatesDir = path.join(suiteDir(suiteId), "candidates");
|
|
29040
|
-
await
|
|
29420
|
+
await fs$1.mkdir(candidatesDir, { recursive: true });
|
|
29041
29421
|
for (const outcome of mined.outcomes) {
|
|
29042
29422
|
if (!outcome.candidate) {
|
|
29043
29423
|
log.warn(`[${BENCHMARK_LANE_ID}] ${caseSource.repo}#${outcome.number} not mined: ${outcome.refusal}`);
|
|
@@ -29133,7 +29513,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29133
29513
|
reason: "the reviewed commit could not be fetched, or the base is not its ancestor"
|
|
29134
29514
|
};
|
|
29135
29515
|
const checkoutPath = path.join(staged.caseDir, "checkout");
|
|
29136
|
-
const cleared = await
|
|
29516
|
+
const cleared = await fs$1.rm(checkoutPath, {
|
|
29137
29517
|
recursive: true,
|
|
29138
29518
|
force: true
|
|
29139
29519
|
}).then(() => null, (error) => errorMessage(error));
|
|
@@ -29154,7 +29534,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29154
29534
|
reason: `could not stage a checkout: ${firstLine(created.err ?? "") || "the isolated checkout failed"}`
|
|
29155
29535
|
};
|
|
29156
29536
|
}
|
|
29157
|
-
const sharedObjectsPath = firstLine(await
|
|
29537
|
+
const sharedObjectsPath = firstLine(await fs$1.readFile(path.join(checkoutPath, ".git", "objects", "info", "alternates"), "utf8").catch(() => ""));
|
|
29158
29538
|
if (!sharedObjectsPath) {
|
|
29159
29539
|
await removeDirectoryOrWarn(checkoutPath);
|
|
29160
29540
|
return {
|
|
@@ -29172,7 +29552,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29172
29552
|
if (existing) return existing;
|
|
29173
29553
|
const caseDir = path.join(runDir, benchmarkCase.id);
|
|
29174
29554
|
const workDir = path.join(caseDir, "work");
|
|
29175
|
-
await
|
|
29555
|
+
await fs$1.mkdir(workDir, { recursive: true });
|
|
29176
29556
|
const staged = {
|
|
29177
29557
|
checkoutPath: null,
|
|
29178
29558
|
sharedObjectsPath: null,
|
|
@@ -29255,7 +29635,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29255
29635
|
},
|
|
29256
29636
|
judgeSeed: (cell) => cell.index + 1,
|
|
29257
29637
|
async persist(run) {
|
|
29258
|
-
await
|
|
29638
|
+
await fs$1.mkdir(runsDir, { recursive: true });
|
|
29259
29639
|
await writeJsonAtomic(path.join(runsDir, `${runId}.json`), redactedRun(run, usedTokens));
|
|
29260
29640
|
},
|
|
29261
29641
|
reportProgress
|
|
@@ -29282,10 +29662,10 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29282
29662
|
};
|
|
29283
29663
|
usedTokens.add(token.token);
|
|
29284
29664
|
const resultPath = path.join(staged.workDir, SUBJECT_RESULT_FILENAME);
|
|
29285
|
-
await
|
|
29286
|
-
await
|
|
29665
|
+
await fs$1.rm(resultPath, { force: true });
|
|
29666
|
+
await fs$1.writeFile(path.join(staged.workDir, SUBJECT_PROMPT_FILENAME), prompt, "utf8");
|
|
29287
29667
|
const baseSpawnEnv = hooksPathPinnedSpawnEnv(staged.workDir);
|
|
29288
|
-
await
|
|
29668
|
+
await fs$1.mkdir(baseSpawnEnv.GH_CONFIG_DIR, { recursive: true });
|
|
29289
29669
|
const subjectController = new AbortController();
|
|
29290
29670
|
const stopSubject = () => subjectController.abort();
|
|
29291
29671
|
let hasTimedOut = false;
|
|
@@ -29347,8 +29727,8 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29347
29727
|
};
|
|
29348
29728
|
}
|
|
29349
29729
|
async function runJudgeSession({ cell, prompt, model, signal, runDir }) {
|
|
29350
|
-
await
|
|
29351
|
-
const judgeDir = await
|
|
29730
|
+
await fs$1.mkdir(runDir, { recursive: true });
|
|
29731
|
+
const judgeDir = await fs$1.mkdtemp(path.join(runDir, `judge-${cell.index}-`));
|
|
29352
29732
|
const resultPath = path.join(judgeDir, JUDGE_RESULT_FILENAME);
|
|
29353
29733
|
try {
|
|
29354
29734
|
await spawnJudge({
|
|
@@ -29377,7 +29757,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29377
29757
|
costUsd: null
|
|
29378
29758
|
};
|
|
29379
29759
|
} finally {
|
|
29380
|
-
await
|
|
29760
|
+
await fs$1.rm(judgeDir, {
|
|
29381
29761
|
recursive: true,
|
|
29382
29762
|
force: true
|
|
29383
29763
|
}).catch(() => {});
|
|
@@ -29463,7 +29843,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29463
29843
|
return respond(outcome);
|
|
29464
29844
|
}
|
|
29465
29845
|
async function removeLeftoverWorktree(worktreePath) {
|
|
29466
|
-
const projectPath = worktreeOwnerRepo(await
|
|
29846
|
+
const projectPath = worktreeOwnerRepo(await fs$1.readFile(path.join(worktreePath, ".git"), "utf8").catch(() => ""));
|
|
29467
29847
|
if (projectPath) {
|
|
29468
29848
|
const removal = await gitWorkspace.removeWorktreeByPath({
|
|
29469
29849
|
projectPath,
|
|
@@ -29490,7 +29870,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29490
29870
|
}
|
|
29491
29871
|
async function sweepSuiteLeftovers(suiteId) {
|
|
29492
29872
|
const worktreesDir = path.join(suiteDir(suiteId), "worktrees");
|
|
29493
|
-
const worktreeEntries = await
|
|
29873
|
+
const worktreeEntries = await fs$1.readdir(worktreesDir, { withFileTypes: true }).catch(() => []);
|
|
29494
29874
|
for (const entry of worktreeEntries) if (entry.isDirectory()) await removeLeftoverWorktree(path.join(worktreesDir, entry.name));
|
|
29495
29875
|
await removeDirectoryOrWarn(worktreesDir);
|
|
29496
29876
|
await removeDirectoryOrWarn(path.join(suiteDir(suiteId), "work"));
|
|
@@ -29499,7 +29879,7 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
29499
29879
|
async function sweepAllLeftovers() {
|
|
29500
29880
|
if (activeRun) return;
|
|
29501
29881
|
await removeDirectoryOrWarn(runsRoot);
|
|
29502
|
-
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);
|
|
29503
29883
|
for (const suiteId of suiteIds) await sweepSuiteLeftovers(suiteId).catch((error) => log.warn(`[${BENCHMARK_LANE_ID}] could not sweep ${suiteId}: ${errorMessage(error)}`));
|
|
29504
29884
|
}
|
|
29505
29885
|
function sweepLeftovers() {
|
|
@@ -30645,7 +31025,7 @@ function createCoderEnvWiring({ config, persistMapping, onReconciled = () => {},
|
|
|
30645
31025
|
var DEFAULT_INTERVAL_MS$1 = 216e5;
|
|
30646
31026
|
var BRANCH_GC_FETCH_TIMEOUT_MS = 6e4;
|
|
30647
31027
|
function createBranchGcPoller(deps) {
|
|
30648
|
-
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;
|
|
30649
31029
|
const skippedProjectPaths = /* @__PURE__ */ new Set();
|
|
30650
31030
|
async function callGit(method, args) {
|
|
30651
31031
|
try {
|
|
@@ -33386,7 +33766,7 @@ function classifyGitStatusFailure(error) {
|
|
|
33386
33766
|
var DEFAULT_MAX_REPOS = 16;
|
|
33387
33767
|
var DEFAULT_GIT_TIMEOUT_MS = 15e3;
|
|
33388
33768
|
var MAX_GIT_BUFFER_BYTES = 8388608;
|
|
33389
|
-
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" } = {}) {
|
|
33390
33770
|
if (typeof publish !== "function") throw new Error("createGitIngest requires publish");
|
|
33391
33771
|
const publishEvent = publish;
|
|
33392
33772
|
const settleMs = positiveIntOr(sourceConfig.debounceMs, DEFAULT_DEBOUNCE_MS);
|
|
@@ -34809,10 +35189,10 @@ function hasAutoSessionName(session) {
|
|
|
34809
35189
|
return isAutoNameOf(session.name, path.basename(session.path));
|
|
34810
35190
|
}
|
|
34811
35191
|
async function refineTaskTitle(request, model, spawnLane, signal) {
|
|
34812
|
-
const workDir = await
|
|
35192
|
+
const workDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-task-title-"));
|
|
34813
35193
|
const resultPath = path.join(workDir, RESULT_FILE$1);
|
|
34814
35194
|
try {
|
|
34815
|
-
await
|
|
35195
|
+
await fs$1.writeFile(path.join(workDir, PROMPT_FILE$1), buildTaskTitleRefinementPrompt({
|
|
34816
35196
|
currentTitle: request.currentTitle,
|
|
34817
35197
|
recentPrompts: request.recentPrompts,
|
|
34818
35198
|
resultPath,
|
|
@@ -34835,7 +35215,7 @@ async function refineTaskTitle(request, model, spawnLane, signal) {
|
|
|
34835
35215
|
proposedSessionName: request.wantsSessionName ? parseRefinedSessionName(laneResult) : null
|
|
34836
35216
|
};
|
|
34837
35217
|
} finally {
|
|
34838
|
-
await
|
|
35218
|
+
await fs$1.rm(workDir, {
|
|
34839
35219
|
recursive: true,
|
|
34840
35220
|
force: true
|
|
34841
35221
|
});
|
|
@@ -37922,6 +38302,7 @@ function createRadarWiring({ config, investigationSessions, closeSessionDataClie
|
|
|
37922
38302
|
const issueId = safeIssueId(issue.issueId);
|
|
37923
38303
|
const workspace = await Promise.resolve(gitWorkspace.create({
|
|
37924
38304
|
projectPath: repoPath,
|
|
38305
|
+
owner: "lane:radar-fix",
|
|
37925
38306
|
teamId: "radar-fix",
|
|
37926
38307
|
label: `${safeIssueId(projectId)}-${issueId}-${newFixDiscriminator()}`,
|
|
37927
38308
|
worktreeBase: worktreeBaseFor(repoPath),
|
|
@@ -38429,9 +38810,13 @@ function createMyPrsPoller(dependencies) {
|
|
|
38429
38810
|
});
|
|
38430
38811
|
async function failedOutcome(errorText) {
|
|
38431
38812
|
const rateLimitWaitMs = await store.rateLimitWaitMs(now(), MY_PRS_RATE_LIMIT_RESOURCES) ?? secondaryRateLimitWaitMs(errorText);
|
|
38432
|
-
return rateLimitWaitMs === null ? {
|
|
38813
|
+
return rateLimitWaitMs === null ? {
|
|
38433
38814
|
failed: true,
|
|
38434
|
-
|
|
38815
|
+
error: errorText
|
|
38816
|
+
} : {
|
|
38817
|
+
failed: true,
|
|
38818
|
+
retryAfterMs: rateLimitWaitMs,
|
|
38819
|
+
error: errorText
|
|
38435
38820
|
};
|
|
38436
38821
|
}
|
|
38437
38822
|
async function runTick() {
|
|
@@ -38758,10 +39143,11 @@ function createMyPrsPoller(dependencies) {
|
|
|
38758
39143
|
error: "Reviews polling is stopped."
|
|
38759
39144
|
};
|
|
38760
39145
|
registerWatches();
|
|
38761
|
-
|
|
38762
|
-
|
|
38763
|
-
|
|
38764
|
-
|
|
39146
|
+
return loop.refresh(async () => {
|
|
39147
|
+
const refreshed = await store.refreshWatches(watchNames);
|
|
39148
|
+
rebuild();
|
|
39149
|
+
return refreshed;
|
|
39150
|
+
});
|
|
38765
39151
|
}
|
|
38766
39152
|
return {
|
|
38767
39153
|
start,
|
|
@@ -38784,10 +39170,10 @@ function defaultAgentSearchRoots() {
|
|
|
38784
39170
|
return roots.filter((root, index) => roots.indexOf(root) === index);
|
|
38785
39171
|
}
|
|
38786
39172
|
async function readEntryNames(directory) {
|
|
38787
|
-
return
|
|
39173
|
+
return fs$1.readdir(directory).catch(() => []);
|
|
38788
39174
|
}
|
|
38789
39175
|
async function ownedSocketAt(socketPath, ownerUid) {
|
|
38790
|
-
const stats = await
|
|
39176
|
+
const stats = await fs$1.lstat(socketPath).catch(() => null);
|
|
38791
39177
|
if (!stats?.isSocket()) return null;
|
|
38792
39178
|
if (ownerUid !== null && stats.uid !== ownerUid) return null;
|
|
38793
39179
|
return {
|
|
@@ -38835,12 +39221,12 @@ function createSshSigner({ env = process.env, agentSearchRoots = defaultAgentSea
|
|
|
38835
39221
|
ok: false,
|
|
38836
39222
|
error: NO_AGENT_HOLDS_KEY
|
|
38837
39223
|
};
|
|
38838
|
-
const workDir = await
|
|
39224
|
+
const workDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-sign-"));
|
|
38839
39225
|
try {
|
|
38840
39226
|
const keyPath = path.join(workDir, "signing-key.pub");
|
|
38841
39227
|
const payloadPath = path.join(workDir, "payload");
|
|
38842
|
-
await
|
|
38843
|
-
await
|
|
39228
|
+
await fs$1.writeFile(keyPath, `${formatSshPublicKey(publicKey)}\n`, { mode: 384 });
|
|
39229
|
+
await fs$1.writeFile(payloadPath, payload, { mode: 384 });
|
|
38844
39230
|
await execFileAsync("ssh-keygen", [
|
|
38845
39231
|
"-Y",
|
|
38846
39232
|
"sign",
|
|
@@ -38860,7 +39246,7 @@ function createSshSigner({ env = process.env, agentSearchRoots = defaultAgentSea
|
|
|
38860
39246
|
});
|
|
38861
39247
|
return {
|
|
38862
39248
|
ok: true,
|
|
38863
|
-
signature: await
|
|
39249
|
+
signature: await fs$1.readFile(`${payloadPath}.sig`, "utf8")
|
|
38864
39250
|
};
|
|
38865
39251
|
} catch (error) {
|
|
38866
39252
|
const stderr = error?.stderr;
|
|
@@ -38869,7 +39255,7 @@ function createSshSigner({ env = process.env, agentSearchRoots = defaultAgentSea
|
|
|
38869
39255
|
error: `ssh-keygen could not sign: ${typeof stderr === "string" && stderr.trim() ? stderr.trim() : errorMessage(error)}`
|
|
38870
39256
|
};
|
|
38871
39257
|
} finally {
|
|
38872
|
-
await
|
|
39258
|
+
await fs$1.rm(workDir, {
|
|
38873
39259
|
recursive: true,
|
|
38874
39260
|
force: true
|
|
38875
39261
|
});
|
|
@@ -38884,7 +39270,7 @@ async function readSigningKeySetting(setting, cwd) {
|
|
|
38884
39270
|
const literalKey = parseSshPublicKey(setting);
|
|
38885
39271
|
if (literalKey) return literalKey;
|
|
38886
39272
|
const keyPath = path.resolve(cwd, setting.replace(/^~(?=\/|$)/, os.homedir()));
|
|
38887
|
-
return parseSshPublicKey(await
|
|
39273
|
+
return parseSshPublicKey(await fs$1.readFile(keyPath, "utf8").catch(() => ""));
|
|
38888
39274
|
}
|
|
38889
39275
|
function readTextOrNull(filePath) {
|
|
38890
39276
|
try {
|
|
@@ -39072,8 +39458,8 @@ function createGuardedPrStaging({ spawnSession, repoCache, describeRole = guarde
|
|
|
39072
39458
|
if (!staged.ok) return { error: `could not stage the checkout ${staged.err}` };
|
|
39073
39459
|
}
|
|
39074
39460
|
const excludePath = path.join(checkoutPath, ".git", "info", "exclude");
|
|
39075
|
-
await
|
|
39076
|
-
await
|
|
39461
|
+
await fs$1.mkdir(path.dirname(excludePath), { recursive: true });
|
|
39462
|
+
await fs$1.appendFile(excludePath, keepMergeableExcludeLines(), "utf8");
|
|
39077
39463
|
return {
|
|
39078
39464
|
projectPath,
|
|
39079
39465
|
baseSha: baseSha.data
|
|
@@ -39266,7 +39652,7 @@ function createMyPrMergeabilityFix({ spawnSession, repoCache, workRoot, makeWork
|
|
|
39266
39652
|
};
|
|
39267
39653
|
}
|
|
39268
39654
|
async function fetchSessionCommits(staged, checkoutPath, headSha, handoffRef, signal) {
|
|
39269
|
-
const bundleDir = await
|
|
39655
|
+
const bundleDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-keep-mergeable-"));
|
|
39270
39656
|
try {
|
|
39271
39657
|
const bundlePath = path.join(bundleDir, "session.bundle");
|
|
39272
39658
|
const bundled = await runGit([
|
|
@@ -39288,7 +39674,7 @@ function createMyPrMergeabilityFix({ spawnSession, repoCache, workRoot, makeWork
|
|
|
39288
39674
|
`+${KEEP_MERGEABLE_WORK_BRANCH_REF}:${handoffRef}`
|
|
39289
39675
|
], staged.projectPath, signal);
|
|
39290
39676
|
} finally {
|
|
39291
|
-
await
|
|
39677
|
+
await fs$1.rm(bundleDir, {
|
|
39292
39678
|
recursive: true,
|
|
39293
39679
|
force: true
|
|
39294
39680
|
});
|
|
@@ -39373,8 +39759,8 @@ function createMyPrMergeabilityFix({ spawnSession, repoCache, workRoot, makeWork
|
|
|
39373
39759
|
if ("error" in staged) return reportRepairFailure(pr, `not started: ${staged.error}`);
|
|
39374
39760
|
projectPath = staged.projectPath;
|
|
39375
39761
|
if (signal.aborted) return stopped;
|
|
39376
|
-
await
|
|
39377
|
-
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 });
|
|
39378
39764
|
const outcome = await runSession({
|
|
39379
39765
|
idPrefix: MY_PRS_LANE_ID,
|
|
39380
39766
|
name: `Keep mergeable ${pr.key}`,
|
|
@@ -40213,12 +40599,16 @@ function createWorkflowsPoller(dependencies) {
|
|
|
40213
40599
|
return stateLoad;
|
|
40214
40600
|
}
|
|
40215
40601
|
function publishStatus() {
|
|
40602
|
+
const { nextAttemptAt, retry, isRefreshing, refreshNotice } = loop.scheduleStatus();
|
|
40216
40603
|
onTickComplete({
|
|
40217
40604
|
type: "workflows-status",
|
|
40218
40605
|
ts: now(),
|
|
40219
40606
|
configured: true,
|
|
40220
40607
|
error: pollingError,
|
|
40221
|
-
|
|
40608
|
+
nextAttemptAt,
|
|
40609
|
+
retry,
|
|
40610
|
+
isRefreshing,
|
|
40611
|
+
refreshNotice
|
|
40222
40612
|
});
|
|
40223
40613
|
}
|
|
40224
40614
|
const loop = createTickLoop({
|
|
@@ -40398,8 +40788,8 @@ function createWorkflowSpawn({ staging, workRoot, makeWorkDir = makeTeamReviewWo
|
|
|
40398
40788
|
const staged = await staging.stageCheckout(pr, workDir.dir, signal);
|
|
40399
40789
|
if (signal.aborted) return;
|
|
40400
40790
|
if ("error" in staged) return warn(`not started: ${staged.error}`);
|
|
40401
|
-
await
|
|
40402
|
-
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 });
|
|
40403
40793
|
if (await staging.runSession({
|
|
40404
40794
|
idPrefix: "workflows",
|
|
40405
40795
|
name: workflowSessionName({
|
|
@@ -40490,15 +40880,28 @@ function createWorkflowsWiring({ config, notificationManager, spawnSession, log
|
|
|
40490
40880
|
}
|
|
40491
40881
|
//#endregion
|
|
40492
40882
|
//#region server/github/github-clock.ts
|
|
40493
|
-
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 }) {
|
|
40494
40884
|
const subscriptions = /* @__PURE__ */ new Set();
|
|
40495
40885
|
let timer = null;
|
|
40496
40886
|
let isSweeping = false;
|
|
40497
|
-
let
|
|
40498
|
-
|
|
40499
|
-
|
|
40500
|
-
|
|
40501
|
-
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
|
+
});
|
|
40502
40905
|
}
|
|
40503
40906
|
async function sweep() {
|
|
40504
40907
|
if (isSweeping) return;
|
|
@@ -40540,7 +40943,56 @@ function createGithubClock({ baseIntervalMs, setIntervalFn = DEFAULT_TIMER_FNS.s
|
|
|
40540
40943
|
return {
|
|
40541
40944
|
schedule,
|
|
40542
40945
|
exclusive,
|
|
40543
|
-
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
|
|
40544
40996
|
};
|
|
40545
40997
|
}
|
|
40546
40998
|
//#endregion
|
|
@@ -41594,7 +42046,7 @@ function isFresh(fetchedAt, nowMs) {
|
|
|
41594
42046
|
if (!Number.isFinite(fetchedMs)) return false;
|
|
41595
42047
|
return nowMs - fetchedMs < CACHE_TTL_MS;
|
|
41596
42048
|
}
|
|
41597
|
-
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 } = {}) {
|
|
41598
42050
|
if (!fetchEnabled) return {
|
|
41599
42051
|
table: normalizePricingTable(claude_pricing_default),
|
|
41600
42052
|
source: "snapshot",
|
|
@@ -42293,7 +42745,7 @@ async function walkSourceFiles(roots, fsPromises, laneLog) {
|
|
|
42293
42745
|
};
|
|
42294
42746
|
}
|
|
42295
42747
|
function createUsageScanner(deps = {}) {
|
|
42296
|
-
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 = {
|
|
42297
42749
|
codex: true,
|
|
42298
42750
|
grok: true
|
|
42299
42751
|
}, warehousePath = null, warehouseRetainDays = 365, budget: budgetDep = null, laneMap = null, logger = noopLogger, byteBudget = DEFAULT_BYTE_BUDGET, chunkSize = DEFAULT_CHUNK_SIZE, yieldNowFn = yieldNow } = deps;
|
|
@@ -43127,7 +43579,7 @@ function formatBudgetUsd(value) {
|
|
|
43127
43579
|
function budgetAlertText({ scope, threshold, spentUsd, budgetUsd }) {
|
|
43128
43580
|
return `Usage budget: ${scope === "monthly" ? "monthly" : "daily"} spend ${formatBudgetUsd(spentUsd)} reached ${threshold}% of ${formatBudgetUsd(budgetUsd)}`;
|
|
43129
43581
|
}
|
|
43130
|
-
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 }) {
|
|
43131
43583
|
const laneLog = createLaneLog({
|
|
43132
43584
|
prefix: "[usage]",
|
|
43133
43585
|
logger,
|
|
@@ -43667,7 +44119,7 @@ function createUsageWiring({ config, sessions = /* @__PURE__ */ new Map(), broad
|
|
|
43667
44119
|
}
|
|
43668
44120
|
//#endregion
|
|
43669
44121
|
//#region server/usage/usage-lane-ledger.ts
|
|
43670
|
-
function createLaneLedger({ ledgerPath = null, fsPromises =
|
|
44122
|
+
function createLaneLedger({ ledgerPath = null, fsPromises = fs$1, nowFn = Date.now, retainDays = 365, logger = null } = {}) {
|
|
43671
44123
|
const laneLog = createLaneLog({
|
|
43672
44124
|
prefix: "[usage]",
|
|
43673
44125
|
logger
|
|
@@ -44699,7 +45151,7 @@ function errorResult(reason, errorSource = ERROR_SOURCE_TRANSPORT) {
|
|
|
44699
45151
|
async function readCommentsResult(resultPath, { lineCount = 0, onBytesRead = null } = {}) {
|
|
44700
45152
|
let raw = "";
|
|
44701
45153
|
try {
|
|
44702
|
-
raw = await
|
|
45154
|
+
raw = await fs$1.readFile(resultPath, "utf8");
|
|
44703
45155
|
} catch {
|
|
44704
45156
|
return errorResult("no readable result file");
|
|
44705
45157
|
}
|
|
@@ -44795,11 +45247,11 @@ function createVisionsSpawn({ sessions = /* @__PURE__ */ new Map(), closeSession
|
|
|
44795
45247
|
};
|
|
44796
45248
|
}
|
|
44797
45249
|
function makeVisionsWorkDir() {
|
|
44798
|
-
return
|
|
45250
|
+
return fs$1.mkdtemp(path.join(os.tmpdir(), "glimmervoid-visions-"));
|
|
44799
45251
|
}
|
|
44800
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) => {
|
|
44801
45253
|
try {
|
|
44802
|
-
await
|
|
45254
|
+
await fs$1.rm(dir, {
|
|
44803
45255
|
recursive: true,
|
|
44804
45256
|
force: true
|
|
44805
45257
|
});
|
|
@@ -44869,7 +45321,7 @@ function createVisionsDispatcher({ spawnSession, timeoutSeconds = 180, model = n
|
|
|
44869
45321
|
});
|
|
44870
45322
|
const sizeDecision = decidePromptSize(generatedPrompt);
|
|
44871
45323
|
if (!sizeDecision.dispatch) return errorResult(sizeDecision.gate);
|
|
44872
|
-
await
|
|
45324
|
+
await fs$1.writeFile(path.join(workDir, PROMPT_FILE), generatedPrompt, "utf8");
|
|
44873
45325
|
return await spawnWithTimeout({
|
|
44874
45326
|
id: idFor(uri),
|
|
44875
45327
|
name: `visions ${uri}`,
|
|
@@ -45276,7 +45728,7 @@ function changeFailureReason(uri, version, result) {
|
|
|
45276
45728
|
if (result.reason === "stale-version") return `stale-version (uri=${uri} incoming=${result.version} current=${result.currentVersion})`;
|
|
45277
45729
|
return `${result.reason} (uri=${uri} version=${version})`;
|
|
45278
45730
|
}
|
|
45279
|
-
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 } = {}) {
|
|
45280
45732
|
const wss = new WebSocketServer({
|
|
45281
45733
|
noServer: true,
|
|
45282
45734
|
maxPayload
|
|
@@ -46147,8 +46599,15 @@ function createBackendLanes(dependencies) {
|
|
|
46147
46599
|
warn: (message) => logger.warn(message)
|
|
46148
46600
|
});
|
|
46149
46601
|
const spawnGate = createSpawnGate({ memoryAdmission });
|
|
46150
|
-
const
|
|
46151
|
-
|
|
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 });
|
|
46152
46611
|
const laneLedger = createLaneLedger({
|
|
46153
46612
|
ledgerPath: configSiblingPath(configStore.configPath, "usage-lanes.json"),
|
|
46154
46613
|
retainDays: resolveUsageConfig(config.usage).warehouseRetainDays,
|
|
@@ -46242,7 +46701,8 @@ function createBackendLanes(dependencies) {
|
|
|
46242
46701
|
});
|
|
46243
46702
|
const githubClock = createGithubClock({
|
|
46244
46703
|
baseIntervalMs: 6e4,
|
|
46245
|
-
log: logger
|
|
46704
|
+
log: logger,
|
|
46705
|
+
githubStatus: createGithubStatus({ log: logger })
|
|
46246
46706
|
});
|
|
46247
46707
|
const glimmervoidHome = glimmervoidHomeDir$1();
|
|
46248
46708
|
const github = createPrGh(glimmervoidHome);
|
|
@@ -46636,6 +47096,7 @@ function createBackendLanes(dependencies) {
|
|
|
46636
47096
|
currentVisions,
|
|
46637
47097
|
gitWorkspace,
|
|
46638
47098
|
gitWorkspaceSync,
|
|
47099
|
+
ownedPaths,
|
|
46639
47100
|
githubStore,
|
|
46640
47101
|
investigationSessions,
|
|
46641
47102
|
planReview,
|
|
@@ -48834,7 +49295,7 @@ var SessionRecorder = class {
|
|
|
48834
49295
|
if (this._retainDays <= 0 && this._retainFiles <= 0 && this._retainBytes <= 0) return;
|
|
48835
49296
|
let entries;
|
|
48836
49297
|
try {
|
|
48837
|
-
entries = await
|
|
49298
|
+
entries = await fs$1.readdir(this._baseDir);
|
|
48838
49299
|
} catch {
|
|
48839
49300
|
return;
|
|
48840
49301
|
}
|
|
@@ -48852,7 +49313,7 @@ var SessionRecorder = class {
|
|
|
48852
49313
|
for (const entry of recordings) {
|
|
48853
49314
|
if (doomed.has(entry)) continue;
|
|
48854
49315
|
try {
|
|
48855
|
-
if ((await
|
|
49316
|
+
if ((await fs$1.stat(path.join(this._baseDir, entry))).mtimeMs < cutoff) doomed.add(entry);
|
|
48856
49317
|
} catch {}
|
|
48857
49318
|
}
|
|
48858
49319
|
}
|
|
@@ -48860,14 +49321,14 @@ var SessionRecorder = class {
|
|
|
48860
49321
|
const filepath = path.join(this._baseDir, entry);
|
|
48861
49322
|
if (filepath === this._filepath || openRecordingPaths.has(filepath)) continue;
|
|
48862
49323
|
try {
|
|
48863
|
-
await
|
|
49324
|
+
await fs$1.unlink(filepath);
|
|
48864
49325
|
} catch {}
|
|
48865
49326
|
}
|
|
48866
49327
|
if (this._retainBytes <= 0) return;
|
|
48867
49328
|
const current = this._filepath ? path.basename(this._filepath) : null;
|
|
48868
49329
|
const measured = (await Promise.all(recordings.filter((entry) => !doomed.has(entry) && entry !== current).map(async (entry) => {
|
|
48869
49330
|
try {
|
|
48870
|
-
const stat = await
|
|
49331
|
+
const stat = await fs$1.stat(path.join(this._baseDir, entry));
|
|
48871
49332
|
return {
|
|
48872
49333
|
entry,
|
|
48873
49334
|
size: stat.size,
|
|
@@ -48882,7 +49343,7 @@ var SessionRecorder = class {
|
|
|
48882
49343
|
for (const entry of evictable) {
|
|
48883
49344
|
if (totalBytes <= this._retainBytes) return;
|
|
48884
49345
|
try {
|
|
48885
|
-
await
|
|
49346
|
+
await fs$1.unlink(path.join(this._baseDir, entry.entry));
|
|
48886
49347
|
totalBytes -= entry.size;
|
|
48887
49348
|
} catch (err) {
|
|
48888
49349
|
if (isMissingFileError(err, { includeNotDir: false })) totalBytes -= entry.size;
|
|
@@ -49010,6 +49471,8 @@ function createSessionFactory(dependencies) {
|
|
|
49010
49471
|
isSettingOn: (dependencies.getConfig?.() ?? config).coherence?.enabled === true
|
|
49011
49472
|
});
|
|
49012
49473
|
const builtMapping = project.environment;
|
|
49474
|
+
const sessionTempFolder = builtMapping ? null : sessionTempFolderFor(project.id);
|
|
49475
|
+
if (sessionTempFolder) fs.mkdirSync(sessionTempFolder, { recursive: true });
|
|
49013
49476
|
const session = new Session({
|
|
49014
49477
|
id: project.id,
|
|
49015
49478
|
name: project.name,
|
|
@@ -49047,6 +49510,10 @@ function createSessionFactory(dependencies) {
|
|
|
49047
49510
|
commitSigningProgram: dependencies.commitSigningProgram ?? null,
|
|
49048
49511
|
getRemoteLaunch: builtMapping ? () => remoteLaunchFor(project, builtMapping, dependencies.getConfig?.() ?? config) : null,
|
|
49049
49512
|
...overrides,
|
|
49513
|
+
spawnEnv: sessionTempFolder ? {
|
|
49514
|
+
CLAUDE_CODE_TMPDIR: sessionTempFolder,
|
|
49515
|
+
...overrides.spawnEnv ?? {}
|
|
49516
|
+
} : overrides.spawnEnv,
|
|
49050
49517
|
prepareGuardrails: "prepareGuardrails" in overrides ? overrides.prepareGuardrails : defaultGuardrailsFor(project, config, overrides)
|
|
49051
49518
|
});
|
|
49052
49519
|
const captureConfig = {
|
|
@@ -50906,22 +51373,37 @@ function registerControlHandlers(controlWss, deps) {
|
|
|
50906
51373
|
console.log(`[control] Removed spawned session via UI: ${sess.name}`);
|
|
50907
51374
|
return { ok: true };
|
|
50908
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
|
+
};
|
|
50909
51386
|
if (config.projects.some((project) => project.id === sess.id && project.environment) && coderEnv) {
|
|
50910
51387
|
sess.killSession();
|
|
50911
51388
|
const settlement = await coderEnv.settleRemoval({
|
|
50912
51389
|
projectId: sess.id,
|
|
50913
|
-
discardRemoteWork: msg.
|
|
51390
|
+
discardRemoteWork: msg.discardWork === true
|
|
50914
51391
|
});
|
|
50915
51392
|
if (!settlement.ok) return {
|
|
50916
51393
|
ok: false,
|
|
50917
51394
|
error: settlement.reason,
|
|
50918
|
-
|
|
51395
|
+
canDiscardWork: settlement.canDiscardRemoteWork
|
|
50919
51396
|
};
|
|
50920
51397
|
}
|
|
50921
51398
|
const freshConfig = configStore.save((cfg) => {
|
|
50922
51399
|
cfg.projects = cfg.projects.filter((p) => p.id !== sess.id);
|
|
50923
51400
|
});
|
|
50924
|
-
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);
|
|
50925
51407
|
console.log(`[control] Removed session via UI: ${sess.name}`);
|
|
50926
51408
|
return { ok: true };
|
|
50927
51409
|
}
|
|
@@ -53782,7 +54264,7 @@ async function downloadCapped(url, { fetchImpl, timeoutMs = DOWNLOAD_TIMEOUT_MS
|
|
|
53782
54264
|
}
|
|
53783
54265
|
}
|
|
53784
54266
|
async function findBinary(dir, platform) {
|
|
53785
|
-
const entries = await
|
|
54267
|
+
const entries = await fs$1.readdir(dir, { withFileTypes: true });
|
|
53786
54268
|
const nested = [];
|
|
53787
54269
|
for (const entry of entries) {
|
|
53788
54270
|
const full = path.join(dir, entry.name);
|
|
@@ -53800,13 +54282,13 @@ async function findBinary(dir, platform) {
|
|
|
53800
54282
|
}
|
|
53801
54283
|
async function removeQuietly(target) {
|
|
53802
54284
|
try {
|
|
53803
|
-
await
|
|
54285
|
+
await fs$1.rm(target, {
|
|
53804
54286
|
recursive: true,
|
|
53805
54287
|
force: true
|
|
53806
54288
|
});
|
|
53807
54289
|
} catch {}
|
|
53808
54290
|
}
|
|
53809
|
-
async function moveIntoPlace(source, target, renameImpl =
|
|
54291
|
+
async function moveIntoPlace(source, target, renameImpl = fs$1.rename) {
|
|
53810
54292
|
try {
|
|
53811
54293
|
await renameImpl(source, target);
|
|
53812
54294
|
return;
|
|
@@ -53815,15 +54297,15 @@ async function moveIntoPlace(source, target, renameImpl = fsp.rename) {
|
|
|
53815
54297
|
}
|
|
53816
54298
|
const partial = `${target}.partial`;
|
|
53817
54299
|
try {
|
|
53818
|
-
await
|
|
54300
|
+
await fs$1.copyFile(source, partial);
|
|
53819
54301
|
await renameImpl(partial, target);
|
|
53820
54302
|
} catch (err) {
|
|
53821
54303
|
await removeQuietly(partial);
|
|
53822
54304
|
throw err;
|
|
53823
54305
|
}
|
|
53824
|
-
await
|
|
54306
|
+
await fs$1.unlink(source);
|
|
53825
54307
|
}
|
|
53826
|
-
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) } = {}) {
|
|
53827
54309
|
if (!asset) return {
|
|
53828
54310
|
ok: false,
|
|
53829
54311
|
reason: `unsupported platform ${platform}-${arch}`
|
|
@@ -53834,7 +54316,7 @@ async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoi
|
|
|
53834
54316
|
};
|
|
53835
54317
|
const stagingDir = path.join(glimmervoidHome, "tmp", `rtk-${crypto.randomBytes(6).toString("hex")}`);
|
|
53836
54318
|
try {
|
|
53837
|
-
await
|
|
54319
|
+
await fs$1.mkdir(stagingDir, { recursive: true });
|
|
53838
54320
|
} catch (err) {
|
|
53839
54321
|
return {
|
|
53840
54322
|
ok: false,
|
|
@@ -53857,9 +54339,9 @@ async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoi
|
|
|
53857
54339
|
reason: `sha256 mismatch for ${asset.file}: expected ${asset.sha256}, got ${actualHex}`
|
|
53858
54340
|
};
|
|
53859
54341
|
const archivePath = path.join(stagingDir, asset.file);
|
|
53860
|
-
await
|
|
54342
|
+
await fs$1.writeFile(archivePath, downloaded.bytes);
|
|
53861
54343
|
const extractDir = path.join(stagingDir, "extract");
|
|
53862
|
-
await
|
|
54344
|
+
await fs$1.mkdir(extractDir, { recursive: true });
|
|
53863
54345
|
try {
|
|
53864
54346
|
const listing = await execFileImpl("tar", ["-tf", archivePath]);
|
|
53865
54347
|
const escaping = findEscapingArchiveMember(listing.stdout);
|
|
@@ -53884,13 +54366,13 @@ async function installRtk({ homeDir = os.homedir(), glimmervoidHome = glimmervoi
|
|
|
53884
54366
|
ok: false,
|
|
53885
54367
|
reason: `extract failed: no rtk binary inside ${asset.file}`
|
|
53886
54368
|
};
|
|
53887
|
-
if (!(await
|
|
54369
|
+
if (!(await fs$1.lstat(extracted)).isFile()) return {
|
|
53888
54370
|
ok: false,
|
|
53889
54371
|
reason: `extract failed: rtk inside ${asset.file} is not a regular file`
|
|
53890
54372
|
};
|
|
53891
|
-
if (platform !== "win32") await
|
|
54373
|
+
if (platform !== "win32") await fs$1.chmod(extracted, 493);
|
|
53892
54374
|
const target = installTargetPath(glimmervoidHome, platform);
|
|
53893
|
-
await
|
|
54375
|
+
await fs$1.mkdir(path.dirname(target), { recursive: true });
|
|
53894
54376
|
await removeQuietly(target);
|
|
53895
54377
|
await moveIntoPlace(extracted, target, renameImpl);
|
|
53896
54378
|
log?.log?.(`[rtk] installed rtk ${asset.version} at ${target}`);
|
|
@@ -53981,13 +54463,13 @@ function sleepFor(durationMs) {
|
|
|
53981
54463
|
function createClaudeFolderTrust({ env = process.env, homeDir = os.homedir(), now = Date.now, platform = process.platform, sleep = sleepFor } = {}) {
|
|
53982
54464
|
async function resolveConfiguredPath() {
|
|
53983
54465
|
const [legacyConfigPath, currentConfigPath] = listClaudeGlobalConfigPathCandidates(env, homeDir);
|
|
53984
|
-
return await
|
|
54466
|
+
return await fs$1.access(legacyConfigPath).then(() => true, () => false) ? legacyConfigPath : currentConfigPath;
|
|
53985
54467
|
}
|
|
53986
54468
|
function refusal(repositoryRoot, configPath, reason) {
|
|
53987
54469
|
return /* @__PURE__ */ new Error(`Could not trust ${repositoryRoot} for Claude Code: ${configPath} was left unchanged because ${reason}`);
|
|
53988
54470
|
}
|
|
53989
54471
|
async function readTrustDecision(repositoryRoot, configPath) {
|
|
53990
|
-
const decision = decideFolderTrust(await
|
|
54472
|
+
const decision = decideFolderTrust(await fs$1.readFile(configPath, "utf8").catch((error) => {
|
|
53991
54473
|
throw refusal(repositoryRoot, configPath, `it could not be read (${errorCode(error) ?? "unknown error"})`);
|
|
53992
54474
|
}), repositoryRoot, platform);
|
|
53993
54475
|
if (decision.kind === "unreadable") throw refusal(repositoryRoot, configPath, decision.reason);
|
|
@@ -53995,13 +54477,13 @@ function createClaudeFolderTrust({ env = process.env, homeDir = os.homedir(), no
|
|
|
53995
54477
|
}
|
|
53996
54478
|
async function tryTakeConfigLock(lockPath) {
|
|
53997
54479
|
try {
|
|
53998
|
-
await
|
|
54480
|
+
await fs$1.mkdir(lockPath);
|
|
53999
54481
|
return true;
|
|
54000
54482
|
} catch (error) {
|
|
54001
54483
|
if (errorCode(error) !== "EEXIST") throw error;
|
|
54002
54484
|
}
|
|
54003
|
-
const heldLock = await
|
|
54004
|
-
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(() => {});
|
|
54005
54487
|
return false;
|
|
54006
54488
|
}
|
|
54007
54489
|
async function takeConfigLock(repositoryRoot, configuredPath) {
|
|
@@ -54014,17 +54496,17 @@ function createClaudeFolderTrust({ env = process.env, homeDir = os.homedir(), no
|
|
|
54014
54496
|
}
|
|
54015
54497
|
async function ensureTrusted(repositoryRoot) {
|
|
54016
54498
|
const configuredPath = await resolveConfiguredPath();
|
|
54017
|
-
const configPath = await
|
|
54499
|
+
const configPath = await fs$1.realpath(configuredPath).catch(() => configuredPath);
|
|
54018
54500
|
if ((await readTrustDecision(repositoryRoot, configPath)).kind === "trusted") return "already-trusted";
|
|
54019
54501
|
const lockPath = await takeConfigLock(repositoryRoot, configuredPath);
|
|
54020
54502
|
try {
|
|
54021
54503
|
const decision = await readTrustDecision(repositoryRoot, configPath);
|
|
54022
54504
|
if (decision.kind === "trusted") return "already-trusted";
|
|
54023
|
-
const { mode } = await
|
|
54505
|
+
const { mode } = await fs$1.stat(configPath);
|
|
54024
54506
|
await writeTextAtomic(configPath, decision.nextConfigText, { mode: mode & 511 });
|
|
54025
54507
|
return "granted";
|
|
54026
54508
|
} finally {
|
|
54027
|
-
await
|
|
54509
|
+
await fs$1.rmdir(lockPath).catch(() => {});
|
|
54028
54510
|
}
|
|
54029
54511
|
}
|
|
54030
54512
|
return { ensureTrusted };
|
|
@@ -54266,11 +54748,11 @@ function spawnConnectPty(file, args, options) {
|
|
|
54266
54748
|
return pty.spawn(file, args, options);
|
|
54267
54749
|
}
|
|
54268
54750
|
async function makePrivateDir(directory) {
|
|
54269
|
-
await
|
|
54751
|
+
await fs$1.mkdir(directory, {
|
|
54270
54752
|
recursive: true,
|
|
54271
54753
|
mode: 448
|
|
54272
54754
|
});
|
|
54273
|
-
await
|
|
54755
|
+
await fs$1.chmod(directory, 448);
|
|
54274
54756
|
}
|
|
54275
54757
|
function createAgentConnect({ loginsRoot, loginSource, launchFor, onChange, ptySpawn = spawnConnectPty, makeHomeDir = makePrivateDir, setTimeoutFn = setTimeout, clearTimeoutFn = clearTimeout }) {
|
|
54276
54758
|
const statusByAgent = new Map(CONNECTABLE_AGENTS.map((agent) => [agent, idleLoginStatus(agent, null)]));
|
|
@@ -54475,7 +54957,7 @@ function chatgptAuthTokensRefreshResult(login) {
|
|
|
54475
54957
|
//#region server/codex-credentials.ts
|
|
54476
54958
|
var HOST_REFRESH_TIMEOUT_MS = 6e4;
|
|
54477
54959
|
async function readTextFileOrNull(filePath) {
|
|
54478
|
-
return
|
|
54960
|
+
return fs$1.readFile(filePath, "utf8").catch(() => null);
|
|
54479
54961
|
}
|
|
54480
54962
|
function codexHomeDir(env, homeDir) {
|
|
54481
54963
|
const override = env.CODEX_HOME?.trim();
|
|
@@ -55084,7 +55566,8 @@ function createUpdateApplyLane(dependencies) {
|
|
|
55084
55566
|
await runWorkspaceStep("stage", () => dependencies.gitWorkspace.stageDetachedWorktree({
|
|
55085
55567
|
projectPath: dependencies.packageRoot,
|
|
55086
55568
|
worktreePath: stagingPath,
|
|
55087
|
-
sha: status.latestSha || void 0
|
|
55569
|
+
sha: status.latestSha || void 0,
|
|
55570
|
+
owner: "lane:update-apply"
|
|
55088
55571
|
}));
|
|
55089
55572
|
const lockfileDiff = await gitCommand([
|
|
55090
55573
|
"diff",
|
|
@@ -55455,19 +55938,19 @@ async function collectWorktreePaths(projects) {
|
|
|
55455
55938
|
}
|
|
55456
55939
|
async function readFileNames(folder) {
|
|
55457
55940
|
try {
|
|
55458
|
-
return (await
|
|
55941
|
+
return (await fs$1.readdir(folder, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name);
|
|
55459
55942
|
} catch (error) {
|
|
55460
55943
|
if (isMissingFileError(error)) return null;
|
|
55461
55944
|
throw error;
|
|
55462
55945
|
}
|
|
55463
55946
|
}
|
|
55464
55947
|
async function removeFolderIfEmpty(folder) {
|
|
55465
|
-
if ((await
|
|
55466
|
-
await
|
|
55948
|
+
if ((await fs$1.readdir(folder)).length > 0) return;
|
|
55949
|
+
await fs$1.rmdir(folder);
|
|
55467
55950
|
}
|
|
55468
55951
|
async function isRealDirectory(folder) {
|
|
55469
55952
|
try {
|
|
55470
|
-
return (await
|
|
55953
|
+
return (await fs$1.lstat(folder)).isDirectory();
|
|
55471
55954
|
} catch (error) {
|
|
55472
55955
|
if (isMissingFileError(error)) return false;
|
|
55473
55956
|
throw error;
|
|
@@ -55475,10 +55958,10 @@ async function isRealDirectory(folder) {
|
|
|
55475
55958
|
}
|
|
55476
55959
|
async function isReportFolderOwnedByWorktree(worktreePath, reportFolder) {
|
|
55477
55960
|
if (!await isRealDirectory(path.dirname(reportFolder)) || !await isRealDirectory(reportFolder)) return false;
|
|
55478
|
-
const realWorktreePath = await
|
|
55479
|
-
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;
|
|
55480
55963
|
const reportFolderTopLevel = (await gitOutput(["rev-parse", "--show-toplevel"], reportFolder)).trim();
|
|
55481
|
-
return equalsIgnoringCaseOnWindows(await
|
|
55964
|
+
return equalsIgnoringCaseOnWindows(await fs$1.realpath(reportFolderTopLevel), realWorktreePath);
|
|
55482
55965
|
}
|
|
55483
55966
|
async function listTrackedReportGitPaths(worktreePath) {
|
|
55484
55967
|
return (await gitOutput([
|
|
@@ -55495,7 +55978,7 @@ async function cleanWorktree(worktreePath) {
|
|
|
55495
55978
|
if (fileNames === null) return NOTHING_CLEANED;
|
|
55496
55979
|
const trackedReportGitPaths = await listTrackedReportGitPaths(worktreePath);
|
|
55497
55980
|
const deletableFileNames = untrackedReportFileNames(fileNames, trackedReportGitPaths);
|
|
55498
|
-
for (const fileName of deletableFileNames) await
|
|
55981
|
+
for (const fileName of deletableFileNames) await fs$1.rm(path.join(reportFolder, fileName), { force: true });
|
|
55499
55982
|
await removeFolderIfEmpty(reportFolder);
|
|
55500
55983
|
await removeFolderIfEmpty(path.dirname(reportFolder));
|
|
55501
55984
|
return {
|
|
@@ -56549,6 +57032,12 @@ function createBackend(httpServer, options = {}) {
|
|
|
56549
57032
|
const reconcileProjectsOnReload = sessionRegistry.applyConfigReload;
|
|
56550
57033
|
rtkInstall.maybeInstall();
|
|
56551
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
|
+
});
|
|
56552
57041
|
laneAssembly.startRuntimeLanes();
|
|
56553
57042
|
function applyCustomAgents(sourceConfig) {
|
|
56554
57043
|
setCustomAgents(sourceConfig.customAgents ?? []);
|