@getpaseo/server 0.1.87 → 0.1.89
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/server/server/agent/agent-manager.js +4 -1
- package/dist/server/server/agent/agent-storage.d.ts +22 -22
- package/dist/server/server/agent/create-agent/create.d.ts +2 -0
- package/dist/server/server/agent/create-agent/create.js +16 -5
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +1 -0
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +4 -0
- package/dist/server/server/agent/mcp-server.d.ts +1 -0
- package/dist/server/server/agent/mcp-server.js +137 -63
- package/dist/server/server/agent/mcp-shared.d.ts +1 -0
- package/dist/server/server/agent/providers/pi/agent.js +13 -0
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
- package/dist/server/server/agent/timeline-projection.d.ts +17 -1
- package/dist/server/server/agent/timeline-projection.js +82 -17
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +1 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +6 -1
- package/dist/server/server/bootstrap.d.ts +7 -2
- package/dist/server/server/bootstrap.js +152 -115
- package/dist/server/server/config.js +41 -0
- package/dist/server/server/loop-service.d.ts +22 -22
- package/dist/server/server/package-version.d.ts +2 -2
- package/dist/server/server/paseo-worktree-archive-service.d.ts +2 -0
- package/dist/server/server/paseo-worktree-archive-service.js +28 -9
- package/dist/server/server/persisted-config.d.ts +89 -33
- package/dist/server/server/persisted-config.js +17 -0
- package/dist/server/server/pid-lock.d.ts +2 -2
- package/dist/server/server/schedule/cron.js +52 -5
- package/dist/server/server/script-health-monitor.d.ts +4 -4
- package/dist/server/server/script-health-monitor.js +6 -6
- package/dist/server/server/script-proxy.d.ts +2 -39
- package/dist/server/server/script-proxy.js +1 -244
- package/dist/server/server/script-route-branch-handler.d.ts +2 -2
- package/dist/server/server/script-route-branch-handler.js +3 -37
- package/dist/server/server/script-status-projection.d.ts +6 -4
- package/dist/server/server/script-status-projection.js +85 -37
- package/dist/server/server/service-proxy.d.ts +237 -0
- package/dist/server/server/service-proxy.js +714 -0
- package/dist/server/server/session.d.ts +11 -4
- package/dist/server/server/session.js +96 -99
- package/dist/server/server/websocket-server.d.ts +7 -4
- package/dist/server/server/websocket-server.js +9 -4
- package/dist/server/server/workspace-directory.js +4 -0
- package/dist/server/server/workspace-git-service.d.ts +3 -0
- package/dist/server/server/workspace-git-service.js +53 -12
- package/dist/server/server/workspace-registry.d.ts +2 -2
- package/dist/server/server/workspace-service-env.d.ts +1 -0
- package/dist/server/server/workspace-service-env.js +23 -18
- package/dist/server/server/worktree/commands.d.ts +2 -0
- package/dist/server/server/worktree/commands.js +4 -1
- package/dist/server/server/worktree-bootstrap.d.ts +4 -3
- package/dist/server/server/worktree-bootstrap.js +14 -13
- package/dist/server/server/worktree-core.d.ts +1 -0
- package/dist/server/server/worktree-core.js +2 -0
- package/dist/server/server/worktree-session.d.ts +6 -2
- package/dist/server/server/worktree-session.js +3 -0
- package/dist/server/services/github-service.d.ts +1 -0
- package/dist/server/services/github-service.js +7 -1
- package/dist/server/terminal/terminal-manager.js +11 -1
- package/dist/server/terminal/terminal-session-controller.d.ts +3 -1
- package/dist/server/terminal/terminal-session-controller.js +22 -12
- package/dist/server/terminal/terminal.d.ts +1 -0
- package/dist/server/terminal/terminal.js +34 -0
- package/dist/server/utils/checkout-git.d.ts +6 -2
- package/dist/server/utils/checkout-git.js +136 -54
- package/dist/server/utils/worktree.d.ts +17 -12
- package/dist/server/utils/worktree.js +39 -22
- package/dist/src/server/persisted-config.js +17 -0
- package/package.json +5 -5
- package/dist/server/utils/script-hostname.d.ts +0 -8
- package/dist/server/utils/script-hostname.js +0 -14
|
@@ -71,6 +71,7 @@ export class WorkspaceGitServiceImpl {
|
|
|
71
71
|
this.checkoutDiffCache = new LRUCache({ max: WORKSPACE_GIT_CHECKOUT_DIFF_CACHE_MAX });
|
|
72
72
|
this.logger = options.logger.child({ module: "workspace-git-service" });
|
|
73
73
|
this.paseoHome = options.paseoHome;
|
|
74
|
+
this.worktreesRoot = options.worktreesRoot;
|
|
74
75
|
this.deps = resolveWorkspaceGitServiceDeps(options.deps);
|
|
75
76
|
}
|
|
76
77
|
registerWorkspace(params, listener) {
|
|
@@ -112,6 +113,7 @@ export class WorkspaceGitServiceImpl {
|
|
|
112
113
|
try {
|
|
113
114
|
const status = await this.deps.getCheckoutStatus(normalizedCwd, {
|
|
114
115
|
paseoHome: this.paseoHome,
|
|
116
|
+
worktreesRoot: this.worktreesRoot,
|
|
115
117
|
logger: this.logger,
|
|
116
118
|
});
|
|
117
119
|
if (!status.isGit) {
|
|
@@ -152,7 +154,10 @@ export class WorkspaceGitServiceImpl {
|
|
|
152
154
|
const normalizedCwd = normalizeWorkspaceId(cwd);
|
|
153
155
|
const normalizedOptions = this.normalizeCheckoutDiffOptions(options);
|
|
154
156
|
const key = this.buildCheckoutDiffCacheKey(normalizedCwd, normalizedOptions);
|
|
155
|
-
return this.readAuxiliaryCache(this.checkoutDiffCache, key, readOptions, () => this.deps.getCheckoutDiff(normalizedCwd, normalizedOptions, {
|
|
157
|
+
return this.readAuxiliaryCache(this.checkoutDiffCache, key, readOptions, () => this.deps.getCheckoutDiff(normalizedCwd, normalizedOptions, {
|
|
158
|
+
paseoHome: this.paseoHome,
|
|
159
|
+
worktreesRoot: this.worktreesRoot,
|
|
160
|
+
}));
|
|
156
161
|
}
|
|
157
162
|
normalizeCheckoutDiffOptions(options) {
|
|
158
163
|
return {
|
|
@@ -221,6 +226,7 @@ export class WorkspaceGitServiceImpl {
|
|
|
221
226
|
return this.readAuxiliaryCache(this.worktreeListCache, key, options, () => this.deps.listPaseoWorktrees({
|
|
222
227
|
cwd: repoRoot,
|
|
223
228
|
paseoHome: this.paseoHome,
|
|
229
|
+
worktreesRoot: this.worktreesRoot,
|
|
224
230
|
}));
|
|
225
231
|
}
|
|
226
232
|
async resolveRepoRoot(cwd, options) {
|
|
@@ -380,7 +386,7 @@ export class WorkspaceGitServiceImpl {
|
|
|
380
386
|
debounceTimer: null,
|
|
381
387
|
selfHealTimer: null,
|
|
382
388
|
githubPollSubscription: null,
|
|
383
|
-
|
|
389
|
+
githubPollKey: null,
|
|
384
390
|
refreshState: { status: "idle" },
|
|
385
391
|
latestGit: null,
|
|
386
392
|
latestGitLoadedAtMs: null,
|
|
@@ -676,21 +682,26 @@ export class WorkspaceGitServiceImpl {
|
|
|
676
682
|
this.stopGitHubPollForTarget(target);
|
|
677
683
|
return;
|
|
678
684
|
}
|
|
679
|
-
const
|
|
680
|
-
const
|
|
685
|
+
const pollTarget = this.resolveGitHubPollTarget(target);
|
|
686
|
+
const remoteUrl = git.remoteUrl;
|
|
687
|
+
const hasGitHubRemote = target.cachedGitHubRemote?.remoteUrl === remoteUrl &&
|
|
681
688
|
target.cachedGitHubRemote.identity !== null;
|
|
682
|
-
if (!
|
|
689
|
+
if (!pollTarget || remoteUrl === null || !hasGitHubRemote) {
|
|
683
690
|
this.stopGitHubPollForTarget(target);
|
|
684
691
|
return;
|
|
685
692
|
}
|
|
686
|
-
|
|
693
|
+
const pollKey = buildWorkspaceGitHubPollKey(remoteUrl, pollTarget);
|
|
694
|
+
if (target.githubPollKey === pollKey && target.githubPollSubscription) {
|
|
687
695
|
return;
|
|
688
696
|
}
|
|
689
697
|
this.stopGitHubPollForTarget(target);
|
|
690
|
-
target.
|
|
698
|
+
target.githubPollKey = pollKey;
|
|
691
699
|
target.githubPollSubscription = this.deps.github.retainCurrentPullRequestStatusPoll({
|
|
692
700
|
cwd: target.cwd,
|
|
693
|
-
headRef,
|
|
701
|
+
headRef: pollTarget.headRef,
|
|
702
|
+
...(pollTarget.headRepositoryOwner
|
|
703
|
+
? { headRepositoryOwner: pollTarget.headRepositoryOwner }
|
|
704
|
+
: {}),
|
|
694
705
|
onStatus: (status) => {
|
|
695
706
|
if (!this.isActiveObservedWorkspaceTarget(target)) {
|
|
696
707
|
return;
|
|
@@ -700,14 +711,33 @@ export class WorkspaceGitServiceImpl {
|
|
|
700
711
|
});
|
|
701
712
|
},
|
|
702
713
|
onError: (error) => {
|
|
703
|
-
this.logger.warn({
|
|
714
|
+
this.logger.warn({
|
|
715
|
+
err: error,
|
|
716
|
+
cwd: target.cwd,
|
|
717
|
+
headRef: pollTarget.headRef,
|
|
718
|
+
headRepositoryOwner: pollTarget.headRepositoryOwner,
|
|
719
|
+
reason: "self-heal-github",
|
|
720
|
+
}, "Failed to run GitHub self-heal refresh");
|
|
704
721
|
},
|
|
705
722
|
});
|
|
706
723
|
}
|
|
724
|
+
resolveGitHubPollTarget(target) {
|
|
725
|
+
const git = target.latestGit;
|
|
726
|
+
if (!git?.currentBranch) {
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
const lookupTarget = target.latestFacts?.isGit && target.latestFacts.currentBranch === git.currentBranch
|
|
730
|
+
? target.latestFacts.pullRequestLookupTarget
|
|
731
|
+
: null;
|
|
732
|
+
if (lookupTarget) {
|
|
733
|
+
return lookupTarget;
|
|
734
|
+
}
|
|
735
|
+
return { headRef: git.currentBranch };
|
|
736
|
+
}
|
|
707
737
|
stopGitHubPollForTarget(target) {
|
|
708
738
|
target.githubPollSubscription?.unsubscribe();
|
|
709
739
|
target.githubPollSubscription = null;
|
|
710
|
-
target.
|
|
740
|
+
target.githubPollKey = null;
|
|
711
741
|
}
|
|
712
742
|
addWorkingTreeWatcher(target, watchPath, shouldTryRecursive) {
|
|
713
743
|
if (target.watchedPaths.has(watchPath)) {
|
|
@@ -998,7 +1028,11 @@ export class WorkspaceGitServiceImpl {
|
|
|
998
1028
|
target.lastShellOutAtMs = now.getTime();
|
|
999
1029
|
const cwd = target.cwd;
|
|
1000
1030
|
const previousGitHubPollKey = this.getGitHubPollKey(target);
|
|
1001
|
-
const baseContext = {
|
|
1031
|
+
const baseContext = {
|
|
1032
|
+
paseoHome: this.paseoHome,
|
|
1033
|
+
worktreesRoot: this.worktreesRoot,
|
|
1034
|
+
logger: this.logger,
|
|
1035
|
+
};
|
|
1002
1036
|
const facts = await this.loadCheckoutFacts(target, {
|
|
1003
1037
|
...baseContext,
|
|
1004
1038
|
allowRecent: !request.force,
|
|
@@ -1075,7 +1109,11 @@ export class WorkspaceGitServiceImpl {
|
|
|
1075
1109
|
if (!githubRemote || githubRemote.remoteUrl !== git.remoteUrl || !githubRemote.identity) {
|
|
1076
1110
|
return null;
|
|
1077
1111
|
}
|
|
1078
|
-
|
|
1112
|
+
const pollTarget = this.resolveGitHubPollTarget(target);
|
|
1113
|
+
if (!pollTarget) {
|
|
1114
|
+
return null;
|
|
1115
|
+
}
|
|
1116
|
+
return buildWorkspaceGitHubPollKey(git.remoteUrl, pollTarget);
|
|
1079
1117
|
}
|
|
1080
1118
|
rememberGitHubSnapshot(target, github, options) {
|
|
1081
1119
|
if (target.closed || this.workspaceTargets.get(target.cwd) !== target) {
|
|
@@ -1317,6 +1355,9 @@ function buildGitHubSnapshotFromStatus(status) {
|
|
|
1317
1355
|
error: null,
|
|
1318
1356
|
};
|
|
1319
1357
|
}
|
|
1358
|
+
function buildWorkspaceGitHubPollKey(remoteUrl, target) {
|
|
1359
|
+
return JSON.stringify([remoteUrl, target.headRef, target.headRepositoryOwner ?? null]);
|
|
1360
|
+
}
|
|
1320
1361
|
async function runGitFetch(cwd) {
|
|
1321
1362
|
await runGitCommand(["fetch", "origin", "--prune"], {
|
|
1322
1363
|
cwd,
|
|
@@ -39,6 +39,7 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
|
|
|
39
39
|
updatedAt: z.ZodString;
|
|
40
40
|
archivedAt: z.ZodNullable<z.ZodString>;
|
|
41
41
|
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
workspaceId: string;
|
|
42
43
|
cwd: string;
|
|
43
44
|
createdAt: string;
|
|
44
45
|
updatedAt: string;
|
|
@@ -46,8 +47,8 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
|
|
|
46
47
|
kind: "worktree" | "local_checkout" | "directory";
|
|
47
48
|
projectId: string;
|
|
48
49
|
displayName: string;
|
|
49
|
-
workspaceId: string;
|
|
50
50
|
}, {
|
|
51
|
+
workspaceId: string;
|
|
51
52
|
cwd: string;
|
|
52
53
|
createdAt: string;
|
|
53
54
|
updatedAt: string;
|
|
@@ -55,7 +56,6 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
|
|
|
55
56
|
kind: "worktree" | "local_checkout" | "directory";
|
|
56
57
|
projectId: string;
|
|
57
58
|
displayName: string;
|
|
58
|
-
workspaceId: string;
|
|
59
59
|
}>;
|
|
60
60
|
export type PersistedProjectRecord = z.infer<typeof PersistedProjectRecordSchema>;
|
|
61
61
|
export type PersistedWorkspaceRecord = z.infer<typeof PersistedWorkspaceRecordSchema>;
|
|
@@ -8,6 +8,7 @@ export interface BuildWorkspaceServiceEnvOptions {
|
|
|
8
8
|
branchName: string | null;
|
|
9
9
|
daemonPort: number | null | undefined;
|
|
10
10
|
daemonListenHost: string | null | undefined;
|
|
11
|
+
serviceProxyPublicBaseUrl?: string | null;
|
|
11
12
|
peers: readonly WorkspaceServicePeer[];
|
|
12
13
|
}
|
|
13
14
|
export declare function normalizeServiceEnvName(scriptName: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { projectServiceProxyUrls } from "./service-proxy.js";
|
|
2
2
|
export function normalizeServiceEnvName(scriptName) {
|
|
3
3
|
return scriptName
|
|
4
4
|
.toUpperCase()
|
|
@@ -16,24 +16,28 @@ export function buildWorkspaceServiceEnv(options) {
|
|
|
16
16
|
HOST: resolveServiceBindHost(options.daemonListenHost),
|
|
17
17
|
PASEO_PORT: String(selfPeer.port),
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
const selfProxyUrl = buildServiceProxyUrl({
|
|
20
|
+
projectSlug: options.projectSlug,
|
|
21
|
+
branchName: options.branchName,
|
|
22
|
+
scriptName: options.scriptName,
|
|
23
|
+
daemonPort: options.daemonPort,
|
|
24
|
+
serviceProxyPublicBaseUrl: options.serviceProxyPublicBaseUrl,
|
|
25
|
+
});
|
|
26
|
+
if (selfProxyUrl) {
|
|
27
|
+
env.PASEO_URL = selfProxyUrl;
|
|
26
28
|
}
|
|
27
29
|
for (const peer of options.peers) {
|
|
28
30
|
const envName = normalizeServiceEnvName(peer.scriptName);
|
|
29
31
|
env[`PASEO_SERVICE_${envName}_PORT`] = String(peer.port);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const peerProxyUrl = buildServiceProxyUrl({
|
|
33
|
+
projectSlug: options.projectSlug,
|
|
34
|
+
branchName: options.branchName,
|
|
35
|
+
scriptName: peer.scriptName,
|
|
36
|
+
daemonPort: options.daemonPort,
|
|
37
|
+
serviceProxyPublicBaseUrl: options.serviceProxyPublicBaseUrl,
|
|
38
|
+
});
|
|
39
|
+
if (peerProxyUrl) {
|
|
40
|
+
env[`PASEO_SERVICE_${envName}_URL`] = peerProxyUrl;
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
return env;
|
|
@@ -42,12 +46,13 @@ export function resolveServiceBindHost(daemonListenHost) {
|
|
|
42
46
|
return isLoopbackListenHost(daemonListenHost) ? "127.0.0.1" : "0.0.0.0";
|
|
43
47
|
}
|
|
44
48
|
function buildServiceProxyUrl(options) {
|
|
45
|
-
|
|
49
|
+
return projectServiceProxyUrls({
|
|
46
50
|
projectSlug: options.projectSlug,
|
|
47
51
|
branchName: options.branchName,
|
|
48
52
|
scriptName: options.scriptName,
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
daemonPort: options.daemonPort,
|
|
54
|
+
publicBaseUrl: options.serviceProxyPublicBaseUrl,
|
|
55
|
+
}).proxyUrl;
|
|
51
56
|
}
|
|
52
57
|
function isLoopbackListenHost(host) {
|
|
53
58
|
if (!host) {
|
|
@@ -13,10 +13,12 @@ export declare function listPaseoWorktreesCommand(dependencies: ListPaseoWorktre
|
|
|
13
13
|
type CreatePaseoWorktreeWorkflow<Result extends CreatePaseoWorktreeResult> = (input: CreatePaseoWorktreeInput) => Promise<Result>;
|
|
14
14
|
export interface CreatePaseoWorktreeCommandDependencies<Result extends CreatePaseoWorktreeResult = CreatePaseoWorktreeResult> {
|
|
15
15
|
paseoHome?: string;
|
|
16
|
+
worktreesRoot?: string;
|
|
16
17
|
createPaseoWorktreeWorkflow?: CreatePaseoWorktreeWorkflow<Result>;
|
|
17
18
|
}
|
|
18
19
|
export type CreatePaseoWorktreeCommandInput = Omit<CreatePaseoWorktreeInput, "paseoHome" | "runSetup"> & {
|
|
19
20
|
paseoHome?: string;
|
|
21
|
+
worktreesRoot?: string;
|
|
20
22
|
};
|
|
21
23
|
export type CreatePaseoWorktreeCommandResult<Result extends CreatePaseoWorktreeResult> = {
|
|
22
24
|
ok: true;
|
|
@@ -17,6 +17,7 @@ export async function createPaseoWorktreeCommand(dependencies, input) {
|
|
|
17
17
|
...input,
|
|
18
18
|
runSetup: false,
|
|
19
19
|
paseoHome: input.paseoHome ?? dependencies.paseoHome,
|
|
20
|
+
worktreesRoot: input.worktreesRoot ?? dependencies.worktreesRoot,
|
|
20
21
|
});
|
|
21
22
|
return { ok: true, createdWorktree };
|
|
22
23
|
}
|
|
@@ -32,6 +33,7 @@ export async function archivePaseoWorktreeCommand(dependencies, input) {
|
|
|
32
33
|
const resolvedTarget = await resolveArchiveTarget(dependencies, input);
|
|
33
34
|
const ownership = await isPaseoOwnedWorktreeCwd(resolvedTarget.targetPath, {
|
|
34
35
|
paseoHome: dependencies.paseoHome,
|
|
36
|
+
worktreesRoot: dependencies.worktreesRoot,
|
|
35
37
|
});
|
|
36
38
|
if (!ownership.allowed) {
|
|
37
39
|
return {
|
|
@@ -46,6 +48,7 @@ export async function archivePaseoWorktreeCommand(dependencies, input) {
|
|
|
46
48
|
targetPath: resolvedTarget.targetPath,
|
|
47
49
|
repoRoot,
|
|
48
50
|
worktreesRoot: ownership.worktreeRoot,
|
|
51
|
+
worktreesBaseRoot: dependencies.worktreesRoot,
|
|
49
52
|
requestId: input.requestId,
|
|
50
53
|
});
|
|
51
54
|
return {
|
|
@@ -78,7 +81,7 @@ async function resolveArchiveTarget(dependencies, input) {
|
|
|
78
81
|
throw new Error("worktreePath, worktreeSlug, or repoRoot+branchName is required");
|
|
79
82
|
}
|
|
80
83
|
async function resolveWorktreeSlugPath(dependencies, repoRoot, worktreeSlug) {
|
|
81
|
-
const worktreesRoot = await getPaseoWorktreesRoot(repoRoot, dependencies.paseoHome);
|
|
84
|
+
const worktreesRoot = await getPaseoWorktreesRoot(repoRoot, dependencies.paseoHome, dependencies.worktreesRoot);
|
|
82
85
|
return join(worktreesRoot, worktreeSlug);
|
|
83
86
|
}
|
|
84
87
|
//# sourceMappingURL=commands.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "pino";
|
|
2
2
|
import type { TerminalManager } from "../terminal/terminal-manager.js";
|
|
3
3
|
import { runWorktreeSetupCommands, type WorktreeConfig, type WorktreeSetupCommandResult } from "../utils/worktree.js";
|
|
4
|
-
import { type
|
|
4
|
+
import { type ServiceProxySubsystem } from "./service-proxy.js";
|
|
5
5
|
import type { WorkspaceScriptRuntimeStore } from "./workspace-script-runtime-store.js";
|
|
6
6
|
import type { AgentTimelineItem, ToolCallDetail } from "./agent/agent-sdk-types.js";
|
|
7
7
|
export interface WorktreeBootstrapTerminalResult {
|
|
@@ -58,7 +58,8 @@ interface SpawnWorkspaceScriptOptions {
|
|
|
58
58
|
scriptName: string;
|
|
59
59
|
daemonPort?: number | null;
|
|
60
60
|
daemonListenHost?: string | null;
|
|
61
|
-
|
|
61
|
+
serviceProxyPublicBaseUrl?: string | null;
|
|
62
|
+
serviceProxy: ServiceProxySubsystem;
|
|
62
63
|
runtimeStore: WorkspaceScriptRuntimeStore;
|
|
63
64
|
terminalManager: TerminalManager;
|
|
64
65
|
logger?: Logger;
|
|
@@ -67,7 +68,7 @@ interface SpawnWorkspaceScriptOptions {
|
|
|
67
68
|
export declare function spawnWorkspaceScript(options: SpawnWorkspaceScriptOptions): Promise<WorktreeScriptResult>;
|
|
68
69
|
export declare function teardownWorktreeScripts(options: {
|
|
69
70
|
hostnames: string[];
|
|
70
|
-
|
|
71
|
+
serviceProxy: Pick<ServiceProxySubsystem, "removeServiceRoutesByHostnames">;
|
|
71
72
|
logger: Logger;
|
|
72
73
|
}): void;
|
|
73
74
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from "uuid";
|
|
2
|
-
import { buildScriptHostname } from "../utils/script-hostname.js";
|
|
3
2
|
import { getScriptConfigs, getWorktreeTerminalSpecs, isServiceScript, paseoConfigParseError, processCarriageReturns, readPaseoConfig, resolveWorktreeRuntimeEnv, runWorktreeSetupCommands, WorktreeSetupError, } from "../utils/worktree.js";
|
|
4
|
-
import { findFreePort } from "./
|
|
3
|
+
import { findFreePort } from "./service-proxy.js";
|
|
5
4
|
import { assertNoServiceEnvNameCollisions, buildWorkspaceServiceEnv, } from "./workspace-service-env.js";
|
|
6
5
|
import { ensureWorkspaceServicePortPlan, requirePlannedWorkspaceServicePort, refreshWorkspaceServicePort, } from "./workspace-service-port-registry.js";
|
|
7
6
|
const MAX_WORKTREE_SETUP_COMMAND_OUTPUT_BYTES = 64 * 1024;
|
|
@@ -501,8 +500,7 @@ export async function runAsyncWorktreeBootstrap(options) {
|
|
|
501
500
|
await runWorktreeTerminalBootstrap(options, runtimeEnv);
|
|
502
501
|
}
|
|
503
502
|
async function setupServiceScriptRoute(params) {
|
|
504
|
-
const { scriptConfigs, config, scriptName, projectSlug, branchName, workspaceId, daemonPort, daemonListenHost, existingRuntimeEntry,
|
|
505
|
-
const hostname = buildScriptHostname({ projectSlug, branchName, scriptName });
|
|
503
|
+
const { scriptConfigs, config, scriptName, projectSlug, branchName, workspaceId, daemonPort, daemonListenHost, serviceProxyPublicBaseUrl, existingRuntimeEntry, serviceProxy, } = params;
|
|
506
504
|
const serviceDeclarations = [];
|
|
507
505
|
for (const [configuredScriptName, scriptConfig] of scriptConfigs) {
|
|
508
506
|
if (isServiceScript(scriptConfig)) {
|
|
@@ -538,16 +536,18 @@ async function setupServiceScriptRoute(params) {
|
|
|
538
536
|
branchName,
|
|
539
537
|
daemonPort,
|
|
540
538
|
daemonListenHost,
|
|
539
|
+
serviceProxyPublicBaseUrl,
|
|
541
540
|
peers,
|
|
542
541
|
});
|
|
543
|
-
|
|
544
|
-
hostname,
|
|
542
|
+
const registeredRoute = serviceProxy.registerWorkspaceService({
|
|
545
543
|
port,
|
|
546
544
|
workspaceId,
|
|
547
545
|
projectSlug,
|
|
546
|
+
branchName,
|
|
548
547
|
scriptName,
|
|
548
|
+
publicBaseUrl: serviceProxyPublicBaseUrl ?? null,
|
|
549
549
|
});
|
|
550
|
-
return { hostname, port, env };
|
|
550
|
+
return { hostname: registeredRoute.hostname, port, env };
|
|
551
551
|
}
|
|
552
552
|
async function acquireWorkspaceScriptTerminal(params) {
|
|
553
553
|
const { serviceScript, existingRuntimeEntry, terminalManager, repoRoot, scriptName, env } = params;
|
|
@@ -565,7 +565,7 @@ async function acquireWorkspaceScriptTerminal(params) {
|
|
|
565
565
|
return { terminal, reusableTerminal };
|
|
566
566
|
}
|
|
567
567
|
export async function spawnWorkspaceScript(options) {
|
|
568
|
-
const { repoRoot, workspaceId, projectSlug, branchName, scriptName, daemonPort, daemonListenHost,
|
|
568
|
+
const { repoRoot, workspaceId, projectSlug, branchName, scriptName, daemonPort, daemonListenHost, serviceProxyPublicBaseUrl, serviceProxy, runtimeStore, terminalManager, logger, onLifecycleChanged, } = options;
|
|
569
569
|
const configResult = readPaseoConfig(repoRoot);
|
|
570
570
|
if (!configResult.ok) {
|
|
571
571
|
throw paseoConfigParseError(configResult);
|
|
@@ -598,8 +598,9 @@ export async function spawnWorkspaceScript(options) {
|
|
|
598
598
|
workspaceId,
|
|
599
599
|
daemonPort,
|
|
600
600
|
daemonListenHost,
|
|
601
|
+
serviceProxyPublicBaseUrl,
|
|
601
602
|
existingRuntimeEntry,
|
|
602
|
-
|
|
603
|
+
serviceProxy,
|
|
603
604
|
});
|
|
604
605
|
hostname = serviceSetup.hostname;
|
|
605
606
|
port = serviceSetup.port;
|
|
@@ -631,7 +632,7 @@ export async function spawnWorkspaceScript(options) {
|
|
|
631
632
|
disposeLifecycleListeners?.();
|
|
632
633
|
disposeLifecycleListeners = null;
|
|
633
634
|
if (input.removeRoute && hostname) {
|
|
634
|
-
|
|
635
|
+
serviceProxy.removeWorkspaceService({ workspaceId, scriptName });
|
|
635
636
|
}
|
|
636
637
|
runtimeStore.set({
|
|
637
638
|
workspaceId,
|
|
@@ -689,7 +690,7 @@ export async function spawnWorkspaceScript(options) {
|
|
|
689
690
|
catch (error) {
|
|
690
691
|
disposeLifecycleListeners?.();
|
|
691
692
|
if (routeRegistered && hostname) {
|
|
692
|
-
|
|
693
|
+
serviceProxy.removeServiceRoutesByHostnames([hostname]);
|
|
693
694
|
}
|
|
694
695
|
if (runtimeRegistered) {
|
|
695
696
|
runtimeStore.remove({ workspaceId, scriptName });
|
|
@@ -707,9 +708,9 @@ export async function spawnWorkspaceScript(options) {
|
|
|
707
708
|
}
|
|
708
709
|
}
|
|
709
710
|
export function teardownWorktreeScripts(options) {
|
|
710
|
-
const { hostnames,
|
|
711
|
+
const { hostnames, serviceProxy, logger } = options;
|
|
712
|
+
serviceProxy.removeServiceRoutesByHostnames(hostnames);
|
|
711
713
|
for (const hostname of hostnames) {
|
|
712
|
-
routeStore.removeRoute(hostname);
|
|
713
714
|
logger.info({ hostname }, "Removed script proxy route");
|
|
714
715
|
}
|
|
715
716
|
}
|
|
@@ -54,6 +54,7 @@ export async function createWorktreeCore(input, deps) {
|
|
|
54
54
|
slug: normalizedSlug,
|
|
55
55
|
repoRoot,
|
|
56
56
|
paseoHome: input.paseoHome,
|
|
57
|
+
worktreesRoot: input.worktreesRoot,
|
|
57
58
|
});
|
|
58
59
|
if (existingWorktree) {
|
|
59
60
|
return { worktree: existingWorktree, intent, repoRoot, created: false };
|
|
@@ -65,6 +66,7 @@ export async function createWorktreeCore(input, deps) {
|
|
|
65
66
|
source: intent,
|
|
66
67
|
runSetup: input.runSetup ?? true,
|
|
67
68
|
paseoHome: input.paseoHome,
|
|
69
|
+
worktreesRoot: input.worktreesRoot,
|
|
68
70
|
}),
|
|
69
71
|
intent,
|
|
70
72
|
repoRoot,
|
|
@@ -5,7 +5,7 @@ import type { PersistedWorkspaceRecord } from "./workspace-registry.js";
|
|
|
5
5
|
import type { WorkspaceGitService } from "./workspace-git-service.js";
|
|
6
6
|
import { runAsyncWorktreeBootstrap } from "./worktree-bootstrap.js";
|
|
7
7
|
import type { TerminalManager } from "../terminal/terminal-manager.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { ServiceProxySubsystem } from "./service-proxy.js";
|
|
9
9
|
import type { WorkspaceScriptRuntimeStore } from "./workspace-script-runtime-store.js";
|
|
10
10
|
import type { GitHubService } from "../services/github-service.js";
|
|
11
11
|
import type { CheckoutExistingBranchResult } from "../utils/checkout-git.js";
|
|
@@ -31,6 +31,7 @@ type AgentWorktreeSetupTimelineWriter = (input: {
|
|
|
31
31
|
}) => Promise<boolean>;
|
|
32
32
|
interface BuildAgentSessionConfigDependencies {
|
|
33
33
|
paseoHome?: string;
|
|
34
|
+
worktreesRoot?: string;
|
|
34
35
|
sessionLogger: Logger;
|
|
35
36
|
workspaceGitService?: WorkspaceGitService;
|
|
36
37
|
createPaseoWorktree: (input: CreatePaseoWorktreeInput, options?: {
|
|
@@ -47,6 +48,7 @@ interface BuildAgentSessionConfigDependencies {
|
|
|
47
48
|
}
|
|
48
49
|
interface CreatePaseoWorktreeInBackgroundDependencies {
|
|
49
50
|
paseoHome?: string;
|
|
51
|
+
worktreesRoot?: string;
|
|
50
52
|
emitWorkspaceUpdateForCwd: (cwd: string, options?: {
|
|
51
53
|
dedupeGitState?: boolean;
|
|
52
54
|
}) => Promise<void>;
|
|
@@ -55,10 +57,11 @@ interface CreatePaseoWorktreeInBackgroundDependencies {
|
|
|
55
57
|
sessionLogger: Logger;
|
|
56
58
|
terminalManager: TerminalManager | null;
|
|
57
59
|
archiveWorkspaceRecord: (workspaceId: string) => Promise<void>;
|
|
58
|
-
|
|
60
|
+
serviceProxy: ServiceProxySubsystem | null;
|
|
59
61
|
scriptRuntimeStore: WorkspaceScriptRuntimeStore | null;
|
|
60
62
|
getDaemonTcpPort: (() => number | null) | null;
|
|
61
63
|
getDaemonTcpHost: (() => string | null) | null;
|
|
64
|
+
serviceProxyPublicBaseUrl?: string | null;
|
|
62
65
|
onScriptsChanged: ((workspaceId: string, workspaceDirectory: string) => void) | null;
|
|
63
66
|
}
|
|
64
67
|
interface CreatePaseoWorktreeWorkflowDependencies extends CreatePaseoWorktreeInBackgroundDependencies {
|
|
@@ -100,6 +103,7 @@ interface HandleWorkspaceSetupStatusRequestDependencies {
|
|
|
100
103
|
}
|
|
101
104
|
interface HandleCreatePaseoWorktreeRequestDependencies {
|
|
102
105
|
paseoHome?: string;
|
|
106
|
+
worktreesRoot?: string;
|
|
103
107
|
describeWorkspaceRecord: (result: CreatePaseoWorktreeResult) => Promise<WorkspaceDescriptorPayload>;
|
|
104
108
|
emit: EmitSessionMessage;
|
|
105
109
|
sessionLogger: Logger;
|
|
@@ -41,6 +41,7 @@ export async function buildAgentSessionConfig(dependencies, config, gitOptions,
|
|
|
41
41
|
firstAgentContext,
|
|
42
42
|
runSetup: false,
|
|
43
43
|
paseoHome: dependencies.paseoHome,
|
|
44
|
+
worktreesRoot: dependencies.worktreesRoot,
|
|
44
45
|
}, {
|
|
45
46
|
resolveDefaultBranch: normalized.baseBranch
|
|
46
47
|
? async () => normalized.baseBranch
|
|
@@ -241,6 +242,7 @@ export async function handleCreatePaseoWorktreeRequest(dependencies, request) {
|
|
|
241
242
|
try {
|
|
242
243
|
const commandResult = await createPaseoWorktreeCommand({
|
|
243
244
|
paseoHome: dependencies.paseoHome,
|
|
245
|
+
worktreesRoot: dependencies.worktreesRoot,
|
|
244
246
|
createPaseoWorktreeWorkflow: dependencies.createPaseoWorktreeWorkflow,
|
|
245
247
|
}, {
|
|
246
248
|
cwd: request.cwd,
|
|
@@ -304,6 +306,7 @@ export async function createPaseoWorktreeWorkflow(dependencies, input, options)
|
|
|
304
306
|
...input,
|
|
305
307
|
runSetup: false,
|
|
306
308
|
paseoHome: input.paseoHome ?? dependencies.paseoHome,
|
|
309
|
+
worktreesRoot: input.worktreesRoot ?? dependencies.worktreesRoot,
|
|
307
310
|
}, options?.resolveDefaultBranch
|
|
308
311
|
? { resolveDefaultBranch: options.resolveDefaultBranch }
|
|
309
312
|
: undefined);
|
|
@@ -228,6 +228,7 @@ export interface GitHubService {
|
|
|
228
228
|
retainCurrentPullRequestStatusPoll?(options: {
|
|
229
229
|
cwd: string;
|
|
230
230
|
headRef: string;
|
|
231
|
+
headRepositoryOwner?: string;
|
|
231
232
|
onStatus?: (status: GitHubCurrentPullRequestStatus | null) => void;
|
|
232
233
|
onError?: (error: unknown) => void;
|
|
233
234
|
}): {
|
|
@@ -425,7 +425,10 @@ export function createGitHubService(options = {}) {
|
|
|
425
425
|
return buildCacheKey({
|
|
426
426
|
cwd: target.cwd,
|
|
427
427
|
method: "getCurrentPullRequestStatus",
|
|
428
|
-
args: {
|
|
428
|
+
args: {
|
|
429
|
+
headRef: target.headRef,
|
|
430
|
+
headRepositoryOwner: target.headRepositoryOwner,
|
|
431
|
+
},
|
|
429
432
|
});
|
|
430
433
|
}
|
|
431
434
|
function updatePollTargetAfterSuccess(update) {
|
|
@@ -465,6 +468,7 @@ export function createGitHubService(options = {}) {
|
|
|
465
468
|
await api.getCurrentPullRequestStatus({
|
|
466
469
|
cwd: target.cwd,
|
|
467
470
|
headRef: target.headRef,
|
|
471
|
+
headRepositoryOwner: target.headRepositoryOwner,
|
|
468
472
|
reason: "self-heal-github",
|
|
469
473
|
});
|
|
470
474
|
}
|
|
@@ -601,6 +605,7 @@ export function createGitHubService(options = {}) {
|
|
|
601
605
|
updatePollTargetAfterSuccess({
|
|
602
606
|
cwd: input.cwd,
|
|
603
607
|
headRef: input.headRef,
|
|
608
|
+
headRepositoryOwner: input.headRepositoryOwner,
|
|
604
609
|
status,
|
|
605
610
|
notify: input.reason === "self-heal-github",
|
|
606
611
|
});
|
|
@@ -795,6 +800,7 @@ export function createGitHubService(options = {}) {
|
|
|
795
800
|
target = {
|
|
796
801
|
cwd: input.cwd,
|
|
797
802
|
headRef: input.headRef,
|
|
803
|
+
headRepositoryOwner: input.headRepositoryOwner,
|
|
798
804
|
retainCount: 0,
|
|
799
805
|
timer: null,
|
|
800
806
|
latestStatus: null,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createTerminal, } from "./terminal.js";
|
|
2
2
|
import { captureTerminalLines } from "./terminal-capture.js";
|
|
3
3
|
import { resolve, sep, win32, posix } from "node:path";
|
|
4
|
+
import { isSameOrDescendantPath } from "../server/path-utils.js";
|
|
4
5
|
export function createTerminalManager() {
|
|
5
6
|
const terminalsByCwd = new Map();
|
|
6
7
|
const terminalsById = new Map();
|
|
@@ -99,7 +100,16 @@ export function createTerminalManager() {
|
|
|
99
100
|
return {
|
|
100
101
|
async getTerminals(cwd) {
|
|
101
102
|
assertAbsolutePath(cwd);
|
|
102
|
-
|
|
103
|
+
// Terminals are bucketed by exact cwd, but an agent can open a terminal in
|
|
104
|
+
// a subdirectory of the workspace. A query for the workspace root must
|
|
105
|
+
// surface those too, so aggregate every bucket at or below `cwd`.
|
|
106
|
+
const sessions = [];
|
|
107
|
+
for (const [bucketCwd, bucketSessions] of terminalsByCwd) {
|
|
108
|
+
if (isSameOrDescendantPath(cwd, bucketCwd)) {
|
|
109
|
+
sessions.push(...bucketSessions);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return sessions;
|
|
103
113
|
},
|
|
104
114
|
async createTerminal(options) {
|
|
105
115
|
assertAbsolutePath(options.cwd);
|
|
@@ -9,6 +9,7 @@ export interface TerminalSessionControllerOptions {
|
|
|
9
9
|
hasBinaryChannel: () => boolean;
|
|
10
10
|
isPathWithinRoot: (rootPath: string, candidatePath: string) => boolean;
|
|
11
11
|
sessionLogger: pino.Logger;
|
|
12
|
+
clientSupportsWrapReflow?: () => boolean;
|
|
12
13
|
}
|
|
13
14
|
export interface TerminalSessionControllerMetrics {
|
|
14
15
|
directorySubscriptionCount: number;
|
|
@@ -21,6 +22,7 @@ export declare class TerminalSessionController {
|
|
|
21
22
|
private readonly hasBinaryChannel;
|
|
22
23
|
private readonly isPathWithinRoot;
|
|
23
24
|
private readonly sessionLogger;
|
|
25
|
+
private readonly clientSupportsWrapReflow;
|
|
24
26
|
private readonly subscribedDirectories;
|
|
25
27
|
private unsubscribeTerminalsChanged;
|
|
26
28
|
private readonly exitSubscriptions;
|
|
@@ -45,7 +47,7 @@ export declare class TerminalSessionController {
|
|
|
45
47
|
private handleTerminalsChanged;
|
|
46
48
|
private handleSubscribeTerminalsRequest;
|
|
47
49
|
private handleUnsubscribeTerminalsRequest;
|
|
48
|
-
private
|
|
50
|
+
private emitTerminalsSnapshotForRoot;
|
|
49
51
|
private handleListTerminalsRequest;
|
|
50
52
|
private getAllTerminalSessions;
|
|
51
53
|
private handleCreateTerminalRequest;
|