pi-harness-runtime 1.1.428 → 1.1.430
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/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/dist/index.js +1 -1
- package/packages/clipboard-plugin/dist/index.js.map +1 -1
- package/packages/clipboard-plugin/package.json +1 -2
- package/packages/clipboard-plugin/src/index.ts +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/dist/runtime-api.d.ts +61 -6
- package/packages/runtime/dist/runtime-api.d.ts.map +1 -1
- package/packages/runtime/dist/runtime-api.js +83 -30
- package/packages/runtime/dist/runtime-api.js.map +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/runtime/src/runtime-api.ts +168 -52
- package/packages/runtime/test/runtime-api.test.ts +169 -0
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/tooling-preference/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/two-phase-review/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/lib/runtime-imports.ts +90 -26
- package/scripts/typecheck-packages.mjs +1 -1
- package/scripts/verify-package-contents.ts +42 -0
- package/harness/task-graph.d.ts +0 -51
- package/packages/runtime/command-executor.ts +0 -523
- package/packages/runtime/index.ts +0 -50
- package/packages/runtime/local-runtime-agent.ts +0 -480
- package/packages/runtime/policy-engine.ts +0 -813
- package/packages/runtime/runtime-api.ts +0 -665
- package/packages/runtime/workspace-manager.ts +0 -591
- package/packages/types/src/ai-providers.js +0 -66
- package/packages/types/src/runtime-types.js +0 -3
|
@@ -1,591 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace Manager — RFC-0026
|
|
3
|
-
*
|
|
4
|
-
* Manages workspace directories, worktrees, and cleanup for the harness runtime.
|
|
5
|
-
*
|
|
6
|
-
* Features:
|
|
7
|
-
* - Workspace lifecycle management
|
|
8
|
-
* - Worktree creation and cleanup
|
|
9
|
-
* - Disk usage tracking
|
|
10
|
-
* - Automatic cleanup policies
|
|
11
|
-
* - Workspace snapshots
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
existsSync,
|
|
16
|
-
mkdirSync,
|
|
17
|
-
rmSync,
|
|
18
|
-
readdirSync,
|
|
19
|
-
statSync,
|
|
20
|
-
writeFileSync,
|
|
21
|
-
readFileSync,
|
|
22
|
-
cpSync,
|
|
23
|
-
} from "node:fs";
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
-
import { homedir } from "node:os";
|
|
26
|
-
import { EventEmitter } from "node:events";
|
|
27
|
-
|
|
28
|
-
export interface Workspace {
|
|
29
|
-
id: string;
|
|
30
|
-
path: string;
|
|
31
|
-
createdAt: string;
|
|
32
|
-
lastAccessedAt: string;
|
|
33
|
-
owner: string; // jobId
|
|
34
|
-
purpose: string;
|
|
35
|
-
sizeBytes: number;
|
|
36
|
-
worktree?: string; // Git worktree path
|
|
37
|
-
isActive: boolean;
|
|
38
|
-
metadata: Record<string, unknown>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface WorkspaceConfig {
|
|
42
|
-
/** Root directory for workspaces (default: ~/.pi/workspaces) */
|
|
43
|
-
rootDir?: string;
|
|
44
|
-
/** Maximum workspace age in days (default: 7) */
|
|
45
|
-
maxAgeDays?: number;
|
|
46
|
-
/** Maximum disk usage in bytes (default: 10GB) */
|
|
47
|
-
maxDiskUsage?: number;
|
|
48
|
-
/** Enable automatic cleanup (default: true) */
|
|
49
|
-
autoCleanup?: boolean;
|
|
50
|
-
/** Cleanup interval in ms (default: 1 hour) */
|
|
51
|
-
cleanupIntervalMs?: number;
|
|
52
|
-
/** Workspace prefix (default: ws-) */
|
|
53
|
-
prefix?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface WorkspaceEvent {
|
|
57
|
-
workspaceId: string;
|
|
58
|
-
timestamp: string;
|
|
59
|
-
type: "created" | "accessed" | "cleaned" | "deleted" | "error";
|
|
60
|
-
details?: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const DEFAULT_CONFIG: Required<Omit<WorkspaceConfig, "rootDir">> = {
|
|
64
|
-
maxAgeDays: 7,
|
|
65
|
-
maxDiskUsage: 10 * 1024 * 1024 * 1024, // 10GB
|
|
66
|
-
autoCleanup: true,
|
|
67
|
-
cleanupIntervalMs: 3600000, // 1 hour
|
|
68
|
-
prefix: "ws-",
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export class WorkspaceManager extends EventEmitter {
|
|
72
|
-
private readonly config: WorkspaceConfig;
|
|
73
|
-
private readonly rootDir: string;
|
|
74
|
-
private workspaces: Map<string, Workspace> = new Map();
|
|
75
|
-
private cleanupTimer: NodeJS.Timeout | null = null;
|
|
76
|
-
private manifestPath: string;
|
|
77
|
-
|
|
78
|
-
constructor(config: WorkspaceConfig = {}) {
|
|
79
|
-
super();
|
|
80
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
81
|
-
this.rootDir = this.config.rootDir ?? join(homedir(), ".pi", "workspaces");
|
|
82
|
-
this.manifestPath = join(this.rootDir, "workspaces.json");
|
|
83
|
-
|
|
84
|
-
this.ensureRootDir();
|
|
85
|
-
this.loadManifest();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Create a new workspace
|
|
90
|
-
*/
|
|
91
|
-
create(options: {
|
|
92
|
-
owner: string;
|
|
93
|
-
purpose: string;
|
|
94
|
-
fromTemplate?: string;
|
|
95
|
-
metadata?: Record<string, unknown>;
|
|
96
|
-
}): Workspace {
|
|
97
|
-
const id = this.generateId();
|
|
98
|
-
const path = join(this.rootDir, id);
|
|
99
|
-
|
|
100
|
-
// Create directory
|
|
101
|
-
mkdirSync(path, { recursive: true });
|
|
102
|
-
|
|
103
|
-
// Copy template if specified
|
|
104
|
-
if (options.fromTemplate && existsSync(options.fromTemplate)) {
|
|
105
|
-
this.copyDirectory(options.fromTemplate, path);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const workspace: Workspace = {
|
|
109
|
-
id,
|
|
110
|
-
path,
|
|
111
|
-
createdAt: new Date().toISOString(),
|
|
112
|
-
lastAccessedAt: new Date().toISOString(),
|
|
113
|
-
owner: options.owner,
|
|
114
|
-
purpose: options.purpose,
|
|
115
|
-
sizeBytes: this.calculateDirSize(path),
|
|
116
|
-
isActive: true,
|
|
117
|
-
metadata: options.metadata ?? {},
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
this.workspaces.set(id, workspace);
|
|
121
|
-
this.saveManifest();
|
|
122
|
-
|
|
123
|
-
this.emit("workspace", {
|
|
124
|
-
workspaceId: id,
|
|
125
|
-
timestamp: new Date().toISOString(),
|
|
126
|
-
type: "created",
|
|
127
|
-
details: `Created workspace for ${options.owner}: ${options.purpose}`,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return workspace;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Get a workspace by ID
|
|
135
|
-
*/
|
|
136
|
-
get(id: string): Workspace | null {
|
|
137
|
-
const workspace = this.workspaces.get(id);
|
|
138
|
-
if (workspace) {
|
|
139
|
-
// Update last accessed
|
|
140
|
-
workspace.lastAccessedAt = new Date().toISOString();
|
|
141
|
-
this.saveManifest();
|
|
142
|
-
}
|
|
143
|
-
return workspace ?? null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Get workspace by path
|
|
148
|
-
*/
|
|
149
|
-
getByPath(path: string): Workspace | null {
|
|
150
|
-
for (const workspace of Array.from(this.workspaces.values())) {
|
|
151
|
-
if (workspace.path === path || workspace.path.startsWith(path + "/")) {
|
|
152
|
-
return workspace;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* List all workspaces
|
|
160
|
-
*/
|
|
161
|
-
list(filter?: {
|
|
162
|
-
owner?: string;
|
|
163
|
-
isActive?: boolean;
|
|
164
|
-
olderThan?: Date;
|
|
165
|
-
}): Workspace[] {
|
|
166
|
-
let result = Array.from(this.workspaces.values());
|
|
167
|
-
|
|
168
|
-
if (filter?.owner) {
|
|
169
|
-
result = result.filter((ws) => ws.owner === filter.owner);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (filter?.isActive !== undefined) {
|
|
173
|
-
result = result.filter((ws) => ws.isActive === filter.isActive);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (filter?.olderThan) {
|
|
177
|
-
result = result.filter(
|
|
178
|
-
(ws) => new Date(ws.lastAccessedAt) < filter.olderThan!,
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Update workspace metadata
|
|
187
|
-
*/
|
|
188
|
-
update(id: string, updates: Partial<Workspace>): boolean {
|
|
189
|
-
const workspace = this.workspaces.get(id);
|
|
190
|
-
if (!workspace) {
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
Object.assign(workspace, updates, {
|
|
195
|
-
lastAccessedAt: new Date().toISOString(),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
this.saveManifest();
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Mark workspace as complete (no longer active)
|
|
204
|
-
*/
|
|
205
|
-
complete(id: string): boolean {
|
|
206
|
-
return this.update(id, { isActive: false });
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Delete a workspace
|
|
211
|
-
*/
|
|
212
|
-
delete(id: string, force: boolean = false): boolean {
|
|
213
|
-
const workspace = this.workspaces.get(id);
|
|
214
|
-
if (!workspace) {
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Check if active and not forced
|
|
219
|
-
if (workspace.isActive && !force) {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Delete directory
|
|
224
|
-
try {
|
|
225
|
-
rmSync(workspace.path, { recursive: true, force: true });
|
|
226
|
-
} catch (error) {
|
|
227
|
-
this.emit("workspace", {
|
|
228
|
-
workspaceId: id,
|
|
229
|
-
timestamp: new Date().toISOString(),
|
|
230
|
-
type: "error",
|
|
231
|
-
details: `Failed to delete: ${error}`,
|
|
232
|
-
});
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
this.workspaces.delete(id);
|
|
237
|
-
this.saveManifest();
|
|
238
|
-
|
|
239
|
-
this.emit("workspace", {
|
|
240
|
-
workspaceId: id,
|
|
241
|
-
timestamp: new Date().toISOString(),
|
|
242
|
-
type: "deleted",
|
|
243
|
-
details: `Deleted workspace ${id}`,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
return true;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Delete all workspaces for an owner
|
|
251
|
-
*/
|
|
252
|
-
deleteByOwner(owner: string, force: boolean = false): number {
|
|
253
|
-
const toDelete = this.list({ owner, isActive: false });
|
|
254
|
-
let deleted = 0;
|
|
255
|
-
|
|
256
|
-
for (const workspace of toDelete) {
|
|
257
|
-
if (this.delete(workspace.id, force)) {
|
|
258
|
-
deleted++;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return deleted;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Get workspace disk usage
|
|
267
|
-
*/
|
|
268
|
-
getDiskUsage(): { total: number; byOwner: Record<string, number> } {
|
|
269
|
-
const byOwner: Record<string, number> = {};
|
|
270
|
-
let total = 0;
|
|
271
|
-
|
|
272
|
-
for (const workspace of Array.from(this.workspaces.values())) {
|
|
273
|
-
const size = workspace.sizeBytes;
|
|
274
|
-
total += size;
|
|
275
|
-
byOwner[workspace.owner] = (byOwner[workspace.owner] ?? 0) + size;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return { total, byOwner };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Check if cleanup is needed
|
|
283
|
-
*/
|
|
284
|
-
isCleanupNeeded(): boolean {
|
|
285
|
-
const usage = this.getDiskUsage();
|
|
286
|
-
|
|
287
|
-
// Check disk usage
|
|
288
|
-
const maxDisk = this.config.maxDiskUsage ?? DEFAULT_CONFIG.maxDiskUsage;
|
|
289
|
-
if (usage.total > maxDisk) {
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Check for old workspaces
|
|
294
|
-
const cutoff = new Date();
|
|
295
|
-
const maxAge = this.config.maxAgeDays ?? DEFAULT_CONFIG.maxAgeDays;
|
|
296
|
-
if (maxAge) {
|
|
297
|
-
cutoff.setDate(cutoff.getDate() - maxAge);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
for (const workspace of Array.from(this.workspaces.values())) {
|
|
301
|
-
if (new Date(workspace.lastAccessedAt) < cutoff) {
|
|
302
|
-
return true;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return false;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Run cleanup
|
|
311
|
-
*/
|
|
312
|
-
cleanup(options?: {
|
|
313
|
-
olderThanDays?: number;
|
|
314
|
-
owner?: string;
|
|
315
|
-
dryRun?: boolean;
|
|
316
|
-
}): { deleted: number; freedBytes: number; workspaces: string[] } {
|
|
317
|
-
const olderThan = new Date();
|
|
318
|
-
const daysToSubtract = options?.olderThanDays ?? this.config.maxAgeDays;
|
|
319
|
-
if (daysToSubtract) {
|
|
320
|
-
olderThan.setDate(olderThan.getDate() - daysToSubtract);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const toDelete = this.list({
|
|
324
|
-
owner: options?.owner,
|
|
325
|
-
isActive: false,
|
|
326
|
-
olderThan,
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
let deleted = 0;
|
|
330
|
-
let freedBytes = 0;
|
|
331
|
-
const deletedIds: string[] = [];
|
|
332
|
-
|
|
333
|
-
for (const workspace of Array.from(toDelete)) {
|
|
334
|
-
if (options?.dryRun) {
|
|
335
|
-
deletedIds.push(workspace.id);
|
|
336
|
-
freedBytes += workspace.sizeBytes;
|
|
337
|
-
deleted++;
|
|
338
|
-
} else {
|
|
339
|
-
if (this.delete(workspace.id, true)) {
|
|
340
|
-
deletedIds.push(workspace.id);
|
|
341
|
-
freedBytes += workspace.sizeBytes;
|
|
342
|
-
deleted++;
|
|
343
|
-
|
|
344
|
-
this.emit("workspace", {
|
|
345
|
-
workspaceId: workspace.id,
|
|
346
|
-
timestamp: new Date().toISOString(),
|
|
347
|
-
type: "cleaned",
|
|
348
|
-
details: `Cleaned up workspace (${freedBytes} bytes freed)`,
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return { deleted, freedBytes, workspaces: deletedIds };
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Create a worktree from a workspace
|
|
359
|
-
*/
|
|
360
|
-
createWorktree(workspaceId: string, branchName: string): string | null {
|
|
361
|
-
const workspace = this.workspaces.get(workspaceId);
|
|
362
|
-
if (!workspace) {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const worktreePath = join(this.rootDir, `${workspaceId}-${branchName}`);
|
|
367
|
-
|
|
368
|
-
// This would typically use git worktree add
|
|
369
|
-
// For now, create a symlink-style directory
|
|
370
|
-
try {
|
|
371
|
-
cpSync(workspace.path, worktreePath, { recursive: true });
|
|
372
|
-
workspace.worktree = worktreePath;
|
|
373
|
-
this.saveManifest();
|
|
374
|
-
|
|
375
|
-
return worktreePath;
|
|
376
|
-
} catch {
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Start automatic cleanup timer
|
|
383
|
-
*/
|
|
384
|
-
startAutoCleanup(): void {
|
|
385
|
-
if (this.cleanupTimer) {
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
this.cleanupTimer = setInterval(() => {
|
|
390
|
-
if (this.isCleanupNeeded()) {
|
|
391
|
-
this.cleanup();
|
|
392
|
-
}
|
|
393
|
-
}, this.config.cleanupIntervalMs);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Stop automatic cleanup timer
|
|
398
|
-
*/
|
|
399
|
-
stopAutoCleanup(): void {
|
|
400
|
-
if (this.cleanupTimer) {
|
|
401
|
-
clearInterval(this.cleanupTimer);
|
|
402
|
-
this.cleanupTimer = null;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Snapshot a workspace
|
|
408
|
-
*/
|
|
409
|
-
snapshot(workspaceId: string, name: string): string | null {
|
|
410
|
-
const workspace = this.workspaces.get(workspaceId);
|
|
411
|
-
if (!workspace) {
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const snapshotDir = join(this.rootDir, ".snapshots", workspaceId);
|
|
416
|
-
const snapshotPath = join(snapshotDir, `${name}-${Date.now()}.tar.gz`);
|
|
417
|
-
|
|
418
|
-
try {
|
|
419
|
-
mkdirSync(snapshotDir, { recursive: true });
|
|
420
|
-
// In a real implementation, we'd use tar or a proper archive library
|
|
421
|
-
// For now, just copy the directory
|
|
422
|
-
cpSync(workspace.path, snapshotPath, { recursive: true });
|
|
423
|
-
return snapshotPath;
|
|
424
|
-
} catch {
|
|
425
|
-
return null;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Get workspace statistics
|
|
431
|
-
*/
|
|
432
|
-
getStats(): {
|
|
433
|
-
total: number;
|
|
434
|
-
active: number;
|
|
435
|
-
totalSize: number;
|
|
436
|
-
oldest: string | null;
|
|
437
|
-
newest: string | null;
|
|
438
|
-
} {
|
|
439
|
-
let total = 0;
|
|
440
|
-
let active = 0;
|
|
441
|
-
let totalSize = 0;
|
|
442
|
-
let oldest: string | null = null;
|
|
443
|
-
let newest: string | null = null;
|
|
444
|
-
let oldestDate = new Date(0);
|
|
445
|
-
let newestDate = new Date(0);
|
|
446
|
-
|
|
447
|
-
for (const workspace of Array.from(this.workspaces.values())) {
|
|
448
|
-
total++;
|
|
449
|
-
totalSize += workspace.sizeBytes;
|
|
450
|
-
|
|
451
|
-
if (workspace.isActive) active++;
|
|
452
|
-
|
|
453
|
-
const created = new Date(workspace.createdAt);
|
|
454
|
-
if (created > newestDate) {
|
|
455
|
-
newestDate = created;
|
|
456
|
-
newest = workspace.id;
|
|
457
|
-
}
|
|
458
|
-
if (created < oldestDate) {
|
|
459
|
-
oldestDate = created;
|
|
460
|
-
oldest = workspace.id;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return { total, active, totalSize, oldest, newest };
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Verify workspace integrity
|
|
469
|
-
*/
|
|
470
|
-
verify(id: string): { exists: boolean; size: number; isAccessible: boolean } {
|
|
471
|
-
const workspace = this.workspaces.get(id);
|
|
472
|
-
if (!workspace) {
|
|
473
|
-
return { exists: false, size: 0, isAccessible: false };
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const exists = existsSync(workspace.path);
|
|
477
|
-
let size = 0;
|
|
478
|
-
let isAccessible = false;
|
|
479
|
-
|
|
480
|
-
if (exists) {
|
|
481
|
-
try {
|
|
482
|
-
size = this.calculateDirSize(workspace.path);
|
|
483
|
-
isAccessible = true;
|
|
484
|
-
|
|
485
|
-
// Update stored size
|
|
486
|
-
this.update(id, { sizeBytes: size });
|
|
487
|
-
} catch {
|
|
488
|
-
isAccessible = false;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
return { exists, size, isAccessible };
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// ─── Private Methods ────────────────────────────────────────────────
|
|
496
|
-
|
|
497
|
-
private ensureRootDir(): void {
|
|
498
|
-
if (!existsSync(this.rootDir)) {
|
|
499
|
-
mkdirSync(this.rootDir, { recursive: true });
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
private generateId(): string {
|
|
504
|
-
const timestamp = Date.now().toString(36);
|
|
505
|
-
const random = Math.random().toString(36).substring(2, 8);
|
|
506
|
-
return `${this.config.prefix}${timestamp}-${random}`;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
private calculateDirSize(dirPath: string): number {
|
|
510
|
-
if (!existsSync(dirPath)) {
|
|
511
|
-
return 0;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
let size = 0;
|
|
515
|
-
|
|
516
|
-
try {
|
|
517
|
-
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
518
|
-
|
|
519
|
-
for (const entry of entries) {
|
|
520
|
-
const fullPath = join(dirPath, entry.name);
|
|
521
|
-
|
|
522
|
-
if (entry.isDirectory()) {
|
|
523
|
-
size += this.calculateDirSize(fullPath);
|
|
524
|
-
} else if (entry.isFile()) {
|
|
525
|
-
try {
|
|
526
|
-
size += statSync(fullPath).size;
|
|
527
|
-
} catch {
|
|
528
|
-
// Skip inaccessible files
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
} catch {
|
|
533
|
-
// Skip inaccessible directories
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
return size;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
private copyDirectory(src: string, dest: string): void {
|
|
540
|
-
if (!existsSync(src)) {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
mkdirSync(dest, { recursive: true });
|
|
545
|
-
cpSync(src, dest, { recursive: true });
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
private loadManifest(): void {
|
|
549
|
-
if (!existsSync(this.manifestPath)) {
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
try {
|
|
554
|
-
const data = readFileSync(this.manifestPath, "utf-8");
|
|
555
|
-
const workspaces = JSON.parse(data) as Workspace[];
|
|
556
|
-
|
|
557
|
-
for (const workspace of workspaces) {
|
|
558
|
-
this.workspaces.set(workspace.id, workspace);
|
|
559
|
-
}
|
|
560
|
-
} catch {
|
|
561
|
-
// Ignore corrupt manifest
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
private saveManifest(): void {
|
|
566
|
-
const workspaces = Array.from(this.workspaces.values());
|
|
567
|
-
|
|
568
|
-
try {
|
|
569
|
-
writeFileSync(
|
|
570
|
-
this.manifestPath,
|
|
571
|
-
JSON.stringify(workspaces, null, 2),
|
|
572
|
-
"utf-8",
|
|
573
|
-
);
|
|
574
|
-
} catch {
|
|
575
|
-
// Ignore save errors
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Create a WorkspaceManager with default config for harness runtime
|
|
582
|
-
*/
|
|
583
|
-
export function createHarnessWorkspaceManager(): WorkspaceManager {
|
|
584
|
-
return new WorkspaceManager({
|
|
585
|
-
rootDir: join(homedir(), ".pi", "harness", "workspaces"),
|
|
586
|
-
maxAgeDays: 7,
|
|
587
|
-
maxDiskUsage: 10 * 1024 * 1024 * 1024, // 10GB
|
|
588
|
-
autoCleanup: true,
|
|
589
|
-
cleanupIntervalMs: 3600000, // 1 hour
|
|
590
|
-
});
|
|
591
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Provider Registry
|
|
3
|
-
*
|
|
4
|
-
* Centralized, canonical list of known AI/LLM providers used as keys across
|
|
5
|
-
* mirror.json, quota tracking, TUI signal parsing, and footer display.
|
|
6
|
-
*
|
|
7
|
-
* Adding a new provider = add ONE entry here + update the places that
|
|
8
|
-
* need per-provider behaviour (scraper, TUI parser, footer, etc.).
|
|
9
|
-
*
|
|
10
|
-
* Provider ids are lowercase internal keys.
|
|
11
|
-
* Labels are human-readable display strings.
|
|
12
|
-
*/
|
|
13
|
-
export const KNOWN_AI_PROVIDERS = [
|
|
14
|
-
"minimax",
|
|
15
|
-
"openai",
|
|
16
|
-
"anthropic",
|
|
17
|
-
"glm",
|
|
18
|
-
"openrouter",
|
|
19
|
-
"openai-codex",
|
|
20
|
-
"deepseek",
|
|
21
|
-
"gemini",
|
|
22
|
-
"kimi",
|
|
23
|
-
];
|
|
24
|
-
/** Human-readable display labels (used in footer, notifications). */
|
|
25
|
-
export const PROVIDER_LABELS = {
|
|
26
|
-
minimax: "MiniMax",
|
|
27
|
-
openai: "OpenAI",
|
|
28
|
-
anthropic: "Anthropic",
|
|
29
|
-
glm: "GLM",
|
|
30
|
-
openrouter: "OpenRouter",
|
|
31
|
-
"openai-codex": "OpenAI Codex",
|
|
32
|
-
deepseek: "DeepSeek",
|
|
33
|
-
gemini: "Gemini",
|
|
34
|
-
kimi: "Kimi",
|
|
35
|
-
unknown: "Unknown",
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Providers that support continuous quota scraping via cookie/browser automation.
|
|
39
|
-
* Only MiniMax is currently supported here.
|
|
40
|
-
*/
|
|
41
|
-
export const SCRAPEABLE_PROVIDERS = ["minimax"];
|
|
42
|
-
/**
|
|
43
|
-
* Providers that report quota via TUI signal capture (one-shot on limit hit).
|
|
44
|
-
* These do NOT have a continuous scraping path.
|
|
45
|
-
*/
|
|
46
|
-
export const TUI_SIGNAL_PROVIDERS = [
|
|
47
|
-
"openai",
|
|
48
|
-
"openai-codex",
|
|
49
|
-
"anthropic",
|
|
50
|
-
"glm",
|
|
51
|
-
"openrouter",
|
|
52
|
-
];
|
|
53
|
-
/**
|
|
54
|
-
* Check if a provider id is a known AI provider.
|
|
55
|
-
*/
|
|
56
|
-
export function isKnownAiProvider(id) {
|
|
57
|
-
return KNOWN_AI_PROVIDERS.includes(id);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Get the display label for a provider id.
|
|
61
|
-
* Falls back to the id itself if unknown.
|
|
62
|
-
*/
|
|
63
|
-
export function getProviderLabel(id) {
|
|
64
|
-
return PROVIDER_LABELS[id] ?? id;
|
|
65
|
-
}
|
|
66
|
-
//# sourceMappingURL=ai-providers.js.map
|