procdeck 0.5.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +167 -48
- package/dist/public.d.ts +7 -0
- package/package.json +7 -7
- package/readme.md +153 -225
- package/schema.json +13 -0
- package/ui/dist/assets/index-BkSDVPyT.css +32 -0
- package/ui/dist/assets/{index-CTUfrVhX.js → index-Co81BEum.js} +8 -8
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-BwFe3unL.css +0 -32
package/dist/cli.js
CHANGED
|
@@ -45446,6 +45446,12 @@ var ProcSpecSchema = Schema_exports.Struct({
|
|
|
45446
45446
|
examples: ["http://localhost:${port}"]
|
|
45447
45447
|
})
|
|
45448
45448
|
),
|
|
45449
|
+
port: Schema_exports.optionalKey(
|
|
45450
|
+
Schema_exports.Number.annotate({
|
|
45451
|
+
description: "Pin this proc's assigned `${port}` to a fixed public number instead of a random free one \u2014 dependents outside the deck (dotenv files, mobile simulators, teammates' scripts) keep their hardcoded addresses, and an observed proc still binds a hidden internal port behind the HTTP observer. Requires `${port}` somewhere in the spec.",
|
|
45452
|
+
examples: [8787]
|
|
45453
|
+
}).pipe(Schema_exports.check(Schema_exports.isInt(), Schema_exports.isBetween({ minimum: 1, maximum: 65535 })))
|
|
45454
|
+
),
|
|
45449
45455
|
needs: Schema_exports.optionalKey(
|
|
45450
45456
|
Schema_exports.Array(Schema_exports.String).annotate({
|
|
45451
45457
|
description: "Ids of procs that must be ready before this one spawns. Declarative: readiness is auto-detected, no port numbers in the config.",
|
|
@@ -45501,7 +45507,11 @@ var resolveSpec = (spec, assigned, internal) => {
|
|
|
45501
45507
|
...spec,
|
|
45502
45508
|
...spec.shell === void 0 ? {} : { shell: bind6(spec.shell) },
|
|
45503
45509
|
...spec.cmd === void 0 ? {} : { cmd: spec.cmd.map(bind6) },
|
|
45504
|
-
...spec.env === void 0 ? {} : {
|
|
45510
|
+
...spec.env === void 0 ? {} : {
|
|
45511
|
+
env: Object.fromEntries(
|
|
45512
|
+
Object.entries(spec.env).map(([key, value3]) => [key, bind6(value3)])
|
|
45513
|
+
)
|
|
45514
|
+
},
|
|
45505
45515
|
...spec.url === void 0 ? {} : { url: publicOnly(spec.url) }
|
|
45506
45516
|
};
|
|
45507
45517
|
};
|
|
@@ -45549,6 +45559,25 @@ var validatePortTemplates = (procs) => {
|
|
|
45549
45559
|
}
|
|
45550
45560
|
return void 0;
|
|
45551
45561
|
};
|
|
45562
|
+
var validatePinnedPorts = (config) => {
|
|
45563
|
+
const uiPort = config.port ?? DEFAULT_UI_PORT;
|
|
45564
|
+
const holders = /* @__PURE__ */ new Map();
|
|
45565
|
+
for (const proc of config.procs) {
|
|
45566
|
+
if (proc.port === void 0) continue;
|
|
45567
|
+
if (!usesOwnPort(proc)) {
|
|
45568
|
+
return `proc "${proc.id}" pins port ${proc.port} but does not use \${port} \u2014 the pin is the public side of an assigned port`;
|
|
45569
|
+
}
|
|
45570
|
+
if (proc.port === uiPort) {
|
|
45571
|
+
return `proc "${proc.id}" pins port ${proc.port}, which is the deck UI's own port`;
|
|
45572
|
+
}
|
|
45573
|
+
const holder = holders.get(proc.port);
|
|
45574
|
+
if (holder !== void 0) {
|
|
45575
|
+
return `procs "${holder}" and "${proc.id}" pin the same port ${proc.port}`;
|
|
45576
|
+
}
|
|
45577
|
+
holders.set(proc.port, proc.id);
|
|
45578
|
+
}
|
|
45579
|
+
return void 0;
|
|
45580
|
+
};
|
|
45552
45581
|
var ProcdeckConfigSchema = Schema_exports.Struct({
|
|
45553
45582
|
// JSON configs point at the published schema for editor completion; the key
|
|
45554
45583
|
// is meaningless at runtime but must be accepted, because Struct is exact.
|
|
@@ -45582,7 +45611,7 @@ var ProcdeckConfigSchema = Schema_exports.Struct({
|
|
|
45582
45611
|
}).pipe(
|
|
45583
45612
|
Schema_exports.check(
|
|
45584
45613
|
Schema_exports.makeFilter(
|
|
45585
|
-
(config) => validateGraph(config.procs) ?? validatePortTemplates(config.procs)
|
|
45614
|
+
(config) => validateGraph(config.procs) ?? validatePortTemplates(config.procs) ?? validatePinnedPorts(config)
|
|
45586
45615
|
)
|
|
45587
45616
|
)
|
|
45588
45617
|
).annotate({
|
|
@@ -46120,7 +46149,7 @@ var selfLogs = (follow) => Effect_exports.gen(function* () {
|
|
|
46120
46149
|
}),
|
|
46121
46150
|
(onChange) => Effect_exports.sync(() => unwatchFile(file6, onChange))
|
|
46122
46151
|
);
|
|
46123
|
-
yield* Effect_exports.never;
|
|
46152
|
+
return yield* Effect_exports.never;
|
|
46124
46153
|
}).pipe(Effect_exports.scoped);
|
|
46125
46154
|
var logsQuery = (options) => Effect_exports.gen(function* () {
|
|
46126
46155
|
let sinceMs;
|
|
@@ -46299,7 +46328,9 @@ var errors2 = Command_exports.make(
|
|
|
46299
46328
|
const render = (group2) => {
|
|
46300
46329
|
const head = `[${group2.proc}] ${group2.count}\xD7 \xB7 last ${ago(group2.lastTs)}${group2.count > 1 ? ` \xB7 first ${ago(group2.firstTs)}` : ""}`;
|
|
46301
46330
|
const sample = group2.sample.slice(0, SAMPLE_SHOWN).map((line) => ` ${line}`);
|
|
46302
|
-
const more = group2.sample.length > SAMPLE_SHOWN ? [
|
|
46331
|
+
const more = group2.sample.length > SAMPLE_SHOWN ? [
|
|
46332
|
+
` \u2026 +${group2.sample.length - SAMPLE_SHOWN} more lines (\`procdeck logs ${group2.proc}\`)`
|
|
46333
|
+
] : [];
|
|
46303
46334
|
return [head, ...sample, ...more].join("\n");
|
|
46304
46335
|
};
|
|
46305
46336
|
yield* Console_exports.log(groups.map(render).join("\n\n"));
|
|
@@ -46317,9 +46348,10 @@ var agents = Command_exports.make(
|
|
|
46317
46348
|
const root = path11.dirname(configPath);
|
|
46318
46349
|
const actions = setupAgents(root);
|
|
46319
46350
|
yield* Console_exports.log(
|
|
46320
|
-
[
|
|
46321
|
-
"
|
|
46322
|
-
|
|
46351
|
+
[
|
|
46352
|
+
"procdeck: introducing the deck to coding agents",
|
|
46353
|
+
...actions.map((line) => ` ${line}`)
|
|
46354
|
+
].join("\n")
|
|
46323
46355
|
);
|
|
46324
46356
|
yield* Console_exports.log(
|
|
46325
46357
|
"tip: `claude mcp add procdeck -- procdeck mcp` adds the MCP tools in every procdeck project at once"
|
|
@@ -46334,9 +46366,7 @@ var mcp = Command_exports.make(
|
|
|
46334
46366
|
"mcp",
|
|
46335
46367
|
{
|
|
46336
46368
|
mutations: Flag_exports.boolean("mutations").pipe(
|
|
46337
|
-
Flag_exports.withDescription(
|
|
46338
|
-
"also expose restart_proc / stop_proc / start_proc (default: read-only)"
|
|
46339
|
-
)
|
|
46369
|
+
Flag_exports.withDescription("also expose restart_proc / stop_proc / start_proc (default: read-only)")
|
|
46340
46370
|
)
|
|
46341
46371
|
},
|
|
46342
46372
|
// The server owns stdout from here on: no Console.log in this branch,
|
|
@@ -46782,7 +46812,9 @@ var planInit = (root) => {
|
|
|
46782
46812
|
);
|
|
46783
46813
|
}
|
|
46784
46814
|
const globs = workspaceGlobs(root);
|
|
46785
|
-
const dirs = [
|
|
46815
|
+
const dirs = [
|
|
46816
|
+
...new Set(globs.filter((g) => !g.startsWith("!")).flatMap((g) => expandGlob(root, g)))
|
|
46817
|
+
];
|
|
46786
46818
|
const fromWorkspaces = [];
|
|
46787
46819
|
const workspaceNotes = [];
|
|
46788
46820
|
for (const dir2 of dirs) {
|
|
@@ -46812,7 +46844,9 @@ var planInit = (root) => {
|
|
|
46812
46844
|
}
|
|
46813
46845
|
return done4(
|
|
46814
46846
|
[{ id: "app", shell: "echo 'edit procdeck.config.json'; sleep 1000" }],
|
|
46815
|
-
[
|
|
46847
|
+
[
|
|
46848
|
+
"nothing recognisable here (package.json scripts, Procfile, Django/Go/Rust/Rails/compose) \u2014 wrote a template to edit"
|
|
46849
|
+
],
|
|
46816
46850
|
"template"
|
|
46817
46851
|
);
|
|
46818
46852
|
};
|
|
@@ -47311,12 +47345,18 @@ var make63 = Effect_exports.fn("procdeck.supervisor")(function* (loaded) {
|
|
|
47311
47345
|
const live = yield* PubSub_exports.sliding({ capacity: LIVE_BUFFER });
|
|
47312
47346
|
const wantsPort = loaded.config.procs.filter(usesOwnPort);
|
|
47313
47347
|
const observedSpecs = wantsPort.filter((spec) => spec.observe !== false);
|
|
47348
|
+
const unpinned = wantsPort.filter((spec) => spec.port === void 0);
|
|
47314
47349
|
const freePorts = yield* Effect_exports.promise(
|
|
47315
|
-
() => findFreePorts(
|
|
47350
|
+
() => findFreePorts(unpinned.length + observedSpecs.length)
|
|
47316
47351
|
);
|
|
47317
|
-
const assigned = new Map(
|
|
47352
|
+
const assigned = new Map([
|
|
47353
|
+
...wantsPort.flatMap(
|
|
47354
|
+
(spec) => spec.port === void 0 ? [] : [[spec.id, spec.port]]
|
|
47355
|
+
),
|
|
47356
|
+
...unpinned.map((spec, index2) => [spec.id, freePorts[index2]])
|
|
47357
|
+
]);
|
|
47318
47358
|
const internal = new Map(
|
|
47319
|
-
observedSpecs.map((spec, index2) => [spec.id, freePorts[
|
|
47359
|
+
observedSpecs.map((spec, index2) => [spec.id, freePorts[unpinned.length + index2]])
|
|
47320
47360
|
);
|
|
47321
47361
|
const runtimes = /* @__PURE__ */ new Map();
|
|
47322
47362
|
for (const spec of loaded.config.procs) {
|
|
@@ -47325,9 +47365,13 @@ var make63 = Effect_exports.fn("procdeck.supervisor")(function* (loaded) {
|
|
|
47325
47365
|
const resolved2 = resolveSpec(spec, assigned, internal);
|
|
47326
47366
|
runtimes.set(spec.id, {
|
|
47327
47367
|
spec,
|
|
47328
|
-
resolved: assignedPort === void 0 ? resolved2 :
|
|
47368
|
+
resolved: assignedPort === void 0 ? resolved2 : (
|
|
47369
|
+
// The PaaS contract: the port to bind also arrives as `PORT`.
|
|
47370
|
+
{ ...resolved2, env: { PORT: String(internalPort ?? assignedPort), ...resolved2.env } }
|
|
47371
|
+
),
|
|
47329
47372
|
assignedPort,
|
|
47330
47373
|
internalPort,
|
|
47374
|
+
observerClose: void 0,
|
|
47331
47375
|
status: { id: spec.id, state: "stopped" },
|
|
47332
47376
|
proc: void 0,
|
|
47333
47377
|
ready: Latch_exports.makeUnsafe(false),
|
|
@@ -47344,19 +47388,14 @@ var make63 = Effect_exports.fn("procdeck.supervisor")(function* (loaded) {
|
|
|
47344
47388
|
rows: DEFAULT_ROWS
|
|
47345
47389
|
});
|
|
47346
47390
|
}
|
|
47347
|
-
|
|
47348
|
-
|
|
47349
|
-
|
|
47350
|
-
|
|
47351
|
-
() =>
|
|
47352
|
-
|
|
47353
|
-
|
|
47354
|
-
|
|
47355
|
-
})
|
|
47356
|
-
),
|
|
47357
|
-
(close4) => Effect_exports.promise(close4)
|
|
47358
|
-
);
|
|
47359
|
-
}
|
|
47391
|
+
yield* Effect_exports.acquireRelease(
|
|
47392
|
+
Effect_exports.void,
|
|
47393
|
+
() => Effect_exports.promise(
|
|
47394
|
+
() => Promise.all(
|
|
47395
|
+
[...runtimes.values()].map((runtime) => runtime.observerClose?.() ?? Promise.resolve())
|
|
47396
|
+
).then(() => void 0)
|
|
47397
|
+
)
|
|
47398
|
+
);
|
|
47360
47399
|
const emit = (event) => {
|
|
47361
47400
|
PubSub_exports.publishUnsafe(live, event);
|
|
47362
47401
|
};
|
|
@@ -47385,9 +47424,7 @@ var make63 = Effect_exports.fn("procdeck.supervisor")(function* (loaded) {
|
|
|
47385
47424
|
return Stream_exports.concat(
|
|
47386
47425
|
Stream_exports.fromIterable(backlog),
|
|
47387
47426
|
Stream_exports.fromSubscription(subscription).pipe(
|
|
47388
|
-
Stream_exports.filter(
|
|
47389
|
-
(event) => event.type !== "log" || event.seq >= (seen.get(event.id) ?? 0)
|
|
47390
|
-
)
|
|
47427
|
+
Stream_exports.filter((event) => event.type !== "log" || event.seq >= (seen.get(event.id) ?? 0))
|
|
47391
47428
|
)
|
|
47392
47429
|
);
|
|
47393
47430
|
})
|
|
@@ -47421,6 +47458,28 @@ var make63 = Effect_exports.fn("procdeck.supervisor")(function* (loaded) {
|
|
|
47421
47458
|
}
|
|
47422
47459
|
}
|
|
47423
47460
|
};
|
|
47461
|
+
const bindObserver = (runtime) => startObserver({
|
|
47462
|
+
publicPort: runtime.assignedPort,
|
|
47463
|
+
internalPort: runtime.internalPort,
|
|
47464
|
+
record: (capture) => runtime.http.record(capture)
|
|
47465
|
+
}).then(
|
|
47466
|
+
(close4) => {
|
|
47467
|
+
runtime.observerClose = close4;
|
|
47468
|
+
return true;
|
|
47469
|
+
},
|
|
47470
|
+
() => false
|
|
47471
|
+
);
|
|
47472
|
+
const parkOnTakenPort = (runtime) => {
|
|
47473
|
+
const hint = `free :${runtime.assignedPort} (is "${runtime.spec.id}" already running outside procdeck?), then Start`;
|
|
47474
|
+
say(
|
|
47475
|
+
runtime,
|
|
47476
|
+
`\r
|
|
47477
|
+
procdeck: pinned port :${runtime.assignedPort} is taken \u2014 cannot observe "${runtime.spec.id}"\r
|
|
47478
|
+
procdeck: hint: ${hint}\r
|
|
47479
|
+
`
|
|
47480
|
+
);
|
|
47481
|
+
setStatus2(runtime, { id: runtime.spec.id, state: "blocked", hint });
|
|
47482
|
+
};
|
|
47424
47483
|
const startSync = (runtime) => {
|
|
47425
47484
|
if (runtime.proc !== void 0) return;
|
|
47426
47485
|
setStatus2(runtime, { id: runtime.spec.id, state: "starting" });
|
|
@@ -47496,6 +47555,13 @@ ${lines2.join("\r\n")}\r
|
|
|
47496
47555
|
const start = Effect_exports.fn("procdeck.start")(function* (id2) {
|
|
47497
47556
|
const runtime = require_(id2);
|
|
47498
47557
|
if (runtime.proc !== void 0 || runtime.waiter !== void 0) return;
|
|
47558
|
+
if (runtime.internalPort !== void 0 && runtime.observerClose === void 0) {
|
|
47559
|
+
const bound = yield* Effect_exports.promise(() => bindObserver(runtime));
|
|
47560
|
+
if (!bound) {
|
|
47561
|
+
parkOnTakenPort(runtime);
|
|
47562
|
+
return;
|
|
47563
|
+
}
|
|
47564
|
+
}
|
|
47499
47565
|
const needs = runtime.spec.needs ?? [];
|
|
47500
47566
|
if (needs.length === 0 && runtime.spec.preflight === void 0) {
|
|
47501
47567
|
return yield* Effect_exports.sync(() => startSync(runtime));
|
|
@@ -47571,7 +47637,9 @@ ${lines2.join("\r\n")}\r
|
|
|
47571
47637
|
list: () => [...runtimes.values()].map((runtime) => ({
|
|
47572
47638
|
id: runtime.spec.id,
|
|
47573
47639
|
command: describeCommand(runtime.resolved),
|
|
47574
|
-
|
|
47640
|
+
// A pinned proc has a stable public address even with no `url` in
|
|
47641
|
+
// the config — derive the link instead of making the user repeat it.
|
|
47642
|
+
...runtime.resolved.url !== void 0 ? { url: runtime.resolved.url } : runtime.spec.port === void 0 ? {} : { url: `http://localhost:${runtime.spec.port}` },
|
|
47575
47643
|
...runtime.assignedPort === void 0 ? {} : { assignedPort: runtime.assignedPort },
|
|
47576
47644
|
proxyUrl: `http://${runtime.spec.id}.localhost:${uiPort}`,
|
|
47577
47645
|
status: runtime.status
|
|
@@ -47626,7 +47694,14 @@ ${lines2.join("\r\n")}\r
|
|
|
47626
47694
|
discard: true
|
|
47627
47695
|
});
|
|
47628
47696
|
for (const runtime of runtimes.values()) {
|
|
47629
|
-
if (runtime.
|
|
47697
|
+
if (runtime.internalPort === void 0) continue;
|
|
47698
|
+
const bound = yield* Effect_exports.promise(() => bindObserver(runtime));
|
|
47699
|
+
if (!bound) parkOnTakenPort(runtime);
|
|
47700
|
+
}
|
|
47701
|
+
for (const runtime of runtimes.values()) {
|
|
47702
|
+
if (runtime.spec.autostart !== false && runtime.status.state !== "blocked") {
|
|
47703
|
+
yield* start(runtime.spec.id);
|
|
47704
|
+
}
|
|
47630
47705
|
}
|
|
47631
47706
|
return { supervisor, stopAll };
|
|
47632
47707
|
});
|
|
@@ -47840,6 +47915,7 @@ var routes = (supervisor, deck, hooks) => HttpRouter_exports.addAll([
|
|
|
47840
47915
|
root: instance.root,
|
|
47841
47916
|
port: instance.port,
|
|
47842
47917
|
startedAt: instance.startedAt,
|
|
47918
|
+
version: instance.version,
|
|
47843
47919
|
self: instance.root === deck.root
|
|
47844
47920
|
}))
|
|
47845
47921
|
)
|
|
@@ -48078,7 +48154,7 @@ var runServer = (loaded, configPath) => Effect_exports.gen(function* () {
|
|
|
48078
48154
|
yield* Effect_exports.log(
|
|
48079
48155
|
`procdeck ${VERSION} "${loaded.name}" listening on http://localhost:${port}${host === DEFAULT_UI_HOST ? "" : ` (bound to ${host})`} (pid ${process.pid}, ${mode})`
|
|
48080
48156
|
);
|
|
48081
|
-
yield* Effect_exports.never;
|
|
48157
|
+
return yield* Effect_exports.never;
|
|
48082
48158
|
}).pipe(Effect_exports.scoped);
|
|
48083
48159
|
var fgFlag = Flag_exports.boolean("fg").pipe(
|
|
48084
48160
|
Flag_exports.withDescription("run in the foreground \u2014 Ctrl-C stops the deck")
|
|
@@ -48100,6 +48176,7 @@ var upHandler = ({
|
|
|
48100
48176
|
yield* Console_exports.log(
|
|
48101
48177
|
`procdeck: "${running.name}" is already up \u2192 ${deckUrl(running)} (pid ${running.pid})`
|
|
48102
48178
|
);
|
|
48179
|
+
yield* versionNotice(running);
|
|
48103
48180
|
if (open5) openBrowser(deckUrl(running));
|
|
48104
48181
|
return;
|
|
48105
48182
|
}
|
|
@@ -48122,6 +48199,9 @@ var upHandler = ({
|
|
|
48122
48199
|
yield* Console_exports.log(" procdeck down \xB7 status \xB7 logs -f \xB7 open");
|
|
48123
48200
|
if (open5) openBrowser(deckUrl(instance));
|
|
48124
48201
|
});
|
|
48202
|
+
var versionNotice = (instance) => instance.version === VERSION ? Effect_exports.void : Console_exports.log(
|
|
48203
|
+
`procdeck: deck runs v${instance.version}, CLI is v${VERSION} \u2014 \`procdeck restart\` picks up the new version`
|
|
48204
|
+
);
|
|
48125
48205
|
var downHandler = ({ config }) => Effect_exports.gen(function* () {
|
|
48126
48206
|
const root = yield* resolveRoot(config);
|
|
48127
48207
|
const instance = findInstance(root);
|
|
@@ -48133,7 +48213,11 @@ var downHandler = ({ config }) => Effect_exports.gen(function* () {
|
|
|
48133
48213
|
const outcome = yield* Effect_exports.promise(() => shutdown3(instance));
|
|
48134
48214
|
yield* Console_exports.log(outcome === "stopped" ? "down" : "did not stop in time, killed");
|
|
48135
48215
|
});
|
|
48136
|
-
var up = Command_exports.make(
|
|
48216
|
+
var up = Command_exports.make(
|
|
48217
|
+
"up",
|
|
48218
|
+
{ config: configArgument, fg: fgFlag, open: openFlag },
|
|
48219
|
+
upHandler
|
|
48220
|
+
).pipe(
|
|
48137
48221
|
Command_exports.withDescription(
|
|
48138
48222
|
"Start the deck and open its UI. Detaches by default: the deck keeps running after the terminal closes (`procdeck down` stops it). Idempotent \u2014 an already-running deck is just opened."
|
|
48139
48223
|
)
|
|
@@ -48141,6 +48225,34 @@ var up = Command_exports.make("up", { config: configArgument, fg: fgFlag, open:
|
|
|
48141
48225
|
var down = Command_exports.make("down", { config: configArgument }, downHandler).pipe(
|
|
48142
48226
|
Command_exports.withDescription("Stop the deck: every process tree is terminated, then procdeck exits.")
|
|
48143
48227
|
);
|
|
48228
|
+
var restartAllHandler = Effect_exports.gen(function* () {
|
|
48229
|
+
const instances = listInstances();
|
|
48230
|
+
if (instances.length === 0) return yield* Console_exports.log("procdeck: no decks are up");
|
|
48231
|
+
let failures = 0;
|
|
48232
|
+
for (const instance of instances) {
|
|
48233
|
+
if (instance.mode === "foreground") {
|
|
48234
|
+
yield* Console_exports.log(
|
|
48235
|
+
`procdeck: skipping "${instance.name}" (${pretty2(instance.root)}) \u2014 foreground deck, restart it in its own terminal`
|
|
48236
|
+
);
|
|
48237
|
+
continue;
|
|
48238
|
+
}
|
|
48239
|
+
process.stdout.write(`procdeck: restarting "${instance.name}" (${pretty2(instance.root)})\u2026 `);
|
|
48240
|
+
yield* Effect_exports.promise(() => shutdown3(instance));
|
|
48241
|
+
const outcome = yield* Effect_exports.promise(async () => {
|
|
48242
|
+
try {
|
|
48243
|
+
const fresh2 = await detach(process.argv[1], instance.config, instance.root);
|
|
48244
|
+
return `up \u2192 ${deckUrl(fresh2)} (v${fresh2.version})`;
|
|
48245
|
+
} catch (cause) {
|
|
48246
|
+
failures += 1;
|
|
48247
|
+
return `failed: ${cause.message}`;
|
|
48248
|
+
}
|
|
48249
|
+
});
|
|
48250
|
+
yield* Console_exports.log(outcome);
|
|
48251
|
+
}
|
|
48252
|
+
if (failures > 0) {
|
|
48253
|
+
return yield* fail11(`${failures} deck${failures === 1 ? "" : "s"} did not come back \u2014 see above`);
|
|
48254
|
+
}
|
|
48255
|
+
});
|
|
48144
48256
|
var restart = Command_exports.make(
|
|
48145
48257
|
"restart",
|
|
48146
48258
|
{
|
|
@@ -48148,10 +48260,13 @@ var restart = Command_exports.make(
|
|
|
48148
48260
|
Argument_exports.optional,
|
|
48149
48261
|
Argument_exports.withDescription("proc id \u2014 restart just this process (default: the whole deck)")
|
|
48150
48262
|
),
|
|
48263
|
+
all: Flag_exports.boolean("all").pipe(
|
|
48264
|
+
Flag_exports.withDescription("restart every deck on this machine (e.g. after updating procdeck)")
|
|
48265
|
+
),
|
|
48151
48266
|
fg: fgFlag,
|
|
48152
48267
|
open: openFlag
|
|
48153
48268
|
},
|
|
48154
|
-
({ fg, open: open5, proc }) => Option_exports.isSome(proc) ? Effect_exports.gen(function* () {
|
|
48269
|
+
({ all: all4, fg, open: open5, proc }) => all4 ? Option_exports.isSome(proc) ? fail11(`--all restarts every deck \u2014 drop "${proc.value}"`) : restartAllHandler : Option_exports.isSome(proc) ? Effect_exports.gen(function* () {
|
|
48155
48270
|
const instance = yield* requireInstance(Option_exports.none());
|
|
48156
48271
|
const procs = yield* callApi(() => apiGet(instance, "/procs"));
|
|
48157
48272
|
if (!procs.some((info2) => info2.id === proc.value)) {
|
|
@@ -48170,7 +48285,7 @@ var restart = Command_exports.make(
|
|
|
48170
48285
|
)
|
|
48171
48286
|
).pipe(
|
|
48172
48287
|
Command_exports.withDescription(
|
|
48173
|
-
"Restart one proc (`restart api`),
|
|
48288
|
+
"Restart one proc (`restart api`), the whole deck (`restart`), or every deck on this machine (`restart --all`) \u2014 the deck-wide forms after updating procdeck or editing the config."
|
|
48174
48289
|
)
|
|
48175
48290
|
);
|
|
48176
48291
|
var status = Command_exports.make(
|
|
@@ -48192,8 +48307,9 @@ var status = Command_exports.make(
|
|
|
48192
48307
|
}
|
|
48193
48308
|
const procs = yield* fetchProcs2(instance);
|
|
48194
48309
|
yield* Console_exports.log(
|
|
48195
|
-
`${instance.name} \xB7 ${deckUrl(instance)} \xB7 up ${describeUptime(instance.startedAt)} \xB7 pid ${instance.pid} \xB7 ${countRunning(procs)}${instance.mode === "foreground" ? " \xB7 foreground" : ""}`
|
|
48310
|
+
`${instance.name} \xB7 ${deckUrl(instance)} \xB7 up ${describeUptime(instance.startedAt)} \xB7 pid ${instance.pid} \xB7 ${countRunning(procs)} \xB7 v${instance.version}${instance.mode === "foreground" ? " \xB7 foreground" : ""}`
|
|
48196
48311
|
);
|
|
48312
|
+
yield* versionNotice(instance);
|
|
48197
48313
|
if (procs === void 0 || procs.length === 0) return;
|
|
48198
48314
|
yield* Console_exports.log(
|
|
48199
48315
|
table2(
|
|
@@ -48225,10 +48341,16 @@ var ls = Command_exports.make(
|
|
|
48225
48341
|
`:${instance.port}`,
|
|
48226
48342
|
`up ${describeUptime(instance.startedAt)}`,
|
|
48227
48343
|
countRunning(procs[i]),
|
|
48344
|
+
`v${instance.version}`,
|
|
48228
48345
|
pretty2(instance.root)
|
|
48229
48346
|
])
|
|
48230
48347
|
)
|
|
48231
48348
|
);
|
|
48349
|
+
if (instances.some((instance) => instance.version !== VERSION)) {
|
|
48350
|
+
yield* Console_exports.log(
|
|
48351
|
+
`procdeck: some decks run a different procdeck than the CLI (v${VERSION}) \u2014 \`procdeck restart --all\` brings them over`
|
|
48352
|
+
);
|
|
48353
|
+
}
|
|
48232
48354
|
})
|
|
48233
48355
|
).pipe(Command_exports.withDescription("Every running deck on this machine."));
|
|
48234
48356
|
var open4 = Command_exports.make(
|
|
@@ -48318,12 +48440,7 @@ var NodeLayer = layer2.pipe(
|
|
|
48318
48440
|
layer5,
|
|
48319
48441
|
layer6,
|
|
48320
48442
|
CliConfig_exports.layer({
|
|
48321
|
-
builtIns: [
|
|
48322
|
-
GlobalFlag_exports.Help,
|
|
48323
|
-
GlobalFlag_exports.Version,
|
|
48324
|
-
GlobalFlag_exports.Completions,
|
|
48325
|
-
GlobalFlag_exports.LogLevel
|
|
48326
|
-
]
|
|
48443
|
+
builtIns: [GlobalFlag_exports.Help, GlobalFlag_exports.Version, GlobalFlag_exports.Completions, GlobalFlag_exports.LogLevel]
|
|
48327
48444
|
})
|
|
48328
48445
|
)
|
|
48329
48446
|
)
|
|
@@ -48333,9 +48450,11 @@ procdeck.pipe(
|
|
|
48333
48450
|
Effect_exports.catchTag(
|
|
48334
48451
|
"CliFailure",
|
|
48335
48452
|
(failure) => Console_exports.error(`procdeck: ${failure.message}`).pipe(
|
|
48336
|
-
Effect_exports.andThen(
|
|
48337
|
-
|
|
48338
|
-
|
|
48453
|
+
Effect_exports.andThen(
|
|
48454
|
+
Effect_exports.sync(() => {
|
|
48455
|
+
process.exitCode = failure.code ?? 1;
|
|
48456
|
+
})
|
|
48457
|
+
)
|
|
48339
48458
|
)
|
|
48340
48459
|
),
|
|
48341
48460
|
Effect_exports.provide(NodeLayer),
|
package/dist/public.d.ts
CHANGED
|
@@ -42,6 +42,13 @@ export type ProcSpec = {
|
|
|
42
42
|
readonly autostart?: boolean;
|
|
43
43
|
/** URL this process serves, shown in the UI as a link. */
|
|
44
44
|
readonly url?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Pin this proc's assigned `${port}` to a fixed public number instead of a
|
|
47
|
+
* random free one — dependents outside the deck keep their hardcoded
|
|
48
|
+
* addresses, and an observed proc still binds a hidden internal port.
|
|
49
|
+
* Requires `${port}` somewhere in the spec.
|
|
50
|
+
*/
|
|
51
|
+
readonly port?: number;
|
|
45
52
|
/** Ids of procs that must be ready before this one spawns. */
|
|
46
53
|
readonly needs?: ReadonlyArray<string>;
|
|
47
54
|
/** What "ready" means for dependents. Defaults to "listening". */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "procdeck",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Interactive dev-process multiplexer with a web UI: terminal panes in a browser tab, declarative dependencies, assigned ports and a *.localhost reverse proxy.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -24,18 +24,15 @@
|
|
|
24
24
|
"url": "https://github.com/kondaurovDev/procdeck",
|
|
25
25
|
"directory": "packages/procdeck"
|
|
26
26
|
},
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"node": ">=22"
|
|
27
|
+
"bin": {
|
|
28
|
+
"procdeck": "./dist/cli.js"
|
|
30
29
|
},
|
|
31
30
|
"files": [
|
|
32
31
|
"dist",
|
|
33
32
|
"schema.json",
|
|
34
33
|
"ui/dist"
|
|
35
34
|
],
|
|
36
|
-
"
|
|
37
|
-
"procdeck": "./dist/cli.js"
|
|
38
|
-
},
|
|
35
|
+
"type": "module",
|
|
39
36
|
"exports": {
|
|
40
37
|
".": {
|
|
41
38
|
"types": "./dist/public.d.ts",
|
|
@@ -63,6 +60,9 @@
|
|
|
63
60
|
"vite": "^6.2.0",
|
|
64
61
|
"vitest": "^3.2.4"
|
|
65
62
|
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=22"
|
|
65
|
+
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"ui:dev": "vite ui",
|
|
68
68
|
"build": "pnpm run build:meta && pnpm run build:schema && pnpm run build:ui && pnpm run build:js && pnpm run build:types",
|