@agentuity/cli 2.0.10 → 2.0.12
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/cache/resource-region.d.ts.map +1 -1
- package/dist/cache/resource-region.js +48 -25
- package/dist/cache/resource-region.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +4 -4
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts +20 -0
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +62 -4
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/index.d.ts +0 -1
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +0 -1
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/static-renderer.d.ts +17 -0
- package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
- package/dist/cmd/build/vite/static-renderer.js +18 -6
- package/dist/cmd/build/vite/static-renderer.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +34 -27
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.d.ts +9 -0
- package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js +5 -1
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +12 -1
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite/ws-proxy.d.ts +15 -1
- package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -1
- package/dist/cmd/build/vite/ws-proxy.js +33 -0
- package/dist/cmd/build/vite/ws-proxy.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +98 -39
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/create.js +3 -4
- package/dist/cmd/cloud/sandbox/checkpoint/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/delete.js +3 -4
- package/dist/cmd/cloud/sandbox/checkpoint/delete.js.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/list.js +3 -4
- package/dist/cmd/cloud/sandbox/checkpoint/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/restore.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/checkpoint/restore.js +3 -4
- package/dist/cmd/cloud/sandbox/checkpoint/restore.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +13 -4
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.js +3 -4
- package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
- package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/env.js +3 -5
- package/dist/cmd/cloud/sandbox/env.js.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.js +114 -41
- package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
- package/dist/cmd/cloud/sandbox/execution/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/execution/list.js +3 -5
- package/dist/cmd/cloud/sandbox/execution/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/cp.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/cp.js +61 -113
- package/dist/cmd/cloud/sandbox/fs/cp.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/download.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/download.js +11 -22
- package/dist/cmd/cloud/sandbox/fs/download.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/ls.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/ls.js +3 -5
- package/dist/cmd/cloud/sandbox/fs/ls.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/mkdir.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/mkdir.js +3 -5
- package/dist/cmd/cloud/sandbox/fs/mkdir.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rm.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rm.js +3 -5
- package/dist/cmd/cloud/sandbox/fs/rm.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rmdir.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rmdir.js +3 -5
- package/dist/cmd/cloud/sandbox/fs/rmdir.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/upload.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/upload.js +7 -8
- package/dist/cmd/cloud/sandbox/fs/upload.js.map +1 -1
- package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/get.js +21 -7
- package/dist/cmd/cloud/sandbox/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/job/create.js +3 -4
- package/dist/cmd/cloud/sandbox/job/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/destroy.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/job/destroy.js +3 -4
- package/dist/cmd/cloud/sandbox/job/destroy.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/job/get.js +3 -4
- package/dist/cmd/cloud/sandbox/job/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/job/list.js +3 -4
- package/dist/cmd/cloud/sandbox/job/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/job/logs.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/job/logs.js +4 -4
- package/dist/cmd/cloud/sandbox/job/logs.js.map +1 -1
- package/dist/cmd/cloud/sandbox/pause.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/pause.js +21 -5
- package/dist/cmd/cloud/sandbox/pause.js.map +1 -1
- package/dist/cmd/cloud/sandbox/resume.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/resume.js +3 -4
- package/dist/cmd/cloud/sandbox/resume.js.map +1 -1
- package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/run.js +36 -7
- package/dist/cmd/cloud/sandbox/run.js.map +1 -1
- package/dist/cmd/cloud/sandbox/util.d.ts +19 -0
- package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/util.js +40 -2
- package/dist/cmd/cloud/sandbox/util.js.map +1 -1
- package/dist/cmd/coder/create.d.ts.map +1 -1
- package/dist/cmd/coder/create.js +18 -0
- package/dist/cmd/coder/create.js.map +1 -1
- package/dist/cmd/coder/index.d.ts.map +1 -1
- package/dist/cmd/coder/index.js +4 -0
- package/dist/cmd/coder/index.js.map +1 -1
- package/dist/cmd/coder/start.d.ts.map +1 -1
- package/dist/cmd/coder/start.js +52 -1
- package/dist/cmd/coder/start.js.map +1 -1
- package/dist/cmd/coder/tui-init.js +1 -1
- package/dist/cmd/coder/tui-init.js.map +1 -1
- package/dist/cmd/coder/update.d.ts.map +1 -1
- package/dist/cmd/coder/update.js +21 -1
- package/dist/cmd/coder/update.js.map +1 -1
- package/dist/cmd/coder/workspace/create.d.ts.map +1 -1
- package/dist/cmd/coder/workspace/create.js +57 -13
- package/dist/cmd/coder/workspace/create.js.map +1 -1
- package/dist/cmd/coder/workspace/index.d.ts.map +1 -1
- package/dist/cmd/coder/workspace/index.js +1 -1
- package/dist/cmd/coder/workspace/index.js.map +1 -1
- package/dist/cmd/coder/workspace/list.js +2 -2
- package/dist/cmd/coder/workspace/list.js.map +1 -1
- package/dist/cmd/dev/dev-lock.d.ts.map +1 -1
- package/dist/cmd/dev/dev-lock.js +43 -17
- package/dist/cmd/dev/dev-lock.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +211 -125
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/process-manager.d.ts +41 -1
- package/dist/cmd/dev/process-manager.d.ts.map +1 -1
- package/dist/cmd/dev/process-manager.js +160 -31
- package/dist/cmd/dev/process-manager.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +0 -2
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +0 -3
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +0 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +1 -124
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/cache/resource-region.ts +68 -44
- package/src/cmd/ai/prompt/web.md +43 -17
- package/src/cmd/build/vite/agent-discovery.ts +4 -4
- package/src/cmd/build/vite/bun-dev-server.ts +92 -6
- package/src/cmd/build/vite/index.ts +0 -1
- package/src/cmd/build/vite/static-renderer.ts +18 -7
- package/src/cmd/build/vite/vite-asset-server-config.ts +37 -27
- package/src/cmd/build/vite/vite-asset-server.ts +5 -1
- package/src/cmd/build/vite/vite-builder.ts +12 -1
- package/src/cmd/build/vite/ws-proxy.ts +52 -3
- package/src/cmd/cloud/deploy.ts +117 -49
- package/src/cmd/cloud/sandbox/checkpoint/create.ts +10 -4
- package/src/cmd/cloud/sandbox/checkpoint/delete.ts +10 -4
- package/src/cmd/cloud/sandbox/checkpoint/list.ts +10 -4
- package/src/cmd/cloud/sandbox/checkpoint/restore.ts +10 -4
- package/src/cmd/cloud/sandbox/create.ts +14 -4
- package/src/cmd/cloud/sandbox/delete.ts +10 -4
- package/src/cmd/cloud/sandbox/env.ts +10 -5
- package/src/cmd/cloud/sandbox/exec.ts +157 -42
- package/src/cmd/cloud/sandbox/execution/list.ts +10 -5
- package/src/cmd/cloud/sandbox/fs/cp.ts +94 -126
- package/src/cmd/cloud/sandbox/fs/download.ts +18 -25
- package/src/cmd/cloud/sandbox/fs/ls.ts +10 -5
- package/src/cmd/cloud/sandbox/fs/mkdir.ts +10 -5
- package/src/cmd/cloud/sandbox/fs/rm.ts +10 -5
- package/src/cmd/cloud/sandbox/fs/rmdir.ts +10 -5
- package/src/cmd/cloud/sandbox/fs/upload.ts +14 -8
- package/src/cmd/cloud/sandbox/get.ts +28 -7
- package/src/cmd/cloud/sandbox/job/create.ts +10 -4
- package/src/cmd/cloud/sandbox/job/destroy.ts +10 -4
- package/src/cmd/cloud/sandbox/job/get.ts +10 -4
- package/src/cmd/cloud/sandbox/job/list.ts +10 -4
- package/src/cmd/cloud/sandbox/job/logs.ts +11 -4
- package/src/cmd/cloud/sandbox/pause.ts +31 -5
- package/src/cmd/cloud/sandbox/resume.ts +10 -4
- package/src/cmd/cloud/sandbox/run.ts +49 -11
- package/src/cmd/cloud/sandbox/util.ts +63 -2
- package/src/cmd/coder/create.ts +24 -1
- package/src/cmd/coder/index.ts +4 -0
- package/src/cmd/coder/start.ts +63 -1
- package/src/cmd/coder/tui-init.ts +1 -1
- package/src/cmd/coder/update.ts +18 -1
- package/src/cmd/coder/workspace/create.ts +84 -15
- package/src/cmd/coder/workspace/index.ts +3 -1
- package/src/cmd/coder/workspace/list.ts +2 -2
- package/src/cmd/dev/dev-lock.ts +50 -16
- package/src/cmd/dev/index.ts +249 -134
- package/src/cmd/dev/process-manager.ts +173 -33
- package/src/cmd/project/create.ts +0 -2
- package/src/cmd/project/index.ts +0 -3
- package/src/cmd/project/template-flow.ts +0 -147
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +0 -45
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.js +0 -166
- package/dist/cmd/build/vite/public-asset-path-plugin.js.map +0 -1
- package/dist/cmd/project/auth/generate.d.ts +0 -5
- package/dist/cmd/project/auth/generate.d.ts.map +0 -1
- package/dist/cmd/project/auth/generate.js +0 -102
- package/dist/cmd/project/auth/generate.js.map +0 -1
- package/dist/cmd/project/auth/index.d.ts +0 -2
- package/dist/cmd/project/auth/index.d.ts.map +0 -1
- package/dist/cmd/project/auth/index.js +0 -21
- package/dist/cmd/project/auth/index.js.map +0 -1
- package/dist/cmd/project/auth/init.d.ts +0 -2
- package/dist/cmd/project/auth/init.d.ts.map +0 -1
- package/dist/cmd/project/auth/init.js +0 -213
- package/dist/cmd/project/auth/init.js.map +0 -1
- package/dist/cmd/project/auth/shared.d.ts +0 -93
- package/dist/cmd/project/auth/shared.d.ts.map +0 -1
- package/dist/cmd/project/auth/shared.js +0 -475
- package/dist/cmd/project/auth/shared.js.map +0 -1
- package/src/cmd/build/vite/public-asset-path-plugin.ts +0 -209
- package/src/cmd/project/auth/generate.ts +0 -116
- package/src/cmd/project/auth/index.ts +0 -21
- package/src/cmd/project/auth/init.ts +0 -256
- package/src/cmd/project/auth/shared.ts +0 -591
|
@@ -1,17 +1,69 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
import { fstatSync, readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import type { FileToWrite, Logger } from '@agentuity/core';
|
|
4
|
-
import { APIClient, getServiceUrls, sandboxGet } from '@agentuity/server';
|
|
4
|
+
import { APIClient, getServiceUrls, sandboxGet, sandboxResolve } from '@agentuity/server';
|
|
5
5
|
import { deleteResourceRegion, getResourceInfo, setResourceInfo } from '../../../cache';
|
|
6
6
|
import { getGlobalCatalystAPIClient } from '../../../config';
|
|
7
7
|
import { ErrorCode } from '../../../errors';
|
|
8
8
|
import * as tui from '../../../tui';
|
|
9
9
|
import type { AuthData, Config } from '../../../types';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Detect if a file descriptor is redirected to /dev/null (or NUL on Windows).
|
|
13
|
+
* Used to optimize stream creation - when output goes to /dev/null, we can
|
|
14
|
+
* skip creating the stream entirely on the server.
|
|
15
|
+
*
|
|
16
|
+
* @param fd - File descriptor (1 for stdout, 2 for stderr)
|
|
17
|
+
* @returns true if the fd points to /dev/null
|
|
18
|
+
*/
|
|
19
|
+
export function detectNullStream(fd: number): boolean {
|
|
20
|
+
try {
|
|
21
|
+
const fdStat = fstatSync(fd);
|
|
22
|
+
const nullPath = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
23
|
+
const nullStat = statSync(nullPath);
|
|
24
|
+
return fdStat.dev === nullStat.dev && fdStat.ino === nullStat.ino;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
export function createSandboxClient(logger: Logger, auth: AuthData, region: string): APIClient {
|
|
12
31
|
return new APIClient(getServiceUrls(region).catalyst, logger, auth.apiKey);
|
|
13
32
|
}
|
|
14
33
|
|
|
34
|
+
export interface ResolvedSandboxTarget {
|
|
35
|
+
region: string;
|
|
36
|
+
orgId: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve sandbox routing context using cache-first lookup.
|
|
41
|
+
* Falls back to the CLI resolve endpoint on cache miss or partial cache.
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveSandboxTarget(
|
|
44
|
+
logger: Logger,
|
|
45
|
+
auth: AuthData,
|
|
46
|
+
apiClient: APIClient | null = null,
|
|
47
|
+
sandboxId: string,
|
|
48
|
+
profileName = 'production',
|
|
49
|
+
config?: Config | null
|
|
50
|
+
): Promise<ResolvedSandboxTarget> {
|
|
51
|
+
const cachedInfo = await getResourceInfo('sandbox', profileName, sandboxId);
|
|
52
|
+
if (cachedInfo?.region && cachedInfo?.orgId) {
|
|
53
|
+
logger.trace(
|
|
54
|
+
`[sandbox] Found cached target for ${sandboxId}: ${cachedInfo.region}/${cachedInfo.orgId}`
|
|
55
|
+
);
|
|
56
|
+
return { region: cachedInfo.region, orgId: cachedInfo.orgId };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
logger.trace(`[sandbox] Cache miss for target ${sandboxId}, resolving via CLI API`);
|
|
60
|
+
const globalClient =
|
|
61
|
+
apiClient ?? (await getGlobalCatalystAPIClient(logger, auth, profileName, undefined, config));
|
|
62
|
+
const sandbox = await sandboxResolve(globalClient, sandboxId);
|
|
63
|
+
await setResourceInfo('sandbox', profileName, sandboxId, sandbox.region, sandbox.orgId);
|
|
64
|
+
return { region: sandbox.region, orgId: sandbox.orgId };
|
|
65
|
+
}
|
|
66
|
+
|
|
15
67
|
/**
|
|
16
68
|
* Look up the region for a sandbox, using cache-first strategy.
|
|
17
69
|
* Falls back to API lookup if not in cache.
|
|
@@ -64,6 +116,15 @@ export async function cacheSandboxRegion(
|
|
|
64
116
|
await setResourceInfo('sandbox', profileName, sandboxId, region);
|
|
65
117
|
}
|
|
66
118
|
|
|
119
|
+
export async function cacheSandboxTarget(
|
|
120
|
+
profileName = 'production',
|
|
121
|
+
sandboxId: string,
|
|
122
|
+
region: string,
|
|
123
|
+
orgId?: string
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
await setResourceInfo('sandbox', profileName, sandboxId, region, orgId);
|
|
126
|
+
}
|
|
127
|
+
|
|
67
128
|
/**
|
|
68
129
|
* Clear cached region for a sandbox after delete
|
|
69
130
|
*/
|
package/src/cmd/coder/create.ts
CHANGED
|
@@ -44,6 +44,12 @@ export const createCoderSubcommand = createSubcommand({
|
|
|
44
44
|
),
|
|
45
45
|
description: 'Create with label and tags, return JSON',
|
|
46
46
|
},
|
|
47
|
+
{
|
|
48
|
+
command: getCommand(
|
|
49
|
+
'coder create "Review this change" --default-agent code-review --enabled-agents code-review'
|
|
50
|
+
),
|
|
51
|
+
description: 'Create with a selected agent roster and a custom default route target',
|
|
52
|
+
},
|
|
47
53
|
],
|
|
48
54
|
schema: {
|
|
49
55
|
args: z.object({
|
|
@@ -61,6 +67,10 @@ export const createCoderSubcommand = createSubcommand({
|
|
|
61
67
|
// Session config
|
|
62
68
|
label: z.string().optional().describe('Human-readable session label'),
|
|
63
69
|
agent: z.string().optional().describe('Default agent role (e.g. lead, scout)'),
|
|
70
|
+
defaultAgent: z
|
|
71
|
+
.string()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe('Preferred default agent slug or built-in route target'),
|
|
64
74
|
visibility: z
|
|
65
75
|
.string()
|
|
66
76
|
.optional()
|
|
@@ -86,6 +96,10 @@ export const createCoderSubcommand = createSubcommand({
|
|
|
86
96
|
// Resources
|
|
87
97
|
workspaceId: z.string().optional().describe('Workspace ID to use'),
|
|
88
98
|
tags: z.string().optional().describe('Comma-separated tags'),
|
|
99
|
+
enabledAgents: z
|
|
100
|
+
.string()
|
|
101
|
+
.optional()
|
|
102
|
+
.describe('Comma-separated built-in/custom agents to include'),
|
|
89
103
|
env: z
|
|
90
104
|
.string()
|
|
91
105
|
.optional()
|
|
@@ -105,10 +119,14 @@ export const createCoderSubcommand = createSubcommand({
|
|
|
105
119
|
});
|
|
106
120
|
|
|
107
121
|
// Build the create session request body from flags
|
|
108
|
-
const body: CoderCreateSessionRequest
|
|
122
|
+
const body: CoderCreateSessionRequest & {
|
|
123
|
+
defaultAgent?: string;
|
|
124
|
+
enabledAgents?: string[];
|
|
125
|
+
} = {
|
|
109
126
|
task: args.task,
|
|
110
127
|
...(opts?.label && { label: opts.label }),
|
|
111
128
|
...(opts?.agent && { agent: opts.agent }),
|
|
129
|
+
...(opts?.defaultAgent && { defaultAgent: opts.defaultAgent }),
|
|
112
130
|
...(opts?.visibility && { visibility: normalizeVisibility(opts.visibility) }),
|
|
113
131
|
...(opts?.workflowMode && { workflowMode: opts.workflowMode as 'standard' | 'loop' }),
|
|
114
132
|
};
|
|
@@ -149,6 +167,11 @@ export const createCoderSubcommand = createSubcommand({
|
|
|
149
167
|
.split(',')
|
|
150
168
|
.map((t) => t.trim())
|
|
151
169
|
.filter(Boolean);
|
|
170
|
+
if (opts?.enabledAgents)
|
|
171
|
+
body.enabledAgents = opts.enabledAgents
|
|
172
|
+
.split(',')
|
|
173
|
+
.map((name) => name.trim())
|
|
174
|
+
.filter(Boolean);
|
|
152
175
|
if (opts?.savedSkillIds)
|
|
153
176
|
body.savedSkillIds = opts.savedSkillIds
|
|
154
177
|
.split(',')
|
package/src/cmd/coder/index.ts
CHANGED
|
@@ -24,6 +24,10 @@ export const command = createCommand({
|
|
|
24
24
|
command: getCommand('coder start'),
|
|
25
25
|
description: 'Start a coding session connected to Coder',
|
|
26
26
|
},
|
|
27
|
+
{
|
|
28
|
+
command: getCommand('coder start --dir ~/path/to/my/project'),
|
|
29
|
+
description: 'Start a coding session from a specific local project directory',
|
|
30
|
+
},
|
|
27
31
|
{
|
|
28
32
|
command: getCommand('coder create "Build a REST API"'),
|
|
29
33
|
description: 'Create a new Coder session with a task',
|
package/src/cmd/coder/start.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
2
4
|
import { z } from 'zod';
|
|
3
5
|
import { CoderClient, type CoderSessionListItem } from '@agentuity/core/coder';
|
|
4
6
|
import { ValidationOutputError } from '@agentuity/core';
|
|
@@ -64,6 +66,10 @@ export const startSubcommand = createSubcommand({
|
|
|
64
66
|
command: getCommand('coder start'),
|
|
65
67
|
description: 'Start Pi with auto-detected Hub and extension',
|
|
66
68
|
},
|
|
69
|
+
{
|
|
70
|
+
command: getCommand('coder start --dir ~/path/to/my/project'),
|
|
71
|
+
description: 'Start from a specific local project directory',
|
|
72
|
+
},
|
|
67
73
|
{
|
|
68
74
|
command: getCommand('coder start --url ws://127.0.0.1:3500/api/ws'),
|
|
69
75
|
description: 'Start with explicit Coder URL',
|
|
@@ -97,6 +103,7 @@ export const startSubcommand = createSubcommand({
|
|
|
97
103
|
],
|
|
98
104
|
schema: {
|
|
99
105
|
options: z.object({
|
|
106
|
+
dir: z.string().optional().describe('Local project directory to start from'),
|
|
100
107
|
url: z.string().optional().describe('Coder API URL override'),
|
|
101
108
|
extension: z.string().optional().describe('Coder extension path override'),
|
|
102
109
|
pi: z.string().optional().describe('Path to pi binary'),
|
|
@@ -121,6 +128,32 @@ export const startSubcommand = createSubcommand({
|
|
|
121
128
|
},
|
|
122
129
|
async handler(ctx) {
|
|
123
130
|
const { opts, options } = ctx;
|
|
131
|
+
// Keep Pi's interactive install telemetry disabled for Agentuity CLI sessions.
|
|
132
|
+
process.env.PI_TELEMETRY = '0';
|
|
133
|
+
|
|
134
|
+
// Resolve working directory from optional --dir option
|
|
135
|
+
let cwd = process.cwd();
|
|
136
|
+
if (opts?.dir) {
|
|
137
|
+
// Warn if --dir is provided with --remote or --sandbox (dir is ignored in those modes)
|
|
138
|
+
if (opts?.remote !== undefined || opts?.sandbox !== undefined) {
|
|
139
|
+
tui.warning('--dir is ignored in remote/sandbox mode');
|
|
140
|
+
} else {
|
|
141
|
+
const raw = opts.dir.trim();
|
|
142
|
+
cwd =
|
|
143
|
+
raw === '~' || raw.startsWith('~/')
|
|
144
|
+
? resolve(homedir(), raw.slice(2))
|
|
145
|
+
: resolve(raw);
|
|
146
|
+
|
|
147
|
+
const st = await stat(cwd).catch(() => null);
|
|
148
|
+
if (!st?.isDirectory()) {
|
|
149
|
+
tui.fatal(
|
|
150
|
+
`The specified path is not a valid directory: ${cwd}`,
|
|
151
|
+
ErrorCode.CONFIG_INVALID
|
|
152
|
+
);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
124
157
|
const client = new CoderClient({
|
|
125
158
|
apiKey: ctx.auth.apiKey,
|
|
126
159
|
url: opts?.url,
|
|
@@ -216,6 +249,33 @@ export const startSubcommand = createSubcommand({
|
|
|
216
249
|
// with the coder extension loaded for Hub UI (footer, /hub, commands).
|
|
217
250
|
// Agent.emit() drives native rendering — no [remote_message] blocks.
|
|
218
251
|
if (remoteSessionId) {
|
|
252
|
+
try {
|
|
253
|
+
const session = await tui.spinner({
|
|
254
|
+
message: 'Preparing remote session…',
|
|
255
|
+
callback: async () => client.prepareSessionForRemoteAttach(remoteSessionId!),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (session.historyOnly === true) {
|
|
259
|
+
tui.fatal(
|
|
260
|
+
`Session ${remoteSessionId} is history-only and cannot be attached remotely.`,
|
|
261
|
+
ErrorCode.CONFIG_INVALID
|
|
262
|
+
);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (session.runtimeAvailable === false) {
|
|
267
|
+
tui.fatal(
|
|
268
|
+
`Session ${remoteSessionId} is offline and could not be resumed for remote attach.`,
|
|
269
|
+
ErrorCode.NETWORK_ERROR
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
} catch (err) {
|
|
274
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
275
|
+
tui.fatal(`Failed to prepare remote session: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
219
279
|
if (!options.json) {
|
|
220
280
|
tui.newline();
|
|
221
281
|
tui.output(` Hub: ${tui.bold(hubWsUrl)}`);
|
|
@@ -330,6 +390,7 @@ export const startSubcommand = createSubcommand({
|
|
|
330
390
|
AGENTUITY_CODER_HUB_URL: hubWsUrl,
|
|
331
391
|
};
|
|
332
392
|
env.AGENTUITY_CODER_API_KEY = ctx.auth.apiKey;
|
|
393
|
+
env.AGENTUITY_ORGID = ctx.orgId;
|
|
333
394
|
|
|
334
395
|
if (opts?.agent) {
|
|
335
396
|
env.AGENTUITY_CODER_AGENT = opts.agent;
|
|
@@ -343,6 +404,7 @@ export const startSubcommand = createSubcommand({
|
|
|
343
404
|
tui.output(` Hub: ${tui.bold(hubWsUrl)}`);
|
|
344
405
|
tui.output(` Extension: ${tui.bold(extensionPath)}`);
|
|
345
406
|
tui.output(` Pi: ${tui.bold(piBinary)}`);
|
|
407
|
+
if (opts?.dir) tui.output(` Dir: ${tui.bold(cwd)}`);
|
|
346
408
|
if (opts?.agent) tui.output(` Agent: ${tui.bold(opts.agent)}`);
|
|
347
409
|
tui.newline();
|
|
348
410
|
}
|
|
@@ -351,7 +413,7 @@ export const startSubcommand = createSubcommand({
|
|
|
351
413
|
try {
|
|
352
414
|
const proc = Bun.spawn([piBinary, ...piArgs], {
|
|
353
415
|
env,
|
|
354
|
-
cwd
|
|
416
|
+
cwd,
|
|
355
417
|
stdin: 'inherit',
|
|
356
418
|
stdout: 'inherit',
|
|
357
419
|
stderr: 'inherit',
|
package/src/cmd/coder/update.ts
CHANGED
|
@@ -40,6 +40,10 @@ export const updateSubcommand = createSubcommand({
|
|
|
40
40
|
url: z.string().optional().describe('Coder API URL override'),
|
|
41
41
|
label: z.string().optional().describe('Updated session label'),
|
|
42
42
|
agent: z.string().optional().describe('Updated default agent role'),
|
|
43
|
+
defaultAgent: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe('Updated preferred default agent slug or built-in route target'),
|
|
43
47
|
visibility: z
|
|
44
48
|
.string()
|
|
45
49
|
.optional()
|
|
@@ -50,6 +54,10 @@ export const updateSubcommand = createSubcommand({
|
|
|
50
54
|
loopAutoContinue: z.boolean().optional().describe('Auto-continue loop'),
|
|
51
55
|
loopAllowDetached: z.boolean().optional().describe('Allow detached loop execution'),
|
|
52
56
|
tags: z.string().optional().describe('Comma-separated tags (replaces existing)'),
|
|
57
|
+
enabledAgents: z
|
|
58
|
+
.string()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe('Comma-separated built-in/custom agents (replaces existing)'),
|
|
53
61
|
}),
|
|
54
62
|
},
|
|
55
63
|
async handler(ctx) {
|
|
@@ -64,6 +72,7 @@ export const updateSubcommand = createSubcommand({
|
|
|
64
72
|
|
|
65
73
|
if (opts?.label) body.label = opts.label;
|
|
66
74
|
if (opts?.agent) body.agent = opts.agent;
|
|
75
|
+
if (opts?.defaultAgent) body.defaultAgent = opts.defaultAgent;
|
|
67
76
|
if (opts?.visibility) body.visibility = normalizeVisibility(opts.visibility);
|
|
68
77
|
if (opts?.workflowMode) body.workflowMode = opts.workflowMode;
|
|
69
78
|
if (opts?.tags) {
|
|
@@ -72,6 +81,12 @@ export const updateSubcommand = createSubcommand({
|
|
|
72
81
|
.map((t) => t.trim())
|
|
73
82
|
.filter(Boolean);
|
|
74
83
|
}
|
|
84
|
+
if (opts?.enabledAgents) {
|
|
85
|
+
body.enabledAgents = opts.enabledAgents
|
|
86
|
+
.split(',')
|
|
87
|
+
.map((name) => name.trim())
|
|
88
|
+
.filter(Boolean);
|
|
89
|
+
}
|
|
75
90
|
|
|
76
91
|
if (
|
|
77
92
|
opts?.loopGoal ||
|
|
@@ -90,7 +105,7 @@ export const updateSubcommand = createSubcommand({
|
|
|
90
105
|
|
|
91
106
|
if (Object.keys(body).length === 0) {
|
|
92
107
|
tui.fatal(
|
|
93
|
-
'No update fields provided. Use --label, --visibility, --tags, --agent, --workflow-mode, or loop options.',
|
|
108
|
+
'No update fields provided. Use --label, --visibility, --tags, --agent, --default-agent, --enabled-agents, --workflow-mode, or loop options.',
|
|
94
109
|
ErrorCode.VALIDATION_FAILED
|
|
95
110
|
);
|
|
96
111
|
}
|
|
@@ -109,6 +124,8 @@ export const updateSubcommand = createSubcommand({
|
|
|
109
124
|
if (opts?.visibility) fields.push(`Visibility: ${body.visibility}`);
|
|
110
125
|
if (opts?.tags) fields.push(`Tags: ${(body.tags as string[]).join(', ')}`);
|
|
111
126
|
if (opts?.agent) fields.push(`Agent: ${opts.agent}`);
|
|
127
|
+
if (opts?.defaultAgent) fields.push(`Default agent: ${opts.defaultAgent}`);
|
|
128
|
+
if (opts?.enabledAgents) fields.push(`Enabled agents: ${opts.enabledAgents}`);
|
|
112
129
|
if (opts?.workflowMode || body.loop) fields.push(`Workflow: ${body.workflowMode}`);
|
|
113
130
|
|
|
114
131
|
for (const f of fields) {
|
|
@@ -1,37 +1,66 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { APIError, ValidationInputError, ValidationOutputError } from '@agentuity/core';
|
|
3
|
+
import {
|
|
4
|
+
CoderClient,
|
|
5
|
+
CoderCreateWorkspaceRequestSchema,
|
|
6
|
+
type CoderCreateWorkspaceRequest,
|
|
7
|
+
} from '@agentuity/core/coder';
|
|
4
8
|
import { createSubcommand } from '../../../types';
|
|
5
9
|
import * as tui from '../../../tui';
|
|
6
10
|
import { getCommand } from '../../../command-prefix';
|
|
7
11
|
import { ErrorCode } from '../../../errors';
|
|
8
12
|
import { resolveGitHubRepo } from '../resolve-repo';
|
|
9
13
|
|
|
14
|
+
const EMPTY_WORKSPACE_ERROR =
|
|
15
|
+
'A workspace needs at least one repo, saved skill, skill bucket, or agent';
|
|
16
|
+
|
|
17
|
+
function hasWorkspaceSelections(input: CoderCreateWorkspaceRequest): boolean {
|
|
18
|
+
return (
|
|
19
|
+
(input.repos?.length ?? 0) > 0 ||
|
|
20
|
+
(input.savedSkillIds?.length ?? 0) > 0 ||
|
|
21
|
+
(input.skillBucketIds?.length ?? 0) > 0 ||
|
|
22
|
+
(input.enabledAgents?.length ?? 0) > 0
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function formatWorkspaceValidationMessage(issues: Array<{ message: string }>): string {
|
|
27
|
+
const messages = [...new Set(issues.map((issue) => issue.message).filter(Boolean))];
|
|
28
|
+
if (messages.length === 0) {
|
|
29
|
+
return 'Invalid workspace configuration';
|
|
30
|
+
}
|
|
31
|
+
if (messages.includes(EMPTY_WORKSPACE_ERROR)) {
|
|
32
|
+
return `${EMPTY_WORKSPACE_ERROR}. Use --repo or --enabled-agents.`;
|
|
33
|
+
}
|
|
34
|
+
return messages.join('; ');
|
|
35
|
+
}
|
|
36
|
+
|
|
10
37
|
export const createWorkspaceSubcommand = createSubcommand({
|
|
11
38
|
name: 'create',
|
|
12
39
|
aliases: ['new', 'add'],
|
|
13
|
-
description: 'Create a new Coder workspace',
|
|
40
|
+
description: 'Create a new Coder workspace with at least one repo or agent',
|
|
14
41
|
tags: ['mutating', 'requires-auth'],
|
|
15
42
|
requires: { auth: true, org: true },
|
|
16
43
|
examples: [
|
|
17
|
-
{
|
|
18
|
-
command: getCommand('coder workspace create "My Workspace"'),
|
|
19
|
-
description: 'Create a workspace with a name',
|
|
20
|
-
},
|
|
21
44
|
{
|
|
22
45
|
command: getCommand(
|
|
23
|
-
'coder workspace create "My Workspace" --
|
|
46
|
+
'coder workspace create "My Workspace" --repo https://github.com/org/repo --repo-branch main'
|
|
24
47
|
),
|
|
25
|
-
description: 'Create
|
|
48
|
+
description: 'Create a workspace with a repository',
|
|
26
49
|
},
|
|
27
50
|
{
|
|
28
51
|
command: getCommand(
|
|
29
|
-
'coder workspace create "My Workspace" --
|
|
52
|
+
'coder workspace create "My Workspace" --enabled-agents code-review --description "For frontend work" --scope org'
|
|
30
53
|
),
|
|
31
|
-
description: 'Create
|
|
54
|
+
description: 'Create an org-scoped workspace with description and agents',
|
|
32
55
|
},
|
|
33
56
|
{
|
|
34
|
-
command: getCommand('coder workspace create "My Workspace" --
|
|
57
|
+
command: getCommand('coder workspace create "My Workspace" --enabled-agents code-review'),
|
|
58
|
+
description: 'Create a workspace with an agent roster',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
command: getCommand(
|
|
62
|
+
'coder workspace create "My Workspace" --enabled-agents code-review --json'
|
|
63
|
+
),
|
|
35
64
|
description: 'Create a workspace and return JSON output',
|
|
36
65
|
},
|
|
37
66
|
],
|
|
@@ -45,6 +74,10 @@ export const createWorkspaceSubcommand = createSubcommand({
|
|
|
45
74
|
scope: z.string().optional().describe('Workspace scope: user or org'),
|
|
46
75
|
repo: z.string().optional().describe('Repository URL to add'),
|
|
47
76
|
repoBranch: z.string().optional().describe('Branch for the repository'),
|
|
77
|
+
enabledAgents: z
|
|
78
|
+
.string()
|
|
79
|
+
.optional()
|
|
80
|
+
.describe('Comma-separated built-in/custom agents to include'),
|
|
48
81
|
}),
|
|
49
82
|
},
|
|
50
83
|
async handler(ctx) {
|
|
@@ -72,9 +105,36 @@ export const createWorkspaceSubcommand = createSubcommand({
|
|
|
72
105
|
return;
|
|
73
106
|
}
|
|
74
107
|
}
|
|
108
|
+
if (opts?.enabledAgents) {
|
|
109
|
+
body.enabledAgents = opts.enabledAgents
|
|
110
|
+
.split(',')
|
|
111
|
+
.map((name) => name.trim())
|
|
112
|
+
.filter(Boolean);
|
|
113
|
+
}
|
|
114
|
+
if (!hasWorkspaceSelections(body)) {
|
|
115
|
+
tui.fatal(
|
|
116
|
+
`Failed to create workspace: ${EMPTY_WORKSPACE_ERROR}. Use --repo or --enabled-agents.`,
|
|
117
|
+
ErrorCode.VALIDATION_FAILED
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const validationResult = CoderCreateWorkspaceRequestSchema.safeParse(body);
|
|
122
|
+
if (!validationResult.success) {
|
|
123
|
+
ctx.logger.trace(
|
|
124
|
+
'Validation issues: %s',
|
|
125
|
+
JSON.stringify(validationResult.error.issues, null, 2)
|
|
126
|
+
);
|
|
127
|
+
tui.fatal(
|
|
128
|
+
`Failed to create workspace: ${formatWorkspaceValidationMessage(validationResult.error.issues)}`,
|
|
129
|
+
ErrorCode.VALIDATION_FAILED
|
|
130
|
+
);
|
|
131
|
+
}
|
|
75
132
|
|
|
76
133
|
try {
|
|
77
|
-
const created = await client.createWorkspace(
|
|
134
|
+
const created = await client.createWorkspace(validationResult.data);
|
|
135
|
+
const createdEnabledAgents = Array.isArray(created.enabledAgents)
|
|
136
|
+
? created.enabledAgents.filter((name): name is string => typeof name === 'string')
|
|
137
|
+
: [];
|
|
78
138
|
|
|
79
139
|
if (options.json) {
|
|
80
140
|
return created;
|
|
@@ -88,13 +148,22 @@ export const createWorkspaceSubcommand = createSubcommand({
|
|
|
88
148
|
}
|
|
89
149
|
tui.output(` Scope: ${created.scope}`);
|
|
90
150
|
tui.output(` Repos: ${created.repoCount}`);
|
|
91
|
-
tui.output(`
|
|
151
|
+
tui.output(` Selections: ${created.selectionCount}`);
|
|
152
|
+
if (createdEnabledAgents.length > 0) {
|
|
153
|
+
tui.output(` Agents: ${createdEnabledAgents.join(', ')}`);
|
|
154
|
+
}
|
|
92
155
|
|
|
93
156
|
return created;
|
|
94
157
|
} catch (err) {
|
|
95
|
-
if (err instanceof ValidationOutputError) {
|
|
158
|
+
if (err instanceof ValidationInputError || err instanceof ValidationOutputError) {
|
|
96
159
|
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
97
160
|
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
161
|
+
tui.fatal(
|
|
162
|
+
`Failed to create workspace: ${formatWorkspaceValidationMessage(err.issues)}`,
|
|
163
|
+
ErrorCode.VALIDATION_FAILED
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (err instanceof APIError && err.status >= 400 && err.status < 500) {
|
|
98
167
|
tui.fatal(`Failed to create workspace: ${err.message}`, ErrorCode.VALIDATION_FAILED);
|
|
99
168
|
}
|
|
100
169
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -17,7 +17,9 @@ export const workspaceCommand = createCommand({
|
|
|
17
17
|
description: 'List all workspaces',
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
|
-
command: getCommand(
|
|
20
|
+
command: getCommand(
|
|
21
|
+
'coder workspace create "My Workspace" --repo https://github.com/org/repo'
|
|
22
|
+
),
|
|
21
23
|
description: 'Create a new workspace',
|
|
22
24
|
},
|
|
23
25
|
{
|
|
@@ -83,7 +83,7 @@ export const listSubcommand = createSubcommand({
|
|
|
83
83
|
Name: w.name,
|
|
84
84
|
Scope: w.scope,
|
|
85
85
|
Repos: String(w.repoCount),
|
|
86
|
-
|
|
86
|
+
Selections: String(w.selectionCount),
|
|
87
87
|
Created: formatRelativeTime(w.createdAt),
|
|
88
88
|
})),
|
|
89
89
|
[
|
|
@@ -91,7 +91,7 @@ export const listSubcommand = createSubcommand({
|
|
|
91
91
|
{ name: 'Name', alignment: 'left' },
|
|
92
92
|
{ name: 'Scope', alignment: 'center' },
|
|
93
93
|
{ name: 'Repos', alignment: 'right' },
|
|
94
|
-
{ name: '
|
|
94
|
+
{ name: 'Selections', alignment: 'right' },
|
|
95
95
|
{ name: 'Created', alignment: 'right' },
|
|
96
96
|
]
|
|
97
97
|
);
|
package/src/cmd/dev/dev-lock.ts
CHANGED
|
@@ -71,35 +71,69 @@ function pidExists(pid: number): boolean {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
74
|
+
* Send a signal to a process group (negative PID) with fallback to direct PID.
|
|
75
|
+
* Returns true if the signal was sent successfully.
|
|
75
76
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
function killProcessTree(pid: number, signal: NodeJS.Signals, logger: LoggerLike): boolean {
|
|
78
|
+
// Safety: never send signals to PID 0 (own process group), PID 1 (init/systemd),
|
|
79
|
+
// or other dangerously low PIDs. process.kill(-1) would signal every process
|
|
80
|
+
// the user owns, which would crash the entire desktop session.
|
|
81
|
+
if (pid <= 1) {
|
|
82
|
+
logger.debug('Refusing to kill dangerous pid %d, skipping process tree kill', pid);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
78
85
|
|
|
86
|
+
// Try process group kill first (kills all children too)
|
|
79
87
|
try {
|
|
80
|
-
process.kill(pid,
|
|
81
|
-
logger.debug('Sent
|
|
88
|
+
process.kill(-pid, signal);
|
|
89
|
+
logger.debug('Sent %s to process group -%d', signal, pid);
|
|
90
|
+
return true;
|
|
82
91
|
} catch (err: unknown) {
|
|
83
92
|
const error = err as NodeJS.ErrnoException;
|
|
84
|
-
if (error.code
|
|
85
|
-
|
|
93
|
+
if (error.code !== 'ESRCH') {
|
|
94
|
+
logger.debug(
|
|
95
|
+
'Process group kill failed for pid %d (%s), falling back to direct',
|
|
96
|
+
pid,
|
|
97
|
+
error.code
|
|
98
|
+
);
|
|
99
|
+
}
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
//
|
|
89
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
90
|
-
|
|
91
|
-
if (!pidExists(pid)) return;
|
|
92
|
-
|
|
93
|
-
// Force kill
|
|
102
|
+
// Fall back to direct PID kill
|
|
94
103
|
try {
|
|
95
|
-
process.kill(pid,
|
|
96
|
-
logger.debug('Sent
|
|
104
|
+
process.kill(pid, signal);
|
|
105
|
+
logger.debug('Sent %s to pid %d (direct)', signal, pid);
|
|
106
|
+
return true;
|
|
97
107
|
} catch (err: unknown) {
|
|
98
108
|
const error = err as NodeJS.ErrnoException;
|
|
99
109
|
if (error.code !== 'ESRCH') {
|
|
100
|
-
logger.debug('
|
|
110
|
+
logger.debug('Direct kill failed for pid %d: %s', pid, error.code);
|
|
101
111
|
}
|
|
112
|
+
return false;
|
|
102
113
|
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Kill a process by PID with SIGTERM, then SIGKILL if still alive.
|
|
118
|
+
* Targets the entire process tree to prevent orphaned child processes.
|
|
119
|
+
*/
|
|
120
|
+
async function killPid(pid: number, logger: LoggerLike): Promise<void> {
|
|
121
|
+
if (!pidExists(pid)) return;
|
|
122
|
+
|
|
123
|
+
// Send SIGTERM to process tree
|
|
124
|
+
const sent = killProcessTree(pid, 'SIGTERM', logger);
|
|
125
|
+
if (!sent) return;
|
|
126
|
+
|
|
127
|
+
// Give it a moment to exit gracefully
|
|
128
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
129
|
+
|
|
130
|
+
// Always attempt SIGKILL on the process tree even if the leader has exited.
|
|
131
|
+
// On Unix, process groups persist after the leader exits and signaling via
|
|
132
|
+
// negative PGID still reaches remaining members. killProcessTree() handles
|
|
133
|
+
// ESRCH gracefully if the group no longer exists.
|
|
134
|
+
|
|
135
|
+
// Force kill the entire process tree
|
|
136
|
+
killProcessTree(pid, 'SIGKILL', logger);
|
|
103
137
|
|
|
104
138
|
// Wait for process to fully terminate
|
|
105
139
|
await new Promise((r) => setTimeout(r, 100));
|