glimmervoid 0.45.0 → 0.45.1-next.46
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 +15 -15
- package/dist/chunks/{adapters-D99c4zCx.js → adapters-BE9BKVW0.js} +24 -24
- package/dist/chunks/{adapters-BGuI5U_-.js → adapters-CPaysZkO.js} +1 -1
- package/dist/chunks/{agent-api-cli-DdrR2vnB.js → agent-api-cli-B0l8uLos.js} +1 -1
- package/dist/chunks/{agent-api-cli-DIGH1v3_.js → agent-api-cli-Bq9J0_D5.js} +148 -15
- package/dist/chunks/{agent-api-core-D49OzKho.js → agent-api-core-ClU9-5K3.js} +9 -3
- package/dist/chunks/{agent-setup-cli-7hG401Io.js → agent-setup-cli-BA_1AdhQ.js} +2 -2
- package/dist/chunks/{contracts-CucdticH.js → build-channel-qgKwNssc.js} +37 -3
- package/dist/chunks/{config-BLeRKmn9.js → config-BqJTnPae.js} +16 -33
- package/dist/chunks/{config-path-core-DFt-TemS.js → config-path-core-DUWk0Lui.js} +5 -1
- package/dist/chunks/config-store-5xpRLHFo.js +1 -0
- package/dist/chunks/{config-store-CXLnauAa.js → config-store-BGTg8oKO.js} +77 -78
- package/dist/chunks/{git-version-C3lznLS_.js → git-version-BpdkKH2j.js} +1 -1
- package/dist/chunks/{grok-Di4aQGre.js → grok-7jK8ZNtf.js} +1 -1
- package/dist/chunks/{json-file-DFCTvPjj.js → json-file-BjFK_eSH.js} +2 -7
- package/dist/chunks/{main-CX9CkTDx.js → main-x9ygUnoL.js} +1030 -988
- package/dist/chunks/{pair-cli-BPyh48--.js → pair-cli-Cph4TZ2R.js} +2 -2
- package/dist/chunks/{remote-config-B0zBDBak.js → remote-config-CmP36r_s.js} +2 -2
- package/dist/chunks/{rtk-resolver-BDHi6RMv.js → rtk-resolver-C_IReILP.js} +1 -1
- package/dist/chunks/rtk-resolver-DgnNb1or.js +1 -0
- package/dist/chunks/{session-CUc6d7aS.js → session-BwlLY_mv.js} +36 -14
- package/dist/chunks/{session-routes--hrsvv7a.js → session-routes-CviNvxcz.js} +9 -1
- package/dist/chunks/{settings-cli-DzIasX94.js → settings-cli-Bete5oVk.js} +11 -10
- package/dist/chunks/{settings-readout-core-BOv6tqdK.js → settings-readout-core-CS8MPaIE.js} +14 -8
- package/dist/chunks/shown-files-core-soMzCQ1D.js +145 -0
- package/dist/chunks/{ssh-sign-cli-C_QYhDvI.js → ssh-sign-cli-DXhuqzuy.js} +2 -2
- package/dist/client/assets/action-status-BwqYb9eF.js +1 -0
- package/dist/client/assets/action-status-BwqYb9eF.js.br +0 -0
- package/dist/client/assets/benchmark-panel-cv4w_jfa.js +1 -0
- package/dist/client/assets/benchmark-panel-cv4w_jfa.js.br +0 -0
- package/dist/client/assets/card-dom-DkZgkeG9.js +1 -0
- package/dist/client/assets/card-dom-DkZgkeG9.js.br +0 -0
- package/dist/client/assets/control-ws-CAcwW-r9.js +4 -0
- package/dist/client/assets/control-ws-CAcwW-r9.js.br +0 -0
- package/dist/client/assets/dialogs-CBLquNyw.js +79 -0
- package/dist/client/assets/dialogs-CBLquNyw.js.br +0 -0
- package/dist/client/assets/dom-helpers-xJduxDcc.js +1 -0
- package/dist/client/assets/dom-helpers-xJduxDcc.js.br +0 -0
- package/dist/client/assets/hooks-panel-R2N1db0F.js +1 -0
- package/dist/client/assets/hooks-panel-R2N1db0F.js.br +0 -0
- package/dist/client/assets/index-CY9JZfNS.css +2 -0
- package/dist/client/assets/index-CY9JZfNS.css.br +0 -0
- package/dist/client/assets/index-vCdYvr8W.js +84 -0
- package/dist/client/assets/index-vCdYvr8W.js.br +0 -0
- package/dist/client/assets/issues-panel-D1-aYqED.js +1 -0
- package/dist/client/assets/issues-panel-D1-aYqED.js.br +0 -0
- package/dist/client/assets/modal-mWl2H7A_.js +1 -0
- package/dist/client/assets/modal-mWl2H7A_.js.br +0 -0
- package/dist/client/assets/plan-face-fMTjf0DW.js +3 -0
- package/dist/client/assets/plan-face-fMTjf0DW.js.br +0 -0
- package/dist/client/assets/{poll-ago-ItO-gOWA.js → poll-ago-Bc-8mMFj.js} +1 -1
- package/dist/client/assets/poll-ago-Bc-8mMFj.js.br +0 -0
- package/dist/client/assets/pr-scope-tabs-BxXGKoO4.js +1 -0
- package/dist/client/assets/pr-scope-tabs-BxXGKoO4.js.br +0 -0
- package/dist/client/assets/radar-panel-Ceg8PWmF.js +1 -0
- package/dist/client/assets/radar-panel-Ceg8PWmF.js.br +0 -0
- package/dist/client/assets/review-queue-panel-CWSHIbOH.js +10 -0
- package/dist/client/assets/review-queue-panel-CWSHIbOH.js.br +0 -0
- package/dist/client/assets/roving-keys-core-BDobx2It.js +1 -0
- package/dist/client/assets/settings-panel-C1c627sY.js +4 -0
- package/dist/client/assets/settings-panel-C1c627sY.js.br +0 -0
- package/dist/client/assets/trace-panel-CjNW2loY.js +2 -0
- package/dist/client/assets/trace-panel-CjNW2loY.js.br +0 -0
- package/dist/client/assets/ui-prefs-CNf6fId2.js +1 -0
- package/dist/client/assets/ui-prefs-CNf6fId2.js.br +0 -0
- package/dist/client/assets/usage-panel-DQQpGJY7.js +1 -0
- package/dist/client/assets/usage-panel-DQQpGJY7.js.br +0 -0
- package/dist/client/assets/visions-panel-CtcJckL5.js +1 -0
- package/dist/client/assets/visions-panel-CtcJckL5.js.br +0 -0
- package/dist/client/index.html +14 -8
- package/dist/client/index.html.br +0 -0
- package/dist/server/index.js +99 -1
- package/dist/session/guardrails-open-launcher.js +28 -16
- package/dist/session/visions-relay.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/config-store-DlaaBY9p.js +0 -1
- package/dist/chunks/rtk-resolver-Dr4iJsiS.js +0 -1
- package/dist/client/assets/action-status-DrMQzohk.js +0 -1
- package/dist/client/assets/action-status-DrMQzohk.js.br +0 -0
- package/dist/client/assets/benchmark-panel-D09qvrAY.js +0 -1
- package/dist/client/assets/benchmark-panel-D09qvrAY.js.br +0 -0
- package/dist/client/assets/dom-helpers-CZ8NZKxw.js +0 -4
- package/dist/client/assets/dom-helpers-CZ8NZKxw.js.br +0 -0
- package/dist/client/assets/hooks-panel-R-L7DuIz.js +0 -1
- package/dist/client/assets/hooks-panel-R-L7DuIz.js.br +0 -0
- package/dist/client/assets/index-B7TvZo7W.js +0 -173
- package/dist/client/assets/index-B7TvZo7W.js.br +0 -0
- package/dist/client/assets/index-C3nOrTwM.css +0 -2
- package/dist/client/assets/index-C3nOrTwM.css.br +0 -0
- package/dist/client/assets/issues-panel-CyB4oq9Z.js +0 -1
- package/dist/client/assets/issues-panel-CyB4oq9Z.js.br +0 -0
- package/dist/client/assets/modal-M_mJ8--V.js +0 -1
- package/dist/client/assets/modal-M_mJ8--V.js.br +0 -0
- package/dist/client/assets/poll-ago-ItO-gOWA.js.br +0 -0
- package/dist/client/assets/pr-queue-columns-Cu3-PADL.js +0 -1
- package/dist/client/assets/pr-queue-columns-Cu3-PADL.js.br +0 -0
- package/dist/client/assets/radar-panel-Cl3yPWed.js +0 -1
- package/dist/client/assets/radar-panel-Cl3yPWed.js.br +0 -0
- package/dist/client/assets/review-queue-panel-CVujzadC.js +0 -2
- package/dist/client/assets/review-queue-panel-CVujzadC.js.br +0 -0
- package/dist/client/assets/settings-panel-DW5An8lQ.js +0 -3
- package/dist/client/assets/settings-panel-DW5An8lQ.js.br +0 -0
- package/dist/client/assets/trace-panel-B6TiM5pb.js +0 -1
- package/dist/client/assets/trace-panel-B6TiM5pb.js.br +0 -0
- package/dist/client/assets/ui-prefs-CQHZFVx6.js +0 -1
- package/dist/client/assets/ui-prefs-CQHZFVx6.js.br +0 -0
- package/dist/client/assets/usage-panel-D2RX3LtG.js +0 -1
- package/dist/client/assets/usage-panel-D2RX3LtG.js.br +0 -0
- package/dist/client/assets/visions-panel-BV9EKCBw.js +0 -1
- package/dist/client/assets/visions-panel-BV9EKCBw.js.br +0 -0
|
@@ -2,14 +2,14 @@ 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 { c as resolvePackageBin, i as clientDir, n as bundled, o as packageRoot, r as cliPath, s as relayPath, t as assetRoot } from "./runtime-paths-ghcH9CeW.js";
|
|
5
|
-
import {
|
|
5
|
+
import { n as glimmervoidHomeDir, r as isOperatorHomeUnderTest, t as decideConfigPath } from "./config-path-core-DUWk0Lui.js";
|
|
6
|
+
import { $ as MyPrMergeStateResponse, $t as MERGEABLE_LIVE_STATES, A as SHOWN_FILE_CAP_BYTES, At as TeamReviewResolveResponse, B as TaskTitle, C as ConversationQuestion, Ct as ReviewFinding, Dt as TeamReviewActionRequest, E as SESSION_DOC_BODY_CAP_BYTES, Et as ReviewVerdict, Ft as TeamReviewThreadsRepository, G as MyPrCheckContext, Gt as REPO_SLUG_RE, H as LinkedPr, Ht as shortSha, I as ShownFile, It as canApproveAfterComment, K as MyPrCheckContextsResponse, Kt as repoParts, Lt as decisionAsIfApprovalRequired, M as SSH_SIGN_BODY_CAP_BYTES, Mt as TeamReviewStatus, Nt as TeamReviewThreadCommentsResponse, O as SESSION_NAME_PATTERN, Pt as TeamReviewThreadResult, Qt as KILLABLE_STATES, R as SshSignRequest, St as ReviewDraft, Tt as ReviewThreadId, U as MyPr, Ut as GH_SEGMENT, Vt as HEX_SHA_RE, W as MyPrAutoRebase, Wt as NODE_ID_RE, X as MyPrMergeMethod, Y as MyPrLaneEntry, Z as MyPrMergeRequest, Zt as BADGE_LABELS, _ as CONVERSATION_PERMISSION_HOOK_EVENT, _t as PostedReviewEvent, at as MyPrsSnapshot, dt as CommitSha, en as RESTARTABLE_STATES, et as MyPrMergeWhenReadyRequest, ft as DECIDING_REVIEW_STATES, g as CONVERSATION_APPROVAL_SUMMARY_MAX_CHARS, gt as GithubReviewState, h as COHERENCE_HOOKS_INACTIVE, it as MyPrThreadsResponse, j as SIGN_URL_ENV, jt as TeamReviewStateEntry, k as SESSION_NAME_RULE_TEXT, kt as TeamReviewCompareFiles, mt as FindingSeverity, n as AGENT_URL_ENV, ot as MyPrsState, pt as EarlierReview, q as MyPrKeepMergeableRequest, r as ASK_USER_QUESTION_TOOL_NAME, rt as MyPrThreadNode, tn as STATES, v as CONVERSATION_TOOL_RESULT_HOOK_EVENT, vt as PostingPlan, wt as ReviewResult, xt as ReviewComment, yt as PrDetail, zt as hasStandingViewerApproval } from "./session-BwlLY_mv.js";
|
|
6
7
|
import { n as execFileAsync, o as spawn, r as execFileSync, t as execFile } from "./child-process-safe-D4hyVIXj.js";
|
|
7
|
-
import { A as
|
|
8
|
-
import { a as permitsHelpCommand, r as describeSessionLimits } from "./session-routes
|
|
9
|
-
import { A as seedTaskTitleSources, B as listRepoWorktreePaths, C as extractPromptTaskTitle, D as parseRefinedTaskTitle, E as parseRefinedSessionName, F as resumePromptFor, H as nextSuggestedName, I as shouldAdoptReportedResumeId, L as claudeProjectsDir, M as RESUME_ID_RE, N as pickAutoResume, O as resolveRefocusTaskTitle, P as resolveResumeTarget, R as encodeProjectDir, S as extractOscTaskTitle, T as isSubstantivePrompt, V as isAutoNameOf, _ as buildTaskTitleRefinementPrompt, a as decideSpawnAllowance, b as decideTaskTitleRefinement, c as describeKeptChildAttention, d as describeUnpushedCloseRefusal, g as buildPersistedTaskTitle, h as projectReviews, i as decideAgentRequest, j as AUTO_CONTINUE_PROMPT, k as resolveTaskTitle, l as describePushLanding, m as projectMyPrs, n as REFUSAL_REASON, o as deriveChildSessionName, p as parseAgentVerb, s as describeChildEndNote, u as describeRoleRefusal, v as chooseRefinedSessionName, w as isSelfWakePrompt, x as extractAiTitle, y as cleanTaskPrompt, z as listRepoConversations } from "./agent-api-core-
|
|
8
|
+
import { A as BenchmarkRun, At as parseExitPlanModeHookPayload, B as GithubPrsFrame, Ct as PLAN_BODY_CAP_BYTES, D as BenchmarkCase, Dt as PLAN_TOOL_NAME, E as BenchmarkActionRequest, Et as PLAN_RESULT_HOOK_EVENT, F as MinedPrReviewData, Ft as IssueUpdate, G as WorkflowsState, H as WorkflowCommentBody, I as PrCheckoutCaseInput, It as IssuesSearchResponse, L as GithubPrDetailNode, Lt as IssuesState, M as BenchmarkSuite, Mt as GithubIssuePullRequestsRepository, N as CommitComparison, Nt as GithubIssueResponse, O as BenchmarkId, Ot as PlanDecision, P as MergedPrListing, Pt as IssueKey, R as GithubPrKey, Rt as IssuesStatus, St as changeMapFactId, T as pullRequestKeyFromReference, Tt as PLAN_HOOK_OUTPUT_MAX_CHARS, U as WorkflowLabelName, V as comparablePrKey, W as WorkflowsSettings, X as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, _ as CONTROL_FRAME_MAX_BYTES, _t as MAX_RAW_LINE_CHARS, a as ConfigUpdate, bt as CoherenceJournalOutput, ct as USAGE_INTEGER_RANGES, d as USAGE_COST_MODES, f as USAGE_VENDOR_KEYS, g as CLIENT_LOG_TEXT_MAX_CHARS, gt as SessionSetup, ht as UpdateJournal, j as BenchmarkStatus, jt as planTitle, k as BenchmarkJudgement, kt as PlanRevision, l as configIssueMessage, m as CHANGES_POLL_INTERVAL_MS, r as BrowserConfig, s as RUNTIME_CONFIG_SCALAR_KEYS, v as ClientMessage, vt as TraceCheckpoint, wt as PLAN_HOOK_EVENT, xt as ChangeMap, yt as TraceRecord, z as GithubPrLightSearchNode, zt as issuePullRequestStateFromGithub } from "./config-BqJTnPae.js";
|
|
9
|
+
import { a as permitsHelpCommand, r as describeSessionLimits } from "./session-routes-CviNvxcz.js";
|
|
10
|
+
import { A as seedTaskTitleSources, B as listRepoWorktreePaths, C as extractPromptTaskTitle, D as parseRefinedTaskTitle, E as parseRefinedSessionName, F as resumePromptFor, H as nextSuggestedName, I as shouldAdoptReportedResumeId, L as claudeProjectsDir, M as RESUME_ID_RE, N as pickAutoResume, O as resolveRefocusTaskTitle, P as resolveResumeTarget, R as encodeProjectDir, S as extractOscTaskTitle, T as isSubstantivePrompt, V as isAutoNameOf, _ as buildTaskTitleRefinementPrompt, a as decideSpawnAllowance, b as decideTaskTitleRefinement, c as describeKeptChildAttention, d as describeUnpushedCloseRefusal, g as buildPersistedTaskTitle, h as projectReviews, i as decideAgentRequest, j as AUTO_CONTINUE_PROMPT, k as resolveTaskTitle, l as describePushLanding, m as projectMyPrs, n as REFUSAL_REASON, o as deriveChildSessionName, p as parseAgentVerb, s as describeChildEndNote, u as describeRoleRefusal, v as chooseRefinedSessionName, w as isSelfWakePrompt, x as extractAiTitle, y as cleanTaskPrompt, z as listRepoConversations } from "./agent-api-core-ClU9-5K3.js";
|
|
10
11
|
import { a as positiveIntOr, c as textOr, i as numberOr, n as isRecord, o as positiveNumberOr, r as nonNegativeIntOr, s as rawTextOr, t as coercedNumberOr } from "./coerce-NSst8oRP.js";
|
|
11
|
-
import {
|
|
12
|
-
import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-C3lznLS_.js";
|
|
12
|
+
import { i as parseCommandLine, n as warnWhenGitIsTooOld } from "./git-version-BpdkKH2j.js";
|
|
13
13
|
import { n as checkGuardrailsDependencies, t as allowGuardedSpawn } from "./guardrails-deps-BjPzk1rE.js";
|
|
14
14
|
import { t as HOOK_URL_ENV } from "./hook-relay-core-DA_bz2KA.js";
|
|
15
15
|
import { t as HOOK_VIA_GUARDRAILS_PROXY_ENV } from "./guardrails-proxy-core-BI3rdY-m.js";
|
|
@@ -19,17 +19,18 @@ import { s as saneYoloFiles } from "./rtk-hook-core-B-MpzPin.js";
|
|
|
19
19
|
import { _ as lineStartOffsets, a as projectForUri, b as replacedSpanOfWholeTextChange, c as visionsLaneReloadSignature, d as applyDidOpen, f as createDocStore, g as lineOfOffset, h as getDoc, i as isUriInProjects, l as applyDidChange, m as formatRange, n as applyEditorNotification, o as resolveVisionsScopeProjects, p as detectBlankLineBoundary, r as createEditorState, s as scopePathsOf, t as ACTIVITY_METHOD, u as applyDidClose, v as listDocs, x as uriOfParams, y as offsetOfPosition } from "./ingest-editor-core-B2tycW_m.js";
|
|
20
20
|
import { n as DEFAULT_MAX_MS, r as nextBackoffMs, t as DEFAULT_BASE_MS } from "./backoff-DjVpfHk7.js";
|
|
21
21
|
import { n as unrefTimer, t as DEFAULT_TIMER_FNS } from "./timer-deps-u2DAQLok.js";
|
|
22
|
+
import { a as readPageTitle, i as filesAfterRetention, n as decideShownKind, o as shownMimeForFilename, r as describeOverCap, t as SHOWN_SNIFF_BYTES } from "./shown-files-core-soMzCQ1D.js";
|
|
22
23
|
import { n as shellQuote } from "./hook-command-core-qTNK-xMF.js";
|
|
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-
|
|
24
|
-
import {
|
|
25
|
-
import { a as
|
|
26
|
-
import { A as
|
|
24
|
+
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-BjFK_eSH.js";
|
|
25
|
+
import { $ as resolveProjectToken, B as buildAiGenerationEvents, G as claudeAccountUuidOf, H as buildExceptionProperties, I as FIRST_RUN_NOTICE, J as decideRemoteTelemetryState, K as codexUserIdOf, L as TELEMETRY_BATCH_URL, Q as parsePosthogReleaseId, R as TELEMETRY_FLAGS_URL, S as validateSettingsBlock, U as buildPersonProperties, V as buildBrowserExceptionProperties, W as classifySessionExit, X as exceptionFingerprint, Y as decideTelemetryConsent, Z as nodeMajorVersion, _ as isRtkBinaryName, a as ensureProjectIds, at as matchedPrefix, b as mergeSettingsBlock, ct as sessionIdFromBranch, dt as canonicalizePath, ft as comparableDirectoryPath, g as installTargetPath, h as findEscapingArchiveMember, i as createConfigStore, it as DEFAULT_BRANCH_GC_PREFIXES, lt as usablePrefixes, m as decideRtkInstall, n as DEFAULT_CONFIG, nt as stableConfigKey, o as generateProjectId, ot as planBranchGc, p as assetForPlatform, pt as isSameDirectoryPath, q as decideIsDevInstall, s as glimmervoidHomeDir$1, st as planWorktreeGc, t as ABORT_CONFIG_SAVE, ut as worktreeIntegrationTips, v as verifyDigest, x as settingsBlockSpecFor, z as adapterBucket } from "./config-store-BGTg8oKO.js";
|
|
26
|
+
import { a as DEEP_HISTORY_SCROLLBACK_LINES, c as DashboardTelemetryReport, d as TELEMETRY_EVENT_SCHEMAS, i as HookEnvelope, o as parseDataAttach, p as TelemetryState, r as HTTP_URL_RE, s as parseDataClientMessage, u as PendingCrashReport } from "./build-channel-qgKwNssc.js";
|
|
27
|
+
import { A as decideFileRead, B as shouldEvaluateDespiteIoFailures, C as pickNewestPublished, D as codexRootCandidates, E as codexHomes, F as isUsageFile, H as splitLines$1, I as passOutcome, L as projectDirCandidates, M as expandTilde, N as grokHomes, O as codexSessionIdFromPath, P as grokRootCandidates, R as resolveProjectsDirs, S as parseResolvedSha, T as codexFallbackRoots, U as claudeCode, V as shouldPersistWarehouse, _ 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 dedupeCodexFiles, k as continuationDelayMs, 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 shortSha$1, x as parseRegistryLatest, y as normalizeUpdateChannel, z as scopedClaudeRoots } from "./adapters-BE9BKVW0.js";
|
|
27
28
|
import { t as HOOK_TOOLS } from "./hook-tools-Dl7-r2Eb.js";
|
|
28
29
|
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-
|
|
30
|
+
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-CmP36r_s.js";
|
|
31
|
+
import { n as configFileViewOf, u as readSettingsForAgent } from "./settings-readout-core-CS8MPaIE.js";
|
|
31
32
|
import { _ as escapeMarkup, g as createLaneLog, n as createVisionsSetup } from "./visions-setup-VMMyqOpl.js";
|
|
32
|
-
import { t as getRtkPath } from "./rtk-resolver-
|
|
33
|
+
import { t as getRtkPath } from "./rtk-resolver-C_IReILP.js";
|
|
33
34
|
import { createRequire } from "node:module";
|
|
34
35
|
import fs, { closeSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, unlinkSync } from "node:fs";
|
|
35
36
|
import path, { join, resolve } from "node:path";
|
|
@@ -38,9 +39,9 @@ import { z } from "zod";
|
|
|
38
39
|
import crypto, { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
39
40
|
import WebSocket$1, { WebSocket, WebSocketServer } from "ws";
|
|
40
41
|
import http from "node:http";
|
|
42
|
+
import { pipeline } from "node:stream";
|
|
41
43
|
import net, { isIPv6 } from "node:net";
|
|
42
44
|
import fs$1, { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
43
|
-
import { pipeline } from "node:stream";
|
|
44
45
|
import express from "express";
|
|
45
46
|
import { EventEmitter } from "node:events";
|
|
46
47
|
import pty from "node-pty";
|
|
@@ -49,6 +50,95 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
49
50
|
import https from "node:https";
|
|
50
51
|
import { StringDecoder } from "node:string_decoder";
|
|
51
52
|
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
53
|
+
//#region server/removal-journal.ts
|
|
54
|
+
function removalsJournalPath(glimmervoidHome) {
|
|
55
|
+
return path.join(glimmervoidHome, "logs", "removals.jsonl");
|
|
56
|
+
}
|
|
57
|
+
async function appendRemovalRecord(journalPath, record, log = console) {
|
|
58
|
+
return appendJsonLine(journalPath, record, { mkdir: true }).then(() => true, (error) => {
|
|
59
|
+
log.warn(`[removals] kept ${record.path ?? record.branch} because its removal record could not be written: ${errorMessage(error)}`);
|
|
60
|
+
return false;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function appendRemovalRecordSync(journalPath, record, log = console) {
|
|
64
|
+
try {
|
|
65
|
+
fs.mkdirSync(path.dirname(journalPath), { recursive: true });
|
|
66
|
+
fs.appendFileSync(journalPath, `${JSON.stringify(record)}\n`, "utf8");
|
|
67
|
+
return true;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
log.warn(`[removals] kept ${record.path ?? record.branch} because its removal record could not be written: ${errorMessage(error)}`);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function recordSweptPath({ cause, kind, path: sweptPath, checksPassed, sessionId = null, journalPath = removalsJournalPath(glimmervoidHomeDir$1()), log = console }) {
|
|
74
|
+
return appendRemovalRecord(journalPath, {
|
|
75
|
+
ts: Date.now(),
|
|
76
|
+
cause,
|
|
77
|
+
kind,
|
|
78
|
+
path: sweptPath,
|
|
79
|
+
branch: null,
|
|
80
|
+
projectPath: null,
|
|
81
|
+
sessionId,
|
|
82
|
+
checksPassed,
|
|
83
|
+
pid: process.pid
|
|
84
|
+
}, log);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region server/core/removal-guard-core.ts
|
|
88
|
+
var SESSION_WORK_RECENCY_FLOOR_MS = 864e5;
|
|
89
|
+
var AUTOMATIC_REMOVAL_CAUSES = /* @__PURE__ */ new Set([
|
|
90
|
+
"branch-gc",
|
|
91
|
+
"boot-reconcile",
|
|
92
|
+
"owned-paths"
|
|
93
|
+
]);
|
|
94
|
+
var SESSION_OWNER_PREFIX$1 = "session:";
|
|
95
|
+
function sessionIdFromOwner(owner) {
|
|
96
|
+
if (owner === null || !owner.startsWith(SESSION_OWNER_PREFIX$1)) return null;
|
|
97
|
+
return owner.slice(8);
|
|
98
|
+
}
|
|
99
|
+
function isRecencyProbeNeeded(facts) {
|
|
100
|
+
return decideKeepByOwnership(facts) === null;
|
|
101
|
+
}
|
|
102
|
+
function decideKeepByOwnership(facts) {
|
|
103
|
+
if (!AUTOMATIC_REMOVAL_CAUSES.has(facts.cause)) return {
|
|
104
|
+
keep: false,
|
|
105
|
+
checksPassed: ["explicit-cause"]
|
|
106
|
+
};
|
|
107
|
+
if (facts.sessionId === null && facts.ledgerOwnerSessionId === null) return {
|
|
108
|
+
keep: false,
|
|
109
|
+
checksPassed: ["not-session-work"]
|
|
110
|
+
};
|
|
111
|
+
if (facts.sessionId !== null && facts.protectedSessionIds.has(facts.sessionId)) return {
|
|
112
|
+
keep: true,
|
|
113
|
+
reason: "protected-session"
|
|
114
|
+
};
|
|
115
|
+
if (facts.ledgerOwnerSessionId !== null && facts.protectedSessionIds.has(facts.ledgerOwnerSessionId)) return {
|
|
116
|
+
keep: true,
|
|
117
|
+
reason: "ledger-owned"
|
|
118
|
+
};
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function decideRemovalKeep(facts) {
|
|
122
|
+
const byOwnership = decideKeepByOwnership(facts);
|
|
123
|
+
if (byOwnership !== null) return byOwnership;
|
|
124
|
+
if (facts.lastTouchedMs === null) return {
|
|
125
|
+
keep: true,
|
|
126
|
+
reason: "touch-probe-failed"
|
|
127
|
+
};
|
|
128
|
+
if (facts.nowMs - facts.lastTouchedMs < SESSION_WORK_RECENCY_FLOOR_MS) return {
|
|
129
|
+
keep: true,
|
|
130
|
+
reason: "recently-touched"
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
keep: false,
|
|
134
|
+
checksPassed: [
|
|
135
|
+
"not-protected",
|
|
136
|
+
"not-ledger-owned",
|
|
137
|
+
"untouched-24h"
|
|
138
|
+
]
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
52
142
|
//#region shared/contracts/owned-paths.ts
|
|
53
143
|
var OwnedPathOwner = z.string().regex(/^(session|lane):[A-Za-z0-9._:#-]+$/);
|
|
54
144
|
var OwnedPathEntry = z.object({
|
|
@@ -82,9 +172,6 @@ function holdsKeptPath(folderPath, keptPaths) {
|
|
|
82
172
|
}
|
|
83
173
|
//#endregion
|
|
84
174
|
//#region server/owned-paths-ledger.ts
|
|
85
|
-
function ownedPathsLedgerPath(glimmervoidHome = glimmervoidHomeDir$1()) {
|
|
86
|
-
return path.join(glimmervoidHome, "owned-paths.json");
|
|
87
|
-
}
|
|
88
175
|
function ledgerKey(ownedPath) {
|
|
89
176
|
return caseFoldedPathKey(path.resolve(ownedPath));
|
|
90
177
|
}
|
|
@@ -140,6 +227,10 @@ function createOwnedPathsLedger({ filePath, log = console }) {
|
|
|
140
227
|
if (!isLoaded || !entriesByPath.delete(ledgerKey(await canonicalOwnedPath(ownedPath)))) return;
|
|
141
228
|
await persist();
|
|
142
229
|
}
|
|
230
|
+
async function ownerOf(ownedPath) {
|
|
231
|
+
await store.load();
|
|
232
|
+
return entriesByPath.get(ledgerKey(await canonicalOwnedPath(ownedPath)))?.owner ?? null;
|
|
233
|
+
}
|
|
143
234
|
async function loadEarlierRunEntries() {
|
|
144
235
|
await store.load();
|
|
145
236
|
if (!isLoaded) return { ok: false };
|
|
@@ -152,6 +243,7 @@ function createOwnedPathsLedger({ filePath, log = console }) {
|
|
|
152
243
|
loadEarlierRunEntries,
|
|
153
244
|
record,
|
|
154
245
|
forget,
|
|
246
|
+
ownerOf,
|
|
155
247
|
idle: () => store.idle()
|
|
156
248
|
};
|
|
157
249
|
}
|
|
@@ -173,14 +265,28 @@ async function removeOrphanedWorktree(entry, git, shareList) {
|
|
|
173
265
|
const removed = await git.removeWorkspaceMember({
|
|
174
266
|
projectPath: entry.projectPath,
|
|
175
267
|
cwd: entry.path,
|
|
176
|
-
|
|
268
|
+
branch: entry.branch,
|
|
269
|
+
shareList,
|
|
270
|
+
cause: "owned-paths"
|
|
177
271
|
});
|
|
272
|
+
if (removed.removalKept) return `the removal guard kept it: ${removed.removalKept}`;
|
|
178
273
|
if (!removed.ok || await pathExists$1(entry.path)) return `git could not remove it: ${removed.err || "the folder is still there"}`;
|
|
179
274
|
return null;
|
|
180
275
|
}
|
|
181
|
-
async function removeOrphanedFolder(entry, ledger, keptPaths) {
|
|
276
|
+
async function removeOrphanedFolder(entry, ledger, keptPaths, journalPath, log) {
|
|
182
277
|
if (holdsKeptPath(entry.path, keptPaths)) return "it holds a checkout that was kept";
|
|
183
278
|
if (await holdsCheckout(entry.path)) return "it still holds a checkout";
|
|
279
|
+
if (!await appendRemovalRecord(journalPath, {
|
|
280
|
+
ts: Date.now(),
|
|
281
|
+
cause: "owned-paths",
|
|
282
|
+
kind: "folder",
|
|
283
|
+
path: entry.path,
|
|
284
|
+
branch: null,
|
|
285
|
+
projectPath: entry.projectPath,
|
|
286
|
+
sessionId: sessionIdFromOwner(entry.owner),
|
|
287
|
+
checksPassed: ["session-not-configured", "holds-no-checkout"],
|
|
288
|
+
pid: process.pid
|
|
289
|
+
}, log)) return "its removal record could not be written";
|
|
184
290
|
const removal = await fs$1.rm(entry.path, {
|
|
185
291
|
recursive: true,
|
|
186
292
|
force: true
|
|
@@ -189,7 +295,7 @@ async function removeOrphanedFolder(entry, ledger, keptPaths) {
|
|
|
189
295
|
await ledger.forget(entry.path);
|
|
190
296
|
return null;
|
|
191
297
|
}
|
|
192
|
-
async function reconcileOwnedPaths({ ledger, configuredSessionIds, git, shareList, log = console }) {
|
|
298
|
+
async function reconcileOwnedPaths({ ledger, configuredSessionIds, git, shareList, journalPath = removalsJournalPath(glimmervoidHomeDir$1()), log = console }) {
|
|
193
299
|
const loaded = await ledger.loadEarlierRunEntries();
|
|
194
300
|
if (!loaded.ok) {
|
|
195
301
|
log.warn("[owned-paths] deleted nothing at startup because the owned paths ledger could not be read");
|
|
@@ -201,7 +307,7 @@ async function reconcileOwnedPaths({ ledger, configuredSessionIds, git, shareLis
|
|
|
201
307
|
await ledger.forget(entry.path);
|
|
202
308
|
continue;
|
|
203
309
|
}
|
|
204
|
-
const keepReason = entry.kind === "folder" ? await removeOrphanedFolder(entry, ledger, keptPaths) : await removeOrphanedWorktree(entry, git, shareList);
|
|
310
|
+
const keepReason = entry.kind === "folder" ? await removeOrphanedFolder(entry, ledger, keptPaths, journalPath, log) : await removeOrphanedWorktree(entry, git, shareList);
|
|
205
311
|
if (!keepReason) {
|
|
206
312
|
log.log(`[owned-paths] deleted ${entry.path}, left by ${entry.owner}`);
|
|
207
313
|
continue;
|
|
@@ -462,13 +568,22 @@ async function listScratchFolderNames(scratchRoot) {
|
|
|
462
568
|
async function sweepOrphanedHostScratch() {
|
|
463
569
|
const scratchRoot = hostScratchRoot();
|
|
464
570
|
const orphanedFolderNames = decideOrphanedScratchFolders(await listScratchFolderNames(scratchRoot), HOST_SCRATCH_PREFIXES, folderNamesCreatedByThisProcess);
|
|
465
|
-
for (const folderName of orphanedFolderNames)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
571
|
+
for (const folderName of orphanedFolderNames) {
|
|
572
|
+
const folderPath = path.join(scratchRoot, folderName);
|
|
573
|
+
if (!await recordSweptPath({
|
|
574
|
+
cause: "host-scratch",
|
|
575
|
+
kind: "folder",
|
|
576
|
+
path: folderPath,
|
|
577
|
+
checksPassed: ["not-made-by-this-process"]
|
|
578
|
+
})) continue;
|
|
579
|
+
try {
|
|
580
|
+
await rm(folderPath, {
|
|
581
|
+
recursive: true,
|
|
582
|
+
force: true
|
|
583
|
+
});
|
|
584
|
+
} catch (error) {
|
|
585
|
+
console.warn(`Could not remove leftover scratch folder ${folderName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
586
|
+
}
|
|
472
587
|
}
|
|
473
588
|
}
|
|
474
589
|
//#endregion
|
|
@@ -1773,53 +1888,98 @@ async function sweepSessionUploads(dir, justWritten) {
|
|
|
1773
1888
|
await fs$1.unlink(path.join(dir, name));
|
|
1774
1889
|
} catch {}
|
|
1775
1890
|
}
|
|
1891
|
+
function streamRequestToFile({ req, savedPath, capBytes }) {
|
|
1892
|
+
return new Promise((resolve) => {
|
|
1893
|
+
const writeStream = fs.createWriteStream(savedPath, { mode: 384 });
|
|
1894
|
+
let bytesReceived = 0;
|
|
1895
|
+
let settled = false;
|
|
1896
|
+
const settle = (outcome) => {
|
|
1897
|
+
if (settled) return;
|
|
1898
|
+
settled = true;
|
|
1899
|
+
resolve(outcome);
|
|
1900
|
+
};
|
|
1901
|
+
let discardWanted = false;
|
|
1902
|
+
let streamClosed = false;
|
|
1903
|
+
const unlinkPartial = () => {
|
|
1904
|
+
fs$1.unlink(savedPath).catch(() => {});
|
|
1905
|
+
};
|
|
1906
|
+
const discardPartial = () => {
|
|
1907
|
+
discardWanted = true;
|
|
1908
|
+
if (streamClosed) unlinkPartial();
|
|
1909
|
+
};
|
|
1910
|
+
writeStream.on("close", () => {
|
|
1911
|
+
streamClosed = true;
|
|
1912
|
+
if (discardWanted) unlinkPartial();
|
|
1913
|
+
});
|
|
1914
|
+
writeStream.on("error", () => {
|
|
1915
|
+
settle({
|
|
1916
|
+
ok: false,
|
|
1917
|
+
reason: "write-failed"
|
|
1918
|
+
});
|
|
1919
|
+
discardPartial();
|
|
1920
|
+
});
|
|
1921
|
+
req.on("data", (chunk) => {
|
|
1922
|
+
bytesReceived += chunk.length;
|
|
1923
|
+
if (!exceedsUploadCap(bytesReceived, capBytes)) return;
|
|
1924
|
+
settle({
|
|
1925
|
+
ok: false,
|
|
1926
|
+
reason: "over-cap"
|
|
1927
|
+
});
|
|
1928
|
+
req.destroy();
|
|
1929
|
+
writeStream.destroy();
|
|
1930
|
+
discardPartial();
|
|
1931
|
+
});
|
|
1932
|
+
pipeline(req, writeStream, (error) => {
|
|
1933
|
+
if (error) {
|
|
1934
|
+
settle({
|
|
1935
|
+
ok: false,
|
|
1936
|
+
reason: "aborted"
|
|
1937
|
+
});
|
|
1938
|
+
discardPartial();
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
if (settled) {
|
|
1942
|
+
discardPartial();
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
settle({
|
|
1946
|
+
ok: true,
|
|
1947
|
+
sizeBytes: bytesReceived
|
|
1948
|
+
});
|
|
1949
|
+
});
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1952
|
+
var UPLOAD_FAILURE_REPLIES = {
|
|
1953
|
+
"over-cap": {
|
|
1954
|
+
status: 413,
|
|
1955
|
+
error: "file is too large"
|
|
1956
|
+
},
|
|
1957
|
+
"write-failed": {
|
|
1958
|
+
status: 500,
|
|
1959
|
+
error: "could not store the upload"
|
|
1960
|
+
},
|
|
1961
|
+
aborted: {
|
|
1962
|
+
status: 400,
|
|
1963
|
+
error: "upload failed"
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1776
1966
|
function receiveUpload({ req, res, sess, dir, filename, savedPath }) {
|
|
1777
|
-
const writeStream = fs.createWriteStream(savedPath);
|
|
1778
|
-
let bytesReceived = 0;
|
|
1779
|
-
let settled = false;
|
|
1780
1967
|
const answer = (status, body) => {
|
|
1781
|
-
if (settled) return;
|
|
1782
|
-
settled = true;
|
|
1783
1968
|
if (!res.headersSent) res.status(status).json(body);
|
|
1784
1969
|
};
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
};
|
|
1794
|
-
writeStream.on("close", () => {
|
|
1795
|
-
streamClosed = true;
|
|
1796
|
-
if (discardWanted) unlinkPartial();
|
|
1797
|
-
});
|
|
1798
|
-
writeStream.on("error", () => {
|
|
1799
|
-
answer(500, { error: "could not store the upload" });
|
|
1800
|
-
discardPartial();
|
|
1801
|
-
});
|
|
1802
|
-
req.on("data", (chunk) => {
|
|
1803
|
-
bytesReceived += chunk.length;
|
|
1804
|
-
if (!exceedsUploadCap(bytesReceived)) return;
|
|
1805
|
-
answer(413, { error: "file is too large" });
|
|
1806
|
-
req.destroy();
|
|
1807
|
-
writeStream.destroy();
|
|
1808
|
-
discardPartial();
|
|
1809
|
-
});
|
|
1810
|
-
pipeline(req, writeStream, (error) => {
|
|
1811
|
-
if (error) {
|
|
1812
|
-
answer(400, { error: "upload failed" });
|
|
1813
|
-
discardPartial();
|
|
1814
|
-
return;
|
|
1815
|
-
}
|
|
1816
|
-
if (settled) {
|
|
1817
|
-
discardPartial();
|
|
1970
|
+
streamRequestToFile({
|
|
1971
|
+
req,
|
|
1972
|
+
savedPath,
|
|
1973
|
+
capBytes: MAX_UPLOAD_BYTES
|
|
1974
|
+
}).then((outcome) => {
|
|
1975
|
+
if (!outcome.ok) {
|
|
1976
|
+
const failure = UPLOAD_FAILURE_REPLIES[outcome.reason];
|
|
1977
|
+
answer(failure.status, { error: failure.error });
|
|
1818
1978
|
return;
|
|
1819
1979
|
}
|
|
1820
1980
|
if (!sess.hasLivePty) {
|
|
1821
1981
|
answer(409, { error: "session has no live terminal" });
|
|
1822
|
-
|
|
1982
|
+
fs$1.unlink(savedPath).catch(() => {});
|
|
1823
1983
|
return;
|
|
1824
1984
|
}
|
|
1825
1985
|
sess.write(framePathPaste(savedPath));
|
|
@@ -1831,6 +1991,112 @@ function receiveUpload({ req, res, sess, dir, filename, savedPath }) {
|
|
|
1831
1991
|
sweepSessionUploads(dir, filename);
|
|
1832
1992
|
});
|
|
1833
1993
|
}
|
|
1994
|
+
function decodeHeaderText(header) {
|
|
1995
|
+
if (typeof header !== "string" || header.length === 0) return null;
|
|
1996
|
+
try {
|
|
1997
|
+
return decodeURIComponent(header);
|
|
1998
|
+
} catch {
|
|
1999
|
+
return null;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
function declaredContentLength(req) {
|
|
2003
|
+
const declared = Number(req.headers["content-length"]);
|
|
2004
|
+
return Number.isFinite(declared) && declared >= 0 ? declared : null;
|
|
2005
|
+
}
|
|
2006
|
+
function answerShow({ req, res, session, shownFiles }) {
|
|
2007
|
+
const name = path.basename(decodeHeaderText(req.headers["x-glimmervoid-show-name"]) ?? "");
|
|
2008
|
+
if (!name) {
|
|
2009
|
+
res.status(400).json({
|
|
2010
|
+
ok: false,
|
|
2011
|
+
error: "show needs the file name in x-glimmervoid-show-name"
|
|
2012
|
+
});
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
const caption = decodeHeaderText(req.headers["x-glimmervoid-show-caption"])?.slice(0, 500) ?? null;
|
|
2016
|
+
const declaredBytes = declaredContentLength(req);
|
|
2017
|
+
if (declaredBytes !== null && declaredBytes > 209715200) {
|
|
2018
|
+
res.status(413).json({
|
|
2019
|
+
ok: false,
|
|
2020
|
+
error: describeOverCap(name, declaredBytes)
|
|
2021
|
+
});
|
|
2022
|
+
req.resume();
|
|
2023
|
+
return;
|
|
2024
|
+
}
|
|
2025
|
+
const answer = (status, body) => {
|
|
2026
|
+
if (!res.headersSent) res.status(status).json(body);
|
|
2027
|
+
};
|
|
2028
|
+
shownFiles.reserveIncomingPath(session.id).then(async (incomingPath) => {
|
|
2029
|
+
const outcome = await streamRequestToFile({
|
|
2030
|
+
req,
|
|
2031
|
+
savedPath: incomingPath,
|
|
2032
|
+
capBytes: SHOWN_FILE_CAP_BYTES
|
|
2033
|
+
});
|
|
2034
|
+
if (!outcome.ok) {
|
|
2035
|
+
const failure = UPLOAD_FAILURE_REPLIES[outcome.reason];
|
|
2036
|
+
answer(failure.status, {
|
|
2037
|
+
ok: false,
|
|
2038
|
+
error: outcome.reason === "over-cap" ? describeOverCap(name, declaredBytes) : failure.error
|
|
2039
|
+
});
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
const admitted = await shownFiles.admit(session, {
|
|
2043
|
+
incomingPath,
|
|
2044
|
+
name,
|
|
2045
|
+
caption,
|
|
2046
|
+
sizeBytes: outcome.sizeBytes
|
|
2047
|
+
});
|
|
2048
|
+
if (!admitted.ok) {
|
|
2049
|
+
answer(admitted.status, {
|
|
2050
|
+
ok: false,
|
|
2051
|
+
error: admitted.error
|
|
2052
|
+
});
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
answer(200, {
|
|
2056
|
+
ok: true,
|
|
2057
|
+
shown: {
|
|
2058
|
+
name: admitted.file.name,
|
|
2059
|
+
kind: admitted.file.kind
|
|
2060
|
+
}
|
|
2061
|
+
});
|
|
2062
|
+
}).catch(() => {
|
|
2063
|
+
answer(500, {
|
|
2064
|
+
ok: false,
|
|
2065
|
+
error: "could not store the file"
|
|
2066
|
+
});
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
var SHOWN_FILE_SANDBOX_POLICY = "sandbox allow-scripts allow-popups allow-modals allow-forms";
|
|
2070
|
+
function serveShownFile(req, res, shownFiles) {
|
|
2071
|
+
const servable = shownFiles?.findServable(req.params.sessionId, req.params.filename) ?? null;
|
|
2072
|
+
if (!servable) {
|
|
2073
|
+
res.status(404).end();
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
res.setHeader("Content-Security-Policy", SHOWN_FILE_SANDBOX_POLICY);
|
|
2077
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
2078
|
+
res.setHeader("Cache-Control", "private, max-age=604800, immutable");
|
|
2079
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
2080
|
+
const byteRange = decideByteRange(req.headers.range, servable.sizeBytes);
|
|
2081
|
+
if (byteRange.kind === "unsatisfiable") {
|
|
2082
|
+
res.status(416).setHeader("Content-Range", `bytes */${servable.sizeBytes}`);
|
|
2083
|
+
res.end();
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
res.setHeader("Content-Type", servable.mime);
|
|
2087
|
+
if (byteRange.kind === "partial") {
|
|
2088
|
+
res.status(206);
|
|
2089
|
+
res.setHeader("Content-Range", `bytes ${byteRange.start}-${byteRange.end}/${servable.sizeBytes}`);
|
|
2090
|
+
res.setHeader("Content-Length", String(byteRange.end - byteRange.start + 1));
|
|
2091
|
+
pipeline(fs.createReadStream(servable.path, {
|
|
2092
|
+
start: byteRange.start,
|
|
2093
|
+
end: byteRange.end
|
|
2094
|
+
}), res, () => {});
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
res.setHeader("Content-Length", String(servable.sizeBytes));
|
|
2098
|
+
pipeline(fs.createReadStream(servable.path), res, () => {});
|
|
2099
|
+
}
|
|
1834
2100
|
function refuseAgentRequest(res) {
|
|
1835
2101
|
res.status(404).json({
|
|
1836
2102
|
ok: false,
|
|
@@ -1897,7 +2163,7 @@ function mountAgentIngressRoutes(app, dependencies) {
|
|
|
1897
2163
|
const { hookRouter, getSession, getUsage, getPlanReview = () => null, refocusContextFor = () => ({
|
|
1898
2164
|
taskTitle: null,
|
|
1899
2165
|
latestPlanTitle: null
|
|
1900
|
-
}), getAgentApi = () => null, recordOutcome = () => {}, logger = console } = dependencies;
|
|
2166
|
+
}), getAgentApi = () => null, getShownFiles = () => null, recordOutcome = () => {}, logger = console } = dependencies;
|
|
1901
2167
|
app.post("/hook/:glimmervoidId/:event", (req, res) => {
|
|
1902
2168
|
if (!isLoopbackRequest(req)) {
|
|
1903
2169
|
recordOutcome("hookRejectedNonLoopback");
|
|
@@ -1962,6 +2228,33 @@ function mountAgentIngressRoutes(app, dependencies) {
|
|
|
1962
2228
|
});
|
|
1963
2229
|
});
|
|
1964
2230
|
});
|
|
2231
|
+
app.post("/agent/:glimmervoidId/show", (req, res) => {
|
|
2232
|
+
const agentApi = getAgentApi();
|
|
2233
|
+
const shownFiles = getShownFiles();
|
|
2234
|
+
const session = getSession(req.params.glimmervoidId);
|
|
2235
|
+
if (!decideAgentRequest({
|
|
2236
|
+
sessionId: req.params.glimmervoidId,
|
|
2237
|
+
presentedToken: presentedToken(req),
|
|
2238
|
+
expectedToken: session?.agentToken ?? null,
|
|
2239
|
+
isLoopback: isLoopbackRequest(req)
|
|
2240
|
+
}).ok || !session || !agentApi || !shownFiles) {
|
|
2241
|
+
req.resume();
|
|
2242
|
+
refuseAgentRequest(res);
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
const roleRefusal = agentApi.refuseRole(session, "show");
|
|
2246
|
+
if (roleRefusal) {
|
|
2247
|
+
req.resume();
|
|
2248
|
+
res.status(roleRefusal.status).json(roleRefusal.body);
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
answerShow({
|
|
2252
|
+
req,
|
|
2253
|
+
res,
|
|
2254
|
+
session,
|
|
2255
|
+
shownFiles
|
|
2256
|
+
});
|
|
2257
|
+
});
|
|
1965
2258
|
app.post("/agent/:glimmervoidId/:verb", (req, res) => {
|
|
1966
2259
|
const agentApi = getAgentApi();
|
|
1967
2260
|
const session = getSession(req.params.glimmervoidId);
|
|
@@ -2150,6 +2443,9 @@ function createBackendHttpApp(dependencies) {
|
|
|
2150
2443
|
if (!res.headersSent) res.status(404).end();
|
|
2151
2444
|
});
|
|
2152
2445
|
});
|
|
2446
|
+
app.get("/shown/:sessionId/:filename", (req, res) => {
|
|
2447
|
+
serveShownFile(req, res, dependencies.getShownFiles?.() ?? null);
|
|
2448
|
+
});
|
|
2153
2449
|
mountCustomSoundRoutes(app, { soundsDir: customSoundsDir });
|
|
2154
2450
|
if (staticDir === "auto") {
|
|
2155
2451
|
if (!fs.existsSync(path.join(clientDir, "index.html"))) throw Object.assign(/* @__PURE__ */ new Error("Dashboard build not found (dist/client). Run `npm run build` first, or use `npm run dev`."), { glimmervoidBoot: true });
|
|
@@ -2313,9 +2609,6 @@ function projectSessionSnapshots(source) {
|
|
|
2313
2609
|
resumeSessionId: source.resumeSessionId,
|
|
2314
2610
|
activeAgents: source.activeAgents,
|
|
2315
2611
|
awaitingBackgroundTasks: source.awaitingBackgroundTasks,
|
|
2316
|
-
isCompacting: source.isCompacting ?? false,
|
|
2317
|
-
isWaitingForMemory: source.isWaitingForMemory === true,
|
|
2318
|
-
pendingWakeup: source.pendingWakeup,
|
|
2319
2612
|
pendingPromptKind: source.pendingPromptKind,
|
|
2320
2613
|
conversationApproval: source.conversationApproval ?? null,
|
|
2321
2614
|
hasConversationBridge: source.hasConversationBridge === true,
|
|
@@ -2882,6 +3175,7 @@ var AGENT_LOGINS_SIBLING_ENTRY = "agent-logins";
|
|
|
2882
3175
|
var GLIMMERVOID_CONFIG_SIBLING_ENTRIES = Object.freeze([
|
|
2883
3176
|
AGENT_LOGINS_SIBLING_ENTRY,
|
|
2884
3177
|
"bin",
|
|
3178
|
+
"owned-paths.json",
|
|
2885
3179
|
"pairings.json",
|
|
2886
3180
|
"pairings-seen.json",
|
|
2887
3181
|
"plans",
|
|
@@ -3206,7 +3500,8 @@ function buildGuardrailsLaunchPlan(role, host) {
|
|
|
3206
3500
|
...filesystemWrites(role, host, writablePaths)
|
|
3207
3501
|
},
|
|
3208
3502
|
terminalScope: isStandardLevel(role) ? "any" : "own",
|
|
3209
|
-
allowAppleEvents: isStandardLevel(role)
|
|
3503
|
+
allowAppleEvents: isStandardLevel(role),
|
|
3504
|
+
allowMachRegister: isStandardLevel(role)
|
|
3210
3505
|
},
|
|
3211
3506
|
launchEnv: Object.assign({ CLAUDE_CODE_TMPDIR: host.tempDir }, ...role.vendors.map((vendor) => VENDOR_PROFILES[vendor].launchEnv), launchHomeEnv(role, host)),
|
|
3212
3507
|
guardedEnv: guardedEnvFor(role, host),
|
|
@@ -3244,6 +3539,12 @@ async function pruneAgedFiles({ directory, suffixes, retainDays, now = Date.now(
|
|
|
3244
3539
|
const filePath = path.join(directory, entry);
|
|
3245
3540
|
try {
|
|
3246
3541
|
if ((await fsPromises.stat(filePath)).mtimeMs >= cutoff) continue;
|
|
3542
|
+
if (!await recordSweptPath({
|
|
3543
|
+
cause: "aged-files",
|
|
3544
|
+
kind: entryMode === "directory" ? "folder" : "file",
|
|
3545
|
+
path: filePath,
|
|
3546
|
+
checksPassed: [`untouched-${retainDays}d`]
|
|
3547
|
+
})) continue;
|
|
3247
3548
|
if (entryMode === "directory") await fsPromises.rm(filePath, {
|
|
3248
3549
|
recursive: true,
|
|
3249
3550
|
force: true
|
|
@@ -3626,12 +3927,15 @@ async function sweepOrphanedGuardrailsLaunches(log = console) {
|
|
|
3626
3927
|
const ownerPath = path.join(policyDir, entryName);
|
|
3627
3928
|
const owner = await readOwnerFile(ownerPath);
|
|
3628
3929
|
if (owner === null || !isOwnerProcessGone(owner)) continue;
|
|
3930
|
+
const launchHomeDir = path.join(glimmervoidHome, LAUNCH_HOME_FOLDER, launchId);
|
|
3931
|
+
const ownedTempDir = owner.ownsTempDir ? path.join(glimmervoidHome, GUARDRAILS_TEMP_FOLDER, launchId) : null;
|
|
3932
|
+
if (!await recordSweptLaunchFolders([launchHomeDir, ownedTempDir], log)) continue;
|
|
3629
3933
|
try {
|
|
3630
3934
|
await removeLaunch({
|
|
3631
3935
|
policyPath: path.join(policyDir, `${launchId}.json`),
|
|
3632
3936
|
ownerPath,
|
|
3633
|
-
launchHomeDir
|
|
3634
|
-
ownedTempDir
|
|
3937
|
+
launchHomeDir,
|
|
3938
|
+
ownedTempDir
|
|
3635
3939
|
}, owner, launchId, { onlyNewerLogins: true });
|
|
3636
3940
|
removedLaunchCount += 1;
|
|
3637
3941
|
} catch (error) {
|
|
@@ -3643,6 +3947,19 @@ async function sweepOrphanedGuardrailsLaunches(log = console) {
|
|
|
3643
3947
|
log.warn(`[guardrails] could not sweep leftover launch folders: ${errorMessage(error)}`);
|
|
3644
3948
|
}
|
|
3645
3949
|
}
|
|
3950
|
+
async function recordSweptLaunchFolders(folderPaths, log) {
|
|
3951
|
+
for (const folderPath of folderPaths) {
|
|
3952
|
+
if (folderPath === null) continue;
|
|
3953
|
+
if (!await recordSweptPath({
|
|
3954
|
+
cause: "guardrails-temp",
|
|
3955
|
+
kind: "folder",
|
|
3956
|
+
path: folderPath,
|
|
3957
|
+
checksPassed: ["owner-process-gone"],
|
|
3958
|
+
log
|
|
3959
|
+
})) return false;
|
|
3960
|
+
}
|
|
3961
|
+
return true;
|
|
3962
|
+
}
|
|
3646
3963
|
function sessionIdsWithLockFilesIn(entryNames) {
|
|
3647
3964
|
return new Set(entryNames.map(sessionIdOfLockFile).filter((sessionId) => sessionId !== null));
|
|
3648
3965
|
}
|
|
@@ -3655,6 +3972,19 @@ async function deleteSessionTempFolderOnceUnheld(policyDir, sessionId, sessionTe
|
|
|
3655
3972
|
return;
|
|
3656
3973
|
}
|
|
3657
3974
|
if (trashFolder === null) return;
|
|
3975
|
+
if (!await recordSweptPath({
|
|
3976
|
+
cause: "guardrails-temp",
|
|
3977
|
+
kind: "folder",
|
|
3978
|
+
path: sessionTempFolder,
|
|
3979
|
+
sessionId,
|
|
3980
|
+
checksPassed: ["no-lock-held"],
|
|
3981
|
+
log
|
|
3982
|
+
})) {
|
|
3983
|
+
await rename(trashFolder, sessionTempFolder).catch((error) => {
|
|
3984
|
+
log.warn(`[guardrails] could not move session temp folder ${trashFolder} back to ${sessionTempFolder}: ${errorMessage(error)}`);
|
|
3985
|
+
});
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3658
3988
|
await rm(trashFolder, {
|
|
3659
3989
|
recursive: true,
|
|
3660
3990
|
force: true
|
|
@@ -4704,6 +5034,7 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
4704
5034
|
}),
|
|
4705
5035
|
push,
|
|
4706
5036
|
edit,
|
|
5037
|
+
show: () => refusal(400, "show sends the file itself; run glimmervoid show <path>"),
|
|
4707
5038
|
limits: (session) => ({
|
|
4708
5039
|
status: 200,
|
|
4709
5040
|
body: limits(session)
|
|
@@ -4740,8 +5071,12 @@ function createAgentApiWiring({ config, agentSessions, listAllSessions, listBoar
|
|
|
4740
5071
|
function markChildWorktreeDiscard(childId) {
|
|
4741
5072
|
childIdsToDiscard.add(childId);
|
|
4742
5073
|
}
|
|
5074
|
+
function refuseRole(session, verb) {
|
|
5075
|
+
return describeRoleRefusal(verb, roleFactsOf(session));
|
|
5076
|
+
}
|
|
4743
5077
|
return {
|
|
4744
5078
|
handle,
|
|
5079
|
+
refuseRole,
|
|
4745
5080
|
markChildWorktreeDiscard
|
|
4746
5081
|
};
|
|
4747
5082
|
}
|
|
@@ -4830,11 +5165,7 @@ function createRemoteSetupWiring({ config, agentSessions, makeSession, wireSessi
|
|
|
4830
5165
|
}
|
|
4831
5166
|
//#endregion
|
|
4832
5167
|
//#region server/core/control-replay-core.ts
|
|
4833
|
-
var REPLAYABLE_EXACT = /* @__PURE__ */ new Set([
|
|
4834
|
-
"notify",
|
|
4835
|
-
"session-error",
|
|
4836
|
-
"post-turn-result"
|
|
4837
|
-
]);
|
|
5168
|
+
var REPLAYABLE_EXACT = /* @__PURE__ */ new Set(["notify", "session-error"]);
|
|
4838
5169
|
function isReplayable(type) {
|
|
4839
5170
|
return typeof type === "string" && REPLAYABLE_EXACT.has(type);
|
|
4840
5171
|
}
|
|
@@ -5059,7 +5390,7 @@ function planDeepSlices({ lineCount, rows, sliceRows, isWrapped }) {
|
|
|
5059
5390
|
//#endregion
|
|
5060
5391
|
//#region server/ws-sender.ts
|
|
5061
5392
|
var OPEN = 1;
|
|
5062
|
-
var DEFAULTS
|
|
5393
|
+
var DEFAULTS = Object.freeze({
|
|
5063
5394
|
maxSendBuffer: 65536,
|
|
5064
5395
|
highWaterMark: 1 << 20,
|
|
5065
5396
|
lowWaterMark: 1 << 18,
|
|
@@ -5067,10 +5398,10 @@ var DEFAULTS$1 = Object.freeze({
|
|
|
5067
5398
|
});
|
|
5068
5399
|
function createWsSender(ws, opts = {}) {
|
|
5069
5400
|
const cfg = {
|
|
5070
|
-
maxSendBuffer: opts.maxSendBuffer ?? DEFAULTS
|
|
5071
|
-
highWaterMark: opts.highWaterMark ?? DEFAULTS
|
|
5072
|
-
lowWaterMark: opts.lowWaterMark ?? DEFAULTS
|
|
5073
|
-
stallCloseMs: opts.stallCloseMs ?? DEFAULTS
|
|
5401
|
+
maxSendBuffer: opts.maxSendBuffer ?? DEFAULTS.maxSendBuffer,
|
|
5402
|
+
highWaterMark: opts.highWaterMark ?? DEFAULTS.highWaterMark,
|
|
5403
|
+
lowWaterMark: opts.lowWaterMark ?? DEFAULTS.lowWaterMark,
|
|
5404
|
+
stallCloseMs: opts.stallCloseMs ?? DEFAULTS.stallCloseMs
|
|
5074
5405
|
};
|
|
5075
5406
|
const scheduleImmediate = opts.setImmediateFn || setImmediate;
|
|
5076
5407
|
const setTimer = opts.setTimeoutFn || ((fn, ms) => setTimeout(fn, ms));
|
|
@@ -6367,605 +6698,6 @@ function compareEntries(a, b) {
|
|
|
6367
6698
|
return laneKey(a.vendor, a.sessionId).localeCompare(laneKey(b.vendor, b.sessionId));
|
|
6368
6699
|
}
|
|
6369
6700
|
//#endregion
|
|
6370
|
-
//#region session/core/slop-code-patterns.ts
|
|
6371
|
-
var TS_EXTS = /* @__PURE__ */ new Set([
|
|
6372
|
-
".ts",
|
|
6373
|
-
".tsx",
|
|
6374
|
-
".mts",
|
|
6375
|
-
".cts"
|
|
6376
|
-
]);
|
|
6377
|
-
var PY_EXTS = /* @__PURE__ */ new Set([".py"]);
|
|
6378
|
-
function extOf(relPath) {
|
|
6379
|
-
if (!relPath) return "";
|
|
6380
|
-
const s = String(relPath).replace(/\\/g, "/");
|
|
6381
|
-
const base = s.slice(s.lastIndexOf("/") + 1);
|
|
6382
|
-
const dot = base.lastIndexOf(".");
|
|
6383
|
-
return dot <= 0 ? "" : base.slice(dot).toLowerCase();
|
|
6384
|
-
}
|
|
6385
|
-
var SUBRULES = [
|
|
6386
|
-
{
|
|
6387
|
-
id: "swallowed-exception",
|
|
6388
|
-
axis: "lies",
|
|
6389
|
-
re: /catch\s*(?:\([^)]*\))?\s*\{\s*(?:\/\/[^\n]*\s*|\/\*[\s\S]*?\*\/\s*)?\}/g
|
|
6390
|
-
},
|
|
6391
|
-
{
|
|
6392
|
-
id: "opener-comment",
|
|
6393
|
-
axis: "noise",
|
|
6394
|
-
re: /^[ \t]*(?:\/\/+|#|\*)[ \t]*(?:(?:now|first|next|then|here)[, ]+(?:we|let)\b|let'?s\b|we\s+(?:now|then|will|can|need|first)\b|this\s+(?:function|method|class|code|section|block|module|file)\b|step\s+\d+\s*:)/gim
|
|
6395
|
-
},
|
|
6396
|
-
{
|
|
6397
|
-
id: "placeholder",
|
|
6398
|
-
axis: "lies",
|
|
6399
|
-
re: /(?:in\s+a\s+real\s+(?:implementation|app|application|world|system|scenario|project)|in\s+production(?:[,\s]|\b)|for\s+now[, ]+(?:we|just|this|i'?ll|return|use)|this\s+is\s+(?:a\s+)?simplified|simplified\s+(?:version|example|implementation)|for\s+(?:demonstration|illustration|example)\s+purposes|placeholder\s+(?:for|implementation|value)|you\s+(?:would|could|might|may)\s+(?:want\s+to\s+)?(?:add|implement|replace|put))/gi
|
|
6400
|
-
},
|
|
6401
|
-
{
|
|
6402
|
-
id: "debug-leftover",
|
|
6403
|
-
axis: "noise",
|
|
6404
|
-
re: /\bconsole\.(?:log|debug|info|trace)\s*\(|\bdebugger\b/g
|
|
6405
|
-
},
|
|
6406
|
-
{
|
|
6407
|
-
id: "debug-print-py",
|
|
6408
|
-
axis: "noise",
|
|
6409
|
-
langs: PY_EXTS,
|
|
6410
|
-
re: /^[ \t]*print\s*\(/gim
|
|
6411
|
-
},
|
|
6412
|
-
{
|
|
6413
|
-
id: "hedge-comment",
|
|
6414
|
-
axis: "soul",
|
|
6415
|
-
re: /(?:\/\/+|#)[^\n]*\b(?:should\s+(?:work|be\s+fine|do\s+it|handle)|not\s+(?:entirely\s+)?sure|probably\s+(?:works?|fine|ok|okay)|hopefully|i\s+think\s+this|might\s+(?:work|break|fail)|not\s+sure\s+(?:why|if|how))\b/gi
|
|
6416
|
-
},
|
|
6417
|
-
{
|
|
6418
|
-
id: "type-escape",
|
|
6419
|
-
axis: "soul",
|
|
6420
|
-
langs: TS_EXTS,
|
|
6421
|
-
re: /\bas\s+any\b|:\s*any(?:\b|\[)|@ts-(?:ignore|nocheck|expect-error)\b|eslint-disable(?:-next-line|-line)?\b/g
|
|
6422
|
-
}
|
|
6423
|
-
];
|
|
6424
|
-
var DEFAULT_FINDINGS_CAP = 200;
|
|
6425
|
-
function detectCodeSlop(content, relPath, cap = DEFAULT_FINDINGS_CAP) {
|
|
6426
|
-
if (typeof content !== "string" || content.length === 0) return [];
|
|
6427
|
-
const ext = extOf(relPath);
|
|
6428
|
-
const findings = [];
|
|
6429
|
-
for (const sub of SUBRULES) {
|
|
6430
|
-
if (sub.langs && !sub.langs.has(ext)) continue;
|
|
6431
|
-
sub.re.lastIndex = 0;
|
|
6432
|
-
for (;;) {
|
|
6433
|
-
const m = sub.re.exec(content);
|
|
6434
|
-
if (m === null) break;
|
|
6435
|
-
findings.push({
|
|
6436
|
-
subrule: sub.id,
|
|
6437
|
-
axis: sub.axis,
|
|
6438
|
-
index: m.index
|
|
6439
|
-
});
|
|
6440
|
-
if (findings.length >= cap) break;
|
|
6441
|
-
}
|
|
6442
|
-
if (findings.length >= cap) break;
|
|
6443
|
-
}
|
|
6444
|
-
findings.sort((a, b) => a.index - b.index);
|
|
6445
|
-
return findings;
|
|
6446
|
-
}
|
|
6447
|
-
//#endregion
|
|
6448
|
-
//#region session/core/post-turn-rules.ts
|
|
6449
|
-
var BOM = String.fromCharCode(65279);
|
|
6450
|
-
var NL = String.fromCharCode(10);
|
|
6451
|
-
var CRLF = String.fromCharCode(13, 10);
|
|
6452
|
-
function posAt(content, offset) {
|
|
6453
|
-
let line = 1;
|
|
6454
|
-
let col = 1;
|
|
6455
|
-
const end = Math.min(offset, content.length);
|
|
6456
|
-
for (let i = 0; i < end; i++) {
|
|
6457
|
-
if (content[i] !== NL) {
|
|
6458
|
-
col++;
|
|
6459
|
-
continue;
|
|
6460
|
-
}
|
|
6461
|
-
line++;
|
|
6462
|
-
col = 1;
|
|
6463
|
-
}
|
|
6464
|
-
return {
|
|
6465
|
-
line,
|
|
6466
|
-
col
|
|
6467
|
-
};
|
|
6468
|
-
}
|
|
6469
|
-
function detectSlop(content, ctx) {
|
|
6470
|
-
const relPath = ctx?.relPath;
|
|
6471
|
-
const matches = detectCodeSlop(content, relPath);
|
|
6472
|
-
const findings = [];
|
|
6473
|
-
let pos = 0;
|
|
6474
|
-
let line = 1;
|
|
6475
|
-
let col = 1;
|
|
6476
|
-
for (const m of matches) {
|
|
6477
|
-
const target = m.index < content.length ? m.index : content.length;
|
|
6478
|
-
while (pos < target) {
|
|
6479
|
-
if (content.charCodeAt(pos) === 10) {
|
|
6480
|
-
line++;
|
|
6481
|
-
col = 1;
|
|
6482
|
-
pos++;
|
|
6483
|
-
continue;
|
|
6484
|
-
}
|
|
6485
|
-
col++;
|
|
6486
|
-
pos++;
|
|
6487
|
-
}
|
|
6488
|
-
findings.push({
|
|
6489
|
-
rule: "slop",
|
|
6490
|
-
subrule: m.subrule,
|
|
6491
|
-
axis: m.axis,
|
|
6492
|
-
line,
|
|
6493
|
-
col
|
|
6494
|
-
});
|
|
6495
|
-
}
|
|
6496
|
-
return {
|
|
6497
|
-
content,
|
|
6498
|
-
findings
|
|
6499
|
-
};
|
|
6500
|
-
}
|
|
6501
|
-
function fixTrailingWhitespace(content) {
|
|
6502
|
-
const findings = [];
|
|
6503
|
-
return {
|
|
6504
|
-
content: content.replace(/[ \t]+(\r?\n|$)/g, (m, tail, offset) => {
|
|
6505
|
-
const { line, col } = posAt(content, offset);
|
|
6506
|
-
findings.push({
|
|
6507
|
-
rule: "trailingWs",
|
|
6508
|
-
line,
|
|
6509
|
-
col,
|
|
6510
|
-
before: m,
|
|
6511
|
-
after: tail
|
|
6512
|
-
});
|
|
6513
|
-
return tail;
|
|
6514
|
-
}),
|
|
6515
|
-
findings
|
|
6516
|
-
};
|
|
6517
|
-
}
|
|
6518
|
-
function fixFinalNewline(content) {
|
|
6519
|
-
if (content === "" || content.charCodeAt(content.length - 1) === 10) return {
|
|
6520
|
-
content,
|
|
6521
|
-
findings: []
|
|
6522
|
-
};
|
|
6523
|
-
const nl = content.indexOf(CRLF) !== -1 ? CRLF : NL;
|
|
6524
|
-
const { line, col } = posAt(content, content.length);
|
|
6525
|
-
return {
|
|
6526
|
-
content: content + nl,
|
|
6527
|
-
findings: [{
|
|
6528
|
-
rule: "finalNewline",
|
|
6529
|
-
line,
|
|
6530
|
-
col,
|
|
6531
|
-
before: "",
|
|
6532
|
-
after: nl
|
|
6533
|
-
}]
|
|
6534
|
-
};
|
|
6535
|
-
}
|
|
6536
|
-
function stripBom(content) {
|
|
6537
|
-
if (content.charCodeAt(0) !== 65279) return {
|
|
6538
|
-
content,
|
|
6539
|
-
findings: []
|
|
6540
|
-
};
|
|
6541
|
-
return {
|
|
6542
|
-
content: content.slice(1),
|
|
6543
|
-
findings: [{
|
|
6544
|
-
rule: "bom",
|
|
6545
|
-
line: 1,
|
|
6546
|
-
col: 1,
|
|
6547
|
-
before: BOM,
|
|
6548
|
-
after: ""
|
|
6549
|
-
}]
|
|
6550
|
-
};
|
|
6551
|
-
}
|
|
6552
|
-
var RULES = Object.freeze({
|
|
6553
|
-
bom: { fix: stripBom },
|
|
6554
|
-
trailingWs: { fix: fixTrailingWhitespace },
|
|
6555
|
-
finalNewline: { fix: fixFinalNewline },
|
|
6556
|
-
slop: { fix: detectSlop }
|
|
6557
|
-
});
|
|
6558
|
-
var RULE_ORDER = [
|
|
6559
|
-
"bom",
|
|
6560
|
-
"trailingWs",
|
|
6561
|
-
"finalNewline",
|
|
6562
|
-
"slop"
|
|
6563
|
-
];
|
|
6564
|
-
var EXEMPT_MARKER = "glimmervoid-no-fix";
|
|
6565
|
-
function exemptions(content) {
|
|
6566
|
-
const head = content.slice(0, 4096);
|
|
6567
|
-
if (head.indexOf(EXEMPT_MARKER) === -1) return {
|
|
6568
|
-
all: false,
|
|
6569
|
-
rules: /* @__PURE__ */ new Set()
|
|
6570
|
-
};
|
|
6571
|
-
const rules = /* @__PURE__ */ new Set();
|
|
6572
|
-
const re = new RegExp(`${EXEMPT_MARKER}:([a-zA-Z]+)`, "g");
|
|
6573
|
-
let m = re.exec(head);
|
|
6574
|
-
while (m !== null) {
|
|
6575
|
-
rules.add(m[1]);
|
|
6576
|
-
m = re.exec(head);
|
|
6577
|
-
}
|
|
6578
|
-
return {
|
|
6579
|
-
all: new RegExp(`${EXEMPT_MARKER}(?!:)`).test(head),
|
|
6580
|
-
rules
|
|
6581
|
-
};
|
|
6582
|
-
}
|
|
6583
|
-
function applyRules(content, rules, ctx) {
|
|
6584
|
-
const ex = exemptions(content);
|
|
6585
|
-
const findings = [];
|
|
6586
|
-
let current = content;
|
|
6587
|
-
for (const name of RULE_ORDER) {
|
|
6588
|
-
const cfg = rules?.[name];
|
|
6589
|
-
if (!cfg || !cfg.enabled) continue;
|
|
6590
|
-
if (ex.all || ex.rules.has(name)) continue;
|
|
6591
|
-
const res = RULES[name].fix(current, ctx);
|
|
6592
|
-
if (res.findings.length === 0) continue;
|
|
6593
|
-
for (const f of res.findings) findings.push(f);
|
|
6594
|
-
if (cfg.mode === "fix") current = res.content;
|
|
6595
|
-
}
|
|
6596
|
-
return {
|
|
6597
|
-
content: current,
|
|
6598
|
-
findings,
|
|
6599
|
-
changed: current !== content
|
|
6600
|
-
};
|
|
6601
|
-
}
|
|
6602
|
-
var REGEX_METACHARACTERS = [
|
|
6603
|
-
".",
|
|
6604
|
-
"+",
|
|
6605
|
-
"^",
|
|
6606
|
-
"$",
|
|
6607
|
-
"{",
|
|
6608
|
-
"}",
|
|
6609
|
-
"(",
|
|
6610
|
-
")",
|
|
6611
|
-
"|",
|
|
6612
|
-
"[",
|
|
6613
|
-
"]",
|
|
6614
|
-
"\\"
|
|
6615
|
-
].join("");
|
|
6616
|
-
function starSegment(glob, index) {
|
|
6617
|
-
if (glob[index + 1] !== "*") return {
|
|
6618
|
-
pattern: "[^/]*",
|
|
6619
|
-
consumed: 0
|
|
6620
|
-
};
|
|
6621
|
-
if (glob[index + 2] === "/") return {
|
|
6622
|
-
pattern: "(?:.*/)?",
|
|
6623
|
-
consumed: 2
|
|
6624
|
-
};
|
|
6625
|
-
return {
|
|
6626
|
-
pattern: ".*",
|
|
6627
|
-
consumed: 1
|
|
6628
|
-
};
|
|
6629
|
-
}
|
|
6630
|
-
function globToRegExp(glob) {
|
|
6631
|
-
let re = "^";
|
|
6632
|
-
for (let i = 0; i < glob.length; i++) {
|
|
6633
|
-
const c = glob[i];
|
|
6634
|
-
if (c === "*") {
|
|
6635
|
-
const segment = starSegment(glob, i);
|
|
6636
|
-
re += segment.pattern;
|
|
6637
|
-
i += segment.consumed;
|
|
6638
|
-
continue;
|
|
6639
|
-
}
|
|
6640
|
-
if (c === "?") {
|
|
6641
|
-
re += "[^/]";
|
|
6642
|
-
continue;
|
|
6643
|
-
}
|
|
6644
|
-
if (REGEX_METACHARACTERS.indexOf(c) !== -1) {
|
|
6645
|
-
re += `\\${c}`;
|
|
6646
|
-
continue;
|
|
6647
|
-
}
|
|
6648
|
-
re += c;
|
|
6649
|
-
}
|
|
6650
|
-
re += "$";
|
|
6651
|
-
return new RegExp(re);
|
|
6652
|
-
}
|
|
6653
|
-
function shouldCheckPath(relPath, { include, exclude } = {}) {
|
|
6654
|
-
const p = String(relPath).replace(/\\/g, "/");
|
|
6655
|
-
const inc = include?.length ? include : ["**/*"];
|
|
6656
|
-
const matchAny = (globs) => globs.some((g) => globToRegExp(g).test(p));
|
|
6657
|
-
if (!matchAny(inc)) return false;
|
|
6658
|
-
if (exclude?.length && matchAny(exclude)) return false;
|
|
6659
|
-
return true;
|
|
6660
|
-
}
|
|
6661
|
-
function looksBinary(buf) {
|
|
6662
|
-
const n = Math.min(buf.length, 8192);
|
|
6663
|
-
const at = typeof buf === "string" ? (i) => buf.charCodeAt(i) : (i) => buf[i];
|
|
6664
|
-
for (let i = 0; i < n; i++) if (at(i) === 0) return true;
|
|
6665
|
-
return false;
|
|
6666
|
-
}
|
|
6667
|
-
//#endregion
|
|
6668
|
-
//#region server/git/git-exec.ts
|
|
6669
|
-
var DEFAULT_COMMAND_TIMEOUT_MS = 2e4;
|
|
6670
|
-
function outputText(value) {
|
|
6671
|
-
if (typeof value === "string") return value;
|
|
6672
|
-
if (Buffer.isBuffer(value)) return value.toString("utf8");
|
|
6673
|
-
return value == null ? "" : String(value);
|
|
6674
|
-
}
|
|
6675
|
-
function childEnv(env, replaceEnv) {
|
|
6676
|
-
if (replaceEnv) return { env: replaceEnv };
|
|
6677
|
-
if (env) return { env: {
|
|
6678
|
-
...process.env,
|
|
6679
|
-
...env
|
|
6680
|
-
} };
|
|
6681
|
-
return {};
|
|
6682
|
-
}
|
|
6683
|
-
async function runCommand(file, args, { cwd, timeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, maxBuffer, env, replaceEnv, signal, input, trim = true, keepStdoutOnFailure = false, preferStderr = false, execFileFn = execFileAsync } = {}) {
|
|
6684
|
-
const execOptions = {
|
|
6685
|
-
encoding: "utf8",
|
|
6686
|
-
timeout: timeoutMs,
|
|
6687
|
-
...cwd === void 0 ? {} : { cwd },
|
|
6688
|
-
...maxBuffer === void 0 ? {} : { maxBuffer },
|
|
6689
|
-
...signal ? { signal } : {},
|
|
6690
|
-
...input === void 0 ? {} : { input },
|
|
6691
|
-
...childEnv(env, replaceEnv)
|
|
6692
|
-
};
|
|
6693
|
-
try {
|
|
6694
|
-
const { stdout, stderr } = await execFileFn(file, args, execOptions);
|
|
6695
|
-
const out = outputText(stdout);
|
|
6696
|
-
return {
|
|
6697
|
-
ok: true,
|
|
6698
|
-
out: trim ? out.trim() : out,
|
|
6699
|
-
err: "",
|
|
6700
|
-
stderr: outputText(stderr)
|
|
6701
|
-
};
|
|
6702
|
-
} catch (error) {
|
|
6703
|
-
const failure = error ?? {};
|
|
6704
|
-
const stdout = outputText(failure.stdout);
|
|
6705
|
-
const stderr = outputText(failure.stderr);
|
|
6706
|
-
const message = errorMessage(error);
|
|
6707
|
-
return {
|
|
6708
|
-
ok: false,
|
|
6709
|
-
out: keepStdoutOnFailure ? trim ? stdout.trim() : stdout : "",
|
|
6710
|
-
err: preferStderr ? stderr || message : message,
|
|
6711
|
-
stderr,
|
|
6712
|
-
error,
|
|
6713
|
-
timedOut: failure.killed === true && Boolean(failure.signal)
|
|
6714
|
-
};
|
|
6715
|
-
}
|
|
6716
|
-
}
|
|
6717
|
-
function runGit(args, { gitPath = "git", env, replaceEnv, ...rest } = {}) {
|
|
6718
|
-
const baseEnv = replaceEnv ?? (env ? {
|
|
6719
|
-
...process.env,
|
|
6720
|
-
...env
|
|
6721
|
-
} : process.env);
|
|
6722
|
-
return runCommand(gitPath, args, {
|
|
6723
|
-
...rest,
|
|
6724
|
-
replaceEnv: {
|
|
6725
|
-
...baseEnv,
|
|
6726
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
6727
|
-
}
|
|
6728
|
-
});
|
|
6729
|
-
}
|
|
6730
|
-
function gitCommandFailure(args, command, timeoutMs = DEFAULT_COMMAND_TIMEOUT_MS) {
|
|
6731
|
-
if (!command.timedOut) return command.error;
|
|
6732
|
-
return new Error(`git ${args.join(" ")} timed out after ${timeoutMs}ms`, { cause: command.error });
|
|
6733
|
-
}
|
|
6734
|
-
function runGh(args, options = {}) {
|
|
6735
|
-
return runCommand("gh", args, options);
|
|
6736
|
-
}
|
|
6737
|
-
//#endregion
|
|
6738
|
-
//#region server/post-turn-checker.ts
|
|
6739
|
-
var GIT_TIMEOUT_MS$2 = 5e3;
|
|
6740
|
-
var DEFAULTS = Object.freeze({
|
|
6741
|
-
enabled: true,
|
|
6742
|
-
mode: "report",
|
|
6743
|
-
rules: {
|
|
6744
|
-
trailingWs: true,
|
|
6745
|
-
finalNewline: true,
|
|
6746
|
-
bom: true,
|
|
6747
|
-
slop: false
|
|
6748
|
-
},
|
|
6749
|
-
include: ["**/*"],
|
|
6750
|
-
exclude: [
|
|
6751
|
-
"**/node_modules/**",
|
|
6752
|
-
"**/dist/**",
|
|
6753
|
-
"**/.git/**",
|
|
6754
|
-
"**/.glimmervoid/**",
|
|
6755
|
-
"**/*.lock",
|
|
6756
|
-
"**/package-lock.json",
|
|
6757
|
-
"**/pnpm-lock.yaml",
|
|
6758
|
-
"**/yarn.lock",
|
|
6759
|
-
"**/*.png",
|
|
6760
|
-
"**/*.jpg",
|
|
6761
|
-
"**/*.jpeg",
|
|
6762
|
-
"**/*.gif",
|
|
6763
|
-
"**/*.webp",
|
|
6764
|
-
"**/*.ico",
|
|
6765
|
-
"**/*.pdf",
|
|
6766
|
-
"**/*.zip",
|
|
6767
|
-
"**/*.gz",
|
|
6768
|
-
"**/*.woff",
|
|
6769
|
-
"**/*.woff2",
|
|
6770
|
-
"**/*.ttf",
|
|
6771
|
-
"**/*.eot"
|
|
6772
|
-
],
|
|
6773
|
-
maxFiles: 200,
|
|
6774
|
-
maxFileBytes: 524288,
|
|
6775
|
-
debounceMs: 1500
|
|
6776
|
-
});
|
|
6777
|
-
function explicitMode(mode) {
|
|
6778
|
-
return mode === "fix" ? "fix" : "report";
|
|
6779
|
-
}
|
|
6780
|
-
function normalizeRule(val, topMode) {
|
|
6781
|
-
if (val === false) return {
|
|
6782
|
-
enabled: false,
|
|
6783
|
-
mode: topMode
|
|
6784
|
-
};
|
|
6785
|
-
if (val === true || val == null) return {
|
|
6786
|
-
enabled: true,
|
|
6787
|
-
mode: topMode
|
|
6788
|
-
};
|
|
6789
|
-
if (typeof val === "object") {
|
|
6790
|
-
const source = val;
|
|
6791
|
-
return {
|
|
6792
|
-
enabled: source.enabled !== false,
|
|
6793
|
-
mode: source.mode == null ? topMode : explicitMode(source.mode)
|
|
6794
|
-
};
|
|
6795
|
-
}
|
|
6796
|
-
return {
|
|
6797
|
-
enabled: true,
|
|
6798
|
-
mode: topMode
|
|
6799
|
-
};
|
|
6800
|
-
}
|
|
6801
|
-
var SCALAR_KEYS = [
|
|
6802
|
-
"enabled",
|
|
6803
|
-
"mode",
|
|
6804
|
-
"maxFiles",
|
|
6805
|
-
"maxFileBytes",
|
|
6806
|
-
"debounceMs"
|
|
6807
|
-
];
|
|
6808
|
-
function resolveCheckConfig(globalCfg, projectCfg) {
|
|
6809
|
-
const base = JSON.parse(JSON.stringify(DEFAULTS));
|
|
6810
|
-
const layers = [globalCfg, projectCfg].filter((x) => Boolean(x) && typeof x === "object");
|
|
6811
|
-
for (const layer of layers) {
|
|
6812
|
-
for (const k of SCALAR_KEYS) if (layer[k] != null) Object.assign(base, { [k]: layer[k] });
|
|
6813
|
-
if (Array.isArray(layer.include)) base.include = layer.include.slice();
|
|
6814
|
-
if (Array.isArray(layer.exclude)) base.exclude = layer.exclude.slice();
|
|
6815
|
-
if (layer.rules && typeof layer.rules === "object") base.rules = Object.assign({}, base.rules, layer.rules);
|
|
6816
|
-
}
|
|
6817
|
-
const topMode = explicitMode(base.mode);
|
|
6818
|
-
const rules = {};
|
|
6819
|
-
for (const name of Object.keys(base.rules)) rules[name] = normalizeRule(base.rules[name], topMode);
|
|
6820
|
-
return {
|
|
6821
|
-
enabled: base.enabled !== false,
|
|
6822
|
-
mode: topMode,
|
|
6823
|
-
rules,
|
|
6824
|
-
include: base.include,
|
|
6825
|
-
exclude: base.exclude,
|
|
6826
|
-
maxFiles: base.maxFiles,
|
|
6827
|
-
maxFileBytes: base.maxFileBytes,
|
|
6828
|
-
debounceMs: base.debounceMs
|
|
6829
|
-
};
|
|
6830
|
-
}
|
|
6831
|
-
var GIT_MAX_BUFFER_BYTES$1 = 8388608;
|
|
6832
|
-
async function execGit(args, cwd) {
|
|
6833
|
-
const result = await runGit(args, {
|
|
6834
|
-
cwd,
|
|
6835
|
-
timeoutMs: GIT_TIMEOUT_MS$2,
|
|
6836
|
-
maxBuffer: GIT_MAX_BUFFER_BYTES$1,
|
|
6837
|
-
trim: false
|
|
6838
|
-
});
|
|
6839
|
-
if (!result.ok) throw result.error;
|
|
6840
|
-
return result.out;
|
|
6841
|
-
}
|
|
6842
|
-
async function gitRoot(cwd) {
|
|
6843
|
-
return (await execGit(["rev-parse", "--show-toplevel"], cwd)).toString().trim();
|
|
6844
|
-
}
|
|
6845
|
-
async function listChangedFiles(root) {
|
|
6846
|
-
const lines = (await execGit([
|
|
6847
|
-
"--no-optional-locks",
|
|
6848
|
-
"-c",
|
|
6849
|
-
"core.quotepath=false",
|
|
6850
|
-
"status",
|
|
6851
|
-
"--porcelain",
|
|
6852
|
-
"-uall",
|
|
6853
|
-
"--no-renames"
|
|
6854
|
-
], root)).toString().split(/\r?\n/);
|
|
6855
|
-
const files = [];
|
|
6856
|
-
for (const ln of lines) {
|
|
6857
|
-
if (!ln) continue;
|
|
6858
|
-
const xy = ln.slice(0, 2);
|
|
6859
|
-
const p = ln.slice(3);
|
|
6860
|
-
if (!p) continue;
|
|
6861
|
-
if (xy.indexOf("D") !== -1) continue;
|
|
6862
|
-
files.push(p);
|
|
6863
|
-
}
|
|
6864
|
-
return files;
|
|
6865
|
-
}
|
|
6866
|
-
async function runPostTurnChecks({ cwd, config, deps = {} } = {}) {
|
|
6867
|
-
const started = Date.now();
|
|
6868
|
-
const cfg = config || resolveCheckConfig();
|
|
6869
|
-
const report = {
|
|
6870
|
-
ok: true,
|
|
6871
|
-
skipped: null,
|
|
6872
|
-
mode: cfg.mode,
|
|
6873
|
-
root: null,
|
|
6874
|
-
filesScanned: 0,
|
|
6875
|
-
filesFixed: 0,
|
|
6876
|
-
findings: [],
|
|
6877
|
-
errors: [],
|
|
6878
|
-
durationMs: 0
|
|
6879
|
-
};
|
|
6880
|
-
const done = () => {
|
|
6881
|
-
report.durationMs = Date.now() - started;
|
|
6882
|
-
return report;
|
|
6883
|
-
};
|
|
6884
|
-
if (!cfg.enabled) {
|
|
6885
|
-
report.skipped = "disabled";
|
|
6886
|
-
return done();
|
|
6887
|
-
}
|
|
6888
|
-
const _gitRoot = deps.gitRoot || gitRoot;
|
|
6889
|
-
const _list = deps.listChangedFiles || listChangedFiles;
|
|
6890
|
-
const _readFile = deps.readFile || ((p) => fs.readFileSync(p));
|
|
6891
|
-
const _writeFile = deps.writeFile || ((p, c) => fs.writeFileSync(p, c));
|
|
6892
|
-
const _stat = deps.stat || ((p) => fs.statSync(p));
|
|
6893
|
-
let root;
|
|
6894
|
-
try {
|
|
6895
|
-
root = await _gitRoot(cwd);
|
|
6896
|
-
} catch {
|
|
6897
|
-
report.skipped = "no-git";
|
|
6898
|
-
return done();
|
|
6899
|
-
}
|
|
6900
|
-
report.root = root;
|
|
6901
|
-
let files;
|
|
6902
|
-
try {
|
|
6903
|
-
files = await _list(root);
|
|
6904
|
-
} catch (err) {
|
|
6905
|
-
report.skipped = "no-git";
|
|
6906
|
-
report.errors.push({
|
|
6907
|
-
file: null,
|
|
6908
|
-
message: errorMessage(err)
|
|
6909
|
-
});
|
|
6910
|
-
return done();
|
|
6911
|
-
}
|
|
6912
|
-
if (!files.length) {
|
|
6913
|
-
report.skipped = "no-changes";
|
|
6914
|
-
return done();
|
|
6915
|
-
}
|
|
6916
|
-
const eligible = files.filter((rel) => shouldCheckPath(rel, {
|
|
6917
|
-
include: cfg.include,
|
|
6918
|
-
exclude: cfg.exclude
|
|
6919
|
-
})).slice(0, cfg.maxFiles);
|
|
6920
|
-
let fileIndex = 0;
|
|
6921
|
-
for (const rel of eligible) {
|
|
6922
|
-
if (fileIndex++ > 0) await new Promise((resolve) => setImmediate(resolve));
|
|
6923
|
-
const abs = path.join(root, rel);
|
|
6924
|
-
try {
|
|
6925
|
-
const before = _stat(abs);
|
|
6926
|
-
if (!before) {
|
|
6927
|
-
report.errors.push({
|
|
6928
|
-
file: rel,
|
|
6929
|
-
message: "skipped-race"
|
|
6930
|
-
});
|
|
6931
|
-
continue;
|
|
6932
|
-
}
|
|
6933
|
-
if (before && before.size != null && before.size > cfg.maxFileBytes) continue;
|
|
6934
|
-
const buf = _readFile(abs);
|
|
6935
|
-
if (looksBinary(buf)) continue;
|
|
6936
|
-
const res = applyRules(buf.toString("utf8"), cfg.rules, { relPath: rel });
|
|
6937
|
-
report.filesScanned++;
|
|
6938
|
-
if (res.findings.length) {
|
|
6939
|
-
const counts = {};
|
|
6940
|
-
for (const f of res.findings) counts[f.rule] = (counts[f.rule] || 0) + 1;
|
|
6941
|
-
for (const [rule, count] of Object.entries(counts)) report.findings.push({
|
|
6942
|
-
file: rel,
|
|
6943
|
-
rule,
|
|
6944
|
-
count
|
|
6945
|
-
});
|
|
6946
|
-
}
|
|
6947
|
-
if (res.changed) {
|
|
6948
|
-
const after = _stat(abs);
|
|
6949
|
-
if (after && (after.mtimeMs !== before.mtimeMs || after.size !== before.size)) {
|
|
6950
|
-
report.errors.push({
|
|
6951
|
-
file: rel,
|
|
6952
|
-
message: "skipped-race"
|
|
6953
|
-
});
|
|
6954
|
-
continue;
|
|
6955
|
-
}
|
|
6956
|
-
_writeFile(abs, res.content);
|
|
6957
|
-
report.filesFixed++;
|
|
6958
|
-
}
|
|
6959
|
-
} catch (err) {
|
|
6960
|
-
report.errors.push({
|
|
6961
|
-
file: rel,
|
|
6962
|
-
message: errorMessage(err)
|
|
6963
|
-
});
|
|
6964
|
-
}
|
|
6965
|
-
}
|
|
6966
|
-
return done();
|
|
6967
|
-
}
|
|
6968
|
-
//#endregion
|
|
6969
6701
|
//#region server/session/session-event-wiring.ts
|
|
6970
6702
|
var IDLE_COMPACTION_CONFIRM_MS = 1e4;
|
|
6971
6703
|
function waitingCopy(name, context) {
|
|
@@ -7000,7 +6732,6 @@ function createSessionEventWiring(dependencies) {
|
|
|
7000
6732
|
dependencies.taskTitleRefiner?.attachSession(session);
|
|
7001
6733
|
dependencies.coherenceFeed?.attachSession(session);
|
|
7002
6734
|
dependencies.planReview?.attachSession(session);
|
|
7003
|
-
let postTurnDebounce = null;
|
|
7004
6735
|
let pendingPromptKind = null;
|
|
7005
6736
|
let pendingAgentNote = null;
|
|
7006
6737
|
const notifyGate = createNotifyGate();
|
|
@@ -7080,10 +6811,6 @@ function createSessionEventWiring(dependencies) {
|
|
|
7080
6811
|
dependencies.telemetry?.captureException(error, { handled: true });
|
|
7081
6812
|
});
|
|
7082
6813
|
session.on("exit", ({ exitCode, signal, reason, exitedOnItsOwn }) => {
|
|
7083
|
-
if (postTurnDebounce) {
|
|
7084
|
-
clearTimeout(postTurnDebounce);
|
|
7085
|
-
postTurnDebounce = null;
|
|
7086
|
-
}
|
|
7087
6814
|
clearCompactionConfirm();
|
|
7088
6815
|
const reasonText = reason ? `, reason=${reason}` : "";
|
|
7089
6816
|
dependencies.logger.log(`[${session.name}] exited (code=${exitCode}, signal=${signal}${reasonText})`);
|
|
@@ -7120,32 +6847,6 @@ function createSessionEventWiring(dependencies) {
|
|
|
7120
6847
|
dependencies.getIngestLane()?.detachSessionTap(session);
|
|
7121
6848
|
captureSessionEnded(cause);
|
|
7122
6849
|
});
|
|
7123
|
-
const resolvePostTurn = () => {
|
|
7124
|
-
const project = dependencies.config.projects.find((candidate) => candidate.id === session.id);
|
|
7125
|
-
return project ? resolveCheckConfig(dependencies.config.postTurnChecks, project.postTurnChecks) : null;
|
|
7126
|
-
};
|
|
7127
|
-
session.on("post-turn-check", () => {
|
|
7128
|
-
const config = resolvePostTurn();
|
|
7129
|
-
if (!config?.enabled) return;
|
|
7130
|
-
if (postTurnDebounce) clearTimeout(postTurnDebounce);
|
|
7131
|
-
postTurnDebounce = setTimeout(() => {
|
|
7132
|
-
postTurnDebounce = null;
|
|
7133
|
-
const runConfig = resolvePostTurn();
|
|
7134
|
-
if (!runConfig?.enabled) return;
|
|
7135
|
-
runPostTurnChecks({
|
|
7136
|
-
cwd: session.effectiveCwd(),
|
|
7137
|
-
config: runConfig
|
|
7138
|
-
}).then((report) => {
|
|
7139
|
-
dependencies.broadcastControl({
|
|
7140
|
-
type: "post-turn-result",
|
|
7141
|
-
id: session.id,
|
|
7142
|
-
session: session.name,
|
|
7143
|
-
...report,
|
|
7144
|
-
timestamp: Date.now()
|
|
7145
|
-
});
|
|
7146
|
-
}).catch((error) => dependencies.logger.warn(`[${session.name}] post-turn checks failed: ${errorMessage(error)}`));
|
|
7147
|
-
}, config.debounceMs);
|
|
7148
|
-
});
|
|
7149
6850
|
session.on("post-turn-check", () => dependencies.usage.nudgeSession(liveTranscriptPath));
|
|
7150
6851
|
session.on("state-change", ({ from, to, event, detail }) => {
|
|
7151
6852
|
if (to === STATES.IDLE || to === STATES.COMPLETE) {
|
|
@@ -7245,9 +6946,14 @@ function createSessionEventWiring(dependencies) {
|
|
|
7245
6946
|
timestamp: Date.now()
|
|
7246
6947
|
});
|
|
7247
6948
|
});
|
|
7248
|
-
relay("wakeup-change", "session-wakeup");
|
|
7249
6949
|
session.on("wakeup-change", persistWasWorking);
|
|
7250
|
-
|
|
6950
|
+
session.on("prompt-kind-change", ({ pendingPromptKind: nextKind }) => dependencies.broadcastControl({
|
|
6951
|
+
type: "session-prompt",
|
|
6952
|
+
id: session.id,
|
|
6953
|
+
session: session.name,
|
|
6954
|
+
pendingPromptKind: nextKind,
|
|
6955
|
+
timestamp: Date.now()
|
|
6956
|
+
}));
|
|
7251
6957
|
relay("unseen-outcome-change", "session-unseen");
|
|
7252
6958
|
relay("conversation-change", "session-conversation");
|
|
7253
6959
|
session.on("merge-status", ({ mergeStatus, reason, parked }) => {
|
|
@@ -7410,10 +7116,7 @@ function runAutoResume(sessions, config, spawnGate, logger = console) {
|
|
|
7410
7116
|
runs.push(spawnGate.run(() => {
|
|
7411
7117
|
if (session.state !== STATES.DORMANT) return null;
|
|
7412
7118
|
return session.start({ resumePrompt });
|
|
7413
|
-
}, {
|
|
7414
|
-
isStillWanted: () => session.state === STATES.DORMANT,
|
|
7415
|
-
onMemoryWait: (isWaiting) => session.setWaitingForMemory(isWaiting)
|
|
7416
|
-
}).catch((error) => {
|
|
7119
|
+
}, { isStillWanted: () => session.state === STATES.DORMANT }).catch((error) => {
|
|
7417
7120
|
logger.warn(`[boot] auto-resume failed for ${session.name}: ${errorMessage(error)}`);
|
|
7418
7121
|
}));
|
|
7419
7122
|
}
|
|
@@ -7473,7 +7176,8 @@ function reconcileSessionWorktrees({ projects, sessions, gitWorkspaceSync, integ
|
|
|
7473
7176
|
gitWorkspaceSync.removeWorktreeByPath({
|
|
7474
7177
|
projectPath: project.path,
|
|
7475
7178
|
cwd: worktree.cwd,
|
|
7476
|
-
branch: worktree.branch
|
|
7179
|
+
branch: worktree.branch,
|
|
7180
|
+
cause: "boot-reconcile"
|
|
7477
7181
|
});
|
|
7478
7182
|
}
|
|
7479
7183
|
}
|
|
@@ -7654,6 +7358,8 @@ function createSessionRegistry(dependencies) {
|
|
|
7654
7358
|
//#endregion
|
|
7655
7359
|
//#region shared/team-review-markdown.ts
|
|
7656
7360
|
var AUTOMATED_REVIEW_NOTE = "> [!NOTE]\n> Automated review. Not written by a human.";
|
|
7361
|
+
var CREDITED_REVIEW_NOTE = "> [!NOTE]\n> Automated review by [Glimmervoid](https://glimmervoid.dev). Not written by a human.";
|
|
7362
|
+
var HAND_APPROVED_REVIEW_NOTE = "> [!NOTE]\n> Automated review by [Glimmervoid](https://glimmervoid.dev), not written by a human. Approved by hand after checking it.";
|
|
7657
7363
|
var SEVERITY_ALTERNATION = FindingSeverity.options.join("|");
|
|
7658
7364
|
var FINDING_HEADER_SOURCE = String.raw`\*\*(?:\[([^\]\n]+)\]\s+)?(${SEVERITY_ALTERNATION})\*\*`;
|
|
7659
7365
|
function findingHeader$1(reviewer, severity) {
|
|
@@ -7672,7 +7378,12 @@ function parseLeadingFindingHeader(body) {
|
|
|
7672
7378
|
}
|
|
7673
7379
|
function withoutAutomatedNote(body) {
|
|
7674
7380
|
const trimmed = body.trimStart();
|
|
7675
|
-
|
|
7381
|
+
const leadingNote = [
|
|
7382
|
+
AUTOMATED_REVIEW_NOTE,
|
|
7383
|
+
CREDITED_REVIEW_NOTE,
|
|
7384
|
+
HAND_APPROVED_REVIEW_NOTE
|
|
7385
|
+
].find((note) => trimmed.startsWith(note));
|
|
7386
|
+
return leadingNote ? trimmed.slice(leadingNote.length).trim() : body.trim();
|
|
7676
7387
|
}
|
|
7677
7388
|
var FOLDED_FINDING_MARKER = "(line not in the diff)";
|
|
7678
7389
|
var FINDING_BULLET_SOURCE = String.raw`- ${FINDING_HEADER_SOURCE}`;
|
|
@@ -9538,7 +9249,7 @@ function readTeamReviewSettings(config) {
|
|
|
9538
9249
|
const teams = readGithubTeams(config);
|
|
9539
9250
|
const configuredOrg = typeof block?.org === "string" ? block.org.trim() : "";
|
|
9540
9251
|
return {
|
|
9541
|
-
enabled: block?.enabled
|
|
9252
|
+
enabled: block?.enabled !== false,
|
|
9542
9253
|
org: configuredOrg || teams[0]?.org || "",
|
|
9543
9254
|
teams,
|
|
9544
9255
|
reReviewAfterHours: positiveNumberOr(block?.reReviewAfterHours, 24),
|
|
@@ -9699,10 +9410,6 @@ function isDocsOrTests(filePath) {
|
|
|
9699
9410
|
return /(?:^|\/)(?:test|tests|__tests__|spec)\//.test(normalizedPath) || /(?:\.test\.|\.spec\.|_test\.)/.test(fileName);
|
|
9700
9411
|
}
|
|
9701
9412
|
function triagePr(detail) {
|
|
9702
|
-
if (detail.isCrossRepository) return {
|
|
9703
|
-
tier: "skip",
|
|
9704
|
-
reasons: ["fork"]
|
|
9705
|
-
};
|
|
9706
9413
|
const listedLines = detail.files.reduce((total, file) => total + file.additions + file.deletions, 0);
|
|
9707
9414
|
if (detail.files.length >= 100 || listedLines < detail.additions + detail.deletions) return {
|
|
9708
9415
|
tier: "full",
|
|
@@ -9745,20 +9452,26 @@ function isPostableStatus(draft, event) {
|
|
|
9745
9452
|
function canPost(draft, clickedHead, currentHead, event) {
|
|
9746
9453
|
return isPostableStatus(draft, event) && draft.reviewedHead === clickedHead && draft.reviewedHead === currentHead;
|
|
9747
9454
|
}
|
|
9748
|
-
var
|
|
9455
|
+
var POSTED_NOTE_BY_EVENT = {
|
|
9456
|
+
APPROVE: HAND_APPROVED_REVIEW_NOTE,
|
|
9457
|
+
COMMENT: CREDITED_REVIEW_NOTE,
|
|
9458
|
+
REQUEST_CHANGES: CREDITED_REVIEW_NOTE
|
|
9459
|
+
};
|
|
9749
9460
|
function postedReviewBody(event, body) {
|
|
9750
|
-
|
|
9461
|
+
const postedNote = POSTED_NOTE_BY_EVENT[event];
|
|
9462
|
+
if (body.includes(postedNote)) return body;
|
|
9751
9463
|
const noteStart = body.indexOf(AUTOMATED_REVIEW_NOTE);
|
|
9464
|
+
if (noteStart === -1 && event !== "APPROVE") return body;
|
|
9752
9465
|
return [
|
|
9753
9466
|
noteStart === -1 ? body.trim() : body.slice(0, noteStart).trim(),
|
|
9754
|
-
|
|
9755
|
-
noteStart === -1 ? "" : body.slice(noteStart + AUTOMATED_REVIEW_NOTE.length).trim()
|
|
9756
|
-
HAND_APPROVAL_LINE
|
|
9467
|
+
postedNote,
|
|
9468
|
+
noteStart === -1 ? "" : body.slice(noteStart + AUTOMATED_REVIEW_NOTE.length).trim()
|
|
9757
9469
|
].filter(Boolean).join("\n\n");
|
|
9758
9470
|
}
|
|
9759
9471
|
function eventForAction(action) {
|
|
9760
9472
|
if (action === "approve" || action === "approve-only") return "APPROVE";
|
|
9761
9473
|
if (action === "comment") return "COMMENT";
|
|
9474
|
+
if (action === "request-changes") return "REQUEST_CHANGES";
|
|
9762
9475
|
return null;
|
|
9763
9476
|
}
|
|
9764
9477
|
var HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
@@ -9768,6 +9481,16 @@ function diffHeaderPath(line, sidePrefix) {
|
|
|
9768
9481
|
if (unquoted === "/dev/null") return null;
|
|
9769
9482
|
return unquoted.startsWith(sidePrefix) ? unquoted.slice(sidePrefix.length) : unquoted;
|
|
9770
9483
|
}
|
|
9484
|
+
var MAX_EXCERPT_LINE_CHARS = 400;
|
|
9485
|
+
function addDiffRow(file, row) {
|
|
9486
|
+
const rowIndex = file.rows.length;
|
|
9487
|
+
file.rows.push({
|
|
9488
|
+
...row,
|
|
9489
|
+
text: row.text.replace(/\r$/, "").slice(0, MAX_EXCERPT_LINE_CHARS)
|
|
9490
|
+
});
|
|
9491
|
+
if (row.oldLine !== null) file.left.set(row.oldLine, rowIndex);
|
|
9492
|
+
if (row.newLine !== null) file.right.set(row.newLine, rowIndex);
|
|
9493
|
+
}
|
|
9771
9494
|
function commentableLines(diffText) {
|
|
9772
9495
|
const linesByPath = /* @__PURE__ */ new Map();
|
|
9773
9496
|
let oldPath = null;
|
|
@@ -9776,25 +9499,44 @@ function commentableLines(diffText) {
|
|
|
9776
9499
|
let newLine = 0;
|
|
9777
9500
|
let oldRemaining = 0;
|
|
9778
9501
|
let newRemaining = 0;
|
|
9502
|
+
let hunk = 0;
|
|
9779
9503
|
for (const line of diffText.split("\n")) {
|
|
9780
9504
|
if (oldRemaining > 0 || newRemaining > 0) {
|
|
9781
9505
|
if (!currentFile) continue;
|
|
9782
9506
|
const marker = line.charAt(0);
|
|
9507
|
+
const text = line.slice(1);
|
|
9783
9508
|
if (marker === "\\") continue;
|
|
9784
9509
|
if (marker === "-") {
|
|
9785
|
-
currentFile
|
|
9510
|
+
addDiffRow(currentFile, {
|
|
9511
|
+
oldLine,
|
|
9512
|
+
newLine: null,
|
|
9513
|
+
kind: "del",
|
|
9514
|
+
text,
|
|
9515
|
+
hunk
|
|
9516
|
+
});
|
|
9786
9517
|
oldLine += 1;
|
|
9787
9518
|
oldRemaining -= 1;
|
|
9788
9519
|
continue;
|
|
9789
9520
|
}
|
|
9790
9521
|
if (marker === "+") {
|
|
9791
|
-
currentFile
|
|
9522
|
+
addDiffRow(currentFile, {
|
|
9523
|
+
oldLine: null,
|
|
9524
|
+
newLine,
|
|
9525
|
+
kind: "add",
|
|
9526
|
+
text,
|
|
9527
|
+
hunk
|
|
9528
|
+
});
|
|
9792
9529
|
newLine += 1;
|
|
9793
9530
|
newRemaining -= 1;
|
|
9794
9531
|
continue;
|
|
9795
9532
|
}
|
|
9796
|
-
currentFile
|
|
9797
|
-
|
|
9533
|
+
addDiffRow(currentFile, {
|
|
9534
|
+
oldLine,
|
|
9535
|
+
newLine,
|
|
9536
|
+
kind: "context",
|
|
9537
|
+
text,
|
|
9538
|
+
hunk
|
|
9539
|
+
});
|
|
9798
9540
|
oldLine += 1;
|
|
9799
9541
|
newLine += 1;
|
|
9800
9542
|
oldRemaining -= 1;
|
|
@@ -9813,21 +9555,41 @@ function commentableLines(diffText) {
|
|
|
9813
9555
|
if (line.startsWith("+++ ")) {
|
|
9814
9556
|
const filePath = diffHeaderPath(line, "b/") ?? oldPath;
|
|
9815
9557
|
currentFile = filePath === null ? null : linesByPath.get(filePath) ?? {
|
|
9816
|
-
left: /* @__PURE__ */ new
|
|
9817
|
-
right: /* @__PURE__ */ new
|
|
9558
|
+
left: /* @__PURE__ */ new Map(),
|
|
9559
|
+
right: /* @__PURE__ */ new Map(),
|
|
9560
|
+
rows: []
|
|
9818
9561
|
};
|
|
9819
9562
|
if (filePath !== null && currentFile) linesByPath.set(filePath, currentFile);
|
|
9820
9563
|
continue;
|
|
9821
9564
|
}
|
|
9822
|
-
const
|
|
9823
|
-
if (!
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9565
|
+
const hunkHeader = HUNK_HEADER.exec(line);
|
|
9566
|
+
if (!hunkHeader || !currentFile) continue;
|
|
9567
|
+
hunk += 1;
|
|
9568
|
+
oldLine = Number(hunkHeader[1]);
|
|
9569
|
+
oldRemaining = hunkHeader[2] === void 0 ? 1 : Number(hunkHeader[2]);
|
|
9570
|
+
newLine = Number(hunkHeader[3]);
|
|
9571
|
+
newRemaining = hunkHeader[4] === void 0 ? 1 : Number(hunkHeader[4]);
|
|
9828
9572
|
}
|
|
9829
9573
|
return linesByPath;
|
|
9830
9574
|
}
|
|
9575
|
+
function diffExcerpt(commentable, filePath, side, line, radius = 3) {
|
|
9576
|
+
const fileLines = commentable.get(filePath);
|
|
9577
|
+
const rowIndex = fileLines ? (side === "LEFT" ? fileLines.left : fileLines.right).get(line) : void 0;
|
|
9578
|
+
if (!fileLines || rowIndex === void 0) return null;
|
|
9579
|
+
const citedHunk = fileLines.rows[rowIndex]?.hunk;
|
|
9580
|
+
return fileLines.rows.slice(Math.max(0, rowIndex - radius), rowIndex + radius + 1).filter((row) => row.hunk === citedHunk).map((row) => ({
|
|
9581
|
+
line: side === "LEFT" ? row.oldLine : row.newLine,
|
|
9582
|
+
kind: row.kind,
|
|
9583
|
+
text: row.text
|
|
9584
|
+
}));
|
|
9585
|
+
}
|
|
9586
|
+
function withExcerpt(comment, commentable) {
|
|
9587
|
+
const excerpt = commentable ? diffExcerpt(commentable, comment.path, comment.side, comment.line) : null;
|
|
9588
|
+
return excerpt ? {
|
|
9589
|
+
...comment,
|
|
9590
|
+
excerpt
|
|
9591
|
+
} : comment;
|
|
9592
|
+
}
|
|
9831
9593
|
function isLineCommentable(commentable, filePath, side, line) {
|
|
9832
9594
|
const fileLines = commentable.get(filePath);
|
|
9833
9595
|
if (!fileLines) return false;
|
|
@@ -9841,7 +9603,7 @@ function isSettledAtHead(entry, head) {
|
|
|
9841
9603
|
if (entry?.draft?.status === "discarded") return true;
|
|
9842
9604
|
if (!entry || entry.reviewedHead !== head) return false;
|
|
9843
9605
|
if (entry.draft?.status === "error") return entry.reviewAttempts >= MAX_REVIEW_ATTEMPTS;
|
|
9844
|
-
return entry.draft !== null
|
|
9606
|
+
return entry.draft !== null;
|
|
9845
9607
|
}
|
|
9846
9608
|
function shouldAutoReview(storedEntry, currentHead, nowMs, reReviewAfterMs) {
|
|
9847
9609
|
if (!storedEntry) return true;
|
|
@@ -10001,24 +9763,7 @@ function presentedDraft(entry, draft) {
|
|
|
10001
9763
|
function draftsNewestFirst(state) {
|
|
10002
9764
|
return Object.values(state).filter((entry) => entry.draft !== null).sort((left, right) => right.updatedAt - left.updatedAt).flatMap((entry) => entry.draft ? [presentedDraft(entry, entry.draft)] : []);
|
|
10003
9765
|
}
|
|
10004
|
-
|
|
10005
|
-
function isSameQueuedReview(left, right) {
|
|
10006
|
-
return JSON.stringify(left ?? null, QUEUED_REVIEW_FIELDS) === JSON.stringify(right, QUEUED_REVIEW_FIELDS);
|
|
10007
|
-
}
|
|
10008
|
-
function handReviewRows(state) {
|
|
10009
|
-
return Object.values(state).flatMap((entry) => {
|
|
10010
|
-
if (!entry.handReview || !entry.liveHead || entry.inFlight || entry.skipReason !== "fork" || entry.reviewedHead !== entry.liveHead) return [];
|
|
10011
|
-
if (hasViewerReviewedAt(entry.githubReviews, entry.liveHead)) return [];
|
|
10012
|
-
if (hasStandingViewerApproval({
|
|
10013
|
-
githubReviews: entry.githubReviews,
|
|
10014
|
-
reviewDecision: entry.reviewDecision,
|
|
10015
|
-
liveHead: entry.liveHead,
|
|
10016
|
-
requeuedHead: entry.requeuedHead
|
|
10017
|
-
})) return [];
|
|
10018
|
-
return [entry.handReview];
|
|
10019
|
-
});
|
|
10020
|
-
}
|
|
10021
|
-
function teamReviewStatus({ ts, configured, reason = null, drafts = [], inFlight = [], queued = [], handReview = [], team }) {
|
|
9766
|
+
function teamReviewStatus({ ts, configured, reason = null, drafts = [], inFlight = [], queued = [], team }) {
|
|
10022
9767
|
return {
|
|
10023
9768
|
type: "team-review-status",
|
|
10024
9769
|
ts,
|
|
@@ -10027,7 +9772,6 @@ function teamReviewStatus({ ts, configured, reason = null, drafts = [], inFlight
|
|
|
10027
9772
|
drafts,
|
|
10028
9773
|
inFlight,
|
|
10029
9774
|
queued,
|
|
10030
|
-
handReview,
|
|
10031
9775
|
team
|
|
10032
9776
|
};
|
|
10033
9777
|
}
|
|
@@ -10475,7 +10219,7 @@ function readyDraft({ candidate, tier, reasons, result, reviewedBase, commentabl
|
|
|
10475
10219
|
summary: result.summary,
|
|
10476
10220
|
...assessment ? { assessment } : {},
|
|
10477
10221
|
body: rendered.body,
|
|
10478
|
-
comments: rendered.comments,
|
|
10222
|
+
comments: rendered.comments.map((comment) => withExcerpt(comment, commentable)),
|
|
10479
10223
|
status: "ready"
|
|
10480
10224
|
};
|
|
10481
10225
|
}
|
|
@@ -12976,8 +12720,9 @@ function createIssuesWiring({ config, gitWorkspace, broadcast, hasSession, store
|
|
|
12976
12720
|
}
|
|
12977
12721
|
const runner = createLaneRunner({
|
|
12978
12722
|
tag: ISSUES_LANE_ID,
|
|
12979
|
-
gate: () => ({ start: true }),
|
|
12723
|
+
gate: () => ({ start: config.issues?.enabled === true }),
|
|
12980
12724
|
cfgKey: () => JSON.stringify({
|
|
12725
|
+
enabled: config.issues?.enabled === true,
|
|
12981
12726
|
projects: config.projects.map((project) => ({
|
|
12982
12727
|
id: project.id,
|
|
12983
12728
|
path: project.path,
|
|
@@ -17124,6 +16869,76 @@ function createSessionHookLifecycle(options) {
|
|
|
17124
16869
|
};
|
|
17125
16870
|
}
|
|
17126
16871
|
//#endregion
|
|
16872
|
+
//#region server/git/git-exec.ts
|
|
16873
|
+
var DEFAULT_COMMAND_TIMEOUT_MS = 2e4;
|
|
16874
|
+
function outputText(value) {
|
|
16875
|
+
if (typeof value === "string") return value;
|
|
16876
|
+
if (Buffer.isBuffer(value)) return value.toString("utf8");
|
|
16877
|
+
return value == null ? "" : String(value);
|
|
16878
|
+
}
|
|
16879
|
+
function childEnv(env, replaceEnv) {
|
|
16880
|
+
if (replaceEnv) return { env: replaceEnv };
|
|
16881
|
+
if (env) return { env: {
|
|
16882
|
+
...process.env,
|
|
16883
|
+
...env
|
|
16884
|
+
} };
|
|
16885
|
+
return {};
|
|
16886
|
+
}
|
|
16887
|
+
async function runCommand(file, args, { cwd, timeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, maxBuffer, env, replaceEnv, signal, input, trim = true, keepStdoutOnFailure = false, preferStderr = false, execFileFn = execFileAsync } = {}) {
|
|
16888
|
+
const execOptions = {
|
|
16889
|
+
encoding: "utf8",
|
|
16890
|
+
timeout: timeoutMs,
|
|
16891
|
+
...cwd === void 0 ? {} : { cwd },
|
|
16892
|
+
...maxBuffer === void 0 ? {} : { maxBuffer },
|
|
16893
|
+
...signal ? { signal } : {},
|
|
16894
|
+
...input === void 0 ? {} : { input },
|
|
16895
|
+
...childEnv(env, replaceEnv)
|
|
16896
|
+
};
|
|
16897
|
+
try {
|
|
16898
|
+
const { stdout, stderr } = await execFileFn(file, args, execOptions);
|
|
16899
|
+
const out = outputText(stdout);
|
|
16900
|
+
return {
|
|
16901
|
+
ok: true,
|
|
16902
|
+
out: trim ? out.trim() : out,
|
|
16903
|
+
err: "",
|
|
16904
|
+
stderr: outputText(stderr)
|
|
16905
|
+
};
|
|
16906
|
+
} catch (error) {
|
|
16907
|
+
const failure = error ?? {};
|
|
16908
|
+
const stdout = outputText(failure.stdout);
|
|
16909
|
+
const stderr = outputText(failure.stderr);
|
|
16910
|
+
const message = errorMessage(error);
|
|
16911
|
+
return {
|
|
16912
|
+
ok: false,
|
|
16913
|
+
out: keepStdoutOnFailure ? trim ? stdout.trim() : stdout : "",
|
|
16914
|
+
err: preferStderr ? stderr || message : message,
|
|
16915
|
+
stderr,
|
|
16916
|
+
error,
|
|
16917
|
+
timedOut: failure.killed === true && Boolean(failure.signal)
|
|
16918
|
+
};
|
|
16919
|
+
}
|
|
16920
|
+
}
|
|
16921
|
+
function runGit(args, { gitPath = "git", env, replaceEnv, ...rest } = {}) {
|
|
16922
|
+
const baseEnv = replaceEnv ?? (env ? {
|
|
16923
|
+
...process.env,
|
|
16924
|
+
...env
|
|
16925
|
+
} : process.env);
|
|
16926
|
+
return runCommand(gitPath, args, {
|
|
16927
|
+
...rest,
|
|
16928
|
+
replaceEnv: {
|
|
16929
|
+
...baseEnv,
|
|
16930
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
16931
|
+
}
|
|
16932
|
+
});
|
|
16933
|
+
}
|
|
16934
|
+
function gitCommandFailure(args, command, timeoutMs = DEFAULT_COMMAND_TIMEOUT_MS) {
|
|
16935
|
+
if (!command.timedOut) return command.error;
|
|
16936
|
+
return new Error(`git ${args.join(" ")} timed out after ${timeoutMs}ms`, { cause: command.error });
|
|
16937
|
+
}
|
|
16938
|
+
function runGh(args, options = {}) {
|
|
16939
|
+
return runCommand("gh", args, options);
|
|
16940
|
+
}
|
|
16941
|
+
//#endregion
|
|
17127
16942
|
//#region shared/coalesce-timer.ts
|
|
17128
16943
|
function createCoalescedTimer({ mode, delayMs, run, setTimeoutFn = DEFAULT_TIMER_FNS.setTimeoutFn, clearTimeoutFn = DEFAULT_TIMER_FNS.clearTimeoutFn, unref = true }) {
|
|
17129
16944
|
let timer = null;
|
|
@@ -17760,7 +17575,8 @@ function createSessionWorkspaceMembers({ plan, owner, sessionName, shareList, gi
|
|
|
17760
17575
|
projectPath: member.repoPath,
|
|
17761
17576
|
cwd: member.dir,
|
|
17762
17577
|
shareList,
|
|
17763
|
-
discardWork
|
|
17578
|
+
discardWork,
|
|
17579
|
+
cause: "session"
|
|
17764
17580
|
});
|
|
17765
17581
|
if (removed.ok) return null;
|
|
17766
17582
|
return {
|
|
@@ -18284,7 +18100,8 @@ function createSessionWorktreeLifecycle({ id, projectPath, integrationBranch = n
|
|
|
18284
18100
|
if (gitWorkspace && lifecycleState.workspace) try {
|
|
18285
18101
|
await gitWorkspace.discard({
|
|
18286
18102
|
projectPath: currentProjectPath(),
|
|
18287
|
-
workspace: lifecycleState.workspace
|
|
18103
|
+
workspace: lifecycleState.workspace,
|
|
18104
|
+
cause: "session"
|
|
18288
18105
|
});
|
|
18289
18106
|
} catch {}
|
|
18290
18107
|
clearWorktreeState("none");
|
|
@@ -19586,7 +19403,6 @@ var Session = class extends EventEmitter {
|
|
|
19586
19403
|
_lastSignal;
|
|
19587
19404
|
_compaction;
|
|
19588
19405
|
_pendingPromptKind;
|
|
19589
|
-
_isWaitingForMemory;
|
|
19590
19406
|
_pendingPromptToolName;
|
|
19591
19407
|
_pendingAttentionNotes;
|
|
19592
19408
|
_titleQuiet;
|
|
@@ -19720,7 +19536,6 @@ var Session = class extends EventEmitter {
|
|
|
19720
19536
|
this._lastSignal = null;
|
|
19721
19537
|
this._compaction = createCompactionTracking();
|
|
19722
19538
|
this._pendingPromptKind = null;
|
|
19723
|
-
this._isWaitingForMemory = false;
|
|
19724
19539
|
this._pendingPromptToolName = null;
|
|
19725
19540
|
this._pendingAttentionNotes = [];
|
|
19726
19541
|
this._titleQuiet = false;
|
|
@@ -20104,16 +19919,10 @@ var Session = class extends EventEmitter {
|
|
|
20104
19919
|
});
|
|
20105
19920
|
if (this._compaction.isCompacting !== wasCompacting) this._emitSessionDetail();
|
|
20106
19921
|
}
|
|
20107
|
-
setWaitingForMemory(isWaiting) {
|
|
20108
|
-
if (isWaiting === this._isWaitingForMemory) return;
|
|
20109
|
-
this._isWaitingForMemory = isWaiting;
|
|
20110
|
-
this._emitSessionDetail();
|
|
20111
|
-
}
|
|
20112
19922
|
_emitSessionDetail() {
|
|
20113
19923
|
this.emit("prompt-kind-change", {
|
|
20114
19924
|
pendingPromptKind: this._pendingPromptKind,
|
|
20115
|
-
isCompacting: this._compaction.isCompacting
|
|
20116
|
-
isWaitingForMemory: this._isWaitingForMemory
|
|
19925
|
+
isCompacting: this._compaction.isCompacting
|
|
20117
19926
|
});
|
|
20118
19927
|
}
|
|
20119
19928
|
_finishCompaction() {
|
|
@@ -20499,13 +20308,10 @@ var Session = class extends EventEmitter {
|
|
|
20499
20308
|
resumeSessionId: this._resumeSessionId,
|
|
20500
20309
|
activeAgents: active,
|
|
20501
20310
|
awaitingBackgroundTasks: this.backgroundTracking.awaitingBackgroundTasks(),
|
|
20502
|
-
pendingWakeup: this.backgroundTracking.pendingWakeup(),
|
|
20503
20311
|
pendingPromptKind: this._pendingPromptKind,
|
|
20504
20312
|
conversationApproval: this._conversationApproval,
|
|
20505
20313
|
hasConversationBridge: this._codexBridge !== null,
|
|
20506
20314
|
isConversationViewOn: this._isConversationViewEnabled(),
|
|
20507
|
-
isCompacting: this._compaction.isCompacting,
|
|
20508
|
-
isWaitingForMemory: this._isWaitingForMemory,
|
|
20509
20315
|
hasPlan: this._planReviewPort?.hasPlan(this.id) === true,
|
|
20510
20316
|
mergeStatus: this.mergeStatus,
|
|
20511
20317
|
mergeReason: this.mergeReason,
|
|
@@ -20608,7 +20414,6 @@ var Session = class extends EventEmitter {
|
|
|
20608
20414
|
}
|
|
20609
20415
|
async _startBody({ fresh = false, resumePrompt = null } = {}) {
|
|
20610
20416
|
if (this._destroyed) return;
|
|
20611
|
-
this.setWaitingForMemory(false);
|
|
20612
20417
|
if (this.ptyProcess) {
|
|
20613
20418
|
console.warn(`[session:${this.name}] start() called while PTY exists - killing previous PTY first`);
|
|
20614
20419
|
const oldPid = this.ptyProcess.pid;
|
|
@@ -23522,7 +23327,7 @@ function createPrGh(cwd, commandRunner = run) {
|
|
|
23522
23327
|
err: "invalid commit id",
|
|
23523
23328
|
reviewId: null
|
|
23524
23329
|
};
|
|
23525
|
-
if (event
|
|
23330
|
+
if (!PostedReviewEvent.safeParse(event).success) return {
|
|
23526
23331
|
ok: false,
|
|
23527
23332
|
err: "invalid review event",
|
|
23528
23333
|
reviewId: null
|
|
@@ -24125,7 +23930,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24125
23930
|
team: teamProfile,
|
|
24126
23931
|
drafts: draftsNewestFirst(state),
|
|
24127
23932
|
inFlight: inFlightReviews(),
|
|
24128
|
-
handReview: handReviewRows(state),
|
|
24129
23933
|
queued: waitingForSlot.filter((candidate) => !state[candidate.key]?.inFlight)
|
|
24130
23934
|
}),
|
|
24131
23935
|
error: pollingError,
|
|
@@ -24160,7 +23964,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24160
23964
|
draft: null,
|
|
24161
23965
|
reviewedHead: null,
|
|
24162
23966
|
inFlight: false,
|
|
24163
|
-
skipReason: null,
|
|
24164
23967
|
reviewAttempts: 0,
|
|
24165
23968
|
updatedAt: now()
|
|
24166
23969
|
};
|
|
@@ -24205,7 +24008,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24205
24008
|
...wasRequeuedAtReviewedHead ? { requeuedHead: draft.reviewedHead } : {}
|
|
24206
24009
|
};
|
|
24207
24010
|
entry.reviewedHead = draft.reviewedHead;
|
|
24208
|
-
entry.skipReason = null;
|
|
24209
24011
|
entry.updatedAt = now();
|
|
24210
24012
|
entry.reviewedAt = entry.updatedAt;
|
|
24211
24013
|
entry.draft.reviewedAt = entry.reviewedAt;
|
|
@@ -24222,7 +24024,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24222
24024
|
status: "discarded"
|
|
24223
24025
|
} : notMineDraft(args.candidate, args.detail.headRefOid);
|
|
24224
24026
|
entry.reviewedHead = entry.draft.reviewedHead;
|
|
24225
|
-
entry.skipReason = null;
|
|
24226
24027
|
await settleNotMine(args.candidate.key, entry);
|
|
24227
24028
|
if (waitingForSlot.length > 0 && !loop.isStopped()) loop.tick();
|
|
24228
24029
|
}
|
|
@@ -24255,14 +24056,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24255
24056
|
entry.reviewedHead = isReviewedByViewer ? head : null;
|
|
24256
24057
|
isDirty = true;
|
|
24257
24058
|
}
|
|
24258
|
-
if (entry.handReview && (entry.skipReason !== "fork" || entry.reviewedHead !== head)) {
|
|
24259
|
-
delete entry.handReview;
|
|
24260
|
-
isDirty = true;
|
|
24261
|
-
}
|
|
24262
|
-
if (entry.skipReason === "fork" && entry.reviewedHead === head && entry.draft?.status !== "discarded" && !isSameQueuedReview(entry.handReview, candidate)) {
|
|
24263
|
-
entry.handReview = { ...candidate };
|
|
24264
|
-
isDirty = true;
|
|
24265
|
-
}
|
|
24266
24059
|
if (entry.draft) {
|
|
24267
24060
|
const priority = {
|
|
24268
24061
|
requestSource: candidate.requestSource,
|
|
@@ -24520,18 +24313,7 @@ function createTeamReviewPoller(deps) {
|
|
|
24520
24313
|
continue;
|
|
24521
24314
|
}
|
|
24522
24315
|
entry.updatedAt = now();
|
|
24523
|
-
delete entry.handReview;
|
|
24524
24316
|
isDirty = true;
|
|
24525
|
-
if (triage.tier === "skip") {
|
|
24526
|
-
if (resume) await discardResumable(resume);
|
|
24527
|
-
entry.reviewedHead = detail.headRefOid;
|
|
24528
|
-
entry.liveHead = detail.headRefOid;
|
|
24529
|
-
entry.skipReason = triage.reasons.join(", ") || "skipped";
|
|
24530
|
-
if (triage.reasons.includes("fork")) entry.handReview = { ...candidate };
|
|
24531
|
-
delete entry.requeuedHead;
|
|
24532
|
-
continue;
|
|
24533
|
-
}
|
|
24534
|
-
entry.skipReason = null;
|
|
24535
24317
|
const priorReview = priorReviewFor(entry, detail.headRefOid) ?? void 0;
|
|
24536
24318
|
const earlierReviewKept = earlierReviewToKeep(entry, detail.headRefOid);
|
|
24537
24319
|
delete entry.priorReview;
|
|
@@ -24866,7 +24648,7 @@ function createTeamReviewPoller(deps) {
|
|
|
24866
24648
|
};
|
|
24867
24649
|
return settleNotMine(key, entry);
|
|
24868
24650
|
}
|
|
24869
|
-
const candidate = waitingForSlot.find((queued) => queued.key === key) ?? entry?.
|
|
24651
|
+
const candidate = waitingForSlot.find((queued) => queued.key === key) ?? entry?.draft ?? void 0;
|
|
24870
24652
|
if (!candidate) return {
|
|
24871
24653
|
ok: false,
|
|
24872
24654
|
error: "That pull request is no longer in the review queue"
|
|
@@ -24889,7 +24671,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24889
24671
|
const notMineEntry = entryFor(key);
|
|
24890
24672
|
notMineEntry.draft = notMineDraft(candidate, head);
|
|
24891
24673
|
notMineEntry.reviewedHead = head;
|
|
24892
|
-
delete notMineEntry.handReview;
|
|
24893
24674
|
return settleNotMine(key, notMineEntry);
|
|
24894
24675
|
}
|
|
24895
24676
|
async function settleNotMine(key, entry) {
|
|
@@ -24921,10 +24702,6 @@ function createTeamReviewPoller(deps) {
|
|
|
24921
24702
|
key,
|
|
24922
24703
|
status: "running"
|
|
24923
24704
|
};
|
|
24924
|
-
if (entry.skipReason && entry.reviewedHead !== null && entry.reviewedHead === entry.liveHead) return {
|
|
24925
|
-
ok: false,
|
|
24926
|
-
error: `team review skips this pull request: ${entry.skipReason}`
|
|
24927
|
-
};
|
|
24928
24705
|
if (entry.draft && !isThreadPlaceholderDraft(entry.draft)) {
|
|
24929
24706
|
if (!await requeue(key, entry.draft.reviewedHead)) return {
|
|
24930
24707
|
ok: false,
|
|
@@ -25637,6 +25414,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
25637
25414
|
const rerereEnabled = opts.rerere !== false;
|
|
25638
25415
|
const glimmervoidHome = opts.glimmervoidHome ?? glimmervoidHomeDir$1();
|
|
25639
25416
|
const glimmervoidHomes = comparableGlimmervoidHomes(glimmervoidHome);
|
|
25417
|
+
const removalGuard = resolveRemovalGuard(opts.removalGuard, glimmervoidHome);
|
|
25640
25418
|
const engineQueue = createSerialQueue();
|
|
25641
25419
|
const isolatedCall = (body, args) => () => isolationContext.run({
|
|
25642
25420
|
...opts.isolation,
|
|
@@ -25712,6 +25490,54 @@ function createGitWorkspace(opts = {}) {
|
|
|
25712
25490
|
if (await directoryExists$1(ownedPath)) return;
|
|
25713
25491
|
await opts.ownedPaths?.forget(ownedPath);
|
|
25714
25492
|
}
|
|
25493
|
+
async function admitRemoval(target) {
|
|
25494
|
+
const facts = removalOwnershipFacts(target, target.path === null ? null : await opts.ownedPaths?.ownerOf(target.path) ?? null, removalGuard.protectedSessionIds());
|
|
25495
|
+
const lastTouchedMs = isRecencyProbeNeeded(facts) ? await probeLastTouchedMs(target) : null;
|
|
25496
|
+
const verdict = decideRemovalKeep({
|
|
25497
|
+
...facts,
|
|
25498
|
+
lastTouchedMs,
|
|
25499
|
+
nowMs: removalGuard.now()
|
|
25500
|
+
});
|
|
25501
|
+
if (verdict.keep) {
|
|
25502
|
+
log.warn(`[removals] kept ${describeRemovalTarget(target)} from a ${target.cause} removal because of ${verdict.reason}`);
|
|
25503
|
+
return {
|
|
25504
|
+
admitted: false,
|
|
25505
|
+
reason: verdict.reason
|
|
25506
|
+
};
|
|
25507
|
+
}
|
|
25508
|
+
const record = buildRemovalRecord(target, facts, verdict.checksPassed, removalGuard.now());
|
|
25509
|
+
if (!await appendRemovalRecord(removalGuard.journalPath, record, log)) return {
|
|
25510
|
+
admitted: false,
|
|
25511
|
+
reason: "removal-record-unwritten"
|
|
25512
|
+
};
|
|
25513
|
+
return { admitted: true };
|
|
25514
|
+
}
|
|
25515
|
+
async function probeLastTouchedMs({ projectPath, path: targetPath, branch, tipSha }) {
|
|
25516
|
+
const touchedTimesMs = [];
|
|
25517
|
+
const targetStat = targetPath === null ? "missing" : await statUnlessMissing(targetPath);
|
|
25518
|
+
if (targetStat === null) return null;
|
|
25519
|
+
if (targetPath !== null && targetStat !== "missing") {
|
|
25520
|
+
touchedTimesMs.push(targetStat.mtimeMs);
|
|
25521
|
+
const adminDir = await run(["rev-parse", "--absolute-git-dir"], targetPath);
|
|
25522
|
+
const adminStat = adminDir.ok ? await fsp.stat(adminDir.out).catch(() => null) : null;
|
|
25523
|
+
if (adminStat === null) return null;
|
|
25524
|
+
touchedTimesMs.push(adminStat.mtimeMs);
|
|
25525
|
+
}
|
|
25526
|
+
const commitRef = tipSha ?? (branch ? `refs/heads/${branch}` : null);
|
|
25527
|
+
if (commitRef !== null) {
|
|
25528
|
+
const commitTime = await run([
|
|
25529
|
+
"log",
|
|
25530
|
+
"-1",
|
|
25531
|
+
"--format=%ct",
|
|
25532
|
+
commitRef
|
|
25533
|
+
], projectPath);
|
|
25534
|
+
const commitTimeMs = commitTime.ok ? parseCommitTimeMs(commitTime.out) : null;
|
|
25535
|
+
if (commitTimeMs === null) return null;
|
|
25536
|
+
touchedTimesMs.push(commitTimeMs);
|
|
25537
|
+
}
|
|
25538
|
+
if (touchedTimesMs.length === 0) return null;
|
|
25539
|
+
return Math.max(...touchedTimesMs);
|
|
25540
|
+
}
|
|
25715
25541
|
async function recordOwnedFolder({ folderPath, owner }) {
|
|
25716
25542
|
await opts.ownedPaths?.record({
|
|
25717
25543
|
path: folderPath,
|
|
@@ -26021,11 +25847,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26021
25847
|
isGit: false,
|
|
26022
25848
|
reason: "no-base-branch"
|
|
26023
25849
|
};
|
|
26024
|
-
await
|
|
26025
|
-
"branch",
|
|
26026
|
-
"-D",
|
|
26027
|
-
branch
|
|
26028
|
-
], projectPath);
|
|
25850
|
+
await deleteStaleSessionBranch(projectPath, branch);
|
|
26029
25851
|
if (worktreeBase) try {
|
|
26030
25852
|
fs.mkdirSync(worktreeBase, { recursive: true });
|
|
26031
25853
|
} catch {}
|
|
@@ -26235,7 +26057,29 @@ function createGitWorkspace(opts = {}) {
|
|
|
26235
26057
|
} catch {}
|
|
26236
26058
|
}
|
|
26237
26059
|
}
|
|
26060
|
+
async function deleteStaleSessionBranch(projectPath, branch) {
|
|
26061
|
+
if (!(await readLocalBranchSha(projectPath, branch)).out) return;
|
|
26062
|
+
if (!(await admitRemoval({
|
|
26063
|
+
cause: "session-create",
|
|
26064
|
+
kind: "local-branch",
|
|
26065
|
+
projectPath,
|
|
26066
|
+
path: null,
|
|
26067
|
+
branch
|
|
26068
|
+
})).admitted) return;
|
|
26069
|
+
await run([
|
|
26070
|
+
"branch",
|
|
26071
|
+
"-D",
|
|
26072
|
+
branch
|
|
26073
|
+
], projectPath);
|
|
26074
|
+
}
|
|
26238
26075
|
async function tearDownWorktree(projectPath, wt, branch) {
|
|
26076
|
+
if (!(await admitRemoval({
|
|
26077
|
+
cause: "merge",
|
|
26078
|
+
kind: "worktree",
|
|
26079
|
+
projectPath,
|
|
26080
|
+
path: wt,
|
|
26081
|
+
branch: branch ?? null
|
|
26082
|
+
})).admitted) return;
|
|
26239
26083
|
removeWorktreeLinks(wt);
|
|
26240
26084
|
await removeWorktreeEntry(runInProject(projectPath), wt, ["--force"]);
|
|
26241
26085
|
await pruneWorktreeEntries(runInProject(projectPath));
|
|
@@ -26709,8 +26553,15 @@ function createGitWorkspace(opts = {}) {
|
|
|
26709
26553
|
rerereReplayed
|
|
26710
26554
|
};
|
|
26711
26555
|
}
|
|
26712
|
-
async function discardBody({ projectPath, workspace }) {
|
|
26556
|
+
async function discardBody({ projectPath, workspace, cause }) {
|
|
26713
26557
|
if (!workspace || !workspace.isGit) return;
|
|
26558
|
+
if (!(await admitRemoval({
|
|
26559
|
+
cause,
|
|
26560
|
+
kind: "worktree",
|
|
26561
|
+
projectPath,
|
|
26562
|
+
path: workspace.cwd || null,
|
|
26563
|
+
branch: workspace.branch || null
|
|
26564
|
+
})).admitted) return;
|
|
26714
26565
|
if (workspace.cwd) {
|
|
26715
26566
|
removeWorktreeLinks(workspace.cwd);
|
|
26716
26567
|
await removeWorktreeEntry(runInProject(projectPath), workspace.cwd, ["--force"]);
|
|
@@ -26884,7 +26735,15 @@ function createGitWorkspace(opts = {}) {
|
|
|
26884
26735
|
disableRepoCommands
|
|
26885
26736
|
});
|
|
26886
26737
|
}
|
|
26887
|
-
async function removeWorktreeByPathBody({ projectPath, cwd, branch, disableHooks, replaceEnv }) {
|
|
26738
|
+
async function removeWorktreeByPathBody({ projectPath, cwd, branch, cause, disableHooks, replaceEnv }) {
|
|
26739
|
+
const admission = await admitRemoval({
|
|
26740
|
+
cause,
|
|
26741
|
+
kind: cwd ? "worktree" : "local-branch",
|
|
26742
|
+
projectPath,
|
|
26743
|
+
path: cwd || null,
|
|
26744
|
+
branch: branch || null
|
|
26745
|
+
});
|
|
26746
|
+
if (!admission.admitted) return keptRemovalResult(admission.reason);
|
|
26888
26747
|
const isolation = {
|
|
26889
26748
|
disableHooks,
|
|
26890
26749
|
replaceEnv
|
|
@@ -26934,6 +26793,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26934
26793
|
if (worktreePath === null) return removeWorktreeByPathBody({
|
|
26935
26794
|
projectPath,
|
|
26936
26795
|
branch,
|
|
26796
|
+
cause: "session",
|
|
26937
26797
|
disableHooks,
|
|
26938
26798
|
replaceEnv
|
|
26939
26799
|
});
|
|
@@ -26947,6 +26807,7 @@ function createGitWorkspace(opts = {}) {
|
|
|
26947
26807
|
projectPath,
|
|
26948
26808
|
cwd: worktreePath,
|
|
26949
26809
|
branch,
|
|
26810
|
+
cause: "session",
|
|
26950
26811
|
disableHooks,
|
|
26951
26812
|
replaceEnv
|
|
26952
26813
|
});
|
|
@@ -26975,12 +26836,20 @@ function createGitWorkspace(opts = {}) {
|
|
|
26975
26836
|
}).catch(() => fsp.rmdir(linkPath)).catch(() => {});
|
|
26976
26837
|
}
|
|
26977
26838
|
}
|
|
26978
|
-
async function removeWorkspaceMemberBody({ projectPath, cwd, shareList, discardWork = false }) {
|
|
26839
|
+
async function removeWorkspaceMemberBody({ projectPath, cwd, branch, shareList, cause, discardWork = false }) {
|
|
26979
26840
|
if (!cwd) return {
|
|
26980
26841
|
ok: false,
|
|
26981
26842
|
out: "",
|
|
26982
26843
|
err: "a workspace member removal needs a path"
|
|
26983
26844
|
};
|
|
26845
|
+
const admission = await admitRemoval({
|
|
26846
|
+
cause,
|
|
26847
|
+
kind: "worktree",
|
|
26848
|
+
projectPath,
|
|
26849
|
+
path: cwd,
|
|
26850
|
+
branch: branch || null
|
|
26851
|
+
});
|
|
26852
|
+
if (!admission.admitted) return keptRemovalResult(admission.reason);
|
|
26984
26853
|
await removeSharedLinks(cwd, shareList);
|
|
26985
26854
|
const removed = await removeWorktreeEntry(runInProject(projectPath), cwd, discardWork ? ["--force", "--force"] : []);
|
|
26986
26855
|
if (discardWork && await directoryExists$1(cwd)) await fsp.rm(cwd, {
|
|
@@ -27687,7 +27556,16 @@ function createGitWorkspace(opts = {}) {
|
|
|
27687
27556
|
async function treeOid({ projectPath, sha, timeoutMs = MERGE_PROBE_TIMEOUT_MS }) {
|
|
27688
27557
|
return run(["rev-parse", `${sha ?? ""}^{tree}`], projectPath, { timeout: timeoutMs });
|
|
27689
27558
|
}
|
|
27690
|
-
async function deleteRemoteBranchBody({ projectPath, name, tipSha }) {
|
|
27559
|
+
async function deleteRemoteBranchBody({ projectPath, name, tipSha, cause }) {
|
|
27560
|
+
const admission = await admitRemoval({
|
|
27561
|
+
cause,
|
|
27562
|
+
kind: "remote-branch",
|
|
27563
|
+
projectPath,
|
|
27564
|
+
path: null,
|
|
27565
|
+
branch: name ?? null,
|
|
27566
|
+
tipSha: tipSha ?? null
|
|
27567
|
+
});
|
|
27568
|
+
if (!admission.admitted) return keptRemovalResult(admission.reason);
|
|
27691
27569
|
const branchRef = `refs/heads/${name ?? ""}`;
|
|
27692
27570
|
return run([
|
|
27693
27571
|
"push",
|
|
@@ -27756,6 +27634,8 @@ function createGitWorkspace(opts = {}) {
|
|
|
27756
27634
|
function createGitWorkspaceSync(opts = {}) {
|
|
27757
27635
|
const glimmervoidHome = opts.glimmervoidHome ?? glimmervoidHomeDir$1();
|
|
27758
27636
|
const glimmervoidHomes = comparableGlimmervoidHomes(glimmervoidHome);
|
|
27637
|
+
const removalGuard = resolveRemovalGuard(opts.removalGuard, glimmervoidHome);
|
|
27638
|
+
const log = opts.log || console;
|
|
27759
27639
|
const git = opts.git || ((args, cwd) => execFileSync("git", args, {
|
|
27760
27640
|
cwd,
|
|
27761
27641
|
encoding: "utf8",
|
|
@@ -27880,7 +27760,61 @@ function createGitWorkspaceSync(opts = {}) {
|
|
|
27880
27760
|
}
|
|
27881
27761
|
return out;
|
|
27882
27762
|
}
|
|
27883
|
-
function
|
|
27763
|
+
function admitRemovalSync(target) {
|
|
27764
|
+
const facts = removalOwnershipFacts(target, null, removalGuard.protectedSessionIds());
|
|
27765
|
+
const lastTouchedMs = isRecencyProbeNeeded(facts) ? probeLastTouchedMsSync(target) : null;
|
|
27766
|
+
const verdict = decideRemovalKeep({
|
|
27767
|
+
...facts,
|
|
27768
|
+
lastTouchedMs,
|
|
27769
|
+
nowMs: removalGuard.now()
|
|
27770
|
+
});
|
|
27771
|
+
if (verdict.keep) {
|
|
27772
|
+
log.warn(`[removals] kept ${describeRemovalTarget(target)} from a ${target.cause} removal because of ${verdict.reason}`);
|
|
27773
|
+
return {
|
|
27774
|
+
admitted: false,
|
|
27775
|
+
reason: verdict.reason
|
|
27776
|
+
};
|
|
27777
|
+
}
|
|
27778
|
+
const record = buildRemovalRecord(target, facts, verdict.checksPassed, removalGuard.now());
|
|
27779
|
+
if (!appendRemovalRecordSync(removalGuard.journalPath, record, log)) return {
|
|
27780
|
+
admitted: false,
|
|
27781
|
+
reason: "removal-record-unwritten"
|
|
27782
|
+
};
|
|
27783
|
+
return { admitted: true };
|
|
27784
|
+
}
|
|
27785
|
+
function probeLastTouchedMsSync({ projectPath, path: targetPath, branch }) {
|
|
27786
|
+
const touchedTimesMs = [];
|
|
27787
|
+
const targetStat = targetPath === null ? "missing" : statUnlessMissingSync(targetPath);
|
|
27788
|
+
if (targetStat === null) return null;
|
|
27789
|
+
if (targetPath !== null && targetStat !== "missing") {
|
|
27790
|
+
touchedTimesMs.push(targetStat.mtimeMs);
|
|
27791
|
+
const adminDir = run(["rev-parse", "--absolute-git-dir"], targetPath);
|
|
27792
|
+
const adminStat = adminDir.ok ? statUnlessMissingSync(adminDir.out) : null;
|
|
27793
|
+
if (adminStat === null || adminStat === "missing") return null;
|
|
27794
|
+
touchedTimesMs.push(adminStat.mtimeMs);
|
|
27795
|
+
}
|
|
27796
|
+
if (branch) {
|
|
27797
|
+
const commitTime = run([
|
|
27798
|
+
"log",
|
|
27799
|
+
"-1",
|
|
27800
|
+
"--format=%ct",
|
|
27801
|
+
`refs/heads/${branch}`
|
|
27802
|
+
], projectPath);
|
|
27803
|
+
const commitTimeMs = commitTime.ok ? parseCommitTimeMs(commitTime.out) : null;
|
|
27804
|
+
if (commitTimeMs === null) return null;
|
|
27805
|
+
touchedTimesMs.push(commitTimeMs);
|
|
27806
|
+
}
|
|
27807
|
+
if (touchedTimesMs.length === 0) return null;
|
|
27808
|
+
return Math.max(...touchedTimesMs);
|
|
27809
|
+
}
|
|
27810
|
+
function removeWorktreeByPath({ projectPath, cwd, branch, cause }) {
|
|
27811
|
+
if (!admitRemovalSync({
|
|
27812
|
+
cause,
|
|
27813
|
+
kind: cwd ? "worktree" : "local-branch",
|
|
27814
|
+
projectPath,
|
|
27815
|
+
path: cwd || null,
|
|
27816
|
+
branch: branch || null
|
|
27817
|
+
}).admitted) return;
|
|
27884
27818
|
if (cwd) {
|
|
27885
27819
|
removeWorktreeLinks(cwd);
|
|
27886
27820
|
if (isWorktreeHiddenFromGuardedSessions(cwd, glimmervoidHomes)) run([
|
|
@@ -27924,6 +27858,60 @@ function createGitWorkspaceSync(opts = {}) {
|
|
|
27924
27858
|
removeWorktreeByPath
|
|
27925
27859
|
};
|
|
27926
27860
|
}
|
|
27861
|
+
function resolveRemovalGuard(guard, glimmervoidHome) {
|
|
27862
|
+
return {
|
|
27863
|
+
protectedSessionIds: guard?.protectedSessionIds ?? (() => /* @__PURE__ */ new Set()),
|
|
27864
|
+
journalPath: guard?.journalPath ?? removalsJournalPath(glimmervoidHome),
|
|
27865
|
+
now: guard?.now ?? Date.now
|
|
27866
|
+
};
|
|
27867
|
+
}
|
|
27868
|
+
function removalOwnershipFacts(target, ledgerOwner, protectedSessionIds) {
|
|
27869
|
+
return {
|
|
27870
|
+
cause: target.cause,
|
|
27871
|
+
sessionId: target.branch === null ? null : sessionIdFromBranch(target.branch),
|
|
27872
|
+
ledgerOwnerSessionId: sessionIdFromOwner(ledgerOwner),
|
|
27873
|
+
protectedSessionIds
|
|
27874
|
+
};
|
|
27875
|
+
}
|
|
27876
|
+
function buildRemovalRecord(target, facts, checksPassed, nowMs) {
|
|
27877
|
+
return {
|
|
27878
|
+
ts: nowMs,
|
|
27879
|
+
cause: target.cause,
|
|
27880
|
+
kind: target.kind,
|
|
27881
|
+
path: target.path,
|
|
27882
|
+
branch: target.branch,
|
|
27883
|
+
projectPath: target.projectPath,
|
|
27884
|
+
sessionId: facts.sessionId ?? facts.ledgerOwnerSessionId,
|
|
27885
|
+
checksPassed,
|
|
27886
|
+
pid: process.pid
|
|
27887
|
+
};
|
|
27888
|
+
}
|
|
27889
|
+
function describeRemovalTarget(target) {
|
|
27890
|
+
return target.path ?? `${target.kind} ${target.branch ?? ""}`;
|
|
27891
|
+
}
|
|
27892
|
+
function keptRemovalResult(reason) {
|
|
27893
|
+
return {
|
|
27894
|
+
ok: false,
|
|
27895
|
+
out: "",
|
|
27896
|
+
err: `the removal guard kept it: ${reason}`,
|
|
27897
|
+
removalKept: reason
|
|
27898
|
+
};
|
|
27899
|
+
}
|
|
27900
|
+
function parseCommitTimeMs(commitTimeOutput) {
|
|
27901
|
+
const commitSeconds = Number.parseInt(commitTimeOutput, 10);
|
|
27902
|
+
if (!Number.isFinite(commitSeconds)) return null;
|
|
27903
|
+
return commitSeconds * 1e3;
|
|
27904
|
+
}
|
|
27905
|
+
async function statUnlessMissing(targetPath) {
|
|
27906
|
+
return fsp.stat(targetPath).then((stats) => stats, (error) => isMissingFileError(error) ? "missing" : null);
|
|
27907
|
+
}
|
|
27908
|
+
function statUnlessMissingSync(targetPath) {
|
|
27909
|
+
try {
|
|
27910
|
+
return fs.statSync(targetPath);
|
|
27911
|
+
} catch (error) {
|
|
27912
|
+
return isMissingFileError(error) ? "missing" : null;
|
|
27913
|
+
}
|
|
27914
|
+
}
|
|
27927
27915
|
function removeWorktreeLinks(wtDir) {
|
|
27928
27916
|
if (!wtDir) return;
|
|
27929
27917
|
let entries;
|
|
@@ -28062,7 +28050,8 @@ var UNMARKED_POST_WARNING = "The review was posted on GitHub, but its draft coul
|
|
|
28062
28050
|
var SETTLED_OUTCOME_BY_ACTION = {
|
|
28063
28051
|
approve: "approve",
|
|
28064
28052
|
"approve-only": "approve-only",
|
|
28065
|
-
comment: "comment"
|
|
28053
|
+
comment: "comment",
|
|
28054
|
+
"request-changes": "request-changes"
|
|
28066
28055
|
};
|
|
28067
28056
|
function teamReviewShouldStart(config) {
|
|
28068
28057
|
const settings = readTeamReviewSettings(config);
|
|
@@ -28344,11 +28333,14 @@ function createTeamReviewDispatcher({ store, repoCache, gitWorkspace, spawnSessi
|
|
|
28344
28333
|
async function removeCheckout({ projectPath, worktreePath }) {
|
|
28345
28334
|
const removal = await gitWorkspace.removeWorktreeByPath({
|
|
28346
28335
|
projectPath,
|
|
28347
|
-
cwd: worktreePath
|
|
28336
|
+
cwd: worktreePath,
|
|
28337
|
+
cause: "team-review"
|
|
28348
28338
|
}).catch((error) => ({
|
|
28349
28339
|
ok: false,
|
|
28350
|
-
err: errorMessage(error)
|
|
28340
|
+
err: errorMessage(error),
|
|
28341
|
+
removalKept: void 0
|
|
28351
28342
|
}));
|
|
28343
|
+
if (removal.removalKept) return;
|
|
28352
28344
|
if (removal.ok && !await pathExists(worktreePath)) return;
|
|
28353
28345
|
log.warn(`[${TEAM_REVIEW_LANE_ID}] worktree removal failed for ${worktreePath}, deleting it directly: ${firstLine$1(removal.err ?? "") || "the directory is still present"}`);
|
|
28354
28346
|
await deleteCheckout(gitWorkspace, worktreePath, log);
|
|
@@ -28920,9 +28912,9 @@ function createTeamReviewActions({ drafts, github, store, log = console, telemet
|
|
|
28920
28912
|
reason: "comments-already-posted",
|
|
28921
28913
|
error: "The inline comments were already posted, so the approval cannot carry them again"
|
|
28922
28914
|
});
|
|
28923
|
-
if (event
|
|
28915
|
+
if (event !== "APPROVE" && !body.trim() && comments.length === 0) return refusePost(timing, {
|
|
28924
28916
|
reason: "empty-comment",
|
|
28925
|
-
error: "A comment
|
|
28917
|
+
error: "A comment or change request needs a body or an inline comment"
|
|
28926
28918
|
});
|
|
28927
28919
|
const liveHeadRead = await readStep(timing, "head-before-post", () => store.requestPrHead(draft.repo, draft.number, "priority"));
|
|
28928
28920
|
if (liveHeadRead.isRateLimited) return refusePost(timing, {
|
|
@@ -30206,11 +30198,14 @@ function createBenchmarkWiring({ benchmarksRoot, runsRoot, isEnabled, broadcast,
|
|
|
30206
30198
|
if (projectPath) {
|
|
30207
30199
|
const removal = await gitWorkspace.removeWorktreeByPath({
|
|
30208
30200
|
projectPath,
|
|
30209
|
-
cwd: worktreePath
|
|
30201
|
+
cwd: worktreePath,
|
|
30202
|
+
cause: "benchmark"
|
|
30210
30203
|
}).catch((error) => ({
|
|
30211
30204
|
ok: false,
|
|
30212
|
-
err: errorMessage(error)
|
|
30205
|
+
err: errorMessage(error),
|
|
30206
|
+
removalKept: void 0
|
|
30213
30207
|
}));
|
|
30208
|
+
if (removal.removalKept) return;
|
|
30214
30209
|
if (!removal.ok) log.warn(`[${BENCHMARK_LANE_ID}] could not remove leftover ${worktreePath}: ${firstLine(removal.err ?? "")}`);
|
|
30215
30210
|
}
|
|
30216
30211
|
await removeDirectoryOrWarn(worktreePath);
|
|
@@ -31679,8 +31674,24 @@ function createBranchGcPoller(deps) {
|
|
|
31679
31674
|
const removed = await callGit(gitWorkspace.removeWorktreeByPath, {
|
|
31680
31675
|
projectPath,
|
|
31681
31676
|
cwd: decision.cwd,
|
|
31682
|
-
branch: decision.branch
|
|
31677
|
+
branch: decision.branch,
|
|
31678
|
+
cause: "branch-gc"
|
|
31683
31679
|
});
|
|
31680
|
+
if (removed.removalKept) {
|
|
31681
|
+
summary.worktreesKept.push({
|
|
31682
|
+
cwd: decision.cwd,
|
|
31683
|
+
branch: decision.branch,
|
|
31684
|
+
reason: removed.removalKept
|
|
31685
|
+
});
|
|
31686
|
+
trace({
|
|
31687
|
+
projectPath,
|
|
31688
|
+
name: decision.branch,
|
|
31689
|
+
cwd: decision.cwd,
|
|
31690
|
+
decision: "kept",
|
|
31691
|
+
reason: removed.removalKept
|
|
31692
|
+
});
|
|
31693
|
+
return;
|
|
31694
|
+
}
|
|
31684
31695
|
if (!removed.ok) {
|
|
31685
31696
|
noteGitError({
|
|
31686
31697
|
projectPath,
|
|
@@ -31888,8 +31899,22 @@ function createBranchGcPoller(deps) {
|
|
|
31888
31899
|
const deleted = await callGit(gitWorkspace.deleteRemoteBranch, {
|
|
31889
31900
|
projectPath,
|
|
31890
31901
|
name,
|
|
31891
|
-
tipSha
|
|
31902
|
+
tipSha,
|
|
31903
|
+
cause: "branch-gc"
|
|
31892
31904
|
});
|
|
31905
|
+
if (deleted.removalKept) {
|
|
31906
|
+
summary.kept.push({
|
|
31907
|
+
name,
|
|
31908
|
+
reason: deleted.removalKept
|
|
31909
|
+
});
|
|
31910
|
+
trace({
|
|
31911
|
+
projectPath,
|
|
31912
|
+
name,
|
|
31913
|
+
decision: "kept",
|
|
31914
|
+
reason: deleted.removalKept
|
|
31915
|
+
});
|
|
31916
|
+
continue;
|
|
31917
|
+
}
|
|
31893
31918
|
if (!deleted.ok) {
|
|
31894
31919
|
noteGitError({
|
|
31895
31920
|
projectPath,
|
|
@@ -38761,7 +38786,8 @@ function createRadarWiring({ config, investigationSessions, closeSessionDataClie
|
|
|
38761
38786
|
if (resultFile) await resultFile.cleanup();
|
|
38762
38787
|
await Promise.resolve(gitWorkspace.discard({
|
|
38763
38788
|
projectPath: repoPath,
|
|
38764
|
-
workspace
|
|
38789
|
+
workspace,
|
|
38790
|
+
cause: "radar"
|
|
38765
38791
|
})).catch((e) => console.warn(`[posthog-poller] fix worktree discard failed: ${errorMessage(e)}`));
|
|
38766
38792
|
}
|
|
38767
38793
|
}
|
|
@@ -45444,6 +45470,131 @@ function createTraceChangeBroadcast({ source, broadcast, setTimeoutFunction = se
|
|
|
45444
45470
|
return { stop };
|
|
45445
45471
|
}
|
|
45446
45472
|
//#endregion
|
|
45473
|
+
//#region server/session/shown-files-wiring.ts
|
|
45474
|
+
var SHOWN_FOLDER_NAME = "shown";
|
|
45475
|
+
var INDEX_FILENAME = "index.json";
|
|
45476
|
+
var INCOMING_PREFIX = ".incoming-";
|
|
45477
|
+
var ShownIndex = z.array(ShownFile);
|
|
45478
|
+
async function readFirstChunk(filePath) {
|
|
45479
|
+
const handle = await fs$1.open(filePath, "r");
|
|
45480
|
+
try {
|
|
45481
|
+
const buffer = Buffer.alloc(SHOWN_SNIFF_BYTES);
|
|
45482
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.byteLength, 0);
|
|
45483
|
+
return buffer.subarray(0, bytesRead);
|
|
45484
|
+
} finally {
|
|
45485
|
+
await handle.close().catch(() => {});
|
|
45486
|
+
}
|
|
45487
|
+
}
|
|
45488
|
+
async function readSessionIndex(indexPath) {
|
|
45489
|
+
const text = await fs$1.readFile(indexPath, "utf8").catch(() => null);
|
|
45490
|
+
if (text === null) return [];
|
|
45491
|
+
const parsed = ShownIndex.safeParse(parseJsonOrNull(text));
|
|
45492
|
+
return parsed.success ? parsed.data : [];
|
|
45493
|
+
}
|
|
45494
|
+
async function writeSessionIndex(indexPath, files) {
|
|
45495
|
+
const pendingPath = `${indexPath}.${crypto.randomBytes(4).toString("hex")}.tmp`;
|
|
45496
|
+
await fs$1.writeFile(pendingPath, JSON.stringify(files), { mode: 384 });
|
|
45497
|
+
await fs$1.rename(pendingPath, indexPath);
|
|
45498
|
+
}
|
|
45499
|
+
function createShownFilesWiring({ shownRoot, broadcastControl, now = Date.now }) {
|
|
45500
|
+
const filesBySessionId = /* @__PURE__ */ new Map();
|
|
45501
|
+
const indexQueue = createKeyedSerialQueue();
|
|
45502
|
+
function sessionDir(sessionId) {
|
|
45503
|
+
return path.join(shownRoot, sessionId);
|
|
45504
|
+
}
|
|
45505
|
+
async function loadIndexes() {
|
|
45506
|
+
const sessionIds = await fs$1.readdir(shownRoot).catch(() => []);
|
|
45507
|
+
for (const sessionId of sessionIds) {
|
|
45508
|
+
if (!isSafePathSegment(sessionId)) continue;
|
|
45509
|
+
const files = await readSessionIndex(path.join(sessionDir(sessionId), INDEX_FILENAME));
|
|
45510
|
+
if (files.length > 0 && !filesBySessionId.has(sessionId)) filesBySessionId.set(sessionId, files);
|
|
45511
|
+
}
|
|
45512
|
+
}
|
|
45513
|
+
async function reserveIncomingPath(sessionId) {
|
|
45514
|
+
const dir = sessionDir(sessionId);
|
|
45515
|
+
await fs$1.mkdir(dir, {
|
|
45516
|
+
recursive: true,
|
|
45517
|
+
mode: 448
|
|
45518
|
+
});
|
|
45519
|
+
return path.join(dir, `${INCOMING_PREFIX}${crypto.randomBytes(8).toString("hex")}`);
|
|
45520
|
+
}
|
|
45521
|
+
async function recordAdmitted(sessionId, file) {
|
|
45522
|
+
const dir = sessionDir(sessionId);
|
|
45523
|
+
const withNewest = [...filesBySessionId.get(sessionId) ?? await readSessionIndex(path.join(dir, INDEX_FILENAME)), file];
|
|
45524
|
+
const droppedIds = planUploadRetention(withNewest.map((shown) => shown.id), {
|
|
45525
|
+
keep: 50,
|
|
45526
|
+
justWritten: file.id
|
|
45527
|
+
});
|
|
45528
|
+
const kept = filesAfterRetention(withNewest, droppedIds);
|
|
45529
|
+
await writeSessionIndex(path.join(dir, INDEX_FILENAME), kept);
|
|
45530
|
+
filesBySessionId.set(sessionId, kept);
|
|
45531
|
+
await Promise.all(droppedIds.map((droppedId) => fs$1.unlink(path.join(dir, droppedId)).catch(() => {})));
|
|
45532
|
+
}
|
|
45533
|
+
async function admit(session, arrival) {
|
|
45534
|
+
const firstChunk = await readFirstChunk(arrival.incomingPath);
|
|
45535
|
+
const verdict = decideShownKind(firstChunk, arrival.name);
|
|
45536
|
+
if (!verdict.ok) {
|
|
45537
|
+
await fs$1.unlink(arrival.incomingPath).catch(() => {});
|
|
45538
|
+
return {
|
|
45539
|
+
ok: false,
|
|
45540
|
+
status: 415,
|
|
45541
|
+
error: verdict.error
|
|
45542
|
+
};
|
|
45543
|
+
}
|
|
45544
|
+
const shownAt = now();
|
|
45545
|
+
const fileId = buildUploadFilename({
|
|
45546
|
+
now: shownAt,
|
|
45547
|
+
randomSuffix: crypto.randomBytes(4).toString("hex"),
|
|
45548
|
+
extension: verdict.extension
|
|
45549
|
+
});
|
|
45550
|
+
await fs$1.rename(arrival.incomingPath, path.join(sessionDir(session.id), fileId));
|
|
45551
|
+
const file = {
|
|
45552
|
+
id: fileId,
|
|
45553
|
+
name: arrival.name,
|
|
45554
|
+
caption: arrival.caption,
|
|
45555
|
+
kind: verdict.kind,
|
|
45556
|
+
mime: verdict.mime,
|
|
45557
|
+
sizeBytes: arrival.sizeBytes,
|
|
45558
|
+
pageTitle: verdict.kind === "page" ? readPageTitle(firstChunk) : null,
|
|
45559
|
+
shownAt
|
|
45560
|
+
};
|
|
45561
|
+
await indexQueue.run(session.id, () => recordAdmitted(session.id, file));
|
|
45562
|
+
broadcastControl({
|
|
45563
|
+
type: "session-shown",
|
|
45564
|
+
id: session.id,
|
|
45565
|
+
files: [file]
|
|
45566
|
+
});
|
|
45567
|
+
return {
|
|
45568
|
+
ok: true,
|
|
45569
|
+
file
|
|
45570
|
+
};
|
|
45571
|
+
}
|
|
45572
|
+
function findServable(sessionId, fileId) {
|
|
45573
|
+
if (!isSafePathSegment(sessionId) || !isSafePathSegment(fileId)) return null;
|
|
45574
|
+
const file = filesBySessionId.get(sessionId)?.find((shown) => shown.id === fileId);
|
|
45575
|
+
const mime = shownMimeForFilename(fileId);
|
|
45576
|
+
if (!file || !mime) return null;
|
|
45577
|
+
return {
|
|
45578
|
+
path: path.join(sessionDir(sessionId), fileId),
|
|
45579
|
+
mime,
|
|
45580
|
+
sizeBytes: file.sizeBytes
|
|
45581
|
+
};
|
|
45582
|
+
}
|
|
45583
|
+
function snapshotMessage() {
|
|
45584
|
+
return {
|
|
45585
|
+
type: "session-shown-snapshot",
|
|
45586
|
+
sessions: Object.fromEntries(filesBySessionId)
|
|
45587
|
+
};
|
|
45588
|
+
}
|
|
45589
|
+
return {
|
|
45590
|
+
reserveIncomingPath,
|
|
45591
|
+
admit,
|
|
45592
|
+
findServable,
|
|
45593
|
+
snapshotMessage,
|
|
45594
|
+
loaded: loadIndexes()
|
|
45595
|
+
};
|
|
45596
|
+
}
|
|
45597
|
+
//#endregion
|
|
45447
45598
|
//#region server/uploads-wiring.ts
|
|
45448
45599
|
var UPLOAD_RETAIN_DAYS = 7;
|
|
45449
45600
|
var PRUNE_INTERVAL_MS = 864e5;
|
|
@@ -45452,8 +45603,8 @@ function createUploadsWiring({ configPath, liveSessionIds, setIntervalFn = DEFAU
|
|
|
45452
45603
|
let timer = null;
|
|
45453
45604
|
async function prune() {
|
|
45454
45605
|
const retainedSessionIds = liveSessionIds();
|
|
45455
|
-
await pruneAgedFiles({
|
|
45456
|
-
directory
|
|
45606
|
+
for (const directory of [uploadsRoot, configSiblingPath(configPath, SHOWN_FOLDER_NAME)]) await pruneAgedFiles({
|
|
45607
|
+
directory,
|
|
45457
45608
|
suffixes: [""],
|
|
45458
45609
|
retainDays: UPLOAD_RETAIN_DAYS,
|
|
45459
45610
|
entryMode: "directory",
|
|
@@ -46970,14 +47121,21 @@ function createBackendLanes(dependencies) {
|
|
|
46970
47121
|
});
|
|
46971
47122
|
const spawnGate = createSpawnGate({ memoryAdmission });
|
|
46972
47123
|
const ownedPaths = createOwnedPathsLedger({
|
|
46973
|
-
filePath:
|
|
47124
|
+
filePath: configSiblingPath(configStore.configPath, "owned-paths.json"),
|
|
46974
47125
|
log: logger
|
|
46975
47126
|
});
|
|
47127
|
+
const protectedSessionIds = () => new Set([...config.projects.map((project) => project.id), ...allLiveSessions().map((session) => session.id)].flatMap((sessionId) => [sessionId, sanitizeWorktreeName(sessionId)]));
|
|
46976
47128
|
const gitWorkspace = createGitWorkspace({
|
|
46977
47129
|
rerere: config.worktreeRerere !== false,
|
|
46978
|
-
ownedPaths
|
|
47130
|
+
ownedPaths,
|
|
47131
|
+
removalGuard: { protectedSessionIds },
|
|
47132
|
+
log: logger
|
|
47133
|
+
});
|
|
47134
|
+
const gitWorkspaceSync = createGitWorkspaceSync({
|
|
47135
|
+
ownedPaths,
|
|
47136
|
+
removalGuard: { protectedSessionIds },
|
|
47137
|
+
log: logger
|
|
46979
47138
|
});
|
|
46980
|
-
const gitWorkspaceSync = createGitWorkspaceSync({ ownedPaths });
|
|
46981
47139
|
const laneLedger = createLaneLedger({
|
|
46982
47140
|
ledgerPath: configSiblingPath(configStore.configPath, "usage-lanes.json"),
|
|
46983
47141
|
retainDays: resolveUsageConfig(config.usage).warehouseRetainDays,
|
|
@@ -49987,6 +50145,7 @@ function summarizeCustomAgents(declarations) {
|
|
|
49987
50145
|
function buildSettingsPayload({ configStore, rtkInstallStatus = null, telegramDelivery = null, coderEnvironments = null, agentLogins = null, resolveRtk = getRtkPath }) {
|
|
49988
50146
|
return {
|
|
49989
50147
|
...configStore.getSettings(),
|
|
50148
|
+
isExperimentalBuild: configStore.isExperimentalBuild === true,
|
|
49990
50149
|
rtkAvailable: !!resolveRtk(),
|
|
49991
50150
|
saneYoloAvailable: !!resolvePackageBin("cc-safety-net", "cc-safety-net"),
|
|
49992
50151
|
rtkInstall: rtkInstallStatus || { status: "idle" },
|
|
@@ -53822,6 +53981,7 @@ function createBackendControl(dependencies) {
|
|
|
53822
53981
|
sendLaneSnapshotOnConnect("visions", laneAssembly.currentVisions, false);
|
|
53823
53982
|
sendLaneSnapshotOnConnect("ingest", laneAssembly.currentIngest, true);
|
|
53824
53983
|
sendLaneSnapshotOnConnect("session-docs", () => dependencies.sessionDocs, false);
|
|
53984
|
+
sendLaneSnapshotOnConnect("session-shown", () => dependencies.shownFiles, false);
|
|
53825
53985
|
sendLaneSnapshotOnConnect("coherence-feed", () => dependencies.coherenceFeed, false);
|
|
53826
53986
|
return { removeExitedSession: controlHandlers.removeExitedSession };
|
|
53827
53987
|
}
|
|
@@ -54161,6 +54321,7 @@ function markInterrupted(journal, { reason, now }) {
|
|
|
54161
54321
|
//#region server/update-check.ts
|
|
54162
54322
|
var NPM_REGISTRY_PACKAGE_URL = "https://registry.npmjs.org/glimmervoid";
|
|
54163
54323
|
var NPM_REGISTRY_LATEST_URL = `${NPM_REGISTRY_PACKAGE_URL}/latest`;
|
|
54324
|
+
var NPM_REGISTRY_EXPERIMENTAL_URL = `${NPM_REGISTRY_PACKAGE_URL}/next`;
|
|
54164
54325
|
var REGISTRY_REQUEST_HEADERS = {
|
|
54165
54326
|
Accept: "application/json",
|
|
54166
54327
|
"User-Agent": "glimmervoid-update-check"
|
|
@@ -54168,10 +54329,6 @@ var REGISTRY_REQUEST_HEADERS = {
|
|
|
54168
54329
|
var DEFAULT_TIMEOUT_MS = 8e3;
|
|
54169
54330
|
var GIT_HEAD_TIMEOUT_MS = 3e3;
|
|
54170
54331
|
var RELEASE_ANCESTRY_TIMEOUT_MS = 3e3;
|
|
54171
|
-
var MAIN_CHANNEL_BUDGET_MS = DEFAULT_TIMEOUT_MS;
|
|
54172
|
-
var MAIN_FETCH_TIMEOUT_MS = Math.floor(MAIN_CHANNEL_BUDGET_MS / 2);
|
|
54173
|
-
var MAIN_REMOTE_TIP_TIMEOUT_MS = Math.floor(MAIN_CHANNEL_BUDGET_MS / 4);
|
|
54174
|
-
var MAIN_BEHIND_COUNT_TIMEOUT_MS = Math.floor(MAIN_CHANNEL_BUDGET_MS / 4);
|
|
54175
54332
|
var STATE_FILE_NAME = "update-check.json";
|
|
54176
54333
|
var STATE_TTL_MS = 216e5;
|
|
54177
54334
|
var RELEASE_SOURCE = "npm-registry";
|
|
@@ -54299,143 +54456,29 @@ async function resolveInstalledDeprecation({ currentVersion, fetchFn, signal })
|
|
|
54299
54456
|
const versionUrl = `${NPM_REGISTRY_PACKAGE_URL}/${encodeURIComponent(currentVersion)}`;
|
|
54300
54457
|
return parseRegistryDeprecation(await fetchRegistryDocument(versionUrl, fetchFn, signal));
|
|
54301
54458
|
}
|
|
54302
|
-
async function resolveLatestRegistryVersion({ currentVersion, fetchFn, signal }) {
|
|
54303
|
-
const [latestDocument, deprecation] = await Promise.all([
|
|
54304
|
-
|
|
54305
|
-
fetchFn,
|
|
54306
|
-
|
|
54307
|
-
|
|
54308
|
-
|
|
54459
|
+
async function resolveLatestRegistryVersion({ currentVersion, updateChannel, fetchFn, signal }) {
|
|
54460
|
+
const [latestDocument, experimentalDocument, deprecation] = await Promise.all([
|
|
54461
|
+
fetchRegistryDocument(NPM_REGISTRY_LATEST_URL, fetchFn, signal),
|
|
54462
|
+
updateChannel === "experimental" ? fetchRegistryDocument(NPM_REGISTRY_EXPERIMENTAL_URL, fetchFn, signal) : null,
|
|
54463
|
+
resolveInstalledDeprecation({
|
|
54464
|
+
currentVersion,
|
|
54465
|
+
fetchFn,
|
|
54466
|
+
signal
|
|
54467
|
+
})
|
|
54468
|
+
]);
|
|
54469
|
+
const published = pickNewestPublished(parseRegistryLatest(latestDocument), parseRegistryLatest(experimentalDocument));
|
|
54309
54470
|
if (!published) return {
|
|
54310
54471
|
version: null,
|
|
54311
54472
|
sha: null,
|
|
54312
|
-
behindCount: null,
|
|
54313
54473
|
reason: "release-check-failed",
|
|
54314
54474
|
deprecation
|
|
54315
54475
|
};
|
|
54316
54476
|
return {
|
|
54317
54477
|
...published,
|
|
54318
|
-
behindCount: null,
|
|
54319
54478
|
reason: null,
|
|
54320
54479
|
deprecation
|
|
54321
54480
|
};
|
|
54322
54481
|
}
|
|
54323
|
-
function upstreamBranch(upstream) {
|
|
54324
|
-
const slashIndex = upstream.indexOf("/");
|
|
54325
|
-
if (slashIndex < 0 || slashIndex === upstream.length - 1) return null;
|
|
54326
|
-
return upstream.slice(slashIndex + 1);
|
|
54327
|
-
}
|
|
54328
|
-
async function fetchMainRemote(packageRoot, remote, runCommand, fetchOrigin, signal) {
|
|
54329
|
-
if (remote === "origin" && fetchOrigin) return fetchOrigin({
|
|
54330
|
-
projectPath: packageRoot,
|
|
54331
|
-
timeoutMs: MAIN_FETCH_TIMEOUT_MS
|
|
54332
|
-
});
|
|
54333
|
-
return runGitProbe(runCommand, [
|
|
54334
|
-
"fetch",
|
|
54335
|
-
"--prune",
|
|
54336
|
-
remote
|
|
54337
|
-
], {
|
|
54338
|
-
cwd: packageRoot,
|
|
54339
|
-
timeout: MAIN_FETCH_TIMEOUT_MS,
|
|
54340
|
-
signal
|
|
54341
|
-
});
|
|
54342
|
-
}
|
|
54343
|
-
async function resolveLatestMain(installed, packageRoot, runCommand, fetchOrigin, signal) {
|
|
54344
|
-
if (installed.flavor !== "clone") return {
|
|
54345
|
-
version: null,
|
|
54346
|
-
sha: null,
|
|
54347
|
-
behindCount: null,
|
|
54348
|
-
reason: "main-channel-requires-clone",
|
|
54349
|
-
deprecation: null
|
|
54350
|
-
};
|
|
54351
|
-
if (!installed.installedBranch) return {
|
|
54352
|
-
version: null,
|
|
54353
|
-
sha: null,
|
|
54354
|
-
behindCount: null,
|
|
54355
|
-
reason: "no-branch",
|
|
54356
|
-
deprecation: null
|
|
54357
|
-
};
|
|
54358
|
-
if (!installed.upstream) return {
|
|
54359
|
-
version: null,
|
|
54360
|
-
sha: null,
|
|
54361
|
-
behindCount: null,
|
|
54362
|
-
reason: "no-upstream",
|
|
54363
|
-
deprecation: null
|
|
54364
|
-
};
|
|
54365
|
-
const remote = parseRemoteFromUpstream(installed.upstream);
|
|
54366
|
-
const branch = upstreamBranch(installed.upstream);
|
|
54367
|
-
if (!branch) return {
|
|
54368
|
-
version: null,
|
|
54369
|
-
sha: null,
|
|
54370
|
-
behindCount: null,
|
|
54371
|
-
reason: "no-upstream",
|
|
54372
|
-
deprecation: null
|
|
54373
|
-
};
|
|
54374
|
-
if (!(await fetchMainRemote(packageRoot, remote, runCommand, fetchOrigin, signal)).ok) return {
|
|
54375
|
-
version: null,
|
|
54376
|
-
sha: null,
|
|
54377
|
-
behindCount: null,
|
|
54378
|
-
reason: "fetch-failed",
|
|
54379
|
-
deprecation: null
|
|
54380
|
-
};
|
|
54381
|
-
if (signal.aborted) return {
|
|
54382
|
-
version: null,
|
|
54383
|
-
sha: null,
|
|
54384
|
-
behindCount: null,
|
|
54385
|
-
reason: "remote-tip-unavailable",
|
|
54386
|
-
deprecation: null
|
|
54387
|
-
};
|
|
54388
|
-
const remoteTip = await runGitProbe(runCommand, [
|
|
54389
|
-
"ls-remote",
|
|
54390
|
-
remote,
|
|
54391
|
-
`refs/heads/${branch}`
|
|
54392
|
-
], {
|
|
54393
|
-
cwd: packageRoot,
|
|
54394
|
-
timeout: MAIN_REMOTE_TIP_TIMEOUT_MS,
|
|
54395
|
-
signal
|
|
54396
|
-
});
|
|
54397
|
-
const targetSha = normalizeSha(remoteTip.out.split(/\s+/)[0]);
|
|
54398
|
-
if (!targetSha) return {
|
|
54399
|
-
version: null,
|
|
54400
|
-
sha: null,
|
|
54401
|
-
behindCount: null,
|
|
54402
|
-
reason: "remote-tip-unavailable",
|
|
54403
|
-
deprecation: null
|
|
54404
|
-
};
|
|
54405
|
-
if (signal.aborted) return {
|
|
54406
|
-
version: null,
|
|
54407
|
-
sha: targetSha,
|
|
54408
|
-
behindCount: null,
|
|
54409
|
-
reason: "branch-count-unavailable",
|
|
54410
|
-
deprecation: null
|
|
54411
|
-
};
|
|
54412
|
-
const countsProbe = await runGitProbe(runCommand, [
|
|
54413
|
-
"rev-list",
|
|
54414
|
-
"--left-right",
|
|
54415
|
-
"--count",
|
|
54416
|
-
`HEAD...${targetSha}`
|
|
54417
|
-
], {
|
|
54418
|
-
cwd: packageRoot,
|
|
54419
|
-
timeout: MAIN_BEHIND_COUNT_TIMEOUT_MS,
|
|
54420
|
-
signal
|
|
54421
|
-
});
|
|
54422
|
-
const counts = countsProbe.ok ? parseLeftRightCount(countsProbe.out) : null;
|
|
54423
|
-
if (!counts) return {
|
|
54424
|
-
version: null,
|
|
54425
|
-
sha: targetSha,
|
|
54426
|
-
behindCount: null,
|
|
54427
|
-
reason: "branch-count-unavailable",
|
|
54428
|
-
deprecation: null
|
|
54429
|
-
};
|
|
54430
|
-
const reason = counts.behind > 0 && counts.ahead > 0 ? "branch-diverged" : null;
|
|
54431
|
-
return {
|
|
54432
|
-
version: null,
|
|
54433
|
-
sha: targetSha,
|
|
54434
|
-
behindCount: counts.ahead,
|
|
54435
|
-
reason,
|
|
54436
|
-
deprecation: null
|
|
54437
|
-
};
|
|
54438
|
-
}
|
|
54439
54482
|
function readCheckState(statePath) {
|
|
54440
54483
|
return readRecord(readJsonFile(statePath));
|
|
54441
54484
|
}
|
|
@@ -54447,7 +54490,7 @@ function writeCheckState(statePath, state) {
|
|
|
54447
54490
|
async function finish(installed, latestTarget, currentVersion, channel, platform, lastCheckAt, packageRoot, runCommand, signal) {
|
|
54448
54491
|
let isLatestReleaseAncestorOfHead = false;
|
|
54449
54492
|
const latestSha = normalizeSha(latestTarget.sha);
|
|
54450
|
-
if (
|
|
54493
|
+
if (installed.flavor === "clone" && installed.installedSha && latestSha) isLatestReleaseAncestorOfHead = (await runGitProbe(runCommand, [
|
|
54451
54494
|
"merge-base",
|
|
54452
54495
|
"--is-ancestor",
|
|
54453
54496
|
latestSha,
|
|
@@ -54466,7 +54509,6 @@ async function finish(installed, latestTarget, currentVersion, channel, platform
|
|
|
54466
54509
|
flavor: installed.flavor,
|
|
54467
54510
|
platform,
|
|
54468
54511
|
channel,
|
|
54469
|
-
behindCount: latestTarget.behindCount,
|
|
54470
54512
|
reason: latestTarget.reason,
|
|
54471
54513
|
isLatestReleaseAncestorOfHead,
|
|
54472
54514
|
deprecation: latestTarget.deprecation
|
|
@@ -54478,16 +54520,14 @@ async function finish(installed, latestTarget, currentVersion, channel, platform
|
|
|
54478
54520
|
};
|
|
54479
54521
|
}
|
|
54480
54522
|
function targetFromCache(cached, currentVersion) {
|
|
54481
|
-
const behindCount = Number.isInteger(cached.behindCount) && Number(cached.behindCount) >= 0 ? Number(cached.behindCount) : null;
|
|
54482
54523
|
return {
|
|
54483
54524
|
version: typeof cached.latestVersion === "string" ? cached.latestVersion : null,
|
|
54484
54525
|
sha: typeof cached.latestSha === "string" ? cached.latestSha : null,
|
|
54485
|
-
behindCount,
|
|
54486
54526
|
reason: typeof cached.reason === "string" ? cached.reason : null,
|
|
54487
54527
|
deprecation: cached.installedVersion === currentVersion && typeof cached.deprecation === "string" ? cached.deprecation : null
|
|
54488
54528
|
};
|
|
54489
54529
|
}
|
|
54490
|
-
async function checkForUpdate({ currentVersion, updateChannel = "release", fetchFn = fetch, timeoutMs = DEFAULT_TIMEOUT_MS, abortController = new AbortController(), packageRoot: packageRoot$1 = packageRoot, runCommand = execFileAsync,
|
|
54530
|
+
async function checkForUpdate({ currentVersion, updateChannel = "release", fetchFn = fetch, timeoutMs = DEFAULT_TIMEOUT_MS, abortController = new AbortController(), packageRoot: packageRoot$1 = packageRoot, runCommand = execFileAsync, statePath = defaultStatePath(), ttlMs = STATE_TTL_MS, now = Date.now(), platform = process.platform } = {}) {
|
|
54491
54531
|
const timer = setTimeout(() => abortController.abort(), timeoutMs);
|
|
54492
54532
|
const signal = abortController.signal;
|
|
54493
54533
|
let installed = {
|
|
@@ -54505,12 +54545,12 @@ async function checkForUpdate({ currentVersion, updateChannel = "release", fetch
|
|
|
54505
54545
|
});
|
|
54506
54546
|
const cached = readCheckState(statePath);
|
|
54507
54547
|
const cacheOutrunByInstall = normalizeSha(cached?.latestSha) !== null && normalizeSha(cached?.latestSha) === installed.installedSha;
|
|
54508
|
-
const
|
|
54509
|
-
const isCacheFromSameSource = expectedReleaseSource === null || cached?.releaseSource === expectedReleaseSource;
|
|
54548
|
+
const isCacheFromSameSource = cached?.releaseSource === RELEASE_SOURCE;
|
|
54510
54549
|
if (cached?.channel === updateChannel && isCacheFromSameSource && !cacheOutrunByInstall && isCheckFresh(cached.lastCheckAt, now, ttlMs)) return finish(installed, targetFromCache(cached, currentVersion), currentVersion, updateChannel, platform, now, packageRoot$1, runCommand, signal);
|
|
54511
54550
|
signal.throwIfAborted();
|
|
54512
|
-
const latestTarget =
|
|
54551
|
+
const latestTarget = await resolveLatestRegistryVersion({
|
|
54513
54552
|
currentVersion,
|
|
54553
|
+
updateChannel,
|
|
54514
54554
|
fetchFn,
|
|
54515
54555
|
signal
|
|
54516
54556
|
});
|
|
@@ -54519,8 +54559,7 @@ async function checkForUpdate({ currentVersion, updateChannel = "release", fetch
|
|
|
54519
54559
|
channel: updateChannel,
|
|
54520
54560
|
latestVersion: latestTarget.version,
|
|
54521
54561
|
latestSha: normalizeSha(latestTarget.sha),
|
|
54522
|
-
releaseSource:
|
|
54523
|
-
behindCount: latestTarget.behindCount,
|
|
54562
|
+
releaseSource: RELEASE_SOURCE,
|
|
54524
54563
|
reason: latestTarget.reason,
|
|
54525
54564
|
installedVersion: currentVersion ?? null,
|
|
54526
54565
|
deprecation: latestTarget.deprecation
|
|
@@ -54530,7 +54569,6 @@ async function checkForUpdate({ currentVersion, updateChannel = "release", fetch
|
|
|
54530
54569
|
return finish(installed, {
|
|
54531
54570
|
version: null,
|
|
54532
54571
|
sha: null,
|
|
54533
|
-
behindCount: null,
|
|
54534
54572
|
reason: "update-check-failed",
|
|
54535
54573
|
deprecation: null
|
|
54536
54574
|
}, currentVersion, updateChannel, platform, now, packageRoot$1, runCommand, signal);
|
|
@@ -54594,15 +54632,7 @@ function createBackendUpdateCheck(dependencies) {
|
|
|
54594
54632
|
reason: decision.reason,
|
|
54595
54633
|
message: decision.message
|
|
54596
54634
|
};
|
|
54597
|
-
|
|
54598
|
-
canFastForward: recorded.reason !== "branch-diverged",
|
|
54599
|
-
isTargetAncestorOfHead: false
|
|
54600
|
-
});
|
|
54601
|
-
if (fastForward.ok) return null;
|
|
54602
|
-
return {
|
|
54603
|
-
reason: fastForward.reason,
|
|
54604
|
-
message: fastForward.message
|
|
54605
|
-
};
|
|
54635
|
+
return null;
|
|
54606
54636
|
}
|
|
54607
54637
|
function projectStatus(recorded) {
|
|
54608
54638
|
const journal = dependencies.getUpdateJournal?.();
|
|
@@ -54699,7 +54729,6 @@ function createBackendUpdateCheck(dependencies) {
|
|
|
54699
54729
|
currentVersion: dependencies.currentVersion,
|
|
54700
54730
|
updateChannel: channel,
|
|
54701
54731
|
abortController: updateAbort,
|
|
54702
|
-
...dependencies.fetchOrigin ? { fetchOrigin: dependencies.fetchOrigin } : {},
|
|
54703
54732
|
...ttlMs === void 0 ? {} : { ttlMs }
|
|
54704
54733
|
}).catch(() => null).then((updateResult) => {
|
|
54705
54734
|
if (stopped || channel !== normalizeUpdateChannel(dependencies.config.updateChannel)) return projectStatus({
|
|
@@ -56145,6 +56174,7 @@ function createUpdateApplyLane(dependencies) {
|
|
|
56145
56174
|
stagedDescriptor = null;
|
|
56146
56175
|
await dependencies.gitWorkspace.removeWorktreeByPath({
|
|
56147
56176
|
projectPath: dependencies.packageRoot,
|
|
56177
|
+
cause: "update-apply",
|
|
56148
56178
|
cwd: stagingPath
|
|
56149
56179
|
});
|
|
56150
56180
|
await publish(failRun(journal, {
|
|
@@ -56327,6 +56357,7 @@ function createUpdateApplyLane(dependencies) {
|
|
|
56327
56357
|
}
|
|
56328
56358
|
if ((await dependencies.gitWorkspace.removeWorktreeByPath({
|
|
56329
56359
|
projectPath: dependencies.packageRoot,
|
|
56360
|
+
cause: "update-apply",
|
|
56330
56361
|
cwd: stagingPath,
|
|
56331
56362
|
admissionTimeoutMs: HANDOFF_QUEUE_ADMISSION_TIMEOUT_MS
|
|
56332
56363
|
})).admissionRefused) refusedCleanupSteps.push("staging worktree removal");
|
|
@@ -56389,6 +56420,7 @@ function createUpdateApplyLane(dependencies) {
|
|
|
56389
56420
|
}
|
|
56390
56421
|
await dependencies.gitWorkspace.removeWorktreeByPath({
|
|
56391
56422
|
projectPath: dependencies.packageRoot,
|
|
56423
|
+
cause: "update-apply",
|
|
56392
56424
|
cwd: descriptor.stagingPath,
|
|
56393
56425
|
admissionTimeoutMs: HANDOFF_QUEUE_ADMISSION_TIMEOUT_MS
|
|
56394
56426
|
});
|
|
@@ -56404,6 +56436,7 @@ function createUpdateApplyLane(dependencies) {
|
|
|
56404
56436
|
if (!descriptor || descriptor.state !== "staged" || inFlight) {
|
|
56405
56437
|
await dependencies.gitWorkspace.removeWorktreeByPath({
|
|
56406
56438
|
projectPath: dependencies.packageRoot,
|
|
56439
|
+
cause: "update-apply",
|
|
56407
56440
|
cwd: stagingPath,
|
|
56408
56441
|
admissionTimeoutMs: HANDOFF_QUEUE_ADMISSION_TIMEOUT_MS
|
|
56409
56442
|
});
|
|
@@ -56477,6 +56510,7 @@ function createUpdateApplyLane(dependencies) {
|
|
|
56477
56510
|
}), false);
|
|
56478
56511
|
if (wasRunInFlight || await isStagingCheckoutPresent()) await dependencies.gitWorkspace.removeWorktreeByPath({
|
|
56479
56512
|
projectPath: dependencies.packageRoot,
|
|
56513
|
+
cause: "update-apply",
|
|
56480
56514
|
cwd: stagingPath
|
|
56481
56515
|
});
|
|
56482
56516
|
await fsPromises.rm(path.join(updatePath, QUARANTINED_DIST_NAME), {
|
|
@@ -57561,6 +57595,7 @@ function createCoherenceSessionFeed({ broadcastControl, debounceMs = 1500, now =
|
|
|
57561
57595
|
//#region server/backend/backend.ts
|
|
57562
57596
|
var AGENT_INGRESS_HOST = "127.0.0.1";
|
|
57563
57597
|
function createBackend(httpServer, options = {}) {
|
|
57598
|
+
if (isOperatorHomeUnderTest(os.homedir(), process.env)) throw new Error("createBackend refused to boot on the operator's Glimmervoid home under node:test; run tests through npm test or npm run test:files, which isolate GLIMMERVOID_HOME");
|
|
57564
57599
|
const { staticDir = "auto", settingsDefaults } = options;
|
|
57565
57600
|
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
57566
57601
|
const configStore = createConfigStore({
|
|
@@ -57610,6 +57645,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
57610
57645
|
let broadcastControl = null;
|
|
57611
57646
|
let removeExitedSession = null;
|
|
57612
57647
|
let agentApi = null;
|
|
57648
|
+
let shownFiles = null;
|
|
57613
57649
|
let gitWorkspace = null;
|
|
57614
57650
|
const guardrailsRefusal = checkGuardrailsDependencies({ log: console });
|
|
57615
57651
|
warnWhenGitIsTooOld({ log: console });
|
|
@@ -57638,6 +57674,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
57638
57674
|
latestPlanTitle: laneAssembly.planReview?.latestPlanTitle(glimmervoidId) ?? null
|
|
57639
57675
|
}),
|
|
57640
57676
|
getAgentApi: () => agentApi,
|
|
57677
|
+
getShownFiles: () => shownFiles,
|
|
57641
57678
|
sshSigner,
|
|
57642
57679
|
recordOutcome,
|
|
57643
57680
|
logger: console
|
|
@@ -57677,6 +57714,11 @@ function createBackend(httpServer, options = {}) {
|
|
|
57677
57714
|
const { controlWss, dataWss, controlReplayLog, sessionDataClients, broadcastControl: broadcastControlFromWebSockets, broadcastLocalControl, closeSessionDataClients } = webSockets;
|
|
57678
57715
|
broadcastControl = broadcastControlFromWebSockets;
|
|
57679
57716
|
const sessionDocs = createSessionDocsWiring({ broadcastControl: broadcastControlFromWebSockets });
|
|
57717
|
+
const shownFilesWiring = createShownFilesWiring({
|
|
57718
|
+
shownRoot: configSiblingPath(configStore.configPath, SHOWN_FOLDER_NAME),
|
|
57719
|
+
broadcastControl: broadcastControlFromWebSockets
|
|
57720
|
+
});
|
|
57721
|
+
shownFiles = shownFilesWiring;
|
|
57680
57722
|
const coherenceFeed = createCoherenceSessionFeed({ broadcastControl: broadcastControlFromWebSockets });
|
|
57681
57723
|
const sessions = /* @__PURE__ */ new Map();
|
|
57682
57724
|
const agentSessions = /* @__PURE__ */ new Map();
|
|
@@ -57744,7 +57786,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
57744
57786
|
requestReview: (reference) => teamReview.requestReview(reference),
|
|
57745
57787
|
teamReviewStatus: () => teamReview.getStatus(),
|
|
57746
57788
|
myPrsStatus: () => myPrs.getStatus(),
|
|
57747
|
-
readSettings: (request) => readSettingsForAgent(request, configFileViewOf(configStore.configPath, config, process.env))
|
|
57789
|
+
readSettings: (request) => readSettingsForAgent(request, configFileViewOf(configStore.configPath, config, process.env, configStore.isExperimentalBuild))
|
|
57748
57790
|
},
|
|
57749
57791
|
coderWorkspaceStatus: (workspace) => laneAssembly.coderEnv.workspaceStatus(workspace),
|
|
57750
57792
|
logger: console
|
|
@@ -57862,7 +57904,6 @@ function createBackend(httpServer, options = {}) {
|
|
|
57862
57904
|
currentVersion: packageJson.version,
|
|
57863
57905
|
platform: process.platform,
|
|
57864
57906
|
checkForUpdate: options.checkForUpdate || void 0,
|
|
57865
|
-
fetchOrigin: (args) => assembledGitWorkspace.fetchOrigin(args),
|
|
57866
57907
|
getUpdateJournal: () => updateApply.getJournal(),
|
|
57867
57908
|
isRestartRequested: () => updateApply.isRestartRequested(),
|
|
57868
57909
|
getControlClientCount: () => controlWss.clients.size,
|
|
@@ -58008,6 +58049,7 @@ function createBackend(httpServer, options = {}) {
|
|
|
58008
58049
|
readPlanRevision: laneAssembly.planReview?.readPlanRevision ?? null,
|
|
58009
58050
|
decidePlanReview: laneAssembly.planReview?.decide ?? null,
|
|
58010
58051
|
sessionDocs,
|
|
58052
|
+
shownFiles: shownFilesWiring,
|
|
58011
58053
|
coherenceFeed,
|
|
58012
58054
|
serverBuild: () => serverBuild,
|
|
58013
58055
|
telemetry,
|