@clipboard-health/groundcrew 3.1.2 → 3.1.4
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/README.md +47 -17
- package/crew.config.example.ts +25 -10
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -0
- package/dist/commands/cleaner.d.ts.map +1 -1
- package/dist/commands/cleaner.js +6 -4
- package/dist/commands/cleanupWorkspace.d.ts.map +1 -1
- package/dist/commands/cleanupWorkspace.js +2 -0
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +6 -6
- package/dist/commands/eligibility.d.ts.map +1 -1
- package/dist/commands/eligibility.js +2 -2
- package/dist/commands/interruptWorkspace.d.ts +8 -0
- package/dist/commands/interruptWorkspace.d.ts.map +1 -0
- package/dist/commands/interruptWorkspace.js +108 -0
- package/dist/commands/orchestrator.d.ts +4 -2
- package/dist/commands/orchestrator.d.ts.map +1 -1
- package/dist/commands/orchestrator.js +6 -105
- package/dist/commands/resumeWorkspace.d.ts +7 -0
- package/dist/commands/resumeWorkspace.d.ts.map +1 -0
- package/dist/commands/resumeWorkspace.js +163 -0
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +78 -79
- package/dist/commands/ticketDoctor.d.ts +18 -3
- package/dist/commands/ticketDoctor.d.ts.map +1 -1
- package/dist/commands/ticketDoctor.js +105 -11
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/lib/agentLaunch.d.ts +29 -0
- package/dist/lib/agentLaunch.d.ts.map +1 -0
- package/dist/lib/agentLaunch.js +53 -0
- package/dist/lib/boardSource.d.ts +41 -5
- package/dist/lib/boardSource.d.ts.map +1 -1
- package/dist/lib/boardSource.js +211 -70
- package/dist/lib/config.d.ts +59 -25
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +130 -22
- package/dist/lib/linearIssueStatus.d.ts +3 -1
- package/dist/lib/linearIssueStatus.d.ts.map +1 -1
- package/dist/lib/linearIssueStatus.js +0 -0
- package/dist/lib/runState.d.ts +46 -0
- package/dist/lib/runState.d.ts.map +1 -0
- package/dist/lib/runState.js +137 -0
- package/dist/lib/runStateCleanup.d.ts +4 -0
- package/dist/lib/runStateCleanup.d.ts.map +1 -0
- package/dist/lib/runStateCleanup.js +12 -0
- package/dist/lib/stagedLaunch.d.ts +32 -0
- package/dist/lib/stagedLaunch.d.ts.map +1 -0
- package/dist/lib/stagedLaunch.js +58 -0
- package/dist/lib/util.d.ts +0 -1
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +0 -4
- package/dist/lib/workspaces.d.ts +19 -1
- package/dist/lib/workspaces.d.ts.map +1 -1
- package/dist/lib/workspaces.js +29 -9
- package/dist/lib/worktrees.d.ts.map +1 -1
- package/dist/lib/worktrees.js +12 -4
- package/package.json +1 -1
package/dist/lib/config.js
CHANGED
|
@@ -173,11 +173,11 @@ function uniqueStrings(values) {
|
|
|
173
173
|
function normalizeStatusName(value, fallback, path) {
|
|
174
174
|
return normalizeOptionalString(value, path) ?? fallback;
|
|
175
175
|
}
|
|
176
|
-
function normalizeStatuses(user) {
|
|
177
|
-
const todo = normalizeStatusName(user?.todo, DEFAULT_STATUSES.todo,
|
|
178
|
-
const inProgress = normalizeStatusName(user?.inProgress, DEFAULT_STATUSES.inProgress,
|
|
179
|
-
const done = normalizeStatusName(user?.done, DEFAULT_STATUSES.done,
|
|
180
|
-
const terminal = normalizeOptionalStringArray(user?.terminal,
|
|
176
|
+
function normalizeStatuses(user, path) {
|
|
177
|
+
const todo = normalizeStatusName(user?.todo, DEFAULT_STATUSES.todo, `${path}.todo`);
|
|
178
|
+
const inProgress = normalizeStatusName(user?.inProgress, DEFAULT_STATUSES.inProgress, `${path}.inProgress`);
|
|
179
|
+
const done = normalizeStatusName(user?.done, DEFAULT_STATUSES.done, `${path}.done`);
|
|
180
|
+
const terminal = normalizeOptionalStringArray(user?.terminal, `${path}.terminal`) ?? [];
|
|
181
181
|
return {
|
|
182
182
|
todo,
|
|
183
183
|
inProgress,
|
|
@@ -337,12 +337,83 @@ function requireObject(value, path) {
|
|
|
337
337
|
fail(`${path} must be an object (got ${JSON.stringify(value)})`);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
|
+
function normalizeProject(value, index) {
|
|
341
|
+
const path = `linear.projects[${index}]`;
|
|
342
|
+
if (!isPlainObject(value)) {
|
|
343
|
+
fail(`${path} must be an object (got ${JSON.stringify(value)})`);
|
|
344
|
+
}
|
|
345
|
+
const { projectSlug, statuses } = value;
|
|
346
|
+
requireString(projectSlug, `${path}.projectSlug`);
|
|
347
|
+
const slugId = extractSlugId(projectSlug);
|
|
348
|
+
if (slugId === undefined) {
|
|
349
|
+
fail(`${path}.projectSlug must end with a 12-character hex slugId (got ${JSON.stringify(projectSlug)}). Copy the trailing segment from your Linear project URL, e.g. "ai-strategy-5152195762f3" from "https://linear.app/<workspace>/project/ai-strategy-5152195762f3".`);
|
|
350
|
+
}
|
|
351
|
+
if (statuses !== undefined && !isPlainObject(statuses)) {
|
|
352
|
+
fail(`${path}.statuses must be an object`);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
projectSlug,
|
|
356
|
+
slugId,
|
|
357
|
+
statuses: normalizeStatuses(statuses, `${path}.statuses`),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function failOnLegacyLinearShape(user) {
|
|
361
|
+
const { linear } = user;
|
|
362
|
+
if (!isPlainObject(linear)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (!Object.hasOwn(linear, "projectSlug") && !Object.hasOwn(linear, "statuses")) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const legacySlug = linear["projectSlug"];
|
|
369
|
+
const { projects } = linear;
|
|
370
|
+
const firstProject = Array.isArray(projects) ? projects[0] : undefined;
|
|
371
|
+
const migratedSlug = isPlainObject(firstProject) && typeof firstProject["projectSlug"] === "string"
|
|
372
|
+
? firstProject["projectSlug"]
|
|
373
|
+
: undefined;
|
|
374
|
+
let slugLiteral = `"your-project-name-0123456789ab"`;
|
|
375
|
+
if (typeof legacySlug === "string") {
|
|
376
|
+
slugLiteral = JSON.stringify(legacySlug);
|
|
377
|
+
}
|
|
378
|
+
else if (migratedSlug !== undefined) {
|
|
379
|
+
slugLiteral = JSON.stringify(migratedSlug);
|
|
380
|
+
}
|
|
381
|
+
const statusesBlock = isPlainObject(linear["statuses"])
|
|
382
|
+
? `, statuses: ${JSON.stringify(linear["statuses"])}`
|
|
383
|
+
: "";
|
|
384
|
+
fail([
|
|
385
|
+
"linear.projectSlug / linear.statuses are no longer supported — wrap them in linear.projects[].",
|
|
386
|
+
"Migrate by replacing your `linear` block with:",
|
|
387
|
+
` linear: { projects: [{ projectSlug: ${slugLiteral}${statusesBlock} }] }`,
|
|
388
|
+
"See crew.config.example.ts for the multi-project shape.",
|
|
389
|
+
].join("\n"));
|
|
390
|
+
}
|
|
391
|
+
function normalizeProjects(linear) {
|
|
392
|
+
const { projects } = linear;
|
|
393
|
+
if (!Array.isArray(projects)) {
|
|
394
|
+
fail("linear.projects must be a non-empty array");
|
|
395
|
+
}
|
|
396
|
+
if (projects.length === 0) {
|
|
397
|
+
fail("linear.projects must be a non-empty array");
|
|
398
|
+
}
|
|
399
|
+
const resolved = projects.map((entry, index) => normalizeProject(entry, index));
|
|
400
|
+
const seen = new Map();
|
|
401
|
+
resolved.forEach((project, index) => {
|
|
402
|
+
const previous = seen.get(project.slugId);
|
|
403
|
+
if (previous !== undefined) {
|
|
404
|
+
fail(`linear.projects[${index}].projectSlug duplicates the slugId "${project.slugId}" already used by linear.projects[${previous}]`);
|
|
405
|
+
}
|
|
406
|
+
seen.set(project.slugId, index);
|
|
407
|
+
});
|
|
408
|
+
return resolved;
|
|
409
|
+
}
|
|
340
410
|
function applyDefaults(user) {
|
|
341
411
|
// Guard the top-level shape before reading nested fields, so a
|
|
342
412
|
// malformed runtime config produces a `groundcrew config: ...` error
|
|
343
413
|
// instead of a raw `TypeError: Cannot read properties of undefined`.
|
|
414
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- `user` is loosely typed input from the loader; we narrow with requireObject below
|
|
415
|
+
failOnLegacyLinearShape(user);
|
|
344
416
|
requireObject(user.linear, "linear");
|
|
345
|
-
requireString(user.linear.projectSlug, "linear.projectSlug");
|
|
346
417
|
requireObject(user.workspace, "workspace");
|
|
347
418
|
if (isPlainObject(user.models) && Object.hasOwn(user.models, "isolation")) {
|
|
348
419
|
fail("models.isolation is no longer supported: set `local.runner` ('safehouse' | 'sdx' | 'none' | 'auto') instead");
|
|
@@ -354,16 +425,10 @@ function applyDefaults(user) {
|
|
|
354
425
|
if (userLocal !== undefined && !isPlainObject(userLocal)) {
|
|
355
426
|
fail("local must be an object");
|
|
356
427
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
fail(`linear.projectSlug must end with a 12-character hex slugId (got ${JSON.stringify(user.linear.projectSlug)}). Copy the trailing segment from your Linear project URL, e.g. "ai-strategy-5152195762f3" from "https://linear.app/<workspace>/project/ai-strategy-5152195762f3".`);
|
|
360
|
-
}
|
|
428
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- runtime fields validated by normalizeProjects below
|
|
429
|
+
const projects = normalizeProjects(user.linear);
|
|
361
430
|
return {
|
|
362
|
-
linear: {
|
|
363
|
-
projectSlug: user.linear.projectSlug,
|
|
364
|
-
slugId,
|
|
365
|
-
statuses: normalizeStatuses(user.linear.statuses),
|
|
366
|
-
},
|
|
431
|
+
linear: { projects },
|
|
367
432
|
git: { ...DEFAULT_GIT, ...user.git },
|
|
368
433
|
workspace: {
|
|
369
434
|
projectDir: expandHome(user.workspace.projectDir),
|
|
@@ -394,13 +459,20 @@ function validatePromptPlaceholders(template) {
|
|
|
394
459
|
}
|
|
395
460
|
}
|
|
396
461
|
function validate(config) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
requireString(
|
|
462
|
+
/* v8 ignore next 3 @preserve -- normalizeProjects already enforces non-empty; belt-and-suspenders */
|
|
463
|
+
if (config.linear.projects.length === 0) {
|
|
464
|
+
fail("linear.projects must be a non-empty array");
|
|
465
|
+
}
|
|
466
|
+
config.linear.projects.forEach((project, index) => {
|
|
467
|
+
const path = `linear.projects[${index}]`;
|
|
468
|
+
requireString(project.projectSlug, `${path}.projectSlug`);
|
|
469
|
+
requireString(project.slugId, `${path}.slugId`);
|
|
470
|
+
requireString(project.statuses.todo, `${path}.statuses.todo`);
|
|
471
|
+
requireString(project.statuses.inProgress, `${path}.statuses.inProgress`);
|
|
472
|
+
requireString(project.statuses.done, `${path}.statuses.done`);
|
|
473
|
+
project.statuses.terminal.forEach((status, terminalIndex) => {
|
|
474
|
+
requireString(status, `${path}.statuses.terminal[${terminalIndex}]`);
|
|
475
|
+
});
|
|
404
476
|
});
|
|
405
477
|
requireString(config.git.remote, "git.remote");
|
|
406
478
|
requireString(config.git.defaultBranch, "git.defaultBranch");
|
|
@@ -539,6 +611,42 @@ async function discoverUserConfig() {
|
|
|
539
611
|
// terminating statement; it doesn't track `fail()`'s `never` return.
|
|
540
612
|
throw new Error(`groundcrew config: no crew config found. Create crew.config.ts in your project root, or ${xdgConfigPath("groundcrew", "crew.config.ts")}, or set GROUNDCREW_CONFIG.`);
|
|
541
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Returns the resolved project the issue belongs to, or `undefined` when
|
|
616
|
+
* its slugId isn't in `linear.projects[]`. Callers in the dispatcher
|
|
617
|
+
* path expect a project to always exist (the board fetch only surfaces
|
|
618
|
+
* issues from configured projects); callers in the manual-ticket path
|
|
619
|
+
* (`setupWorkspace`, `ticketDoctor`) use this to detect off-config
|
|
620
|
+
* tickets and surface a clear error.
|
|
621
|
+
*/
|
|
622
|
+
export function findProjectBySlugId(config, slugId) {
|
|
623
|
+
return config.linear.projects.find((project) => project.slugId === slugId);
|
|
624
|
+
}
|
|
625
|
+
// Memoize per-config so blocker checks (called per blocker per tick) don't
|
|
626
|
+
// rebuild the same Set on every call. The resolved config is frozen and
|
|
627
|
+
// long-lived, so the WeakMap key is stable.
|
|
628
|
+
const terminalUnionCache = new WeakMap();
|
|
629
|
+
/**
|
|
630
|
+
* Union of every terminal status name configured across all watched
|
|
631
|
+
* projects. Used for blocker terminal checks when the blocker belongs
|
|
632
|
+
* to a project we don't watch — matches today's single-project "is the
|
|
633
|
+
* status terminal under any configured project?" behavior so off-config
|
|
634
|
+
* blockers don't regress.
|
|
635
|
+
*/
|
|
636
|
+
export function unionTerminalStatuses(config) {
|
|
637
|
+
const cached_ = terminalUnionCache.get(config);
|
|
638
|
+
if (cached_ !== undefined) {
|
|
639
|
+
return cached_;
|
|
640
|
+
}
|
|
641
|
+
const set = new Set();
|
|
642
|
+
for (const project of config.linear.projects) {
|
|
643
|
+
for (const status of project.statuses.terminal) {
|
|
644
|
+
set.add(status);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
terminalUnionCache.set(config, set);
|
|
648
|
+
return set;
|
|
649
|
+
}
|
|
542
650
|
let cached;
|
|
543
651
|
export async function loadConfig() {
|
|
544
652
|
if (cached) {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { LinearClient } from "@linear/sdk";
|
|
2
|
-
import type
|
|
2
|
+
import { type ResolvedConfig } from "./config.ts";
|
|
3
3
|
interface LinearIssueReference {
|
|
4
4
|
id: string;
|
|
5
5
|
uuid: string;
|
|
6
6
|
teamId: string;
|
|
7
|
+
/** SlugId of the issue's Linear project — selects which `inProgress` name to look up on the team's workflow. */
|
|
8
|
+
projectSlugId: string;
|
|
7
9
|
}
|
|
8
10
|
interface LinearIssueStatusUpdater {
|
|
9
11
|
markInProgress(issue: LinearIssueReference): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linearIssueStatus.d.ts","sourceRoot":"","sources":["../../src/lib/linearIssueStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"linearIssueStatus.d.ts","sourceRoot":"","sources":["../../src/lib/linearIssueStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAGvE,UAAU,oBAAoB;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gHAAgH;IAChH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,wBAAwB;IAChC,cAAc,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,2BAA2B,IAAI,IAAI,CAAC;CACrC;AAQD,wBAAgB,8BAA8B,CAAC,UAAU,EAAE;IACzD,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB,GAAG,wBAAwB,CA+D3B"}
|
|
Binary file
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "./config.ts";
|
|
2
|
+
export type RunLifecycleState = "running" | "interrupted" | "resumed" | "failed-to-launch";
|
|
3
|
+
export interface RunState {
|
|
4
|
+
ticket: string;
|
|
5
|
+
repository: string;
|
|
6
|
+
model: string;
|
|
7
|
+
worktreeDir: string;
|
|
8
|
+
branchName: string;
|
|
9
|
+
workspaceName: string;
|
|
10
|
+
state: RunLifecycleState;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
updatedAt: string;
|
|
13
|
+
resumeCount: number;
|
|
14
|
+
reason?: string;
|
|
15
|
+
detail?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface RunStateDraft {
|
|
18
|
+
ticket: string;
|
|
19
|
+
repository: string;
|
|
20
|
+
model: string;
|
|
21
|
+
worktreeDir: string;
|
|
22
|
+
branchName: string;
|
|
23
|
+
workspaceName: string;
|
|
24
|
+
state: RunLifecycleState;
|
|
25
|
+
reason?: string;
|
|
26
|
+
detail?: string;
|
|
27
|
+
resumeCount?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface RecordRunStateInput {
|
|
30
|
+
config: ResolvedConfig;
|
|
31
|
+
state: RunStateDraft;
|
|
32
|
+
}
|
|
33
|
+
export interface UpdateRunStateInput {
|
|
34
|
+
config: ResolvedConfig;
|
|
35
|
+
ticket: string;
|
|
36
|
+
patch: Partial<Omit<RunState, "createdAt" | "ticket">> & {
|
|
37
|
+
state: RunLifecycleState;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export declare function runStateDirectory(config: Pick<ResolvedConfig, "logging">): string;
|
|
41
|
+
export declare function runStatePath(config: Pick<ResolvedConfig, "logging">, ticket: string): string;
|
|
42
|
+
export declare function readRunState(config: ResolvedConfig, ticket: string): RunState | undefined;
|
|
43
|
+
export declare function recordRunState(input: RecordRunStateInput): RunState;
|
|
44
|
+
export declare function updateRunState(input: UpdateRunStateInput): RunState | undefined;
|
|
45
|
+
export declare function removeRunState(config: ResolvedConfig, ticket: string): void;
|
|
46
|
+
//# sourceMappingURL=runState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runState.d.ts","sourceRoot":"","sources":["../../src/lib/runState.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE3F,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC,CAAC,GAAG;QACvD,KAAK,EAAE,iBAAiB,CAAC;KAC1B,CAAC;CACH;AAaD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG,MAAM,CAEjF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5F;AA+ED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAYzF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CAmBnE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,GAAG,SAAS,CAc/E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE3E"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
const TICKET_RE = /^[a-z][\da-z]*-\d+$/;
|
|
4
|
+
const RUN_STATE_DIRECTORY_NAME = "runs";
|
|
5
|
+
function ticketKey(ticket) {
|
|
6
|
+
const normalized = ticket.toLowerCase();
|
|
7
|
+
if (!TICKET_RE.test(normalized)) {
|
|
8
|
+
throw new Error(`Invalid ticket "${ticket}": must be a plain ticket id`);
|
|
9
|
+
}
|
|
10
|
+
return normalized;
|
|
11
|
+
}
|
|
12
|
+
export function runStateDirectory(config) {
|
|
13
|
+
return resolve(dirname(config.logging.file), RUN_STATE_DIRECTORY_NAME);
|
|
14
|
+
}
|
|
15
|
+
export function runStatePath(config, ticket) {
|
|
16
|
+
return resolve(runStateDirectory(config), `${ticketKey(ticket)}.json`);
|
|
17
|
+
}
|
|
18
|
+
function nowIso() {
|
|
19
|
+
return new Date().toISOString();
|
|
20
|
+
}
|
|
21
|
+
function isPlainObject(value) {
|
|
22
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function stringField(value, key) {
|
|
25
|
+
const field = value[key];
|
|
26
|
+
return typeof field === "string" && field.length > 0 ? field : undefined;
|
|
27
|
+
}
|
|
28
|
+
function isRunLifecycleState(value) {
|
|
29
|
+
return (value === "running" ||
|
|
30
|
+
value === "interrupted" ||
|
|
31
|
+
value === "resumed" ||
|
|
32
|
+
value === "failed-to-launch");
|
|
33
|
+
}
|
|
34
|
+
function parseRunState(value) {
|
|
35
|
+
if (!isPlainObject(value)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const ticket = stringField(value, "ticket");
|
|
39
|
+
const repository = stringField(value, "repository");
|
|
40
|
+
const model = stringField(value, "model");
|
|
41
|
+
const worktreeDir = stringField(value, "worktreeDir");
|
|
42
|
+
const branchName = stringField(value, "branchName");
|
|
43
|
+
const workspaceName = stringField(value, "workspaceName");
|
|
44
|
+
const { state, resumeCount } = value;
|
|
45
|
+
const createdAt = stringField(value, "createdAt");
|
|
46
|
+
const updatedAt = stringField(value, "updatedAt");
|
|
47
|
+
const reason = stringField(value, "reason");
|
|
48
|
+
const detail = stringField(value, "detail");
|
|
49
|
+
if (ticket === undefined ||
|
|
50
|
+
repository === undefined ||
|
|
51
|
+
model === undefined ||
|
|
52
|
+
worktreeDir === undefined ||
|
|
53
|
+
branchName === undefined ||
|
|
54
|
+
workspaceName === undefined ||
|
|
55
|
+
!isRunLifecycleState(state) ||
|
|
56
|
+
createdAt === undefined ||
|
|
57
|
+
updatedAt === undefined ||
|
|
58
|
+
typeof resumeCount !== "number" ||
|
|
59
|
+
!Number.isInteger(resumeCount) ||
|
|
60
|
+
resumeCount < 0) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
ticket,
|
|
65
|
+
repository,
|
|
66
|
+
model,
|
|
67
|
+
worktreeDir,
|
|
68
|
+
branchName,
|
|
69
|
+
workspaceName,
|
|
70
|
+
state,
|
|
71
|
+
createdAt,
|
|
72
|
+
updatedAt,
|
|
73
|
+
resumeCount,
|
|
74
|
+
...(reason === undefined ? {} : { reason }),
|
|
75
|
+
...(detail === undefined ? {} : { detail }),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function writeState(config, state) {
|
|
79
|
+
const path = runStatePath(config, state.ticket);
|
|
80
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
81
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
82
|
+
writeFileSync(tmpPath, `${JSON.stringify(state, undefined, 2)}\n`, { mode: 0o600 });
|
|
83
|
+
renameSync(tmpPath, path);
|
|
84
|
+
}
|
|
85
|
+
export function readRunState(config, ticket) {
|
|
86
|
+
let raw;
|
|
87
|
+
try {
|
|
88
|
+
raw = readFileSync(runStatePath(config, ticket), "utf8");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
return parseRunState(JSON.parse(raw));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function recordRunState(input) {
|
|
101
|
+
const existing = readRunState(input.config, input.state.ticket);
|
|
102
|
+
const timestamp = nowIso();
|
|
103
|
+
const state = {
|
|
104
|
+
ticket: ticketKey(input.state.ticket),
|
|
105
|
+
repository: input.state.repository,
|
|
106
|
+
model: input.state.model,
|
|
107
|
+
worktreeDir: input.state.worktreeDir,
|
|
108
|
+
branchName: input.state.branchName,
|
|
109
|
+
workspaceName: input.state.workspaceName,
|
|
110
|
+
state: input.state.state,
|
|
111
|
+
createdAt: existing?.createdAt ?? timestamp,
|
|
112
|
+
updatedAt: timestamp,
|
|
113
|
+
resumeCount: input.state.resumeCount ?? existing?.resumeCount ?? 0,
|
|
114
|
+
...(input.state.reason === undefined ? {} : { reason: input.state.reason }),
|
|
115
|
+
...(input.state.detail === undefined ? {} : { detail: input.state.detail }),
|
|
116
|
+
};
|
|
117
|
+
writeState(input.config, state);
|
|
118
|
+
return state;
|
|
119
|
+
}
|
|
120
|
+
export function updateRunState(input) {
|
|
121
|
+
const existing = readRunState(input.config, input.ticket);
|
|
122
|
+
if (existing === undefined) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
const state = {
|
|
126
|
+
...existing,
|
|
127
|
+
...input.patch,
|
|
128
|
+
ticket: existing.ticket,
|
|
129
|
+
createdAt: existing.createdAt,
|
|
130
|
+
updatedAt: nowIso(),
|
|
131
|
+
};
|
|
132
|
+
writeState(input.config, state);
|
|
133
|
+
return state;
|
|
134
|
+
}
|
|
135
|
+
export function removeRunState(config, ticket) {
|
|
136
|
+
rmSync(runStatePath(config, ticket), { force: true });
|
|
137
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runStateCleanup.d.ts","sourceRoot":"","sources":["../../src/lib/runStateCleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,SAAS,aAAa,EAAE,GAChC,IAAI,CAQN"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { removeRunState } from "./runState.js";
|
|
2
|
+
import { errorMessage, log } from "./util.js";
|
|
3
|
+
export function recordCleanedUpRuns(config, entries) {
|
|
4
|
+
for (const entry of entries) {
|
|
5
|
+
try {
|
|
6
|
+
removeRunState(config, entry.ticket);
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
log(`Run state cleanup failed for ${entry.ticket}: ${errorMessage(error)}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ResolvedConfig } from "./config.ts";
|
|
2
|
+
export interface StagedPrompt {
|
|
3
|
+
directory: string;
|
|
4
|
+
file: string;
|
|
5
|
+
}
|
|
6
|
+
interface PromptTemplateVariables {
|
|
7
|
+
ticket: string;
|
|
8
|
+
worktree: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function stagePromptText(input: {
|
|
13
|
+
prefix: string;
|
|
14
|
+
ticket: string;
|
|
15
|
+
text: string;
|
|
16
|
+
}): StagedPrompt;
|
|
17
|
+
export declare function stagePromptFromTemplate(input: {
|
|
18
|
+
config: ResolvedConfig;
|
|
19
|
+
prefix: string;
|
|
20
|
+
ticket: string;
|
|
21
|
+
variables: PromptTemplateVariables;
|
|
22
|
+
}): StagedPrompt;
|
|
23
|
+
/**
|
|
24
|
+
* Stage a `KEY='value'` env file for any populated build-time secret so
|
|
25
|
+
* the launch command can source it. Returns `undefined` when groundcrew
|
|
26
|
+
* has nothing to forward, leaving the launch command unchanged.
|
|
27
|
+
*/
|
|
28
|
+
export declare function stageBuildSecrets(promptDir: string): string | undefined;
|
|
29
|
+
export declare function stageWorkspaceLaunchCommand(promptDir: string, command: string): string;
|
|
30
|
+
export declare function removeStagedPrompt(directory: string): void;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=stagedLaunch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stagedLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/stagedLaunch.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAItE,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,uBAAuB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,YAAY,CAKf;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,uBAAuB,CAAC;CACpC,GAAG,YAAY,CAMf;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAevE;AAQD,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtF;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE1D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { BUILD_SECRET_NAMES } from "./config.js";
|
|
5
|
+
import { shellSingleQuote } from "./launchCommand.js";
|
|
6
|
+
import { readEnvironmentVariable } from "./util.js";
|
|
7
|
+
function renderPromptTemplate(template, variables) {
|
|
8
|
+
return template
|
|
9
|
+
.replaceAll("{{ticket}}", variables.ticket)
|
|
10
|
+
.replaceAll("{{worktree}}", variables.worktree)
|
|
11
|
+
.replaceAll("{{title}}", variables.title)
|
|
12
|
+
.replaceAll("{{description}}", variables.description);
|
|
13
|
+
}
|
|
14
|
+
export function stagePromptText(input) {
|
|
15
|
+
const promptDir = mkdtempSync(join(tmpdir(), `${input.prefix}-${input.ticket}-`));
|
|
16
|
+
const promptFile = join(promptDir, "prompt.txt");
|
|
17
|
+
writeFileSync(promptFile, input.text);
|
|
18
|
+
return { directory: promptDir, file: promptFile };
|
|
19
|
+
}
|
|
20
|
+
export function stagePromptFromTemplate(input) {
|
|
21
|
+
return stagePromptText({
|
|
22
|
+
prefix: input.prefix,
|
|
23
|
+
ticket: input.ticket,
|
|
24
|
+
text: renderPromptTemplate(input.config.prompts.initial, input.variables),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Stage a `KEY='value'` env file for any populated build-time secret so
|
|
29
|
+
* the launch command can source it. Returns `undefined` when groundcrew
|
|
30
|
+
* has nothing to forward, leaving the launch command unchanged.
|
|
31
|
+
*/
|
|
32
|
+
export function stageBuildSecrets(promptDir) {
|
|
33
|
+
const lines = [];
|
|
34
|
+
for (const name of BUILD_SECRET_NAMES) {
|
|
35
|
+
const value = readEnvironmentVariable(name);
|
|
36
|
+
if (value === undefined || value.length === 0) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
lines.push(`${name}=${shellSingleQuote(value)}`);
|
|
40
|
+
}
|
|
41
|
+
if (lines.length === 0) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const secretsFile = join(promptDir, "secrets.env");
|
|
45
|
+
writeFileSync(secretsFile, `${lines.join("\n")}\n`, { mode: 0o600 });
|
|
46
|
+
return secretsFile;
|
|
47
|
+
}
|
|
48
|
+
function stageLaunchScript(promptDir, command) {
|
|
49
|
+
const launcherFile = join(promptDir, "launch.sh");
|
|
50
|
+
writeFileSync(launcherFile, `#!/usr/bin/env bash\n${command}\n`, { mode: 0o700 });
|
|
51
|
+
return launcherFile;
|
|
52
|
+
}
|
|
53
|
+
export function stageWorkspaceLaunchCommand(promptDir, command) {
|
|
54
|
+
return `bash ${shellSingleQuote(stageLaunchScript(promptDir, command))}`;
|
|
55
|
+
}
|
|
56
|
+
export function removeStagedPrompt(directory) {
|
|
57
|
+
rmSync(directory, { recursive: true, force: true });
|
|
58
|
+
}
|
package/dist/lib/util.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { LinearClient } from "@linear/sdk";
|
|
|
2
2
|
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
3
3
|
export declare function writeOutput(message?: string): void;
|
|
4
4
|
export declare function writeError(message: string): void;
|
|
5
|
-
export declare function clearOutput(): void;
|
|
6
5
|
export declare function setLogFile(path: string | undefined): void;
|
|
7
6
|
export declare function log(message: string): void;
|
|
8
7
|
type LogEventFieldValue = boolean | number | string | readonly string[] | undefined;
|
package/dist/lib/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAOD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEzD;AAkBD,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKzC;AAED,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAUpF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAWxF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED,QAAA,MAAM,sBAAsB,YAAI,2BAA2B,EAAE,gBAAgB,CAAU,CAAC;AAExF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,IAAI,oBAAoB,GAAG,SAAS,CAQtE;AAED,wBAAgB,eAAe,IAAI,YAAY,CAQ9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,YAAY,GAAG,MAAM,YAAY,CAMhF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAcnD"}
|
package/dist/lib/util.js
CHANGED
|
@@ -31,10 +31,6 @@ export function writeError(message) {
|
|
|
31
31
|
// oxlint-disable-next-line no-console -- Centralized CLI stderr writer.
|
|
32
32
|
console.error(message);
|
|
33
33
|
}
|
|
34
|
-
export function clearOutput() {
|
|
35
|
-
// oxlint-disable-next-line no-console -- Centralized CLI screen clear.
|
|
36
|
-
console.clear();
|
|
37
|
-
}
|
|
38
34
|
// Module-scoped sink for tee-ing log()/logEvent() to disk. Unset by default
|
|
39
35
|
// so tests don't write to the host filesystem; the CLI arms it after
|
|
40
36
|
// loadConfig() resolves `logging.file`.
|
package/dist/lib/workspaces.d.ts
CHANGED
|
@@ -41,6 +41,22 @@ export type WorkspaceProbe = {
|
|
|
41
41
|
kind: "unavailable";
|
|
42
42
|
error?: unknown;
|
|
43
43
|
};
|
|
44
|
+
export type WorkspaceInterruptResult = {
|
|
45
|
+
kind: "interrupted";
|
|
46
|
+
} | {
|
|
47
|
+
kind: "missing";
|
|
48
|
+
} | {
|
|
49
|
+
kind: "unavailable";
|
|
50
|
+
error?: unknown;
|
|
51
|
+
};
|
|
52
|
+
export type WorkspaceCloseResult = {
|
|
53
|
+
kind: "closed";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "missing";
|
|
56
|
+
} | {
|
|
57
|
+
kind: "unavailable";
|
|
58
|
+
error?: unknown;
|
|
59
|
+
};
|
|
44
60
|
export interface WorkspaceResolution {
|
|
45
61
|
requested: WorkspaceKindSetting;
|
|
46
62
|
resolved: WorkspaceKind;
|
|
@@ -53,10 +69,12 @@ interface ResolveArguments {
|
|
|
53
69
|
}
|
|
54
70
|
export declare function resolveWorkspaceKind(arguments_: ResolveArguments): WorkspaceResolution;
|
|
55
71
|
declare function probeWorkspaces(config: ResolvedConfig, signal?: AbortSignal): Promise<WorkspaceProbe>;
|
|
72
|
+
declare function interruptWorkspace(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<WorkspaceInterruptResult>;
|
|
56
73
|
export declare const workspaces: {
|
|
57
74
|
open(config: ResolvedConfig, spec: OpenSpec, signal?: AbortSignal): Promise<void>;
|
|
58
75
|
probe: typeof probeWorkspaces;
|
|
59
|
-
close(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<
|
|
76
|
+
close(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<WorkspaceCloseResult>;
|
|
77
|
+
interrupt: typeof interruptWorkspace;
|
|
60
78
|
accessHint(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<WorkspaceAccessHint | undefined>;
|
|
61
79
|
};
|
|
62
80
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/lib/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAI1E,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/lib/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAI1E,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAwU7C,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,EAAE,aAAa,CAAC;IACxB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,GAAG,mBAAmB,CAUtF;AAgOD,iBAAe,eAAe,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,iBAAe,kBAAkB,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAenC;AAED,eAAO,MAAM,UAAU;IACf,IAAI,SAAS,cAAc,QAAQ,QAAQ,WAAW,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvF,KAAK;IACC,KAAK,SACD,cAAc,QAChB,MAAM,WACH,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAIhC,SAAS;IACH,UAAU,SACN,cAAc,QAChB,MAAM,WACH,WAAW,GACnB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;CAI5C,CAAC"}
|