@getpaseo/server 0.1.59 → 0.1.61
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 +78 -8
- package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
- package/dist/server/server/agent/provider-launch-config.js +35 -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 +61 -17
- 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 +1171 -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 +195 -67
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- 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,20 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile, spawn } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync } from "fs";
|
|
4
|
+
import { rm, stat } from "fs/promises";
|
|
4
5
|
import { join, basename, dirname, resolve, sep } from "path";
|
|
5
6
|
import net from "node:net";
|
|
6
7
|
import { createHash } from "node:crypto";
|
|
7
|
-
import
|
|
8
|
+
import * as pty from "node-pty";
|
|
9
|
+
import stripAnsi from "strip-ansi";
|
|
10
|
+
import { buildStringCommandShellInvocation } from "./string-command-shell.js";
|
|
8
11
|
import { normalizeBaseRefName, readPaseoWorktreeMetadata, readPaseoWorktreeRuntimePort, writePaseoWorktreeMetadata, writePaseoWorktreeRuntimeMetadata, } from "./worktree-metadata.js";
|
|
9
12
|
import { runGitCommand } from "./run-git-command.js";
|
|
10
|
-
import { platformBash, spawnProcess } from "./spawn.js";
|
|
11
13
|
import { resolvePaseoHome } from "../server/paseo-home.js";
|
|
14
|
+
import { ensureNodePtySpawnHelperExecutableForCurrentPlatform } from "../terminal/terminal.js";
|
|
12
15
|
import { parseGitRevParsePath, resolveGitRevParsePath } from "./git-rev-parse-path.js";
|
|
13
|
-
const
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
14
17
|
const READ_ONLY_GIT_ENV = {
|
|
15
18
|
...process.env,
|
|
16
19
|
GIT_OPTIONAL_LOCKS: "0",
|
|
17
20
|
};
|
|
21
|
+
export function isServiceScript(config) {
|
|
22
|
+
return "type" in config && config.type === "service";
|
|
23
|
+
}
|
|
18
24
|
export class WorktreeSetupError extends Error {
|
|
19
25
|
constructor(message, results) {
|
|
20
26
|
super(message);
|
|
@@ -29,6 +35,21 @@ export class WorktreeTeardownError extends Error {
|
|
|
29
35
|
this.results = results;
|
|
30
36
|
}
|
|
31
37
|
}
|
|
38
|
+
export class BranchAlreadyCheckedOutError extends Error {
|
|
39
|
+
constructor(branchName) {
|
|
40
|
+
super(`Branch already checked out: ${branchName}`);
|
|
41
|
+
this.name = "BranchAlreadyCheckedOutError";
|
|
42
|
+
this.branchName = branchName;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class UnknownBranchError extends Error {
|
|
46
|
+
constructor(params) {
|
|
47
|
+
super(`Unknown branch: ${params.branchName}`);
|
|
48
|
+
this.name = "UnknownBranchError";
|
|
49
|
+
this.branchName = params.branchName;
|
|
50
|
+
this.cwd = params.cwd;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
32
53
|
function readPaseoConfig(repoRoot) {
|
|
33
54
|
const paseoConfigPath = join(repoRoot, "paseo.json");
|
|
34
55
|
if (!existsSync(paseoConfigPath)) {
|
|
@@ -85,13 +106,89 @@ export function getWorktreeTerminalSpecs(repoRoot) {
|
|
|
85
106
|
}
|
|
86
107
|
return specs;
|
|
87
108
|
}
|
|
109
|
+
export function getScriptConfigs(repoRoot) {
|
|
110
|
+
const config = readPaseoConfig(repoRoot);
|
|
111
|
+
const scripts = config?.scripts;
|
|
112
|
+
if (!scripts || typeof scripts !== "object") {
|
|
113
|
+
return new Map();
|
|
114
|
+
}
|
|
115
|
+
const result = new Map();
|
|
116
|
+
for (const [name, entry] of Object.entries(scripts)) {
|
|
117
|
+
if (!entry || typeof entry !== "object") {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const rawCommand = entry.command;
|
|
121
|
+
if (typeof rawCommand !== "string") {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const command = rawCommand.trim();
|
|
125
|
+
if (!command) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const scriptConfig = entry.type === "service"
|
|
129
|
+
? {
|
|
130
|
+
type: "service",
|
|
131
|
+
command,
|
|
132
|
+
}
|
|
133
|
+
: { command };
|
|
134
|
+
if (isServiceScript(scriptConfig) &&
|
|
135
|
+
typeof entry.port === "number" &&
|
|
136
|
+
Number.isFinite(entry.port)) {
|
|
137
|
+
scriptConfig.port = entry.port;
|
|
138
|
+
}
|
|
139
|
+
result.set(name, scriptConfig);
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
export function processCarriageReturns(text) {
|
|
144
|
+
if (!text.includes("\r")) {
|
|
145
|
+
return text;
|
|
146
|
+
}
|
|
147
|
+
const output = [];
|
|
148
|
+
let line = [];
|
|
149
|
+
let cursor = 0;
|
|
150
|
+
const flushLine = () => {
|
|
151
|
+
output.push(line.join(""));
|
|
152
|
+
line = [];
|
|
153
|
+
cursor = 0;
|
|
154
|
+
};
|
|
155
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
156
|
+
const char = text[index];
|
|
157
|
+
if (char === "\r") {
|
|
158
|
+
if (text[index + 1] === "\n") {
|
|
159
|
+
flushLine();
|
|
160
|
+
output.push("\n");
|
|
161
|
+
index += 1;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
cursor = 0;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (char === "\n") {
|
|
168
|
+
flushLine();
|
|
169
|
+
output.push("\n");
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (cursor < line.length) {
|
|
173
|
+
line[cursor] = char;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
line.push(char);
|
|
177
|
+
}
|
|
178
|
+
cursor += 1;
|
|
179
|
+
}
|
|
180
|
+
if (line.length > 0) {
|
|
181
|
+
output.push(line.join(""));
|
|
182
|
+
}
|
|
183
|
+
return output.join("");
|
|
184
|
+
}
|
|
88
185
|
async function execSetupCommand(command, options) {
|
|
89
186
|
const startedAt = Date.now();
|
|
187
|
+
const shellInvocation = buildStringCommandShellInvocation({ command });
|
|
90
188
|
try {
|
|
91
|
-
const { stdout, stderr } = await
|
|
189
|
+
const { stdout, stderr } = await execFileAsync(shellInvocation.shell, shellInvocation.args, {
|
|
92
190
|
cwd: options.cwd,
|
|
93
191
|
env: options.env,
|
|
94
|
-
...(process.platform === "win32" ? {} : { shell: "/bin/bash" }),
|
|
95
192
|
});
|
|
96
193
|
return {
|
|
97
194
|
command,
|
|
@@ -119,6 +216,27 @@ async function execSetupCommandStreamed(options) {
|
|
|
119
216
|
const stdoutChunks = [];
|
|
120
217
|
const stderrChunks = [];
|
|
121
218
|
let settled = false;
|
|
219
|
+
const emitOutput = (stream, chunk) => {
|
|
220
|
+
const text = stripAnsi(chunk);
|
|
221
|
+
if (!text) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (stream === "stdout") {
|
|
225
|
+
stdoutChunks.push(text);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
stderrChunks.push(text);
|
|
229
|
+
}
|
|
230
|
+
options.onEvent?.({
|
|
231
|
+
type: "output",
|
|
232
|
+
index: options.index,
|
|
233
|
+
total: options.total,
|
|
234
|
+
command: options.command,
|
|
235
|
+
cwd: options.cwd,
|
|
236
|
+
stream,
|
|
237
|
+
chunk: text,
|
|
238
|
+
});
|
|
239
|
+
};
|
|
122
240
|
const finish = (exitCode) => {
|
|
123
241
|
if (settled) {
|
|
124
242
|
return;
|
|
@@ -152,45 +270,48 @@ async function execSetupCommandStreamed(options) {
|
|
|
152
270
|
command: options.command,
|
|
153
271
|
cwd: options.cwd,
|
|
154
272
|
});
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
env: options.env,
|
|
159
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
160
|
-
});
|
|
161
|
-
child.stdout?.on("data", (chunk) => {
|
|
162
|
-
const text = chunk.toString();
|
|
163
|
-
stdoutChunks.push(text);
|
|
164
|
-
options.onEvent?.({
|
|
165
|
-
type: "output",
|
|
166
|
-
index: options.index,
|
|
167
|
-
total: options.total,
|
|
168
|
-
command: options.command,
|
|
273
|
+
const spawnWithPipes = () => {
|
|
274
|
+
const shellInvocation = buildStringCommandShellInvocation({ command: options.command });
|
|
275
|
+
const child = spawn(shellInvocation.shell, shellInvocation.args, {
|
|
169
276
|
cwd: options.cwd,
|
|
170
|
-
|
|
171
|
-
|
|
277
|
+
env: options.env,
|
|
278
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
172
279
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
280
|
+
child.stdout?.on("data", (chunk) => {
|
|
281
|
+
emitOutput("stdout", chunk.toString());
|
|
282
|
+
});
|
|
283
|
+
child.stderr?.on("data", (chunk) => {
|
|
284
|
+
emitOutput("stderr", chunk.toString());
|
|
285
|
+
});
|
|
286
|
+
child.on("error", (error) => {
|
|
287
|
+
emitOutput("stderr", error instanceof Error ? error.message : String(error));
|
|
288
|
+
finish(null);
|
|
289
|
+
});
|
|
290
|
+
child.on("close", (code) => {
|
|
291
|
+
finish(typeof code === "number" ? code : null);
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
try {
|
|
295
|
+
ensureNodePtySpawnHelperExecutableForCurrentPlatform();
|
|
296
|
+
const shellInvocation = buildStringCommandShellInvocation({ command: options.command });
|
|
297
|
+
const terminal = pty.spawn(shellInvocation.shell, shellInvocation.args, {
|
|
182
298
|
cwd: options.cwd,
|
|
183
|
-
|
|
184
|
-
|
|
299
|
+
env: options.env,
|
|
300
|
+
name: "xterm-color",
|
|
301
|
+
cols: 120,
|
|
302
|
+
rows: 30,
|
|
185
303
|
});
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
304
|
+
terminal.onData((data) => {
|
|
305
|
+
emitOutput("stdout", data);
|
|
306
|
+
});
|
|
307
|
+
terminal.onExit(({ exitCode }) => {
|
|
308
|
+
finish(typeof exitCode === "number" ? exitCode : null);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
emitOutput("stderr", error instanceof Error ? error.message : String(error));
|
|
313
|
+
spawnWithPipes();
|
|
314
|
+
}
|
|
194
315
|
});
|
|
195
316
|
}
|
|
196
317
|
async function getAvailablePort() {
|
|
@@ -456,9 +577,6 @@ export function slugify(input) {
|
|
|
456
577
|
}
|
|
457
578
|
return truncated.replace(/-+$/, "");
|
|
458
579
|
}
|
|
459
|
-
function generateWorktreeSlug() {
|
|
460
|
-
return createNameId();
|
|
461
|
-
}
|
|
462
580
|
const WORKTREE_PROJECT_HASH_LENGTH = 8;
|
|
463
581
|
function deriveShortAlphanumericHash(value) {
|
|
464
582
|
const digest = createHash("sha256").update(value).digest();
|
|
@@ -503,36 +621,44 @@ function resolveRepoRootFromGitCommonDir(commonDir) {
|
|
|
503
621
|
: normalizedCommonDir;
|
|
504
622
|
}
|
|
505
623
|
export async function isPaseoOwnedWorktreeCwd(cwd, options) {
|
|
506
|
-
|
|
624
|
+
const resolvedCwd = normalizePathForOwnership(cwd);
|
|
625
|
+
// repoRoot is best-effort: git may be unreachable from the worktree (e.g. a
|
|
626
|
+
// previous archive attempt removed the admin dir before the working tree
|
|
627
|
+
// could be fully cleaned up). We still want to allow archiving in that case.
|
|
628
|
+
let repoRoot;
|
|
507
629
|
try {
|
|
508
|
-
gitCommonDir = await getGitCommonDir(cwd);
|
|
630
|
+
const gitCommonDir = await getGitCommonDir(cwd);
|
|
631
|
+
repoRoot = resolveRepoRootFromGitCommonDir(gitCommonDir);
|
|
509
632
|
}
|
|
510
633
|
catch {
|
|
634
|
+
// ignore
|
|
635
|
+
}
|
|
636
|
+
const paseoHome = options?.paseoHome ? resolve(options.paseoHome) : resolvePaseoHome();
|
|
637
|
+
const paseoWorktreesPrefix = normalizePathForOwnership(join(paseoHome, "worktrees")) + sep;
|
|
638
|
+
// Ownership is defined by the path living under $PASEO_HOME/worktrees/<hash>/<slug>[/...].
|
|
639
|
+
// The <hash>/<slug> prefix is Paseo-private — nothing else writes there — so the
|
|
640
|
+
// path shape alone is sufficient proof of ownership, even when git has already
|
|
641
|
+
// forgotten about the worktree.
|
|
642
|
+
if (!resolvedCwd.startsWith(paseoWorktreesPrefix)) {
|
|
511
643
|
return {
|
|
512
644
|
allowed: false,
|
|
513
|
-
|
|
645
|
+
...(repoRoot !== undefined ? { repoRoot } : {}),
|
|
646
|
+
worktreePath: resolvedCwd,
|
|
514
647
|
};
|
|
515
648
|
}
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
const resolvedCwd = normalizePathForOwnership(cwd);
|
|
520
|
-
if (!resolvedCwd.startsWith(resolvedRoot)) {
|
|
649
|
+
const relative = resolvedCwd.slice(paseoWorktreesPrefix.length);
|
|
650
|
+
const parts = relative.split(sep).filter((part) => part.length > 0);
|
|
651
|
+
if (parts.length < 2) {
|
|
521
652
|
return {
|
|
522
653
|
allowed: false,
|
|
523
|
-
repoRoot,
|
|
524
|
-
worktreeRoot: worktreesRoot,
|
|
654
|
+
...(repoRoot !== undefined ? { repoRoot } : {}),
|
|
525
655
|
worktreePath: resolvedCwd,
|
|
526
656
|
};
|
|
527
657
|
}
|
|
528
|
-
const
|
|
529
|
-
const allowed = worktrees.some((entry) => {
|
|
530
|
-
const worktreePath = resolve(entry.path);
|
|
531
|
-
return resolvedCwd === worktreePath || resolvedCwd.startsWith(worktreePath + sep);
|
|
532
|
-
});
|
|
658
|
+
const worktreesRoot = join(paseoHome, "worktrees", parts[0]);
|
|
533
659
|
return {
|
|
534
|
-
allowed,
|
|
535
|
-
repoRoot,
|
|
660
|
+
allowed: true,
|
|
661
|
+
...(repoRoot !== undefined ? { repoRoot } : {}),
|
|
536
662
|
worktreeRoot: worktreesRoot,
|
|
537
663
|
worktreePath: resolvedCwd,
|
|
538
664
|
};
|
|
@@ -596,6 +722,29 @@ export async function listPaseoWorktrees({ cwd, paseoHome, }) {
|
|
|
596
722
|
createdAt: resolveWorktreeCreatedAtIso(entry.path),
|
|
597
723
|
}));
|
|
598
724
|
}
|
|
725
|
+
export async function resolveExistingWorktreeForSlug({ slug, repoRoot, paseoHome, }) {
|
|
726
|
+
const worktrees = await listPaseoWorktrees({
|
|
727
|
+
cwd: repoRoot,
|
|
728
|
+
paseoHome,
|
|
729
|
+
});
|
|
730
|
+
const slugSuffix = `${sep}${slug}`;
|
|
731
|
+
const existingWorktree = worktrees.find((worktree) => worktree.path.endsWith(slugSuffix));
|
|
732
|
+
if (!existingWorktree) {
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
const { stdout } = await runGitCommand(["branch", "--show-current"], {
|
|
736
|
+
cwd: existingWorktree.path,
|
|
737
|
+
env: READ_ONLY_GIT_ENV,
|
|
738
|
+
});
|
|
739
|
+
const branchName = stdout.trim();
|
|
740
|
+
if (!branchName) {
|
|
741
|
+
throw new Error(`Unable to resolve branch for existing worktree: ${existingWorktree.path}`);
|
|
742
|
+
}
|
|
743
|
+
return {
|
|
744
|
+
branchName,
|
|
745
|
+
worktreePath: existingWorktree.path,
|
|
746
|
+
};
|
|
747
|
+
}
|
|
599
748
|
export async function resolvePaseoWorktreeRootForCwd(cwd, options) {
|
|
600
749
|
let gitCommonDir;
|
|
601
750
|
try {
|
|
@@ -638,119 +787,275 @@ export async function resolvePaseoWorktreeRootForCwd(cwd, options) {
|
|
|
638
787
|
worktreePath: match.path,
|
|
639
788
|
};
|
|
640
789
|
}
|
|
641
|
-
export async function deletePaseoWorktree({ cwd, worktreePath, worktreeSlug, paseoHome, }) {
|
|
790
|
+
export async function deletePaseoWorktree({ cwd, worktreePath, worktreeSlug, worktreesRoot, paseoHome, }) {
|
|
642
791
|
if (!worktreePath && !worktreeSlug) {
|
|
643
792
|
throw new Error("worktreePath or worktreeSlug is required");
|
|
644
793
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
794
|
+
// Resolve the worktrees-root. With a repo cwd we hash it the normal way; if
|
|
795
|
+
// git has forgotten about the worktree we expect the caller to hand us the
|
|
796
|
+
// path-derived worktreesRoot from the ownership check.
|
|
797
|
+
let resolvedWorktreesRoot;
|
|
798
|
+
if (worktreesRoot) {
|
|
799
|
+
resolvedWorktreesRoot = worktreesRoot;
|
|
800
|
+
}
|
|
801
|
+
else if (cwd) {
|
|
802
|
+
resolvedWorktreesRoot = await getPaseoWorktreesRoot(cwd, paseoHome);
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
throw new Error("cwd or worktreesRoot is required to delete a Paseo worktree");
|
|
806
|
+
}
|
|
807
|
+
const resolvedRoot = normalizePathForOwnership(resolvedWorktreesRoot) + sep;
|
|
808
|
+
const requestedPath = worktreePath ?? join(resolvedWorktreesRoot, worktreeSlug);
|
|
648
809
|
const resolvedRequested = normalizePathForOwnership(requestedPath);
|
|
649
810
|
const resolvedWorktree = (await resolvePaseoWorktreeRootForCwd(requestedPath, { paseoHome }))?.worktreePath ??
|
|
650
811
|
resolvedRequested;
|
|
651
812
|
if (!resolvedWorktree.startsWith(resolvedRoot)) {
|
|
652
813
|
throw new Error("Refusing to delete non-Paseo worktree");
|
|
653
814
|
}
|
|
654
|
-
await
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
815
|
+
if (await pathExists(resolvedWorktree)) {
|
|
816
|
+
await runWorktreeTeardownCommands({
|
|
817
|
+
worktreePath: resolvedWorktree,
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
if (cwd) {
|
|
821
|
+
try {
|
|
822
|
+
await runGitCommand(["worktree", "remove", resolvedWorktree, "--force"], {
|
|
823
|
+
cwd,
|
|
824
|
+
timeout: 120000,
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
// `git worktree remove` fails if the admin dir is already gone (e.g. a
|
|
829
|
+
// prior archive attempt removed it before the working tree could be
|
|
830
|
+
// fully cleaned up), or if the repo root has moved. Fall through to the
|
|
831
|
+
// rm retry loop below so the operation stays idempotent.
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
await removeDirectoryWithRetries(resolvedWorktree);
|
|
835
|
+
if (cwd) {
|
|
836
|
+
try {
|
|
837
|
+
await runGitCommand(["worktree", "prune"], { cwd, timeout: 30000 });
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
// not critical; git will prune lazily
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
async function pathExists(path) {
|
|
845
|
+
try {
|
|
846
|
+
await stat(path);
|
|
847
|
+
return true;
|
|
848
|
+
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
if (error.code === "ENOENT") {
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async function removeDirectoryWithRetries(path) {
|
|
857
|
+
if (!(await pathExists(path))) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const delaysMs = [0, 100, 300, 700, 1500];
|
|
861
|
+
let lastError = null;
|
|
862
|
+
for (const delay of delaysMs) {
|
|
863
|
+
if (delay > 0) {
|
|
864
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
865
|
+
}
|
|
866
|
+
try {
|
|
867
|
+
await rm(path, { recursive: true, force: true });
|
|
868
|
+
if (!(await pathExists(path))) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
lastError = new Error(`Directory still present after rm: ${path}`);
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
lastError = error;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (await pathExists(path)) {
|
|
878
|
+
throw lastError instanceof Error
|
|
879
|
+
? lastError
|
|
880
|
+
: new Error(`Failed to remove worktree directory: ${path}`);
|
|
663
881
|
}
|
|
664
882
|
}
|
|
665
883
|
/**
|
|
666
884
|
* Create a git worktree with proper naming conventions
|
|
667
885
|
*/
|
|
668
|
-
export async
|
|
669
|
-
|
|
886
|
+
export const createWorktree = async ({ cwd, source, worktreeSlug, runSetup, paseoHome, }) => {
|
|
887
|
+
const sourcePlan = await resolveWorktreeSourcePlan({ cwd, source, desiredSlug: worktreeSlug });
|
|
888
|
+
let worktreePath = join(await getPaseoWorktreesRoot(cwd, paseoHome), worktreeSlug);
|
|
889
|
+
mkdirSync(dirname(worktreePath), { recursive: true });
|
|
890
|
+
// Also handle worktree path collision
|
|
891
|
+
let finalWorktreePath = worktreePath;
|
|
892
|
+
let pathSuffix = 1;
|
|
893
|
+
while (existsSync(finalWorktreePath)) {
|
|
894
|
+
finalWorktreePath = `${worktreePath}-${pathSuffix}`;
|
|
895
|
+
pathSuffix++;
|
|
896
|
+
}
|
|
897
|
+
// Primitive owner for `git worktree add`; callers route through createWorktreeCore.
|
|
898
|
+
await runGitCommand(["worktree", "add", finalWorktreePath, ...sourcePlan.addArguments], {
|
|
899
|
+
cwd,
|
|
900
|
+
timeout: 120000,
|
|
901
|
+
});
|
|
902
|
+
worktreePath = normalizePathForOwnership(finalWorktreePath);
|
|
903
|
+
if (sourcePlan.pushRemote) {
|
|
904
|
+
await configureWorktreePushRemote({
|
|
905
|
+
cwd,
|
|
906
|
+
branchName: sourcePlan.branchName,
|
|
907
|
+
remote: sourcePlan.pushRemote,
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
writePaseoWorktreeMetadata(worktreePath, { baseRefName: sourcePlan.metadataBaseRefName });
|
|
911
|
+
if (runSetup) {
|
|
912
|
+
await runWorktreeSetupCommands({
|
|
913
|
+
worktreePath,
|
|
914
|
+
branchName: sourcePlan.branchName,
|
|
915
|
+
cleanupOnFailure: true,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
return {
|
|
919
|
+
branchName: sourcePlan.branchName,
|
|
920
|
+
worktreePath,
|
|
921
|
+
};
|
|
922
|
+
};
|
|
923
|
+
async function resolveWorktreeSourcePlan({ cwd, source, desiredSlug, }) {
|
|
924
|
+
switch (source.kind) {
|
|
925
|
+
case "branch-off": {
|
|
926
|
+
const branchName = source.newBranchName;
|
|
927
|
+
validateWorktreeBranchName(branchName);
|
|
928
|
+
const normalizedBaseBranch = normalizeRequiredBaseBranch(source.baseBranch);
|
|
929
|
+
const resolvedBaseBranch = await resolveBaseBranchForWorktree(cwd, normalizedBaseBranch);
|
|
930
|
+
const branchExists = await localBranchExists(cwd, branchName);
|
|
931
|
+
const base = branchExists ? branchName : resolvedBaseBranch;
|
|
932
|
+
const candidateBranch = branchExists ? desiredSlug : branchName;
|
|
933
|
+
const newBranchName = await resolveUniqueLocalBranchName(cwd, candidateBranch);
|
|
934
|
+
return {
|
|
935
|
+
branchName: newBranchName,
|
|
936
|
+
metadataBaseRefName: normalizedBaseBranch,
|
|
937
|
+
addArguments: ["-b", newBranchName, base],
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
case "checkout-branch": {
|
|
941
|
+
validateWorktreeBranchName(source.branchName);
|
|
942
|
+
if (!(await localBranchExists(cwd, source.branchName))) {
|
|
943
|
+
try {
|
|
944
|
+
await runGitCommand(["fetch", "origin", `${source.branchName}:${source.branchName}`], {
|
|
945
|
+
cwd,
|
|
946
|
+
timeout: 120000,
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
catch {
|
|
950
|
+
throw new UnknownBranchError({ branchName: source.branchName, cwd });
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (await isBranchCheckedOut(cwd, source.branchName)) {
|
|
954
|
+
throw new BranchAlreadyCheckedOutError(source.branchName);
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
branchName: source.branchName,
|
|
958
|
+
metadataBaseRefName: source.branchName,
|
|
959
|
+
addArguments: [source.branchName],
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
case "checkout-github-pr": {
|
|
963
|
+
const localBranchCandidate = source.localBranchName ?? source.headRef;
|
|
964
|
+
validateWorktreeBranchName(localBranchCandidate);
|
|
965
|
+
const localBranchName = await resolveUniqueLocalBranchName(cwd, localBranchCandidate);
|
|
966
|
+
const normalizedBaseRefName = normalizeRequiredBaseBranch(source.baseRefName);
|
|
967
|
+
await runGitCommand([
|
|
968
|
+
"fetch",
|
|
969
|
+
"origin",
|
|
970
|
+
`refs/pull/${source.githubPrNumber}/head:refs/heads/${localBranchName}`,
|
|
971
|
+
"--force",
|
|
972
|
+
], {
|
|
973
|
+
cwd,
|
|
974
|
+
timeout: 120000,
|
|
975
|
+
});
|
|
976
|
+
return {
|
|
977
|
+
branchName: localBranchName,
|
|
978
|
+
metadataBaseRefName: normalizedBaseRefName,
|
|
979
|
+
addArguments: [localBranchName],
|
|
980
|
+
...(source.pushRemoteUrl
|
|
981
|
+
? {
|
|
982
|
+
pushRemote: {
|
|
983
|
+
name: `paseo-pr-${source.githubPrNumber}`,
|
|
984
|
+
url: source.pushRemoteUrl,
|
|
985
|
+
headRef: source.headRef,
|
|
986
|
+
},
|
|
987
|
+
}
|
|
988
|
+
: {}),
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
async function configureWorktreePushRemote(options) {
|
|
994
|
+
await runGitCommand(["config", `remote.${options.remote.name}.url`, options.remote.url], {
|
|
995
|
+
cwd: options.cwd,
|
|
996
|
+
});
|
|
997
|
+
await runGitCommand(["config", `remote.${options.remote.name}.push`, `HEAD:refs/heads/${options.remote.headRef}`], { cwd: options.cwd });
|
|
998
|
+
await runGitCommand(["config", `branch.${options.branchName}.remote`, options.remote.name], {
|
|
999
|
+
cwd: options.cwd,
|
|
1000
|
+
});
|
|
1001
|
+
await runGitCommand(["config", `branch.${options.branchName}.merge`, `refs/heads/${options.remote.headRef}`], { cwd: options.cwd });
|
|
1002
|
+
}
|
|
1003
|
+
function validateWorktreeBranchName(branchName) {
|
|
670
1004
|
const validation = validateBranchSlug(branchName);
|
|
671
1005
|
if (!validation.valid) {
|
|
672
1006
|
throw new Error(`Invalid branch name: ${validation.error}`);
|
|
673
1007
|
}
|
|
674
|
-
|
|
1008
|
+
}
|
|
1009
|
+
function normalizeRequiredBaseBranch(baseBranch) {
|
|
1010
|
+
const normalizedBaseBranch = normalizeBaseRefName(baseBranch);
|
|
675
1011
|
if (!normalizedBaseBranch) {
|
|
676
1012
|
throw new Error("Base branch is required when creating a Paseo worktree");
|
|
677
1013
|
}
|
|
678
1014
|
if (normalizedBaseBranch === "HEAD") {
|
|
679
1015
|
throw new Error("Base branch cannot be HEAD when creating a Paseo worktree");
|
|
680
1016
|
}
|
|
681
|
-
|
|
682
|
-
|
|
1017
|
+
return normalizedBaseBranch;
|
|
1018
|
+
}
|
|
1019
|
+
async function resolveBaseBranchForWorktree(cwd, normalizedBaseBranch) {
|
|
683
1020
|
try {
|
|
684
1021
|
await runGitCommand(["rev-parse", "--verify", `origin/${normalizedBaseBranch}`], { cwd });
|
|
685
|
-
|
|
1022
|
+
return `origin/${normalizedBaseBranch}`;
|
|
686
1023
|
}
|
|
687
1024
|
catch {
|
|
688
1025
|
try {
|
|
689
1026
|
await runGitCommand(["rev-parse", "--verify", normalizedBaseBranch], { cwd });
|
|
1027
|
+
return normalizedBaseBranch;
|
|
690
1028
|
}
|
|
691
1029
|
catch {
|
|
692
1030
|
throw new Error(`Base branch not found: ${normalizedBaseBranch}`);
|
|
693
1031
|
}
|
|
694
1032
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
worktreePath = join(await getPaseoWorktreesRoot(cwd, paseoHome), desiredSlug);
|
|
698
|
-
mkdirSync(dirname(worktreePath), { recursive: true });
|
|
699
|
-
// Check if branch already exists
|
|
700
|
-
let branchExists = false;
|
|
1033
|
+
}
|
|
1034
|
+
async function localBranchExists(cwd, branchName) {
|
|
701
1035
|
try {
|
|
702
1036
|
await runGitCommand(["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`], {
|
|
703
1037
|
cwd,
|
|
704
1038
|
});
|
|
705
|
-
|
|
1039
|
+
return true;
|
|
706
1040
|
}
|
|
707
1041
|
catch {
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
// If branchName doesn't exist, create it from baseBranch (resolved to remote if needed)
|
|
713
|
-
const base = branchExists ? branchName : resolvedBaseBranch;
|
|
714
|
-
const candidateBranch = branchExists ? desiredSlug : branchName;
|
|
715
|
-
// Find unique branch name if collision
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
async function resolveUniqueLocalBranchName(cwd, candidateBranch) {
|
|
716
1046
|
let newBranchName = candidateBranch;
|
|
717
1047
|
let suffix = 1;
|
|
718
|
-
while (
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
cwd,
|
|
722
|
-
});
|
|
723
|
-
// Branch exists, try with suffix
|
|
724
|
-
newBranchName = `${candidateBranch}-${suffix}`;
|
|
725
|
-
suffix++;
|
|
726
|
-
}
|
|
727
|
-
catch {
|
|
728
|
-
break;
|
|
729
|
-
}
|
|
1048
|
+
while (await localBranchExists(cwd, newBranchName)) {
|
|
1049
|
+
newBranchName = `${candidateBranch}-${suffix}`;
|
|
1050
|
+
suffix++;
|
|
730
1051
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
finalWorktreePath = `${worktreePath}-${pathSuffix}`;
|
|
736
|
-
pathSuffix++;
|
|
737
|
-
}
|
|
738
|
-
await runGitCommand(["worktree", "add", finalWorktreePath, "-b", newBranchName, base], {
|
|
1052
|
+
return newBranchName;
|
|
1053
|
+
}
|
|
1054
|
+
async function isBranchCheckedOut(cwd, branchName) {
|
|
1055
|
+
const { stdout } = await runGitCommand(["worktree", "list", "--porcelain"], {
|
|
739
1056
|
cwd,
|
|
740
|
-
|
|
1057
|
+
env: READ_ONLY_GIT_ENV,
|
|
741
1058
|
});
|
|
742
|
-
|
|
743
|
-
writePaseoWorktreeMetadata(worktreePath, { baseRefName: normalizedBaseBranch });
|
|
744
|
-
if (runSetup) {
|
|
745
|
-
await runWorktreeSetupCommands({
|
|
746
|
-
worktreePath,
|
|
747
|
-
branchName: newBranchName,
|
|
748
|
-
cleanupOnFailure: true,
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
return {
|
|
752
|
-
branchName: newBranchName,
|
|
753
|
-
worktreePath,
|
|
754
|
-
};
|
|
1059
|
+
return parseWorktreeList(stdout).some((entry) => entry.branchName === branchName);
|
|
755
1060
|
}
|
|
756
1061
|
//# sourceMappingURL=worktree.js.map
|