gsd-pi 2.11.0 → 2.13.0
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/cli.js +18 -1
- package/dist/onboarding.js +3 -0
- package/dist/resource-loader.d.ts +2 -0
- package/dist/resource-loader.js +36 -1
- package/dist/resources/extensions/bg-shell/index.ts +51 -7
- package/dist/resources/extensions/gsd/auto-worktree.ts +509 -0
- package/dist/resources/extensions/gsd/auto.ts +381 -13
- package/dist/resources/extensions/gsd/commands.ts +9 -3
- package/dist/resources/extensions/gsd/doctor.ts +254 -3
- package/dist/resources/extensions/gsd/git-self-heal.ts +198 -0
- package/dist/resources/extensions/gsd/git-service.ts +11 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +81 -9
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +449 -0
- package/dist/resources/extensions/gsd/preferences.ts +209 -1
- package/dist/resources/extensions/gsd/prompt-loader.ts +28 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +10 -8
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -3
- package/dist/resources/extensions/gsd/prompts/queue.md +3 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +32 -29
- package/dist/resources/extensions/gsd/templates/context.md +1 -1
- package/dist/resources/extensions/gsd/templates/state.md +3 -3
- package/dist/resources/extensions/gsd/tests/auto-worktree-merge.test.ts +282 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +259 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +147 -0
- package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +246 -0
- package/dist/resources/extensions/gsd/tests/doctor.test.ts +115 -1
- package/dist/resources/extensions/gsd/tests/git-self-heal.test.ts +234 -0
- package/dist/resources/extensions/gsd/tests/isolation-resolver.test.ts +107 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +88 -0
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
- package/dist/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
- package/dist/resources/extensions/gsd/tests/worktree-e2e.test.ts +315 -0
- package/dist/resources/extensions/gsd/types.ts +109 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +6 -4
- package/dist/resources/extensions/search-the-web/command-search-provider.ts +8 -4
- package/dist/resources/extensions/search-the-web/native-search.ts +15 -10
- package/dist/resources/extensions/search-the-web/provider.ts +19 -2
- package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
- package/dist/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
- package/dist/resources/extensions/search-the-web/tool-search.ts +62 -3
- package/dist/wizard.js +1 -0
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +169 -55
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +13 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +16 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +91 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +273 -63
- package/packages/pi-agent-core/src/agent.ts +24 -0
- package/packages/pi-agent-core/src/types.ts +98 -0
- package/packages/pi-ai/dist/env-api-keys.js +1 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +314 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +236 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +1 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/env-api-keys.ts +1 -0
- package/packages/pi-ai/src/models.generated.ts +236 -0
- package/packages/pi-ai/src/types.ts +2 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +2 -1
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +69 -8
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +3 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +2 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +76 -7
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +3 -3
- package/packages/pi-coding-agent/src/core/system-prompt.ts +9 -0
- package/packages/pi-tui/dist/components/editor.d.ts +11 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +64 -6
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +71 -6
- package/src/resources/extensions/bg-shell/index.ts +51 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +509 -0
- package/src/resources/extensions/gsd/auto.ts +381 -13
- package/src/resources/extensions/gsd/commands.ts +9 -3
- package/src/resources/extensions/gsd/doctor.ts +254 -3
- package/src/resources/extensions/gsd/git-self-heal.ts +198 -0
- package/src/resources/extensions/gsd/git-service.ts +11 -0
- package/src/resources/extensions/gsd/guided-flow.ts +81 -9
- package/src/resources/extensions/gsd/post-unit-hooks.ts +449 -0
- package/src/resources/extensions/gsd/preferences.ts +209 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +28 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +10 -8
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -3
- package/src/resources/extensions/gsd/prompts/queue.md +3 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +32 -29
- package/src/resources/extensions/gsd/templates/context.md +1 -1
- package/src/resources/extensions/gsd/templates/state.md +3 -3
- package/src/resources/extensions/gsd/tests/auto-worktree-merge.test.ts +282 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +259 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +246 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/git-self-heal.test.ts +234 -0
- package/src/resources/extensions/gsd/tests/isolation-resolver.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +315 -0
- package/src/resources/extensions/gsd/types.ts +109 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +6 -4
- package/src/resources/extensions/search-the-web/command-search-provider.ts +8 -4
- package/src/resources/extensions/search-the-web/native-search.ts +15 -10
- package/src/resources/extensions/search-the-web/provider.ts +19 -2
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +62 -3
package/dist/cli.js
CHANGED
|
@@ -2,12 +2,27 @@ import { AuthStorage, DefaultResourceLoader, ModelRegistry, SettingsManager, Ses
|
|
|
2
2
|
import { existsSync, readdirSync, renameSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { agentDir, sessionsDir, authFilePath } from './app-paths.js';
|
|
5
|
-
import { initResources, buildResourceLoader } from './resource-loader.js';
|
|
5
|
+
import { initResources, buildResourceLoader, getNewerManagedResourceVersion } from './resource-loader.js';
|
|
6
6
|
import { ensureManagedTools } from './tool-bootstrap.js';
|
|
7
7
|
import { loadStoredEnvKeys } from './wizard.js';
|
|
8
8
|
import { getPiDefaultModelAndProvider, migratePiCredentials } from './pi-migration.js';
|
|
9
9
|
import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
|
|
10
10
|
import { checkForUpdates } from './update-check.js';
|
|
11
|
+
function exitIfManagedResourcesAreNewer(currentAgentDir) {
|
|
12
|
+
const currentVersion = process.env.GSD_VERSION || '0.0.0';
|
|
13
|
+
const managedVersion = getNewerManagedResourceVersion(currentAgentDir, currentVersion);
|
|
14
|
+
if (!managedVersion) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const yellow = '\x1b[33m';
|
|
18
|
+
const dim = '\x1b[2m';
|
|
19
|
+
const reset = '\x1b[0m';
|
|
20
|
+
const bold = '\x1b[1m';
|
|
21
|
+
process.stderr.write(`[gsd] ${yellow}Version mismatch detected${reset}\n` +
|
|
22
|
+
`[gsd] Synced resources are from ${bold}v${managedVersion}${reset}, but this \`gsd\` binary is ${dim}v${currentVersion}${reset}.\n` +
|
|
23
|
+
`[gsd] Run ${bold}npm install -g gsd-pi@latest${reset} or ${bold}gsd update${reset}, then try again.\n`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
11
26
|
function parseCliArgs(argv) {
|
|
12
27
|
const flags = { extensions: [], messages: [] };
|
|
13
28
|
const args = argv.slice(2); // skip node + script
|
|
@@ -199,6 +214,7 @@ if (isPrintMode) {
|
|
|
199
214
|
appendSystemPrompt = cliFlags.appendSystemPrompt;
|
|
200
215
|
}
|
|
201
216
|
}
|
|
217
|
+
exitIfManagedResourcesAreNewer(agentDir);
|
|
202
218
|
initResources(agentDir);
|
|
203
219
|
const resourceLoader = new DefaultResourceLoader({
|
|
204
220
|
agentDir,
|
|
@@ -272,6 +288,7 @@ if (existsSync(sessionsDir)) {
|
|
|
272
288
|
const sessionManager = cliFlags.continue
|
|
273
289
|
? SessionManager.continueRecent(cwd, projectSessionsDir)
|
|
274
290
|
: SessionManager.create(cwd, projectSessionsDir);
|
|
291
|
+
exitIfManagedResourcesAreNewer(agentDir);
|
|
275
292
|
initResources(agentDir);
|
|
276
293
|
const resourceLoader = buildResourceLoader(agentDir);
|
|
277
294
|
await resourceLoader.reload();
|
package/dist/onboarding.js
CHANGED
|
@@ -48,6 +48,7 @@ const LLM_PROVIDER_IDS = [
|
|
|
48
48
|
'xai',
|
|
49
49
|
'openrouter',
|
|
50
50
|
'mistral',
|
|
51
|
+
'ollama-cloud',
|
|
51
52
|
'custom-openai',
|
|
52
53
|
];
|
|
53
54
|
/** API key prefix validation — loose checks to catch obvious mistakes */
|
|
@@ -61,6 +62,7 @@ const OTHER_PROVIDERS = [
|
|
|
61
62
|
{ value: 'xai', label: 'xAI (Grok)' },
|
|
62
63
|
{ value: 'openrouter', label: 'OpenRouter' },
|
|
63
64
|
{ value: 'mistral', label: 'Mistral' },
|
|
65
|
+
{ value: 'ollama-cloud', label: 'Ollama Cloud' },
|
|
64
66
|
{ value: 'custom-openai', label: 'Custom (OpenAI-compatible)' },
|
|
65
67
|
];
|
|
66
68
|
// ─── Dynamic imports ──────────────────────────────────────────────────────────
|
|
@@ -755,6 +757,7 @@ export function loadStoredEnvKeys(authStorage) {
|
|
|
755
757
|
['slack_bot', 'SLACK_BOT_TOKEN'],
|
|
756
758
|
['discord_bot', 'DISCORD_BOT_TOKEN'],
|
|
757
759
|
['groq', 'GROQ_API_KEY'],
|
|
760
|
+
['ollama-cloud', 'OLLAMA_API_KEY'],
|
|
758
761
|
['custom-openai', 'CUSTOM_OPENAI_API_KEY'],
|
|
759
762
|
];
|
|
760
763
|
for (const [provider, envVar] of providers) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { DefaultResourceLoader } from '@gsd/pi-coding-agent';
|
|
2
2
|
export declare function discoverExtensionEntryPaths(extensionsDir: string): string[];
|
|
3
|
+
export declare function readManagedResourceVersion(agentDir: string): string | null;
|
|
4
|
+
export declare function getNewerManagedResourceVersion(agentDir: string, currentVersion: string): string | null;
|
|
3
5
|
/**
|
|
4
6
|
* Syncs all bundled resources to agentDir (~/.gsd/agent/) on every launch.
|
|
5
7
|
*
|
package/dist/resource-loader.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DefaultResourceLoader } from '@gsd/pi-coding-agent';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { compareSemver } from './update-check.js';
|
|
6
7
|
// Resolve resources directory — prefer dist/resources/ (stable, set at build time)
|
|
7
8
|
// over src/resources/ (live working tree, changes with git branch).
|
|
8
9
|
//
|
|
@@ -16,6 +17,7 @@ const distResources = join(packageRoot, 'dist', 'resources');
|
|
|
16
17
|
const srcResources = join(packageRoot, 'src', 'resources');
|
|
17
18
|
const resourcesDir = existsSync(distResources) ? distResources : srcResources;
|
|
18
19
|
const bundledExtensionsDir = join(resourcesDir, 'extensions');
|
|
20
|
+
const resourceVersionManifestName = 'managed-resources.json';
|
|
19
21
|
function isExtensionFile(name) {
|
|
20
22
|
return name.endsWith('.ts') || name.endsWith('.js');
|
|
21
23
|
}
|
|
@@ -70,6 +72,38 @@ function getExtensionKey(entryPath, extensionsDir) {
|
|
|
70
72
|
const relPath = relative(extensionsDir, entryPath);
|
|
71
73
|
return relPath.split(/[\\/]/)[0];
|
|
72
74
|
}
|
|
75
|
+
function getManagedResourceManifestPath(agentDir) {
|
|
76
|
+
return join(agentDir, resourceVersionManifestName);
|
|
77
|
+
}
|
|
78
|
+
function getBundledGsdVersion() {
|
|
79
|
+
try {
|
|
80
|
+
const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf-8'));
|
|
81
|
+
return typeof pkg?.version === 'string' ? pkg.version : '0.0.0';
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return process.env.GSD_VERSION || '0.0.0';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function writeManagedResourceManifest(agentDir) {
|
|
88
|
+
const manifest = { gsdVersion: getBundledGsdVersion() };
|
|
89
|
+
writeFileSync(getManagedResourceManifestPath(agentDir), JSON.stringify(manifest));
|
|
90
|
+
}
|
|
91
|
+
export function readManagedResourceVersion(agentDir) {
|
|
92
|
+
try {
|
|
93
|
+
const manifest = JSON.parse(readFileSync(getManagedResourceManifestPath(agentDir), 'utf-8'));
|
|
94
|
+
return typeof manifest?.gsdVersion === 'string' ? manifest.gsdVersion : null;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function getNewerManagedResourceVersion(agentDir, currentVersion) {
|
|
101
|
+
const managedVersion = readManagedResourceVersion(agentDir);
|
|
102
|
+
if (!managedVersion) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return compareSemver(managedVersion, currentVersion) > 0 ? managedVersion : null;
|
|
106
|
+
}
|
|
73
107
|
/**
|
|
74
108
|
* Syncs all bundled resources to agentDir (~/.gsd/agent/) on every launch.
|
|
75
109
|
*
|
|
@@ -101,6 +135,7 @@ export function initResources(agentDir) {
|
|
|
101
135
|
if (existsSync(srcSkills)) {
|
|
102
136
|
cpSync(srcSkills, destSkills, { recursive: true, force: true });
|
|
103
137
|
}
|
|
138
|
+
writeManagedResourceManifest(agentDir);
|
|
104
139
|
}
|
|
105
140
|
/**
|
|
106
141
|
* Constructs a DefaultResourceLoader that loads extensions from both
|
|
@@ -574,6 +574,7 @@ interface StartOptions {
|
|
|
574
574
|
type?: ProcessType;
|
|
575
575
|
readyPattern?: string;
|
|
576
576
|
readyPort?: number;
|
|
577
|
+
readyTimeout?: number;
|
|
577
578
|
group?: string;
|
|
578
579
|
env?: Record<string, string>;
|
|
579
580
|
}
|
|
@@ -689,7 +690,7 @@ function startProcess(opts: StartOptions): BgProcess {
|
|
|
689
690
|
|
|
690
691
|
// Port probing for server-type processes
|
|
691
692
|
if (bg.readyPort) {
|
|
692
|
-
startPortProbing(bg, bg.readyPort);
|
|
693
|
+
startPortProbing(bg, bg.readyPort, opts.readyTimeout);
|
|
693
694
|
}
|
|
694
695
|
|
|
695
696
|
// Shell sessions are ready immediately after spawn
|
|
@@ -707,9 +708,17 @@ function startProcess(opts: StartOptions): BgProcess {
|
|
|
707
708
|
|
|
708
709
|
// ── Port Probing Loop ──────────────────────────────────────────────────────
|
|
709
710
|
|
|
710
|
-
function startPortProbing(bg: BgProcess, port: number): void {
|
|
711
|
+
function startPortProbing(bg: BgProcess, port: number, customTimeout?: number): void {
|
|
712
|
+
const timeout = customTimeout || DEFAULT_READY_TIMEOUT;
|
|
711
713
|
const interval = setInterval(async () => {
|
|
712
|
-
if (!bg.alive
|
|
714
|
+
if (!bg.alive) {
|
|
715
|
+
clearInterval(interval);
|
|
716
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-10).map(l => l.line);
|
|
717
|
+
const detail = `Process exited (code ${bg.exitCode}) before port ${port} opened${stderrLines.length > 0 ? ` — ${stderrLines.join("; ").slice(0, 200)}` : ""}`;
|
|
718
|
+
addEvent(bg, { type: "port_timeout", detail, data: { port, exitCode: bg.exitCode } });
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (bg.status !== "starting") {
|
|
713
722
|
clearInterval(interval);
|
|
714
723
|
return;
|
|
715
724
|
}
|
|
@@ -722,8 +731,18 @@ function startPortProbing(bg: BgProcess, port: number): void {
|
|
|
722
731
|
}
|
|
723
732
|
}, READY_POLL_INTERVAL);
|
|
724
733
|
|
|
725
|
-
// Stop probing after timeout
|
|
726
|
-
|
|
734
|
+
// Stop probing after timeout — transition to error state so the process
|
|
735
|
+
// doesn't stay in "starting" forever (fixes #428)
|
|
736
|
+
setTimeout(() => {
|
|
737
|
+
clearInterval(interval);
|
|
738
|
+
if (bg.alive && bg.status === "starting") {
|
|
739
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-10).map(l => l.line);
|
|
740
|
+
const detail = `Port ${port} not open after ${timeout}ms${stderrLines.length > 0 ? ` — ${stderrLines.join("; ").slice(0, 200)}` : ""}`;
|
|
741
|
+
bg.status = "error";
|
|
742
|
+
addEvent(bg, { type: "port_timeout", detail, data: { port, timeout } });
|
|
743
|
+
pushAlert(bg, `Port ${port} readiness timeout after ${timeout / 1000}s`);
|
|
744
|
+
}
|
|
745
|
+
}, timeout);
|
|
727
746
|
}
|
|
728
747
|
|
|
729
748
|
// ── Process Kill ───────────────────────────────────────────────────────────
|
|
@@ -864,9 +883,19 @@ async function waitForReady(bg: BgProcess, timeout: number, signal?: AbortSignal
|
|
|
864
883
|
return { ready: false, detail: "Cancelled" };
|
|
865
884
|
}
|
|
866
885
|
if (!bg.alive) {
|
|
886
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
|
|
887
|
+
const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
|
|
888
|
+
return {
|
|
889
|
+
ready: false,
|
|
890
|
+
detail: `Process exited before becoming ready (code ${bg.exitCode})${bg.recentErrors.length > 0 ? ` — ${bg.recentErrors.slice(-1)[0]}` : ""}${stderrContext}`,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
if (bg.status === "error") {
|
|
894
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
|
|
895
|
+
const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
|
|
867
896
|
return {
|
|
868
897
|
ready: false,
|
|
869
|
-
detail: `Process
|
|
898
|
+
detail: `Process entered error state${bg.readyPort ? ` (port ${bg.readyPort} never opened)` : ""}${stderrContext}`,
|
|
870
899
|
};
|
|
871
900
|
}
|
|
872
901
|
if (bg.status === "ready") {
|
|
@@ -887,7 +916,9 @@ async function waitForReady(bg: BgProcess, timeout: number, signal?: AbortSignal
|
|
|
887
916
|
}
|
|
888
917
|
}
|
|
889
918
|
|
|
890
|
-
|
|
919
|
+
const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
|
|
920
|
+
const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
|
|
921
|
+
return { ready: false, detail: `Timed out after ${timeout}ms waiting for ready signal${stderrContext}` };
|
|
891
922
|
}
|
|
892
923
|
|
|
893
924
|
// ── Query Shell Environment ────────────────────────────────────────────────
|
|
@@ -1234,6 +1265,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
1234
1265
|
cleanupAll();
|
|
1235
1266
|
});
|
|
1236
1267
|
|
|
1268
|
+
// Register signal handlers to clean up bg processes on unexpected exit (fixes #428)
|
|
1269
|
+
// This prevents orphan processes and helps the parent restore terminal state
|
|
1270
|
+
const signalCleanup = () => {
|
|
1271
|
+
cleanupAll();
|
|
1272
|
+
};
|
|
1273
|
+
process.on("SIGTERM", signalCleanup);
|
|
1274
|
+
process.on("SIGINT", signalCleanup);
|
|
1275
|
+
process.on("beforeExit", signalCleanup);
|
|
1276
|
+
|
|
1237
1277
|
// ── Compaction Awareness: Survive Context Resets ───────────────────
|
|
1238
1278
|
|
|
1239
1279
|
/** Build a compact state summary of all alive processes for context re-injection */
|
|
@@ -1424,6 +1464,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
1424
1464
|
ready_port: Type.Optional(
|
|
1425
1465
|
Type.Number({ description: "Port to probe for readiness (for start). When open, process is considered ready." }),
|
|
1426
1466
|
),
|
|
1467
|
+
ready_timeout: Type.Optional(
|
|
1468
|
+
Type.Number({ description: "Max milliseconds to wait for ready_port/ready_pattern before marking as error (default: 30000)" }),
|
|
1469
|
+
),
|
|
1427
1470
|
group: Type.Optional(
|
|
1428
1471
|
Type.String({ description: "Group name for related processes (for start, group_status)" }),
|
|
1429
1472
|
),
|
|
@@ -1449,6 +1492,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1449
1492
|
type: params.type as ProcessType | undefined,
|
|
1450
1493
|
readyPattern: params.ready_pattern,
|
|
1451
1494
|
readyPort: params.ready_port,
|
|
1495
|
+
readyTimeout: params.ready_timeout,
|
|
1452
1496
|
group: params.group,
|
|
1453
1497
|
});
|
|
1454
1498
|
|