specweave 0.28.57 → 0.28.59
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/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +8 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/init/external-import.d.ts +18 -1
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/external-import.js +61 -21
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/cli/workers/import-worker.d.ts +18 -0
- package/dist/src/cli/workers/import-worker.d.ts.map +1 -0
- package/dist/src/cli/workers/import-worker.js +154 -0
- package/dist/src/cli/workers/import-worker.js.map +1 -0
- package/dist/src/core/background/index.d.ts +8 -0
- package/dist/src/core/background/index.d.ts.map +1 -1
- package/dist/src/core/background/index.js +8 -0
- package/dist/src/core/background/index.js.map +1 -1
- package/dist/src/core/background/job-launcher.d.ts +54 -0
- package/dist/src/core/background/job-launcher.d.ts.map +1 -0
- package/dist/src/core/background/job-launcher.js +206 -0
- package/dist/src/core/background/job-launcher.js.map +1 -0
- package/dist/src/importers/ado-importer.d.ts +7 -1
- package/dist/src/importers/ado-importer.d.ts.map +1 -1
- package/dist/src/importers/ado-importer.js +108 -61
- package/dist/src/importers/ado-importer.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/commands/specweave-jobs.md +133 -29
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Background Import Worker
|
|
4
|
+
*
|
|
5
|
+
* Standalone script that runs import in a detached process.
|
|
6
|
+
* Survives terminal close - progress tracked via job state file.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node import-worker.js <jobId> <projectPath>
|
|
10
|
+
*
|
|
11
|
+
* The worker reads job configuration from:
|
|
12
|
+
* .specweave/state/jobs/<jobId>/config.json
|
|
13
|
+
*
|
|
14
|
+
* And updates progress to:
|
|
15
|
+
* .specweave/state/background-jobs.json
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
// Worker-specific imports (loaded dynamically to reduce startup time)
|
|
21
|
+
let ImportCoordinator;
|
|
22
|
+
let getJobManager;
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
/**
|
|
26
|
+
* Main worker entry point
|
|
27
|
+
*/
|
|
28
|
+
async function main() {
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
if (args.length < 2) {
|
|
31
|
+
console.error('Usage: import-worker.js <jobId> <projectPath>');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const jobId = args[0];
|
|
35
|
+
const projectPath = args[1];
|
|
36
|
+
// Write PID file for process management
|
|
37
|
+
const pidFile = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'worker.pid');
|
|
38
|
+
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
|
|
39
|
+
fs.writeFileSync(pidFile, process.pid.toString());
|
|
40
|
+
// Setup cleanup on exit
|
|
41
|
+
const cleanup = () => {
|
|
42
|
+
try {
|
|
43
|
+
if (fs.existsSync(pidFile)) {
|
|
44
|
+
fs.unlinkSync(pidFile);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Ignore cleanup errors
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
process.on('exit', cleanup);
|
|
52
|
+
process.on('SIGTERM', () => {
|
|
53
|
+
cleanup();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
});
|
|
56
|
+
process.on('SIGINT', () => {
|
|
57
|
+
cleanup();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
// Load job configuration
|
|
62
|
+
const configPath = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'config.json');
|
|
63
|
+
if (!fs.existsSync(configPath)) {
|
|
64
|
+
throw new Error(`Job config not found: ${configPath}`);
|
|
65
|
+
}
|
|
66
|
+
const jobConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
67
|
+
// Log to worker-specific log file
|
|
68
|
+
const logPath = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'worker.log');
|
|
69
|
+
const log = (msg) => {
|
|
70
|
+
const timestamp = new Date().toISOString();
|
|
71
|
+
fs.appendFileSync(logPath, `[${timestamp}] ${msg}\n`);
|
|
72
|
+
};
|
|
73
|
+
log(`Worker started for job ${jobId}`);
|
|
74
|
+
log(`Project path: ${projectPath}`);
|
|
75
|
+
log(`PID: ${process.pid}`);
|
|
76
|
+
// Dynamically import heavy dependencies
|
|
77
|
+
const importCoordinatorModule = await import('../../importers/import-coordinator.js');
|
|
78
|
+
ImportCoordinator = importCoordinatorModule.ImportCoordinator;
|
|
79
|
+
const jobManagerModule = await import('../../core/background/job-manager.js');
|
|
80
|
+
getJobManager = jobManagerModule.getJobManager;
|
|
81
|
+
// Get job manager and mark as running
|
|
82
|
+
const jobManager = getJobManager(projectPath);
|
|
83
|
+
jobManager.startJob(jobId);
|
|
84
|
+
log('Dependencies loaded, starting import...');
|
|
85
|
+
// Setup progress tracking
|
|
86
|
+
const coordinatorConfig = jobConfig.coordinatorConfig;
|
|
87
|
+
let totalEstimate = 0;
|
|
88
|
+
let currentCount = 0;
|
|
89
|
+
coordinatorConfig.onProgressEnhanced = (info) => {
|
|
90
|
+
currentCount = info.current || currentCount;
|
|
91
|
+
if (info.total && info.total > totalEstimate) {
|
|
92
|
+
totalEstimate = info.total;
|
|
93
|
+
}
|
|
94
|
+
// Update job progress
|
|
95
|
+
jobManager.updateProgress(jobId, currentCount, info.sourceRepo || info.platform, undefined, undefined);
|
|
96
|
+
// Update total estimate if we now know more
|
|
97
|
+
const job = jobManager.getJob(jobId);
|
|
98
|
+
if (job && totalEstimate > job.progress.total) {
|
|
99
|
+
job.progress.total = totalEstimate;
|
|
100
|
+
// Force save with new total
|
|
101
|
+
jobManager.updateProgress(jobId, currentCount, info.sourceRepo || info.platform);
|
|
102
|
+
}
|
|
103
|
+
log(`Progress: ${currentCount}/${totalEstimate} - ${info.platform} ${info.sourceRepo || ''}`);
|
|
104
|
+
};
|
|
105
|
+
// Rate limit handling
|
|
106
|
+
coordinatorConfig.onRateLimitPause = (platform, seconds) => {
|
|
107
|
+
log(`Rate limited by ${platform}, pausing for ${seconds}s`);
|
|
108
|
+
jobManager.pauseJob(jobId);
|
|
109
|
+
// Set resume time
|
|
110
|
+
const job = jobManager.getJob(jobId);
|
|
111
|
+
if (job) {
|
|
112
|
+
job.resumeAfter = new Date(Date.now() + seconds * 1000);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
// Execute import
|
|
116
|
+
const coordinator = new ImportCoordinator(coordinatorConfig);
|
|
117
|
+
const result = await coordinator.importAll();
|
|
118
|
+
log(`Import complete: ${result.totalCount} items imported`);
|
|
119
|
+
// Mark job as complete
|
|
120
|
+
jobManager.completeJob(jobId);
|
|
121
|
+
// Write result summary
|
|
122
|
+
const resultPath = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'result.json');
|
|
123
|
+
fs.writeFileSync(resultPath, JSON.stringify({
|
|
124
|
+
totalCount: result.totalCount,
|
|
125
|
+
completedAt: new Date().toISOString(),
|
|
126
|
+
summary: result.summary || {}
|
|
127
|
+
}, null, 2));
|
|
128
|
+
log('Worker finished successfully');
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Log error
|
|
133
|
+
const logPath = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'worker.log');
|
|
134
|
+
fs.appendFileSync(logPath, `[${new Date().toISOString()}] ERROR: ${error.message}\n`);
|
|
135
|
+
fs.appendFileSync(logPath, `${error.stack}\n`);
|
|
136
|
+
// Mark job as failed
|
|
137
|
+
try {
|
|
138
|
+
const jobManagerModule = await import('../../core/background/job-manager.js');
|
|
139
|
+
const jobManager = jobManagerModule.getJobManager(projectPath);
|
|
140
|
+
jobManager.completeJob(jobId, error.message);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Ignore if can't update job
|
|
144
|
+
}
|
|
145
|
+
console.error(`Worker error: ${error.message}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Run worker
|
|
150
|
+
main().catch((error) => {
|
|
151
|
+
console.error('Fatal worker error:', error);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=import-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-worker.js","sourceRoot":"","sources":["../../../../src/cli/workers/import-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
* - Repository cloning (multi-repo setup)
|
|
6
6
|
* - Issue import (10K+ items from GitHub/JIRA/ADO)
|
|
7
7
|
* - External sync operations
|
|
8
|
+
*
|
|
9
|
+
* ASYNC ARCHITECTURE (2025-12-01):
|
|
10
|
+
* - Jobs spawn as detached processes that survive terminal close
|
|
11
|
+
* - Progress tracked via filesystem (.specweave/state/jobs/)
|
|
12
|
+
* - Check status: /specweave:jobs
|
|
13
|
+
* - Resume: /specweave:jobs --resume <jobId>
|
|
14
|
+
* - Kill: /specweave:jobs --kill <jobId>
|
|
8
15
|
*/
|
|
9
16
|
export * from './types.js';
|
|
10
17
|
export * from './job-manager.js';
|
|
18
|
+
export * from './job-launcher.js';
|
|
11
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/background/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/background/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC"}
|
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
* - Repository cloning (multi-repo setup)
|
|
6
6
|
* - Issue import (10K+ items from GitHub/JIRA/ADO)
|
|
7
7
|
* - External sync operations
|
|
8
|
+
*
|
|
9
|
+
* ASYNC ARCHITECTURE (2025-12-01):
|
|
10
|
+
* - Jobs spawn as detached processes that survive terminal close
|
|
11
|
+
* - Progress tracked via filesystem (.specweave/state/jobs/)
|
|
12
|
+
* - Check status: /specweave:jobs
|
|
13
|
+
* - Resume: /specweave:jobs --resume <jobId>
|
|
14
|
+
* - Kill: /specweave:jobs --kill <jobId>
|
|
8
15
|
*/
|
|
9
16
|
export * from './types.js';
|
|
10
17
|
export * from './job-manager.js';
|
|
18
|
+
export * from './job-launcher.js';
|
|
11
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/background/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/background/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background Job Launcher
|
|
3
|
+
*
|
|
4
|
+
* Spawns import workers as detached processes that survive terminal close.
|
|
5
|
+
* Progress tracked via file system - check with /specweave:jobs
|
|
6
|
+
*/
|
|
7
|
+
import type { BackgroundJob, JobType } from './types.js';
|
|
8
|
+
export interface LaunchOptions {
|
|
9
|
+
/** Job type */
|
|
10
|
+
type: JobType;
|
|
11
|
+
/** Project path */
|
|
12
|
+
projectPath: string;
|
|
13
|
+
/** Coordinator config for import */
|
|
14
|
+
coordinatorConfig: any;
|
|
15
|
+
/** Estimated total items (can be updated by worker) */
|
|
16
|
+
estimatedTotal?: number;
|
|
17
|
+
/** Run in foreground (blocking) instead of background */
|
|
18
|
+
foreground?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface LaunchResult {
|
|
21
|
+
job: BackgroundJob;
|
|
22
|
+
/** PID of background process (undefined if foreground) */
|
|
23
|
+
pid?: number;
|
|
24
|
+
/** Whether running in background */
|
|
25
|
+
isBackground: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Launch an import job
|
|
29
|
+
*
|
|
30
|
+
* @param options Launch configuration
|
|
31
|
+
* @returns Job info and process details
|
|
32
|
+
*/
|
|
33
|
+
export declare function launchImportJob(options: LaunchOptions): Promise<LaunchResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Check if a background job is still running
|
|
36
|
+
*/
|
|
37
|
+
export declare function isJobRunning(projectPath: string, jobId: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Kill a background job
|
|
40
|
+
*/
|
|
41
|
+
export declare function killJob(projectPath: string, jobId: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get worker log output
|
|
44
|
+
*/
|
|
45
|
+
export declare function getJobLog(projectPath: string, jobId: string, tailLines?: number): string;
|
|
46
|
+
/**
|
|
47
|
+
* Get job result (after completion)
|
|
48
|
+
*/
|
|
49
|
+
export declare function getJobResult(projectPath: string, jobId: string): any | null;
|
|
50
|
+
/**
|
|
51
|
+
* Clean up old job directories
|
|
52
|
+
*/
|
|
53
|
+
export declare function cleanupOldJobs(projectPath: string, keepDays?: number): void;
|
|
54
|
+
//# sourceMappingURL=job-launcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-launcher.d.ts","sourceRoot":"","sources":["../../../../src/core/background/job-launcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAmB,MAAM,YAAY,CAAC;AAE1E,MAAM,WAAW,aAAa;IAC5B,eAAe;IACf,IAAI,EAAE,OAAO,CAAC;IACd,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,iBAAiB,EAAE,GAAG,CAAC;IACvB,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,aAAa,CAAC;IACnB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAiFnF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAiBxE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAqBnE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAexF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAY3E;AA0BD;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,IAAI,CA8B9E"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background Job Launcher
|
|
3
|
+
*
|
|
4
|
+
* Spawns import workers as detached processes that survive terminal close.
|
|
5
|
+
* Progress tracked via file system - check with /specweave:jobs
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import * as fs from '../../utils/fs-native.js';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { getJobManager } from './job-manager.js';
|
|
11
|
+
/**
|
|
12
|
+
* Launch an import job
|
|
13
|
+
*
|
|
14
|
+
* @param options Launch configuration
|
|
15
|
+
* @returns Job info and process details
|
|
16
|
+
*/
|
|
17
|
+
export async function launchImportJob(options) {
|
|
18
|
+
const { type, projectPath, coordinatorConfig, estimatedTotal = 100, foreground = false } = options;
|
|
19
|
+
// Create job via job manager
|
|
20
|
+
const jobManager = getJobManager(projectPath);
|
|
21
|
+
const provider = coordinatorConfig.github ? 'github' :
|
|
22
|
+
coordinatorConfig.jira ? 'jira' :
|
|
23
|
+
coordinatorConfig.ado ? 'ado' : 'github';
|
|
24
|
+
const jobConfig = {
|
|
25
|
+
type: 'import-issues',
|
|
26
|
+
provider: provider,
|
|
27
|
+
projectPath,
|
|
28
|
+
timeRangeMonths: coordinatorConfig.importConfig?.timeRangeMonths || 3,
|
|
29
|
+
repositories: coordinatorConfig.githubRepositories?.map((r) => `${r.owner}/${r.repo}`)
|
|
30
|
+
};
|
|
31
|
+
const job = jobManager.createJob(type, jobConfig, estimatedTotal);
|
|
32
|
+
// Create job-specific directory for config and logs
|
|
33
|
+
const jobDir = path.join(projectPath, '.specweave', 'state', 'jobs', job.id);
|
|
34
|
+
fs.ensureDirSync(jobDir);
|
|
35
|
+
// Write coordinator config for worker
|
|
36
|
+
const configPath = path.join(jobDir, 'config.json');
|
|
37
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
38
|
+
jobId: job.id,
|
|
39
|
+
projectPath,
|
|
40
|
+
coordinatorConfig,
|
|
41
|
+
startedAt: new Date().toISOString()
|
|
42
|
+
}, null, 2));
|
|
43
|
+
// If foreground mode, return job without spawning worker
|
|
44
|
+
// Caller will handle the import directly
|
|
45
|
+
if (foreground) {
|
|
46
|
+
return {
|
|
47
|
+
job,
|
|
48
|
+
isBackground: false
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Find worker script path
|
|
52
|
+
const workerPath = findWorkerPath();
|
|
53
|
+
if (!workerPath) {
|
|
54
|
+
// Fallback to foreground if worker not found
|
|
55
|
+
console.warn('Background worker not found, running in foreground');
|
|
56
|
+
return {
|
|
57
|
+
job,
|
|
58
|
+
isBackground: false
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Spawn detached process
|
|
62
|
+
const child = spawn('node', [workerPath, job.id, projectPath], {
|
|
63
|
+
detached: true,
|
|
64
|
+
stdio: 'ignore',
|
|
65
|
+
cwd: projectPath,
|
|
66
|
+
env: {
|
|
67
|
+
...process.env,
|
|
68
|
+
// Pass any necessary env vars
|
|
69
|
+
SPECWEAVE_BACKGROUND_JOB: '1'
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// Unref to allow parent to exit independently
|
|
73
|
+
child.unref();
|
|
74
|
+
// Update job with PID
|
|
75
|
+
const updatedJob = jobManager.getJob(job.id);
|
|
76
|
+
if (updatedJob) {
|
|
77
|
+
updatedJob.pid = child.pid;
|
|
78
|
+
updatedJob.isBackground = true;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
job: updatedJob || job,
|
|
82
|
+
pid: child.pid,
|
|
83
|
+
isBackground: true
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if a background job is still running
|
|
88
|
+
*/
|
|
89
|
+
export function isJobRunning(projectPath, jobId) {
|
|
90
|
+
const pidFile = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'worker.pid');
|
|
91
|
+
if (!fs.existsSync(pidFile)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
96
|
+
// Check if process is running (signal 0 doesn't kill, just checks)
|
|
97
|
+
process.kill(pid, 0);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Process not running or no permission
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Kill a background job
|
|
107
|
+
*/
|
|
108
|
+
export function killJob(projectPath, jobId) {
|
|
109
|
+
const pidFile = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'worker.pid');
|
|
110
|
+
if (!fs.existsSync(pidFile)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
115
|
+
// Send SIGTERM for graceful shutdown
|
|
116
|
+
process.kill(pid, 'SIGTERM');
|
|
117
|
+
// Update job status
|
|
118
|
+
const jobManager = getJobManager(projectPath);
|
|
119
|
+
jobManager.pauseJob(jobId);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get worker log output
|
|
128
|
+
*/
|
|
129
|
+
export function getJobLog(projectPath, jobId, tailLines) {
|
|
130
|
+
const logPath = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'worker.log');
|
|
131
|
+
if (!fs.existsSync(logPath)) {
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
135
|
+
if (tailLines && tailLines > 0) {
|
|
136
|
+
const lines = content.split('\n');
|
|
137
|
+
return lines.slice(-tailLines).join('\n');
|
|
138
|
+
}
|
|
139
|
+
return content;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get job result (after completion)
|
|
143
|
+
*/
|
|
144
|
+
export function getJobResult(projectPath, jobId) {
|
|
145
|
+
const resultPath = path.join(projectPath, '.specweave', 'state', 'jobs', jobId, 'result.json');
|
|
146
|
+
if (!fs.existsSync(resultPath)) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(fs.readFileSync(resultPath, 'utf-8'));
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Find the worker script path
|
|
158
|
+
*/
|
|
159
|
+
function findWorkerPath() {
|
|
160
|
+
// Try relative paths from different locations
|
|
161
|
+
const possiblePaths = [
|
|
162
|
+
// From dist/src/core/background (compiled)
|
|
163
|
+
path.join(__dirname, '../../cli/workers/import-worker.js'),
|
|
164
|
+
// From src/core/background (dev)
|
|
165
|
+
path.join(__dirname, '../../../dist/src/cli/workers/import-worker.js'),
|
|
166
|
+
// Global install
|
|
167
|
+
path.join(__dirname, '../../../../cli/workers/import-worker.js'),
|
|
168
|
+
];
|
|
169
|
+
for (const p of possiblePaths) {
|
|
170
|
+
const resolved = path.resolve(p);
|
|
171
|
+
if (fs.existsSync(resolved)) {
|
|
172
|
+
return resolved;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Clean up old job directories
|
|
179
|
+
*/
|
|
180
|
+
export function cleanupOldJobs(projectPath, keepDays = 7) {
|
|
181
|
+
const jobsDir = path.join(projectPath, '.specweave', 'state', 'jobs');
|
|
182
|
+
if (!fs.existsSync(jobsDir)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1000;
|
|
186
|
+
try {
|
|
187
|
+
const entries = fs.readdirSync(jobsDir);
|
|
188
|
+
for (const entry of entries) {
|
|
189
|
+
const jobDir = path.join(jobsDir, entry);
|
|
190
|
+
const stat = fs.statSync(jobDir);
|
|
191
|
+
if (stat.isDirectory() && stat.mtimeMs < cutoff) {
|
|
192
|
+
// Check job status before deleting
|
|
193
|
+
const jobManager = getJobManager(projectPath);
|
|
194
|
+
const job = jobManager.getJob(entry);
|
|
195
|
+
// Only delete completed/failed jobs
|
|
196
|
+
if (!job || job.status === 'completed' || job.status === 'failed') {
|
|
197
|
+
fs.rmSync(jobDir, { recursive: true, force: true });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Ignore cleanup errors
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=job-launcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-launcher.js","sourceRoot":"","sources":["../../../../src/core/background/job-launcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,12 +19,18 @@ export declare class ADOImporter implements Importer {
|
|
|
19
19
|
*/
|
|
20
20
|
import(config?: ImportConfig): Promise<ExternalItem[]>;
|
|
21
21
|
/**
|
|
22
|
-
* Paginate through work items using WIQL
|
|
22
|
+
* Paginate through work items using WIQL with date-based pagination for 100K+ items
|
|
23
23
|
*
|
|
24
24
|
* CRITICAL FIX (2025-12-01): Also fetch missing parent items (Epics/Capabilities)
|
|
25
25
|
* Problem: If a parent Epic wasn't modified recently, it won't be in the WIQL results,
|
|
26
26
|
* causing all its child User Stories to be grouped into one generic folder.
|
|
27
27
|
* Solution: After fetching all items, identify missing parents and fetch them separately.
|
|
28
|
+
*
|
|
29
|
+
* ENHANCEMENT (2025-12-01): Date-based pagination to handle 100K+ items
|
|
30
|
+
* ADO WIQL has a 20K ID limit per query. For larger projects, we need to:
|
|
31
|
+
* 1. Run initial WIQL query (up to 20K IDs)
|
|
32
|
+
* 2. If we hit the limit, run another query with older date range
|
|
33
|
+
* 3. Keep paginating by date until we have all items or hit maxItems
|
|
28
34
|
*/
|
|
29
35
|
paginate(config?: ImportConfig): AsyncGenerator<ExternalItem[], void, unknown>;
|
|
30
36
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ado-importer.d.ts","sourceRoot":"","sources":["../../../src/importers/ado-importer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAoCnF;;GAEG;AACH,qBAAa,WAAY,YAAW,QAAQ;IAC1C,QAAQ,CAAC,QAAQ,EAAG,KAAK,CAAU;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAS;gBAER,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM;IAYzD;;OAEG;IACG,MAAM,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAUhE
|
|
1
|
+
{"version":3,"file":"ado-importer.d.ts","sourceRoot":"","sources":["../../../src/importers/ado-importer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAoCnF;;GAEG;AACH,qBAAa,WAAY,YAAW,QAAQ;IAC1C,QAAQ,CAAC,QAAQ,EAAG,KAAK,CAAU;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAS;gBAER,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM;IAYzD;;OAEG;IACG,MAAM,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAUhE;;;;;;;;;;;;;OAaG;IACI,QAAQ,CAAC,MAAM,GAAE,YAAiB,GAAG,cAAc,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC;IA0KzF;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;OAEG;YACW,iBAAiB;IA4B/B;;OAEG;YACW,cAAc;IA8B5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2E7B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;CAkBhC"}
|