@getpaseo/server 0.1.59 → 0.1.60
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/scripts/dev-runner.js +26 -7
- package/dist/scripts/dev-runner.js.map +1 -1
- package/dist/server/client/daemon-client-runtime-metrics.d.ts +39 -0
- package/dist/server/client/daemon-client-runtime-metrics.d.ts.map +1 -0
- package/dist/server/client/daemon-client-runtime-metrics.js +173 -0
- package/dist/server/client/daemon-client-runtime-metrics.js.map +1 -0
- package/dist/server/client/daemon-client.d.ts +58 -9
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +151 -10
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +55 -48
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +541 -331
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.d.ts +3 -2
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.js +31 -16
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
- package/dist/server/server/agent/agent-projections.d.ts +2 -1
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +29 -1
- package/dist/server/server/agent/agent-projections.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +9 -5
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +76 -69
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +13 -6
- package/dist/server/server/agent/agent-storage.js.map +1 -1
- package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
- package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -0
- package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
- package/dist/server/server/agent/agent-stream-coalescer.js.map +1 -0
- package/dist/server/server/agent/agent-timeline-store-types.d.ts +54 -0
- package/dist/server/server/agent/agent-timeline-store-types.d.ts.map +1 -0
- package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
- package/dist/server/server/agent/agent-timeline-store-types.js.map +1 -0
- package/dist/server/server/agent/agent-timeline-store.d.ts +32 -0
- package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -0
- package/dist/server/server/agent/agent-timeline-store.js +245 -0
- package/dist/server/server/agent/agent-timeline-store.js.map +1 -0
- package/dist/server/server/agent/mcp-server.d.ts +12 -1
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +276 -65
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/mcp-shared.d.ts +196 -152
- package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-shared.js +40 -42
- package/dist/server/server/agent/mcp-shared.js.map +1 -1
- package/dist/server/server/agent/model-resolver.d.ts.map +1 -1
- package/dist/server/server/agent/model-resolver.js +3 -1
- package/dist/server/server/agent/model-resolver.js.map +1 -1
- package/dist/server/server/agent/prompt-attachments.d.ts +6 -0
- package/dist/server/server/agent/prompt-attachments.d.ts.map +1 -0
- package/dist/server/server/agent/prompt-attachments.js +31 -0
- package/dist/server/server/agent/prompt-attachments.js.map +1 -0
- package/dist/server/server/agent/provider-launch-config.d.ts +12 -10
- package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
- package/dist/server/server/agent/provider-launch-config.js +34 -0
- package/dist/server/server/agent/provider-launch-config.js.map +1 -1
- package/dist/server/server/agent/provider-manifest.d.ts +1 -0
- package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
- package/dist/server/server/agent/provider-manifest.js +22 -1
- package/dist/server/server/agent/provider-manifest.js.map +1 -1
- package/dist/server/server/agent/provider-registry.d.ts +5 -2
- package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
- package/dist/server/server/agent/provider-registry.js +20 -9
- package/dist/server/server/agent/provider-registry.js.map +1 -1
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +17 -5
- package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
- package/dist/server/server/agent/provider-snapshot-manager.js +150 -61
- package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts +8 -4
- package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.js +73 -8
- package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -2
- package/dist/server/server/agent/providers/claude-agent.d.ts +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +8 -7
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +37 -4
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +61 -31
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +3 -2
- package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +64 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +585 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +19 -4
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +227 -118
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts +69 -0
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/pi-direct-agent.js +1177 -0
- package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +7 -4
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent-attention-policy.d.ts +13 -13
- package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
- package/dist/server/server/agent-attention-policy.js +20 -36
- package/dist/server/server/agent-attention-policy.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts +6 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +113 -11
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/chat/chat-rpc-schemas.d.ts +44 -44
- package/dist/server/server/chat/chat-types.d.ts +6 -6
- package/dist/server/server/checkout-diff-manager.d.ts +0 -1
- package/dist/server/server/checkout-diff-manager.d.ts.map +1 -1
- package/dist/server/server/checkout-diff-manager.js +6 -4
- package/dist/server/server/checkout-diff-manager.js.map +1 -1
- package/dist/server/server/config.d.ts.map +1 -1
- package/dist/server/server/config.js +1 -0
- package/dist/server/server/config.js.map +1 -1
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +2 -1
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/loop/rpc-schemas.d.ts +392 -392
- package/dist/server/server/loop-service.d.ts +52 -52
- package/dist/server/server/paseo-worktree-archive-service.d.ts +41 -0
- package/dist/server/server/paseo-worktree-archive-service.d.ts.map +1 -0
- package/dist/server/server/paseo-worktree-archive-service.js +137 -0
- package/dist/server/server/paseo-worktree-archive-service.js.map +1 -0
- package/dist/server/server/paseo-worktree-service.d.ts +24 -0
- package/dist/server/server/paseo-worktree-service.d.ts.map +1 -0
- package/dist/server/server/paseo-worktree-service.js +94 -0
- package/dist/server/server/paseo-worktree-service.js.map +1 -0
- package/dist/server/server/path-utils.d.ts +1 -0
- package/dist/server/server/path-utils.d.ts.map +1 -1
- package/dist/server/server/path-utils.js +9 -0
- package/dist/server/server/path-utils.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +73 -73
- package/dist/server/server/persistence-hooks.d.ts.map +1 -1
- package/dist/server/server/persistence-hooks.js +3 -0
- package/dist/server/server/persistence-hooks.js.map +1 -1
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +30 -0
- package/dist/server/server/resolve-worktree-creation-intent.d.ts.map +1 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +163 -0
- package/dist/server/server/resolve-worktree-creation-intent.js.map +1 -0
- package/dist/server/server/schedule/rpc-schemas.d.ts +192 -192
- package/dist/server/server/schedule/service.d.ts +1 -1
- package/dist/server/server/schedule/service.d.ts.map +1 -1
- package/dist/server/server/schedule/types.d.ts +44 -44
- package/dist/server/server/script-health-monitor.d.ts +39 -0
- package/dist/server/server/script-health-monitor.d.ts.map +1 -0
- package/dist/server/server/script-health-monitor.js +158 -0
- package/dist/server/server/script-health-monitor.js.map +1 -0
- package/dist/server/server/script-proxy.d.ts +40 -0
- package/dist/server/server/script-proxy.d.ts.map +1 -0
- package/dist/server/server/script-proxy.js +245 -0
- package/dist/server/server/script-proxy.js.map +1 -0
- package/dist/server/server/script-route-branch-handler.d.ts +10 -0
- package/dist/server/server/script-route-branch-handler.d.ts.map +1 -0
- package/dist/server/server/script-route-branch-handler.js +45 -0
- package/dist/server/server/script-route-branch-handler.js.map +1 -0
- package/dist/server/server/script-status-projection.d.ts +29 -0
- package/dist/server/server/script-status-projection.d.ts.map +1 -0
- package/dist/server/server/script-status-projection.js +133 -0
- package/dist/server/server/script-status-projection.js.map +1 -0
- package/dist/server/server/session.d.ts +77 -13
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +1290 -548
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +27 -3
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +112 -29
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/workspace-archive-service.d.ts +8 -0
- package/dist/server/server/workspace-archive-service.d.ts.map +1 -0
- package/dist/server/server/workspace-archive-service.js +17 -0
- package/dist/server/server/workspace-archive-service.js.map +1 -0
- package/dist/server/server/workspace-git-metadata.d.ts +24 -0
- package/dist/server/server/workspace-git-metadata.d.ts.map +1 -0
- package/dist/server/server/workspace-git-metadata.js +78 -0
- package/dist/server/server/workspace-git-metadata.js.map +1 -0
- package/dist/server/server/workspace-git-service.d.ts +104 -5
- package/dist/server/server/workspace-git-service.d.ts.map +1 -1
- package/dist/server/server/workspace-git-service.js +442 -56
- package/dist/server/server/workspace-git-service.js.map +1 -1
- package/dist/server/server/workspace-reconciliation-service.d.ts +54 -0
- package/dist/server/server/workspace-reconciliation-service.d.ts.map +1 -0
- package/dist/server/server/workspace-reconciliation-service.js +176 -0
- package/dist/server/server/workspace-reconciliation-service.js.map +1 -0
- package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -1
- package/dist/server/server/workspace-registry-bootstrap.js +4 -3
- package/dist/server/server/workspace-registry-bootstrap.js.map +1 -1
- package/dist/server/server/workspace-registry.d.ts +8 -8
- package/dist/server/server/workspace-registry.test-helpers.d.ts +37 -0
- package/dist/server/server/workspace-registry.test-helpers.d.ts.map +1 -0
- package/dist/server/server/workspace-registry.test-helpers.js +121 -0
- package/dist/server/server/workspace-registry.test-helpers.js.map +1 -0
- package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
- package/dist/server/server/workspace-script-runtime-store.d.ts.map +1 -0
- package/dist/server/server/workspace-script-runtime-store.js +78 -0
- package/dist/server/server/workspace-script-runtime-store.js.map +1 -0
- package/dist/server/server/workspace-service-env.d.ts +17 -0
- package/dist/server/server/workspace-service-env.d.ts.map +1 -0
- package/dist/server/server/workspace-service-env.js +80 -0
- package/dist/server/server/workspace-service-env.js.map +1 -0
- package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
- package/dist/server/server/workspace-service-port-registry.d.ts.map +1 -0
- package/dist/server/server/workspace-service-port-registry.js +59 -0
- package/dist/server/server/workspace-service-port-registry.js.map +1 -0
- package/dist/server/server/worktree-bootstrap.d.ts +55 -10
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +290 -112
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/server/worktree-core.d.ts +25 -0
- package/dist/server/server/worktree-core.d.ts.map +1 -0
- package/dist/server/server/worktree-core.js +75 -0
- package/dist/server/server/worktree-core.js.map +1 -0
- package/dist/server/server/worktree-errors.d.ts +12 -0
- package/dist/server/server/worktree-errors.d.ts.map +1 -0
- package/dist/server/server/worktree-errors.js +31 -0
- package/dist/server/server/worktree-errors.js.map +1 -0
- package/dist/server/server/worktree-session.d.ts +56 -70
- package/dist/server/server/worktree-session.d.ts.map +1 -1
- package/dist/server/server/worktree-session.js +176 -251
- package/dist/server/server/worktree-session.js.map +1 -1
- package/dist/server/services/github-service.d.ts +225 -0
- package/dist/server/services/github-service.d.ts.map +1 -0
- package/dist/server/services/github-service.js +1381 -0
- package/dist/server/services/github-service.js.map +1 -0
- package/dist/server/shared/messages.d.ts +29408 -12268
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +391 -65
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/server/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
- package/dist/server/terminal/terminal-manager.d.ts +9 -0
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
- package/dist/server/terminal/terminal-manager.js +27 -0
- package/dist/server/terminal/terminal-manager.js.map +1 -1
- package/dist/server/terminal/terminal-output-coalescer.d.ts +30 -0
- package/dist/server/terminal/terminal-output-coalescer.d.ts.map +1 -0
- package/dist/server/terminal/terminal-output-coalescer.js +55 -0
- package/dist/server/terminal/terminal-output-coalescer.js.map +1 -0
- package/dist/server/terminal/terminal.d.ts +32 -1
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +397 -17
- package/dist/server/terminal/terminal.js.map +1 -1
- package/dist/server/utils/checkout-git.d.ts +63 -10
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +321 -229
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/dist/server/utils/promise-timeout.d.ts +9 -0
- package/dist/server/utils/promise-timeout.d.ts.map +1 -0
- package/dist/server/utils/promise-timeout.js +25 -0
- package/dist/server/utils/promise-timeout.js.map +1 -0
- package/dist/server/utils/script-hostname.d.ts +8 -0
- package/dist/server/utils/script-hostname.d.ts.map +1 -0
- package/dist/server/utils/script-hostname.js +14 -0
- package/dist/server/utils/script-hostname.js.map +1 -0
- package/dist/server/utils/string-command-shell.d.ts +10 -0
- package/dist/server/utils/string-command-shell.d.ts.map +1 -0
- package/dist/server/utils/string-command-shell.js +21 -0
- package/dist/server/utils/string-command-shell.js.map +1 -0
- package/dist/server/utils/worktree.d.ts +54 -7
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +434 -129
- package/dist/server/utils/worktree.js.map +1 -1
- package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/src/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
- package/package.json +11 -14
- package/dist/server/server/agent/providers/pi-acp-agent.d.ts +0 -28
- package/dist/server/server/agent/providers/pi-acp-agent.d.ts.map +0 -1
- package/dist/server/server/agent/providers/pi-acp-agent.js +0 -302
- package/dist/server/server/agent/providers/pi-acp-agent.js.map +0 -1
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import { watch } from "node:fs";
|
|
2
2
|
import { readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import { getCheckoutShortstat, getCheckoutStatus, getPullRequestStatus, hasOriginRemote,
|
|
4
|
+
import { getCheckoutDiff, getCheckoutShortstat, getCheckoutStatus, getPullRequestStatus, hasOriginRemote, listBranchSuggestions, resolveRepositoryDefaultBranch, resolveBranchCheckout, resolveAbsoluteGitDir, } from "../utils/checkout-git.js";
|
|
5
|
+
import { createGitHubService } from "../services/github-service.js";
|
|
5
6
|
import { parseGitRevParsePath } from "../utils/git-rev-parse-path.js";
|
|
6
7
|
import { runGitCommand } from "../utils/run-git-command.js";
|
|
8
|
+
import { listPaseoWorktrees } from "../utils/worktree.js";
|
|
7
9
|
import { READ_ONLY_GIT_ENV } from "./checkout-git-utils.js";
|
|
10
|
+
import { buildWorkspaceGitMetadataFromSnapshot, } from "./workspace-git-metadata.js";
|
|
8
11
|
import { normalizeWorkspaceId } from "./workspace-registry-model.js";
|
|
9
12
|
const WORKSPACE_GIT_WATCH_DEBOUNCE_MS = 500;
|
|
10
13
|
const BACKGROUND_GIT_FETCH_INTERVAL_MS = 180000;
|
|
14
|
+
export const WORKSPACE_GIT_SELF_HEAL_INTERVAL_MS = 60000;
|
|
11
15
|
const WORKING_TREE_WATCH_FALLBACK_REFRESH_MS = 5000;
|
|
16
|
+
// Consumer reads may reuse cached values within this window; older peeks cold-load through the service.
|
|
17
|
+
const WORKSPACE_GIT_CONSUMER_TTL_MS = 15000;
|
|
18
|
+
// Non-forced refresh triggers share this minimum gap to absorb watcher/self-heal bursts; force bypasses it.
|
|
19
|
+
const WORKSPACE_GIT_INTERNAL_MIN_GAP_MS = 2000;
|
|
12
20
|
export class WorkspaceGitServiceImpl {
|
|
13
21
|
constructor(options) {
|
|
14
22
|
this.workspaceTargets = new Map();
|
|
@@ -16,6 +24,13 @@ export class WorkspaceGitServiceImpl {
|
|
|
16
24
|
this.workspaceTargetSetups = new Map();
|
|
17
25
|
this.workingTreeWatchTargets = new Map();
|
|
18
26
|
this.workingTreeWatchSetups = new Map();
|
|
27
|
+
this.branchValidationCache = new Map();
|
|
28
|
+
this.localBranchCache = new Map();
|
|
29
|
+
this.branchSuggestionsCache = new Map();
|
|
30
|
+
this.stashListCache = new Map();
|
|
31
|
+
this.worktreeListCache = new Map();
|
|
32
|
+
this.defaultBranchCache = new Map();
|
|
33
|
+
this.checkoutDiffCache = new Map();
|
|
19
34
|
this.logger = options.logger.child({ module: "workspace-git-service" });
|
|
20
35
|
this.paseoHome = options.paseoHome;
|
|
21
36
|
this.deps = {
|
|
@@ -23,8 +38,13 @@ export class WorkspaceGitServiceImpl {
|
|
|
23
38
|
readdir: options.deps?.readdir ?? readdir,
|
|
24
39
|
getCheckoutStatus: options.deps?.getCheckoutStatus ?? getCheckoutStatus,
|
|
25
40
|
getCheckoutShortstat: options.deps?.getCheckoutShortstat ?? getCheckoutShortstat,
|
|
41
|
+
getCheckoutDiff: options.deps?.getCheckoutDiff ?? getCheckoutDiff,
|
|
26
42
|
getPullRequestStatus: options.deps?.getPullRequestStatus ?? getPullRequestStatus,
|
|
27
|
-
|
|
43
|
+
resolveBranchCheckout: options.deps?.resolveBranchCheckout ?? resolveBranchCheckout,
|
|
44
|
+
resolveRepositoryDefaultBranch: options.deps?.resolveRepositoryDefaultBranch ?? resolveRepositoryDefaultBranch,
|
|
45
|
+
listBranchSuggestions: options.deps?.listBranchSuggestions ?? listBranchSuggestions,
|
|
46
|
+
listPaseoWorktrees: options.deps?.listPaseoWorktrees ?? listPaseoWorktrees,
|
|
47
|
+
github: options.deps?.github ?? createGitHubService(),
|
|
28
48
|
resolveAbsoluteGitDir: options.deps?.resolveAbsoluteGitDir ?? resolveAbsoluteGitDir,
|
|
29
49
|
hasOriginRemote: options.deps?.hasOriginRemote ?? hasOriginRemote,
|
|
30
50
|
runGitFetch: options.deps?.runGitFetch ?? runGitFetch,
|
|
@@ -36,6 +56,9 @@ export class WorkspaceGitServiceImpl {
|
|
|
36
56
|
const cwd = normalizeWorkspaceId(params.cwd);
|
|
37
57
|
const target = await this.ensureWorkspaceTarget(cwd);
|
|
38
58
|
target.listeners.add(listener);
|
|
59
|
+
if (target.listeners.size === 1) {
|
|
60
|
+
this.startWorkspaceSubscriptionTimers(target);
|
|
61
|
+
}
|
|
39
62
|
return {
|
|
40
63
|
initial: target.latestSnapshot ?? (await this.getSnapshot(cwd)),
|
|
41
64
|
unsubscribe: () => {
|
|
@@ -43,24 +66,143 @@ export class WorkspaceGitServiceImpl {
|
|
|
43
66
|
},
|
|
44
67
|
};
|
|
45
68
|
}
|
|
46
|
-
async getSnapshot(cwd) {
|
|
69
|
+
async getSnapshot(cwd, options) {
|
|
47
70
|
cwd = normalizeWorkspaceId(cwd);
|
|
48
|
-
const
|
|
49
|
-
|
|
71
|
+
const request = this.normalizeRefreshRequest(options, "getSnapshot", true);
|
|
72
|
+
const target = await this.ensureWorkspaceTarget(cwd);
|
|
73
|
+
if (!request.force && this.isSnapshotWarm(target)) {
|
|
50
74
|
return target.latestSnapshot;
|
|
51
75
|
}
|
|
52
|
-
|
|
53
|
-
return ensuredTarget.latestSnapshot ?? (await this.refreshSnapshot(cwd));
|
|
76
|
+
return this.requestWorkspaceSnapshot(target, request);
|
|
54
77
|
}
|
|
55
78
|
peekSnapshot(cwd) {
|
|
56
79
|
cwd = normalizeWorkspaceId(cwd);
|
|
57
80
|
return this.workspaceTargets.get(cwd)?.latestSnapshot ?? null;
|
|
58
81
|
}
|
|
82
|
+
getCheckoutDiff(cwd, options, readOptions) {
|
|
83
|
+
const normalizedCwd = normalizeWorkspaceId(cwd);
|
|
84
|
+
const normalizedOptions = this.normalizeCheckoutDiffOptions(options);
|
|
85
|
+
const key = this.buildCheckoutDiffCacheKey(normalizedCwd, normalizedOptions);
|
|
86
|
+
return this.readAuxiliaryCache(this.checkoutDiffCache, key, readOptions, () => this.deps.getCheckoutDiff(normalizedCwd, normalizedOptions, { paseoHome: this.paseoHome }));
|
|
87
|
+
}
|
|
88
|
+
normalizeCheckoutDiffOptions(options) {
|
|
89
|
+
return {
|
|
90
|
+
mode: options.mode,
|
|
91
|
+
...(options.mode === "base" && options.baseRef !== undefined
|
|
92
|
+
? { baseRef: options.baseRef }
|
|
93
|
+
: {}),
|
|
94
|
+
...(options.ignoreWhitespace === true ? { ignoreWhitespace: true } : {}),
|
|
95
|
+
...(options.includeStructured === true ? { includeStructured: true } : {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
buildCheckoutDiffCacheKey(cwd, options) {
|
|
99
|
+
// Diff content varies by compare signature. Keep the cache per exact diff read shape so
|
|
100
|
+
// hot diff panes coalesce while base refs and rendering options never share stale patches.
|
|
101
|
+
return JSON.stringify([
|
|
102
|
+
"checkout-diff",
|
|
103
|
+
cwd,
|
|
104
|
+
options.mode,
|
|
105
|
+
options.mode === "base" ? (options.baseRef ?? null) : null,
|
|
106
|
+
options.ignoreWhitespace === true,
|
|
107
|
+
options.includeStructured === true,
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
validateBranchRef(cwd, ref, options) {
|
|
111
|
+
const normalizedCwd = normalizeWorkspaceId(cwd);
|
|
112
|
+
const normalizedRef = ref.trim();
|
|
113
|
+
const key = JSON.stringify(["branch-validation", normalizedCwd, normalizedRef]);
|
|
114
|
+
return this.readAuxiliaryCache(this.branchValidationCache, key, options, () => this.deps.resolveBranchCheckout(normalizedCwd, normalizedRef));
|
|
115
|
+
}
|
|
116
|
+
hasLocalBranch(cwd, branch, options) {
|
|
117
|
+
const normalizedCwd = normalizeWorkspaceId(cwd);
|
|
118
|
+
const normalizedBranch = branch.trim();
|
|
119
|
+
const ref = `refs/heads/${normalizedBranch}`;
|
|
120
|
+
const key = JSON.stringify(["local-branch", normalizedCwd, ref]);
|
|
121
|
+
return this.readAuxiliaryCache(this.localBranchCache, key, options, async () => {
|
|
122
|
+
const result = await this.deps.runGitCommand(["rev-parse", "--verify", "--quiet", ref], {
|
|
123
|
+
cwd: normalizedCwd,
|
|
124
|
+
env: READ_ONLY_GIT_ENV,
|
|
125
|
+
acceptExitCodes: [0, 1],
|
|
126
|
+
});
|
|
127
|
+
return result.exitCode === 0;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
suggestBranchesForCwd(cwd, options, readOptions) {
|
|
131
|
+
const normalizedCwd = normalizeWorkspaceId(cwd);
|
|
132
|
+
const query = options?.query ?? "";
|
|
133
|
+
const limit = options?.limit;
|
|
134
|
+
const key = JSON.stringify(["branch-suggestions", normalizedCwd, query, limit ?? null]);
|
|
135
|
+
return this.readAuxiliaryCache(this.branchSuggestionsCache, key, readOptions, () => this.deps.listBranchSuggestions(normalizedCwd, options));
|
|
136
|
+
}
|
|
137
|
+
listStashes(cwd, options, readOptions) {
|
|
138
|
+
const normalizedCwd = normalizeWorkspaceId(cwd);
|
|
139
|
+
const paseoOnly = options?.paseoOnly !== false;
|
|
140
|
+
const key = JSON.stringify(["stashes", normalizedCwd, paseoOnly]);
|
|
141
|
+
return this.readAuxiliaryCache(this.stashListCache, key, readOptions, async () => {
|
|
142
|
+
const { stdout } = await this.deps.runGitCommand(["stash", "list", "--format=%gd%x00%s"], {
|
|
143
|
+
cwd: normalizedCwd,
|
|
144
|
+
env: READ_ONLY_GIT_ENV,
|
|
145
|
+
});
|
|
146
|
+
return parseWorkspaceGitStashList(stdout, { paseoOnly });
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async listWorktrees(cwdOrRepoRoot, options) {
|
|
150
|
+
const repoRoot = await this.resolveRepoRoot(cwdOrRepoRoot, options);
|
|
151
|
+
const key = JSON.stringify(["worktrees", repoRoot]);
|
|
152
|
+
return this.readAuxiliaryCache(this.worktreeListCache, key, options, () => this.deps.listPaseoWorktrees({
|
|
153
|
+
cwd: repoRoot,
|
|
154
|
+
paseoHome: this.paseoHome,
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
async resolveRepoRoot(cwd, options) {
|
|
158
|
+
const snapshot = await this.getSnapshot(cwd, options);
|
|
159
|
+
if (!snapshot.git.isGit) {
|
|
160
|
+
throw new Error("Create worktree requires a git repository");
|
|
161
|
+
}
|
|
162
|
+
return snapshot.git.isPaseoOwnedWorktree
|
|
163
|
+
? (snapshot.git.mainRepoRoot ?? snapshot.git.repoRoot ?? normalizeWorkspaceId(cwd))
|
|
164
|
+
: (snapshot.git.repoRoot ?? normalizeWorkspaceId(cwd));
|
|
165
|
+
}
|
|
166
|
+
async resolveDefaultBranch(cwdOrRepoRoot, options) {
|
|
167
|
+
const cwd = normalizeWorkspaceId(cwdOrRepoRoot);
|
|
168
|
+
const key = JSON.stringify(["default-branch", cwd]);
|
|
169
|
+
return this.readAuxiliaryCache(this.defaultBranchCache, key, options, async () => {
|
|
170
|
+
const defaultBranch = await this.deps.resolveRepositoryDefaultBranch(cwd);
|
|
171
|
+
if (!defaultBranch) {
|
|
172
|
+
throw new Error("Unable to resolve repository default branch");
|
|
173
|
+
}
|
|
174
|
+
return defaultBranch;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async getWorkspaceGitMetadata(cwd, options) {
|
|
178
|
+
const snapshot = await this.getSnapshot(cwd, options);
|
|
179
|
+
const directoryName = options?.directoryName ??
|
|
180
|
+
normalizeWorkspaceId(cwd).split(/[\\/]/).filter(Boolean).at(-1) ??
|
|
181
|
+
cwd;
|
|
182
|
+
return buildWorkspaceGitMetadataFromSnapshot({
|
|
183
|
+
cwd: normalizeWorkspaceId(cwd),
|
|
184
|
+
directoryName,
|
|
185
|
+
isGit: snapshot.git.isGit,
|
|
186
|
+
repoRoot: snapshot.git.repoRoot,
|
|
187
|
+
mainRepoRoot: snapshot.git.mainRepoRoot,
|
|
188
|
+
currentBranch: snapshot.git.currentBranch,
|
|
189
|
+
remoteUrl: snapshot.git.remoteUrl,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async resolveRepoRemoteUrl(cwd, options) {
|
|
193
|
+
const snapshot = await this.getSnapshot(cwd, options);
|
|
194
|
+
return snapshot.git.remoteUrl;
|
|
195
|
+
}
|
|
59
196
|
async refresh(cwd, _options) {
|
|
60
197
|
cwd = normalizeWorkspaceId(cwd);
|
|
61
198
|
const target = this.workspaceTargets.get(cwd);
|
|
62
199
|
if (target) {
|
|
63
|
-
await this.refreshWorkspaceTarget(target
|
|
200
|
+
await this.refreshWorkspaceTarget(target, {
|
|
201
|
+
force: false,
|
|
202
|
+
includeGitHub: false,
|
|
203
|
+
reason: "refresh",
|
|
204
|
+
notify: true,
|
|
205
|
+
});
|
|
64
206
|
return;
|
|
65
207
|
}
|
|
66
208
|
await this.ensureWorkspaceTarget(cwd);
|
|
@@ -114,6 +256,51 @@ export class WorkspaceGitServiceImpl {
|
|
|
114
256
|
this.workspaceTargetSetups.set(cwd, setup);
|
|
115
257
|
return setup;
|
|
116
258
|
}
|
|
259
|
+
readAuxiliaryCache(cache, key, options, load) {
|
|
260
|
+
if (options?.force && !options.reason) {
|
|
261
|
+
throw new Error("WorkspaceGitService forced read requires a reason");
|
|
262
|
+
}
|
|
263
|
+
const entry = this.ensureAuxiliaryCacheEntry(cache, key);
|
|
264
|
+
const nowMs = this.deps.now().getTime();
|
|
265
|
+
if (!options?.force && entry.value !== null && entry.loadedAtMs !== null) {
|
|
266
|
+
const ageMs = nowMs - entry.loadedAtMs;
|
|
267
|
+
if (ageMs <= WORKSPACE_GIT_CONSUMER_TTL_MS) {
|
|
268
|
+
return Promise.resolve(entry.value);
|
|
269
|
+
}
|
|
270
|
+
if (entry.lastShellOutAtMs !== null &&
|
|
271
|
+
nowMs - entry.lastShellOutAtMs < WORKSPACE_GIT_INTERNAL_MIN_GAP_MS) {
|
|
272
|
+
return Promise.resolve(entry.value);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (entry.inFlight) {
|
|
276
|
+
return entry.inFlight;
|
|
277
|
+
}
|
|
278
|
+
entry.lastShellOutAtMs = nowMs;
|
|
279
|
+
entry.inFlight = load()
|
|
280
|
+
.then((value) => {
|
|
281
|
+
entry.value = value;
|
|
282
|
+
entry.loadedAtMs = this.deps.now().getTime();
|
|
283
|
+
return value;
|
|
284
|
+
})
|
|
285
|
+
.finally(() => {
|
|
286
|
+
entry.inFlight = null;
|
|
287
|
+
});
|
|
288
|
+
return entry.inFlight;
|
|
289
|
+
}
|
|
290
|
+
ensureAuxiliaryCacheEntry(cache, key) {
|
|
291
|
+
const existing = cache.get(key);
|
|
292
|
+
if (existing) {
|
|
293
|
+
return existing;
|
|
294
|
+
}
|
|
295
|
+
const entry = {
|
|
296
|
+
value: null,
|
|
297
|
+
loadedAtMs: null,
|
|
298
|
+
lastShellOutAtMs: null,
|
|
299
|
+
inFlight: null,
|
|
300
|
+
};
|
|
301
|
+
cache.set(key, entry);
|
|
302
|
+
return entry;
|
|
303
|
+
}
|
|
117
304
|
async ensureWorkingTreeWatchTarget(cwd) {
|
|
118
305
|
const existingTarget = this.workingTreeWatchTargets.get(cwd);
|
|
119
306
|
if (existingTarget) {
|
|
@@ -135,24 +322,39 @@ export class WorkspaceGitServiceImpl {
|
|
|
135
322
|
listeners: new Set(),
|
|
136
323
|
watchers: [],
|
|
137
324
|
debounceTimer: null,
|
|
138
|
-
|
|
139
|
-
|
|
325
|
+
selfHealTimer: null,
|
|
326
|
+
githubPollSubscription: null,
|
|
327
|
+
githubPollHeadRef: null,
|
|
328
|
+
refreshState: { status: "idle" },
|
|
140
329
|
latestSnapshot: null,
|
|
330
|
+
latestSnapshotLoadedAtMs: null,
|
|
141
331
|
latestFingerprint: null,
|
|
332
|
+
lastShellOutAtMs: null,
|
|
142
333
|
repoGitRoot: null,
|
|
143
334
|
};
|
|
144
|
-
const initial = await this.refreshSnapshot(cwd);
|
|
145
|
-
this.rememberSnapshot(target, initial);
|
|
146
335
|
this.workspaceTargets.set(cwd, target);
|
|
147
|
-
|
|
148
|
-
|
|
336
|
+
try {
|
|
337
|
+
await this.requestWorkspaceSnapshot(target, {
|
|
338
|
+
force: false,
|
|
339
|
+
includeGitHub: true,
|
|
340
|
+
reason: "initial",
|
|
341
|
+
notify: false,
|
|
342
|
+
});
|
|
343
|
+
const gitDir = await this.deps.resolveAbsoluteGitDir(cwd);
|
|
344
|
+
if (!gitDir) {
|
|
345
|
+
return target;
|
|
346
|
+
}
|
|
347
|
+
const repoGitRoot = await this.resolveWorkspaceGitRefsRoot(gitDir);
|
|
348
|
+
target.repoGitRoot = repoGitRoot;
|
|
349
|
+
this.startWorkspaceWatchers(target, gitDir, repoGitRoot);
|
|
350
|
+
await this.ensureRepoTarget(target);
|
|
149
351
|
return target;
|
|
150
352
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
353
|
+
catch (error) {
|
|
354
|
+
this.closeWorkspaceTarget(target);
|
|
355
|
+
this.workspaceTargets.delete(cwd);
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
156
358
|
}
|
|
157
359
|
async createWorkingTreeWatchTarget(cwd) {
|
|
158
360
|
const repoRoot = await this.resolveCheckoutWatchRoot(cwd);
|
|
@@ -192,7 +394,10 @@ export class WorkspaceGitServiceImpl {
|
|
|
192
394
|
const missingRepoCoverage = repoRoot === null || !hasRecursiveRepoCoverage;
|
|
193
395
|
if (target.watchers.length === 0 || missingRepoCoverage) {
|
|
194
396
|
target.fallbackRefreshInterval = setInterval(() => {
|
|
195
|
-
this.scheduleWorkspaceRefresh(cwd
|
|
397
|
+
this.scheduleWorkspaceRefresh(cwd, {
|
|
398
|
+
force: true,
|
|
399
|
+
reason: "working-tree-watch-fallback",
|
|
400
|
+
});
|
|
196
401
|
for (const listener of target.listeners) {
|
|
197
402
|
listener();
|
|
198
403
|
}
|
|
@@ -281,7 +486,7 @@ export class WorkspaceGitServiceImpl {
|
|
|
281
486
|
this.repoTargets.set(repoGitRoot, repoTarget);
|
|
282
487
|
void this.runRepoFetch(repoTarget);
|
|
283
488
|
}
|
|
284
|
-
scheduleWorkspaceRefresh(targetOrCwd) {
|
|
489
|
+
scheduleWorkspaceRefresh(targetOrCwd, options) {
|
|
285
490
|
const target = typeof targetOrCwd === "string"
|
|
286
491
|
? this.workspaceTargets.get(normalizeWorkspaceId(targetOrCwd))
|
|
287
492
|
: targetOrCwd;
|
|
@@ -293,9 +498,65 @@ export class WorkspaceGitServiceImpl {
|
|
|
293
498
|
}
|
|
294
499
|
target.debounceTimer = setTimeout(() => {
|
|
295
500
|
target.debounceTimer = null;
|
|
296
|
-
void this.refreshWorkspaceTarget(target
|
|
501
|
+
void this.refreshWorkspaceTarget(target, {
|
|
502
|
+
force: options?.force === true,
|
|
503
|
+
includeGitHub: false,
|
|
504
|
+
reason: options?.reason ?? "watch",
|
|
505
|
+
notify: true,
|
|
506
|
+
});
|
|
297
507
|
}, WORKSPACE_GIT_WATCH_DEBOUNCE_MS);
|
|
298
508
|
}
|
|
509
|
+
startWorkspaceSubscriptionTimers(target) {
|
|
510
|
+
if (!target.selfHealTimer) {
|
|
511
|
+
target.selfHealTimer = setInterval(() => {
|
|
512
|
+
this.getSnapshot(target.cwd, { reason: "self-heal-git" }).catch((error) => {
|
|
513
|
+
this.logger.warn({ err: error, cwd: target.cwd, reason: "self-heal-git" }, "Failed to run workspace git self-heal refresh");
|
|
514
|
+
});
|
|
515
|
+
}, WORKSPACE_GIT_SELF_HEAL_INTERVAL_MS);
|
|
516
|
+
}
|
|
517
|
+
this.updateGitHubPollForTarget(target);
|
|
518
|
+
}
|
|
519
|
+
updateGitHubPollForTarget(target) {
|
|
520
|
+
if (target.listeners.size === 0) {
|
|
521
|
+
this.stopGitHubPollForTarget(target);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const snapshot = target.latestSnapshot;
|
|
525
|
+
if (!snapshot || !this.deps.github.retainCurrentPullRequestStatusPoll) {
|
|
526
|
+
this.stopGitHubPollForTarget(target);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const headRef = snapshot.git.currentBranch;
|
|
530
|
+
if (!headRef || !hasGitHubRemoteUrl(snapshot.git.remoteUrl)) {
|
|
531
|
+
this.stopGitHubPollForTarget(target);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (target.githubPollHeadRef === headRef && target.githubPollSubscription) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
this.stopGitHubPollForTarget(target);
|
|
538
|
+
target.githubPollHeadRef = headRef;
|
|
539
|
+
target.githubPollSubscription = this.deps.github.retainCurrentPullRequestStatusPoll({
|
|
540
|
+
cwd: target.cwd,
|
|
541
|
+
headRef,
|
|
542
|
+
onStatus: () => {
|
|
543
|
+
void this.refreshWorkspaceTarget(target, {
|
|
544
|
+
force: false,
|
|
545
|
+
includeGitHub: true,
|
|
546
|
+
reason: "self-heal-github",
|
|
547
|
+
notify: true,
|
|
548
|
+
});
|
|
549
|
+
},
|
|
550
|
+
onError: (error) => {
|
|
551
|
+
this.logger.warn({ err: error, cwd: target.cwd, headRef, reason: "self-heal-github" }, "Failed to run GitHub self-heal refresh");
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
stopGitHubPollForTarget(target) {
|
|
556
|
+
target.githubPollSubscription?.unsubscribe();
|
|
557
|
+
target.githubPollSubscription = null;
|
|
558
|
+
target.githubPollHeadRef = null;
|
|
559
|
+
}
|
|
299
560
|
addWorkingTreeWatcher(target, watchPath, shouldTryRecursive) {
|
|
300
561
|
if (target.watchedPaths.has(watchPath)) {
|
|
301
562
|
return false;
|
|
@@ -305,7 +566,10 @@ export class WorkspaceGitServiceImpl {
|
|
|
305
566
|
if (process.platform === "linux" && target.repoWatchPath) {
|
|
306
567
|
void this.refreshLinuxRepoTreeWatchers(target);
|
|
307
568
|
}
|
|
308
|
-
this.scheduleWorkspaceRefresh(cwd
|
|
569
|
+
this.scheduleWorkspaceRefresh(cwd, {
|
|
570
|
+
force: true,
|
|
571
|
+
reason: "working-tree-watch",
|
|
572
|
+
});
|
|
309
573
|
for (const listener of target.listeners) {
|
|
310
574
|
listener();
|
|
311
575
|
}
|
|
@@ -415,37 +679,119 @@ export class WorkspaceGitServiceImpl {
|
|
|
415
679
|
}
|
|
416
680
|
return directories;
|
|
417
681
|
}
|
|
418
|
-
async refreshWorkspaceTarget(target) {
|
|
419
|
-
if (target.refreshPromise) {
|
|
420
|
-
target.refreshQueued = true;
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
target.refreshPromise = (async () => {
|
|
424
|
-
do {
|
|
425
|
-
target.refreshQueued = false;
|
|
426
|
-
try {
|
|
427
|
-
const snapshot = await this.refreshSnapshot(target.cwd);
|
|
428
|
-
this.rememberSnapshot(target, snapshot, { notify: true });
|
|
429
|
-
}
|
|
430
|
-
catch (error) {
|
|
431
|
-
this.logger.warn({ err: error, cwd: target.cwd }, "Failed to refresh workspace git snapshot");
|
|
432
|
-
}
|
|
433
|
-
} while (target.refreshQueued);
|
|
434
|
-
})();
|
|
682
|
+
async refreshWorkspaceTarget(target, request) {
|
|
435
683
|
try {
|
|
436
|
-
await target
|
|
684
|
+
await this.requestWorkspaceSnapshot(target, request);
|
|
437
685
|
}
|
|
438
|
-
|
|
439
|
-
target.
|
|
686
|
+
catch (error) {
|
|
687
|
+
this.logger.warn({ err: error, cwd: target.cwd, reason: request.reason }, "Failed to refresh workspace git snapshot");
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
requestWorkspaceSnapshot(target, request) {
|
|
691
|
+
if (target.refreshState.status === "in-flight") {
|
|
692
|
+
if (request.force && !target.refreshState.force) {
|
|
693
|
+
target.refreshState.queued = this.mergeQueuedRefresh(target.refreshState.queued, request);
|
|
694
|
+
}
|
|
695
|
+
return target.refreshState.promise;
|
|
696
|
+
}
|
|
697
|
+
if (!request.force && this.shouldThrottleNonForcedRefresh(target)) {
|
|
698
|
+
return Promise.resolve(target.latestSnapshot);
|
|
699
|
+
}
|
|
700
|
+
const promise = this.runWorkspaceRefreshLoop(target, request).finally(() => {
|
|
701
|
+
const state = target.refreshState;
|
|
702
|
+
if (state.status === "in-flight" && state.promise === promise) {
|
|
703
|
+
target.refreshState = { status: "idle" };
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
target.refreshState = {
|
|
707
|
+
status: "in-flight",
|
|
708
|
+
promise,
|
|
709
|
+
force: request.force,
|
|
710
|
+
includeGitHub: request.includeGitHub,
|
|
711
|
+
queued: null,
|
|
712
|
+
};
|
|
713
|
+
return promise;
|
|
714
|
+
}
|
|
715
|
+
normalizeRefreshRequest(options, defaultReason, notify) {
|
|
716
|
+
if (options?.force && !options.reason) {
|
|
717
|
+
throw new Error("WorkspaceGitService.getSnapshot force refresh requires a reason");
|
|
718
|
+
}
|
|
719
|
+
const force = options?.force === true;
|
|
720
|
+
return {
|
|
721
|
+
force,
|
|
722
|
+
includeGitHub: options?.includeGitHub ?? force,
|
|
723
|
+
reason: options?.reason ?? defaultReason,
|
|
724
|
+
notify,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
isSnapshotWarm(target) {
|
|
728
|
+
if (!target.latestSnapshot || target.latestSnapshotLoadedAtMs === null) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
return (this.deps.now().getTime() - target.latestSnapshotLoadedAtMs <= WORKSPACE_GIT_CONSUMER_TTL_MS);
|
|
732
|
+
}
|
|
733
|
+
shouldThrottleNonForcedRefresh(target) {
|
|
734
|
+
if (!target.latestSnapshot || target.lastShellOutAtMs === null) {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
return this.deps.now().getTime() - target.lastShellOutAtMs < WORKSPACE_GIT_INTERNAL_MIN_GAP_MS;
|
|
738
|
+
}
|
|
739
|
+
mergeQueuedRefresh(queued, request) {
|
|
740
|
+
if (!queued) {
|
|
741
|
+
return {
|
|
742
|
+
force: request.force,
|
|
743
|
+
includeGitHub: request.includeGitHub,
|
|
744
|
+
reason: request.reason,
|
|
745
|
+
notify: request.notify,
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
const force = queued.force || request.force;
|
|
749
|
+
return {
|
|
750
|
+
force,
|
|
751
|
+
includeGitHub: queued.includeGitHub || request.includeGitHub,
|
|
752
|
+
reason: request.force && !queued.force ? request.reason : queued.reason,
|
|
753
|
+
notify: queued.notify || request.notify,
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
async runWorkspaceRefreshLoop(target, initialRequest) {
|
|
757
|
+
let request = initialRequest;
|
|
758
|
+
let snapshot;
|
|
759
|
+
while (true) {
|
|
760
|
+
snapshot = await this.refreshSnapshot(target, request);
|
|
761
|
+
this.rememberSnapshot(target, snapshot, {
|
|
762
|
+
notify: request.notify,
|
|
763
|
+
forceEmit: request.force,
|
|
764
|
+
});
|
|
765
|
+
const state = target.refreshState;
|
|
766
|
+
if (state.status !== "in-flight" || !state.queued) {
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
request = state.queued;
|
|
770
|
+
state.queued = null;
|
|
771
|
+
state.force = request.force;
|
|
772
|
+
state.includeGitHub = request.includeGitHub;
|
|
440
773
|
}
|
|
774
|
+
return snapshot;
|
|
441
775
|
}
|
|
442
|
-
async refreshSnapshot(
|
|
443
|
-
|
|
776
|
+
async refreshSnapshot(target, request) {
|
|
777
|
+
const now = this.deps.now();
|
|
778
|
+
target.lastShellOutAtMs = now.getTime();
|
|
779
|
+
const forceGitHub = request.force && request.includeGitHub;
|
|
780
|
+
if (forceGitHub) {
|
|
781
|
+
this.deps.github.invalidate({ cwd: target.cwd });
|
|
782
|
+
}
|
|
783
|
+
const snapshot = await loadWorkspaceGitRuntimeSnapshot(target.cwd, { paseoHome: this.paseoHome }, now, this.deps, { force: request.force, forceGitHub, reason: request.reason });
|
|
784
|
+
target.latestSnapshotLoadedAtMs = now.getTime();
|
|
785
|
+
return snapshot;
|
|
444
786
|
}
|
|
445
787
|
rememberSnapshot(target, snapshot, options) {
|
|
446
788
|
target.latestSnapshot = snapshot;
|
|
789
|
+
if (target.listeners.size > 0) {
|
|
790
|
+
this.updateGitHubPollForTarget(target);
|
|
791
|
+
}
|
|
447
792
|
const fingerprint = JSON.stringify(snapshot);
|
|
448
|
-
|
|
793
|
+
const fingerprintMatches = target.latestFingerprint === fingerprint;
|
|
794
|
+
if (fingerprintMatches && !options?.forceEmit) {
|
|
449
795
|
return;
|
|
450
796
|
}
|
|
451
797
|
target.latestFingerprint = fingerprint;
|
|
@@ -475,7 +821,12 @@ export class WorkspaceGitServiceImpl {
|
|
|
475
821
|
if (!workspaceTarget) {
|
|
476
822
|
return;
|
|
477
823
|
}
|
|
478
|
-
await this.refreshWorkspaceTarget(workspaceTarget
|
|
824
|
+
await this.refreshWorkspaceTarget(workspaceTarget, {
|
|
825
|
+
force: false,
|
|
826
|
+
includeGitHub: false,
|
|
827
|
+
reason: "repo-fetch",
|
|
828
|
+
notify: true,
|
|
829
|
+
});
|
|
479
830
|
}));
|
|
480
831
|
}
|
|
481
832
|
}
|
|
@@ -519,6 +870,11 @@ export class WorkspaceGitServiceImpl {
|
|
|
519
870
|
clearTimeout(target.debounceTimer);
|
|
520
871
|
target.debounceTimer = null;
|
|
521
872
|
}
|
|
873
|
+
if (target.selfHealTimer) {
|
|
874
|
+
clearInterval(target.selfHealTimer);
|
|
875
|
+
target.selfHealTimer = null;
|
|
876
|
+
}
|
|
877
|
+
this.stopGitHubPollForTarget(target);
|
|
522
878
|
for (const watcher of target.watchers) {
|
|
523
879
|
watcher.close();
|
|
524
880
|
}
|
|
@@ -545,18 +901,20 @@ export class WorkspaceGitServiceImpl {
|
|
|
545
901
|
target.workspaceKeys.clear();
|
|
546
902
|
}
|
|
547
903
|
}
|
|
548
|
-
async function loadWorkspaceGitRuntimeSnapshot(cwd, context, now, deps) {
|
|
904
|
+
async function loadWorkspaceGitRuntimeSnapshot(cwd, context, now, deps, options) {
|
|
549
905
|
const checkoutStatus = await deps.getCheckoutStatus(cwd, context);
|
|
550
906
|
if (!checkoutStatus.isGit) {
|
|
551
907
|
return buildNotGitSnapshot(cwd);
|
|
552
908
|
}
|
|
553
909
|
const [diffStat, github] = await Promise.all([
|
|
554
|
-
deps.getCheckoutShortstat(cwd, context),
|
|
910
|
+
deps.getCheckoutShortstat(cwd, context, { force: options?.force }),
|
|
555
911
|
loadGitHubSnapshot({
|
|
556
912
|
cwd,
|
|
557
913
|
remoteUrl: checkoutStatus.remoteUrl,
|
|
558
914
|
now,
|
|
559
915
|
deps,
|
|
916
|
+
force: options?.forceGitHub,
|
|
917
|
+
reason: options?.reason,
|
|
560
918
|
}),
|
|
561
919
|
]);
|
|
562
920
|
return {
|
|
@@ -569,9 +927,11 @@ async function loadWorkspaceGitRuntimeSnapshot(cwd, context, now, deps) {
|
|
|
569
927
|
remoteUrl: checkoutStatus.remoteUrl,
|
|
570
928
|
isPaseoOwnedWorktree: checkoutStatus.isPaseoOwnedWorktree,
|
|
571
929
|
isDirty: checkoutStatus.isDirty,
|
|
930
|
+
baseRef: checkoutStatus.baseRef,
|
|
572
931
|
aheadBehind: checkoutStatus.aheadBehind,
|
|
573
932
|
aheadOfOrigin: checkoutStatus.aheadOfOrigin,
|
|
574
933
|
behindOfOrigin: checkoutStatus.behindOfOrigin,
|
|
934
|
+
hasRemote: checkoutStatus.hasRemote,
|
|
575
935
|
diffStat,
|
|
576
936
|
},
|
|
577
937
|
github,
|
|
@@ -583,27 +943,27 @@ async function loadGitHubSnapshot(options) {
|
|
|
583
943
|
featuresEnabled: false,
|
|
584
944
|
pullRequest: null,
|
|
585
945
|
error: null,
|
|
586
|
-
refreshedAt: null,
|
|
587
946
|
};
|
|
588
947
|
}
|
|
589
948
|
try {
|
|
590
|
-
await options.deps.
|
|
949
|
+
await options.deps.github.isAuthenticated({ cwd: options.cwd });
|
|
591
950
|
}
|
|
592
951
|
catch {
|
|
593
952
|
return {
|
|
594
953
|
featuresEnabled: false,
|
|
595
954
|
pullRequest: null,
|
|
596
955
|
error: null,
|
|
597
|
-
refreshedAt: null,
|
|
598
956
|
};
|
|
599
957
|
}
|
|
600
958
|
try {
|
|
601
|
-
const result = await options.deps.getPullRequestStatus(options.cwd
|
|
959
|
+
const result = await options.deps.getPullRequestStatus(options.cwd, options.deps.github, {
|
|
960
|
+
force: options.force,
|
|
961
|
+
reason: options.reason,
|
|
962
|
+
});
|
|
602
963
|
return {
|
|
603
964
|
featuresEnabled: true,
|
|
604
965
|
pullRequest: result.status,
|
|
605
966
|
error: null,
|
|
606
|
-
refreshedAt: options.now.toISOString(),
|
|
607
967
|
};
|
|
608
968
|
}
|
|
609
969
|
catch (error) {
|
|
@@ -613,7 +973,6 @@ async function loadGitHubSnapshot(options) {
|
|
|
613
973
|
error: {
|
|
614
974
|
message: error instanceof Error ? error.message : String(error),
|
|
615
975
|
},
|
|
616
|
-
refreshedAt: options.now.toISOString(),
|
|
617
976
|
};
|
|
618
977
|
}
|
|
619
978
|
}
|
|
@@ -625,6 +984,32 @@ function hasGitHubRemoteUrl(remoteUrl) {
|
|
|
625
984
|
remoteUrl.startsWith("git@github.com:") ||
|
|
626
985
|
remoteUrl.startsWith("ssh://git@github.com/"));
|
|
627
986
|
}
|
|
987
|
+
function parseWorkspaceGitStashList(stdout, options) {
|
|
988
|
+
const entries = [];
|
|
989
|
+
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
990
|
+
for (const line of lines) {
|
|
991
|
+
const sepIdx = line.indexOf("\0");
|
|
992
|
+
if (sepIdx < 0) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
const refPart = line.slice(0, sepIdx);
|
|
996
|
+
const subject = line.slice(sepIdx + 1);
|
|
997
|
+
const indexMatch = refPart.match(/\{(\d+)\}/);
|
|
998
|
+
if (!indexMatch) {
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
const index = Number(indexMatch[1]);
|
|
1002
|
+
const prefix = "paseo-auto-stash:";
|
|
1003
|
+
const prefixIdx = subject.indexOf(prefix);
|
|
1004
|
+
const isPaseo = prefixIdx >= 0;
|
|
1005
|
+
const branch = isPaseo ? subject.slice(prefixIdx + prefix.length).trim() || null : null;
|
|
1006
|
+
if (options.paseoOnly && !isPaseo) {
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
entries.push({ index, message: subject, branch, isPaseo });
|
|
1010
|
+
}
|
|
1011
|
+
return entries;
|
|
1012
|
+
}
|
|
628
1013
|
function buildNotGitSnapshot(cwd) {
|
|
629
1014
|
return {
|
|
630
1015
|
cwd,
|
|
@@ -636,16 +1021,17 @@ function buildNotGitSnapshot(cwd) {
|
|
|
636
1021
|
remoteUrl: null,
|
|
637
1022
|
isPaseoOwnedWorktree: false,
|
|
638
1023
|
isDirty: null,
|
|
1024
|
+
baseRef: null,
|
|
639
1025
|
aheadBehind: null,
|
|
640
1026
|
aheadOfOrigin: null,
|
|
641
1027
|
behindOfOrigin: null,
|
|
1028
|
+
hasRemote: false,
|
|
642
1029
|
diffStat: null,
|
|
643
1030
|
},
|
|
644
1031
|
github: {
|
|
645
1032
|
featuresEnabled: false,
|
|
646
1033
|
pullRequest: null,
|
|
647
1034
|
error: null,
|
|
648
|
-
refreshedAt: null,
|
|
649
1035
|
},
|
|
650
1036
|
};
|
|
651
1037
|
}
|