@paleo/workspace 0.15.1 → 0.16.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/README.md +1 -1
- package/dist/cli.js +1 -1
- package/dist/dev-server.d.ts +7 -0
- package/dist/dev-server.js +16 -1
- package/dist/helpers.d.ts +7 -2
- package/dist/helpers.js +15 -10
- package/dist/index.d.ts +1 -1
- package/dist/workspace.d.ts +26 -4
- package/dist/workspace.js +12 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The agent reads the skill, adapts the reference scripts to your stack, installs
|
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
29
|
npm run workspace -- setup feat/42 -c # new branch + worktree + isolated env
|
|
30
|
-
npm run dev # foreground: stream logs
|
|
30
|
+
npm run dev # foreground: stream logs, CTRL+C stops; attaches if already running
|
|
31
31
|
npm run dev -- up # start in the background (no-op if already running here)
|
|
32
32
|
npm run dev -- up --restart # stop the dev-server in this worktree if running, then start fresh
|
|
33
33
|
npm run dev -- up --evict # if devLimit is reached, evict the oldest dev-server and start
|
package/dist/cli.js
CHANGED
|
@@ -287,7 +287,7 @@ export function printDevHelp() {
|
|
|
287
287
|
"",
|
|
288
288
|
"Commands:",
|
|
289
289
|
" dev Start in the foreground, streaming logs from startup; CTRL+C stops it.",
|
|
290
|
-
" If one is already running here, attach to its logs instead",
|
|
290
|
+
" If one is already running here, attach to its logs instead.",
|
|
291
291
|
" dev up Start in the background and return once ready.",
|
|
292
292
|
" dev restart Stop this worktree's dev-server if running, then start in the background.",
|
|
293
293
|
" dev down [--all] Stop this worktree's dev-server, or every dev-server with --all.",
|
package/dist/dev-server.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StartupError } from "./errors.js";
|
|
1
2
|
import type { CallbackServer, ServerContext, ServerDescriptor, SpawnServer } from "./server-descriptor.js";
|
|
2
3
|
import { type ResolvedSlot, type SlotEntry } from "./slots.js";
|
|
3
4
|
export type { CallbackServer, ServerContext, ServerDescriptor, SpawnServer };
|
|
@@ -33,6 +34,12 @@ export interface DevServerSummaryContext {
|
|
|
33
34
|
}[];
|
|
34
35
|
}
|
|
35
36
|
export declare function runDevServer(config: DevServerConfig): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Wraps an error thrown by a callback server's `start()` into a {@link StartupError}, so a failing
|
|
39
|
+
* callback (e.g. `docker compose up -d`) gets the same clean rollback-and-report path as a spawn
|
|
40
|
+
* server instead of surfacing as a raw unhandled-rejection stack trace.
|
|
41
|
+
*/
|
|
42
|
+
export declare function toCallbackStartupError(name: string, err: unknown): StartupError;
|
|
36
43
|
/**
|
|
37
44
|
* Poll the foreground's own spawn PIDs; when none remain alive, the servers were stopped from
|
|
38
45
|
* outside this process (`dev down`, `down --all`, eviction, or a manual kill). Fires `onStopped`
|
package/dist/dev-server.js
CHANGED
|
@@ -207,6 +207,16 @@ async function spawnWithRollback(config, ctx, state, isAborted = () => false, on
|
|
|
207
207
|
}
|
|
208
208
|
return !isAborted();
|
|
209
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Wraps an error thrown by a callback server's `start()` into a {@link StartupError}, so a failing
|
|
212
|
+
* callback (e.g. `docker compose up -d`) gets the same clean rollback-and-report path as a spawn
|
|
213
|
+
* server instead of surfacing as a raw unhandled-rejection stack trace.
|
|
214
|
+
*/
|
|
215
|
+
export function toCallbackStartupError(name, err) {
|
|
216
|
+
if (err instanceof StartupError)
|
|
217
|
+
return err;
|
|
218
|
+
return new StartupError(name, err instanceof Error ? err.message : String(err));
|
|
219
|
+
}
|
|
210
220
|
async function spawnAndAwait(config, ctx, state, onSpawned) {
|
|
211
221
|
for (const server of config.servers) {
|
|
212
222
|
console.log(`Starting ${server.name} dev server...`);
|
|
@@ -214,7 +224,12 @@ async function spawnAndAwait(config, ctx, state, onSpawned) {
|
|
|
214
224
|
state.spawnPids[server.name] = spawnServer(server, config.runtimeDir, ctx.cwd);
|
|
215
225
|
}
|
|
216
226
|
else {
|
|
217
|
-
|
|
227
|
+
try {
|
|
228
|
+
await server.start(ctx);
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
throw toCallbackStartupError(server.name, err);
|
|
232
|
+
}
|
|
218
233
|
state.startedCallbacks.push(server);
|
|
219
234
|
}
|
|
220
235
|
}
|
package/dist/helpers.d.ts
CHANGED
|
@@ -14,10 +14,15 @@ export declare function readPortFromEnvFile(file: string, varName: string): numb
|
|
|
14
14
|
export declare function readPortFromJsonFile(file: string, jsonPath: string): number;
|
|
15
15
|
export interface CopyAndPatchCtx {
|
|
16
16
|
currentWorktree: string;
|
|
17
|
-
mainWorktree: string;
|
|
18
17
|
log: (msg: string) => void;
|
|
19
18
|
}
|
|
20
|
-
|
|
19
|
+
/** Resolved initial-content source for {@link copyAndPatchFile}. `path` is absolute. */
|
|
20
|
+
export type ResolvedFileSource = {
|
|
21
|
+
path: string;
|
|
22
|
+
} | {
|
|
23
|
+
content: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function copyAndPatchFile(ctx: CopyAndPatchCtx, relPath: string, source: ResolvedFileSource, patchFn: (content: string) => string, label: string, force: boolean, optional?: boolean): void;
|
|
21
26
|
/**
|
|
22
27
|
* Formats a millisecond duration as the two largest units among `d`/`h`/`m`/`s`.
|
|
23
28
|
* Drops the smaller unit when zero (`5d` instead of `5d 0h`). Sub-second values
|
package/dist/helpers.js
CHANGED
|
@@ -67,24 +67,29 @@ export function readPortFromJsonFile(file, jsonPath) {
|
|
|
67
67
|
}
|
|
68
68
|
return toPort(String(cur), file);
|
|
69
69
|
}
|
|
70
|
-
export function copyAndPatchFile(ctx, relPath, patchFn, label, force, optional = false) {
|
|
70
|
+
export function copyAndPatchFile(ctx, relPath, source, patchFn, label, force, optional = false) {
|
|
71
71
|
const targetPath = join(ctx.currentWorktree, relPath);
|
|
72
|
-
const sourcePath = join(ctx.mainWorktree, relPath);
|
|
73
72
|
const alreadyExists = existsSync(targetPath);
|
|
74
73
|
if (alreadyExists && !force) {
|
|
75
74
|
ctx.log(`Skipped ${label} (already exists; use --force to overwrite).`);
|
|
76
75
|
return;
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
let content;
|
|
78
|
+
if ("content" in source) {
|
|
79
|
+
content = source.content;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
if (!existsSync(source.path)) {
|
|
83
|
+
if (!optional) {
|
|
84
|
+
console.error(`Error: source ${source.path} not found. Bootstrap the main worktree first ` +
|
|
85
|
+
"(`workspace setup`), provide a `source`, or mark the entry as optional.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
ctx.log(`Warning: source ${source.path} not found, skipping (optional).`);
|
|
89
|
+
return;
|
|
83
90
|
}
|
|
84
|
-
|
|
85
|
-
return;
|
|
91
|
+
content = readFileSync(source.path, "utf-8");
|
|
86
92
|
}
|
|
87
|
-
const content = readFileSync(sourcePath, "utf-8");
|
|
88
93
|
const patched = patchFn(content);
|
|
89
94
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
90
95
|
writeFileSync(targetPath, patched);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { runWorkspace } from "./workspace.js";
|
|
2
2
|
export { defaultWorktreeDirName } from "./worktree.js";
|
|
3
3
|
export type { WorktreeDirNameFn } from "./worktree.js";
|
|
4
|
-
export type { WorkspaceConfig, SetupContext, SummaryContext, PatchContext, ConfigFileEntry, PurgeContext, } from "./workspace.js";
|
|
4
|
+
export type { WorkspaceConfig, SetupContext, SummaryContext, PatchContext, ConfigFileEntry, ConfigFileSource, ConfigFileSourceSpec, PurgeContext, } from "./workspace.js";
|
|
5
5
|
export { runDevServer } from "./dev-server.js";
|
|
6
6
|
export type { DevServerConfig, DevServerSummaryContext, ServerDescriptor, ServerContext, SpawnServer, CallbackServer, } from "./dev-server.js";
|
|
7
7
|
export type { ResolvedSlot } from "./slots.js";
|
package/dist/workspace.d.ts
CHANGED
|
@@ -121,16 +121,38 @@ export interface PurgeContext {
|
|
|
121
121
|
mainWorktree: string;
|
|
122
122
|
verbose: boolean;
|
|
123
123
|
}
|
|
124
|
-
/**
|
|
124
|
+
/** A `{ path }` (relative to the main worktree) or `{ content }` (verbatim) initial source. */
|
|
125
|
+
export type ConfigFileSourceSpec = {
|
|
126
|
+
path: string;
|
|
127
|
+
} | {
|
|
128
|
+
content: string;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Overrides where a {@link ConfigFileEntry}'s initial content comes from. Defaults to reading
|
|
132
|
+
* `entry.path` from the main worktree.
|
|
133
|
+
*
|
|
134
|
+
* - `{ path }` — read this path (relative to the main worktree) instead of `entry.path`,
|
|
135
|
+
* e.g. seed from a committed example: `{ path: "packages/api/.env.local.example" }`.
|
|
136
|
+
* - `{ content }` — use this string as the initial content verbatim.
|
|
137
|
+
* - a callback returning either of the above, resolved per worktree with the same
|
|
138
|
+
* {@link PatchContext} the `patch` callback receives.
|
|
139
|
+
*/
|
|
140
|
+
export type ConfigFileSource = ConfigFileSourceSpec | ((ctx: PatchContext) => ConfigFileSourceSpec);
|
|
141
|
+
/** One config file seeded from its source (the main worktree by default) and patched per slot. */
|
|
125
142
|
export interface ConfigFileEntry {
|
|
126
|
-
/** Path relative to the worktree root.
|
|
143
|
+
/** Path relative to the worktree root. Written to the current worktree. */
|
|
127
144
|
path: string;
|
|
145
|
+
/**
|
|
146
|
+
* Overrides the initial content's source. Defaults to reading `path` from the main worktree.
|
|
147
|
+
* Use this to seed from a committed example or supplied content instead. See {@link ConfigFileSource}.
|
|
148
|
+
*/
|
|
149
|
+
source?: ConfigFileSource;
|
|
128
150
|
/** Returns the patched content given the source content and the slot's ports. */
|
|
129
151
|
patch: (content: string, ctx: PatchContext) => string;
|
|
130
152
|
/**
|
|
131
|
-
* When `true`, a missing
|
|
153
|
+
* When `true`, a missing `{ path }` source logs a warning and skips the entry.
|
|
132
154
|
* Default: required (missing source aborts setup). Bootstrap the main worktree first via
|
|
133
|
-
* `workspace setup`, or seed sources in `preSetup`.
|
|
155
|
+
* `workspace setup`, or seed sources in `preSetup`. Ignored for `{ content }` sources.
|
|
134
156
|
*/
|
|
135
157
|
optional?: boolean;
|
|
136
158
|
}
|
package/dist/workspace.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
4
4
|
import { parseWorkspaceArgs, printWorkspaceHelp } from "./cli.js";
|
|
5
5
|
import { findOwnEntry, liveWorktrees, readDevServers, removeDevServerEntryByWorktree, } from "./dev-servers-registry.js";
|
|
6
6
|
import { ConfigError } from "./errors.js";
|
|
7
|
-
import { copyAndPatchFile, formatDuration, setupLogPath } from "./helpers.js";
|
|
7
|
+
import { copyAndPatchFile, formatDuration, setupLogPath, } from "./helpers.js";
|
|
8
8
|
import { isProcessAlive } from "./process-control.js";
|
|
9
9
|
import { defaultComputePorts, isValidPort, resolvePortScheme } from "./ports.js";
|
|
10
10
|
import { handleSetOwner, markSlotFailed, markSlotReady, readSlots, resolveAndRegisterSlot, resolveCurrentSlot, validateSlotAvailability, writeSlots, } from "./slots.js";
|
|
@@ -478,14 +478,23 @@ function linkSharedDirectories(ctx, dirs, log) {
|
|
|
478
478
|
}
|
|
479
479
|
function generateConfigFiles(ctx, entries, slot, ports, force, log) {
|
|
480
480
|
for (const entry of entries) {
|
|
481
|
-
|
|
481
|
+
const patchCtx = {
|
|
482
482
|
slot,
|
|
483
483
|
ports,
|
|
484
484
|
mainWorktree: ctx.mainWorktree,
|
|
485
485
|
currentWorktree: ctx.currentWorktree,
|
|
486
|
-
}
|
|
486
|
+
};
|
|
487
|
+
copyAndPatchFile({ currentWorktree: ctx.currentWorktree, log }, entry.path, resolveConfigSource(entry, patchCtx), (content) => entry.patch(content, patchCtx), entry.path, force, entry.optional ?? false);
|
|
487
488
|
}
|
|
488
489
|
}
|
|
490
|
+
function resolveConfigSource(entry, ctx) {
|
|
491
|
+
const spec = typeof entry.source === "function" ? entry.source(ctx) : entry.source;
|
|
492
|
+
if (spec === undefined)
|
|
493
|
+
return { path: join(ctx.mainWorktree, entry.path) };
|
|
494
|
+
if ("content" in spec)
|
|
495
|
+
return { content: spec.content };
|
|
496
|
+
return { path: join(ctx.mainWorktree, spec.path) };
|
|
497
|
+
}
|
|
489
498
|
function resolveRemoveTarget(command, ctx, registry) {
|
|
490
499
|
if (command.branch === undefined) {
|
|
491
500
|
if (ctx.isMainWorktree) {
|