portweave 0.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/LICENSE +21 -0
- package/README.md +535 -0
- package/dist/allocator/allocate.concurrent.d.ts +3 -0
- package/dist/allocator/allocate.concurrent.d.ts.map +1 -0
- package/dist/allocator/allocate.concurrent.js +10 -0
- package/dist/allocator/allocate.concurrent.js.map +1 -0
- package/dist/allocator/allocate.d.ts +20 -0
- package/dist/allocator/allocate.d.ts.map +1 -0
- package/dist/allocator/allocate.js +106 -0
- package/dist/allocator/allocate.js.map +1 -0
- package/dist/allocator/cross-project.d.ts +2 -0
- package/dist/allocator/cross-project.d.ts.map +1 -0
- package/dist/allocator/cross-project.js +9 -0
- package/dist/allocator/cross-project.js.map +1 -0
- package/dist/allocator/order.d.ts +2 -0
- package/dist/allocator/order.d.ts.map +1 -0
- package/dist/allocator/order.js +8 -0
- package/dist/allocator/order.js.map +1 -0
- package/dist/allocator/pool.d.ts +35 -0
- package/dist/allocator/pool.d.ts.map +1 -0
- package/dist/allocator/pool.js +74 -0
- package/dist/allocator/pool.js.map +1 -0
- package/dist/allocator/probe.d.ts +22 -0
- package/dist/allocator/probe.d.ts.map +1 -0
- package/dist/allocator/probe.js +40 -0
- package/dist/allocator/probe.js.map +1 -0
- package/dist/cli/banner.d.ts +25 -0
- package/dist/cli/banner.d.ts.map +1 -0
- package/dist/cli/banner.js +61 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/run.d.ts +15 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +156 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/show.d.ts +21 -0
- package/dist/cli/show.d.ts.map +1 -0
- package/dist/cli/show.js +141 -0
- package/dist/cli/show.js.map +1 -0
- package/dist/cli/spawn.d.ts +30 -0
- package/dist/cli/spawn.d.ts.map +1 -0
- package/dist/cli/spawn.js +85 -0
- package/dist/cli/spawn.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/anonymous.d.ts +5 -0
- package/dist/config/anonymous.d.ts.map +1 -0
- package/dist/config/anonymous.js +21 -0
- package/dist/config/anonymous.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +8 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +59 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +21 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +125 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/env/build.d.ts +4 -0
- package/dist/env/build.d.ts.map +1 -0
- package/dist/env/build.js +17 -0
- package/dist/env/build.js.map +1 -0
- package/dist/env/dotenv-merge.d.ts +5 -0
- package/dist/env/dotenv-merge.d.ts.map +1 -0
- package/dist/env/dotenv-merge.js +73 -0
- package/dist/env/dotenv-merge.js.map +1 -0
- package/dist/env/index.d.ts +4 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +4 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/resolve.d.ts +11 -0
- package/dist/env/resolve.d.ts.map +1 -0
- package/dist/env/resolve.js +29 -0
- package/dist/env/resolve.js.map +1 -0
- package/dist/env/templates.d.ts +2 -0
- package/dist/env/templates.d.ts.map +1 -0
- package/dist/env/templates.js +11 -0
- package/dist/env/templates.js.map +1 -0
- package/dist/env/writer.d.ts +6 -0
- package/dist/env/writer.d.ts.map +1 -0
- package/dist/env/writer.js +68 -0
- package/dist/env/writer.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/atomic-write.d.ts +3 -0
- package/dist/registry/atomic-write.d.ts.map +1 -0
- package/dist/registry/atomic-write.js +43 -0
- package/dist/registry/atomic-write.js.map +1 -0
- package/dist/registry/lock.d.ts +4 -0
- package/dist/registry/lock.d.ts.map +1 -0
- package/dist/registry/lock.js +86 -0
- package/dist/registry/lock.js.map +1 -0
- package/dist/registry/paths.d.ts +7 -0
- package/dist/registry/paths.d.ts.map +1 -0
- package/dist/registry/paths.js +13 -0
- package/dist/registry/paths.js.map +1 -0
- package/dist/registry/prune.d.ts +3 -0
- package/dist/registry/prune.d.ts.map +1 -0
- package/dist/registry/prune.js +24 -0
- package/dist/registry/prune.js.map +1 -0
- package/dist/registry/serialize.d.ts +6 -0
- package/dist/registry/serialize.d.ts.map +1 -0
- package/dist/registry/serialize.js +134 -0
- package/dist/registry/serialize.js.map +1 -0
- package/dist/registry/storage.concurrent.d.ts +3 -0
- package/dist/registry/storage.concurrent.d.ts.map +1 -0
- package/dist/registry/storage.concurrent.js +10 -0
- package/dist/registry/storage.concurrent.js.map +1 -0
- package/dist/registry/storage.d.ts +11 -0
- package/dist/registry/storage.d.ts.map +1 -0
- package/dist/registry/storage.js +101 -0
- package/dist/registry/storage.js.map +1 -0
- package/dist/registry/types.d.ts +14 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +2 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/result.d.ts +11 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +6 -0
- package/dist/result.js.map +1 -0
- package/dist/runtime/error-passthrough.d.ts +20 -0
- package/dist/runtime/error-passthrough.d.ts.map +1 -0
- package/dist/runtime/error-passthrough.js +20 -0
- package/dist/runtime/error-passthrough.js.map +1 -0
- package/dist/runtime/exports-smoke.d.ts +4 -0
- package/dist/runtime/exports-smoke.d.ts.map +1 -0
- package/dist/runtime/exports-smoke.js +36 -0
- package/dist/runtime/exports-smoke.js.map +1 -0
- package/dist/runtime/index.d.ts +15 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +99 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/upward-walk.d.ts +14 -0
- package/dist/runtime/upward-walk.d.ts.map +1 -0
- package/dist/runtime/upward-walk.js +39 -0
- package/dist/runtime/upward-walk.js.map +1 -0
- package/dist/worktree/git.d.ts +13 -0
- package/dist/worktree/git.d.ts.map +1 -0
- package/dist/worktree/git.js +66 -0
- package/dist/worktree/git.js.map +1 -0
- package/dist/worktree/key.d.ts +10 -0
- package/dist/worktree/key.d.ts.map +1 -0
- package/dist/worktree/key.js +38 -0
- package/dist/worktree/key.js.map +1 -0
- package/dist/worktree/namespace.d.ts +8 -0
- package/dist/worktree/namespace.d.ts.map +1 -0
- package/dist/worktree/namespace.js +58 -0
- package/dist/worktree/namespace.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { PortweaveError, PW_ERROR_CODES } from "../errors.js";
|
|
2
|
+
import { err, ok } from "../result.js";
|
|
3
|
+
import { withRegistry } from "../registry/storage.js";
|
|
4
|
+
import { findFreeBlock, resolvePoolRange } from "./pool.js";
|
|
5
|
+
import { probeBlock, probePort } from "./probe.js";
|
|
6
|
+
export const MAX_PROBE_RETRIES = 100;
|
|
7
|
+
/**
|
|
8
|
+
* Re-order services so that services sharing a `group` label are contiguous
|
|
9
|
+
* and groups appear in first-occurrence order. Ungrouped services keep their
|
|
10
|
+
* original sequential position relative to the groups.
|
|
11
|
+
*
|
|
12
|
+
* Pure — no I/O.
|
|
13
|
+
*/
|
|
14
|
+
export function orderServicesForAllocation(config) {
|
|
15
|
+
const groupFirstSeen = new Map();
|
|
16
|
+
for (const [i, s] of config.services.entries()) {
|
|
17
|
+
if (s.group !== undefined && !groupFirstSeen.has(s.group)) {
|
|
18
|
+
groupFirstSeen.set(s.group, i);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [...config.services].sort((a, b) => {
|
|
22
|
+
const ai = a.group !== undefined
|
|
23
|
+
? (groupFirstSeen.get(a.group) ?? config.services.indexOf(a))
|
|
24
|
+
: config.services.indexOf(a);
|
|
25
|
+
const bi = b.group !== undefined
|
|
26
|
+
? (groupFirstSeen.get(b.group) ?? config.services.indexOf(b))
|
|
27
|
+
: config.services.indexOf(b);
|
|
28
|
+
if (ai !== bi) {
|
|
29
|
+
return ai - bi;
|
|
30
|
+
}
|
|
31
|
+
return config.services.indexOf(a) - config.services.indexOf(b);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function keysEqual(a, b) {
|
|
35
|
+
return (a.worktreeRoot === b.worktreeRoot &&
|
|
36
|
+
a.namespace === b.namespace &&
|
|
37
|
+
a.gitCommonDir === b.gitCommonDir);
|
|
38
|
+
}
|
|
39
|
+
function buildPortsMap(orderedServices, candidate) {
|
|
40
|
+
const ports = {};
|
|
41
|
+
for (const [i, service] of orderedServices.entries()) {
|
|
42
|
+
ports[service.name] = candidate + i;
|
|
43
|
+
}
|
|
44
|
+
return ports;
|
|
45
|
+
}
|
|
46
|
+
async function tryReuseExisting(handle, key) {
|
|
47
|
+
const existing = handle.entries.find((e) => keysEqual(e.key, key));
|
|
48
|
+
if (existing === undefined) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
for (const port of Object.values(existing.ports)) {
|
|
52
|
+
const probeResult = await probePort(port);
|
|
53
|
+
if (probeResult === 'taken') {
|
|
54
|
+
handle.remove(key);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
handle.touch(key);
|
|
59
|
+
const touched = handle.entries.find((e) => keysEqual(e.key, key));
|
|
60
|
+
return { allocation: touched ?? existing, reused: true };
|
|
61
|
+
}
|
|
62
|
+
async function allocateFreshBlock(handle, key, orderedServices, range) {
|
|
63
|
+
const occupied = Array.from(new Set(handle.entries.flatMap((e) => Object.values(e.ports)))).sort((a, b) => a - b);
|
|
64
|
+
const externallyOccupied = [];
|
|
65
|
+
for (let retries = 0; retries < MAX_PROBE_RETRIES; retries++) {
|
|
66
|
+
const allOccupied = [...occupied, ...externallyOccupied].sort((a, b) => a - b);
|
|
67
|
+
const candidate = findFreeBlock(allOccupied, orderedServices.length, range);
|
|
68
|
+
if (candidate === null) {
|
|
69
|
+
return err(new PortweaveError(PW_ERROR_CODES.ALLOCATION_EXHAUSTED, 'Port pool exhausted: no contiguous block available in registry'));
|
|
70
|
+
}
|
|
71
|
+
const probeResult = await probeBlock(candidate, orderedServices.length);
|
|
72
|
+
if (probeResult.allFree) {
|
|
73
|
+
const ports = buildPortsMap(orderedServices, candidate);
|
|
74
|
+
const entry = {
|
|
75
|
+
key,
|
|
76
|
+
lastUsedAt: new Date().toISOString(),
|
|
77
|
+
namespace: key.namespace,
|
|
78
|
+
ports,
|
|
79
|
+
};
|
|
80
|
+
handle.upsert(entry);
|
|
81
|
+
return ok({ allocation: entry, reused: false });
|
|
82
|
+
}
|
|
83
|
+
// A port in the candidate block is externally taken — skip past it
|
|
84
|
+
externallyOccupied.push(probeResult.firstTakenPort);
|
|
85
|
+
}
|
|
86
|
+
return err(new PortweaveError(PW_ERROR_CODES.ALLOCATION_EXHAUSTED, 'Port pool exhausted: too many externally-bound ports prevented allocation'));
|
|
87
|
+
}
|
|
88
|
+
export async function allocate(key, config, env = process.env) {
|
|
89
|
+
const range = resolvePoolRange(env);
|
|
90
|
+
const orderedServices = orderServicesForAllocation(config);
|
|
91
|
+
// Wrap the inner Result in a container so withRegistry<T> doesn't
|
|
92
|
+
// double-wrap our error path. The outer Result carries lock/IO errors;
|
|
93
|
+
// the inner carries allocator-level errors.
|
|
94
|
+
const outer = await withRegistry(async (handle) => {
|
|
95
|
+
const reused = await tryReuseExisting(handle, key);
|
|
96
|
+
if (reused !== null) {
|
|
97
|
+
return ok(reused);
|
|
98
|
+
}
|
|
99
|
+
return allocateFreshBlock(handle, key, orderedServices, range);
|
|
100
|
+
}, env);
|
|
101
|
+
if (!outer.ok) {
|
|
102
|
+
return outer;
|
|
103
|
+
}
|
|
104
|
+
return outer.value;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=allocate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocate.js","sourceRoot":"","sources":["../../src/allocator/allocate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-project.d.ts","sourceRoot":"","sources":["../../src/allocator/cross-project.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
// Cross-project collision test surface — this module exists to satisfy
|
|
3
|
+
// structure:check which requires a source file at this location for
|
|
4
|
+
// __tests__/cross-project.test.ts. The test exercises the runtime
|
|
5
|
+
// allocate() function across multiple AllocationKeys sharing a single
|
|
6
|
+
// registry, verifying that distinct repos and worktrees receive
|
|
7
|
+
// non-overlapping port sets. No runtime exports are provided here;
|
|
8
|
+
// the test imports directly from allocate.ts and registry/types.ts.
|
|
9
|
+
//# sourceMappingURL=cross-project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-project.js","sourceRoot":"","sources":["../../src/allocator/cross-project.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,oEAAoE;AACpE,kEAAkE;AAClE,sEAAsE;AACtE,gEAAgE;AAChE,mEAAmE;AACnE,oEAAoE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../src/allocator/order.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
// Service-ordering test surface — this module exists to satisfy structure:check
|
|
3
|
+
// which requires a source file at this location for __tests__/order.test.ts.
|
|
4
|
+
// The test exercises orderServicesForAllocation() from allocate.ts in
|
|
5
|
+
// isolation, verifying that scattered grouped services are made contiguous and
|
|
6
|
+
// that group first-occurrence order is preserved. No additional runtime exports
|
|
7
|
+
// are provided here; the test imports directly from allocate.ts.
|
|
8
|
+
//# sourceMappingURL=order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order.js","sourceRoot":"","sources":["../../src/allocator/order.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,6EAA6E;AAC7E,sEAAsE;AACtE,+EAA+E;AAC/E,gFAAgF;AAChF,iEAAiE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const POOL_START_DEFAULT: 30000;
|
|
2
|
+
export declare const POOL_END_DEFAULT: 60000;
|
|
3
|
+
export interface PoolRange {
|
|
4
|
+
readonly end: number;
|
|
5
|
+
readonly start: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse PORTWEAVE_POOL_RANGE from the environment.
|
|
9
|
+
*
|
|
10
|
+
* Format: "<start>-<end>" where both are integers, start >= 1024, and
|
|
11
|
+
* start < end. Malformed, non-integer, privileged, or inverted values fall
|
|
12
|
+
* back to the default and emit a one-line warning to stderr so a typo
|
|
13
|
+
* doesn't silently produce surprising allocations.
|
|
14
|
+
*
|
|
15
|
+
* The fallback semantics match PORTWEAVE_LOCK_TIMEOUT_MS (decision-log #19);
|
|
16
|
+
* the difference is the warning line — silent fallback was discovered to be
|
|
17
|
+
* surprising in the v0 review pass.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolvePoolRange(env?: NodeJS.ProcessEnv, stderr?: {
|
|
20
|
+
write: (msg: string) => boolean;
|
|
21
|
+
}): PoolRange;
|
|
22
|
+
/**
|
|
23
|
+
* Find the first ascending port at which `slotCount` contiguous ports avoid
|
|
24
|
+
* every entry in `occupiedSorted` and fit within `range`.
|
|
25
|
+
*
|
|
26
|
+
* Pure — no I/O. The probe loop in allocate.ts calls it repeatedly, each
|
|
27
|
+
* call passing an enlarged "occupied" set as probes fail.
|
|
28
|
+
*
|
|
29
|
+
* @param occupiedSorted - Sorted array of currently-occupied ports.
|
|
30
|
+
* @param slotCount - Number of contiguous ports needed.
|
|
31
|
+
* @param range - Pool range (exclusive upper bound).
|
|
32
|
+
* @returns The start port of the first free block, or null if no block fits.
|
|
33
|
+
*/
|
|
34
|
+
export declare function findFreeBlock(occupiedSorted: readonly number[], slotCount: number, range: PoolRange): null | number;
|
|
35
|
+
//# sourceMappingURL=pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/allocator/pool.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,EAAG,KAAc,CAAA;AAChD,eAAO,MAAM,gBAAgB,EAAG,KAAc,CAAA;AAO9C,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,MAAM,GAAE;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAmB,GAC3D,SAAS,CAsBX;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,GACf,IAAI,GAAG,MAAM,CA4Bf"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const POOL_START_DEFAULT = 30000;
|
|
2
|
+
export const POOL_END_DEFAULT = 60000;
|
|
3
|
+
// Ports below 1024 are privileged on POSIX (require root to bind). Allocating
|
|
4
|
+
// into that range would silently produce ports the dev server can't bind to —
|
|
5
|
+
// treat any start < 1024 as malformed so the user gets a warning and the
|
|
6
|
+
// default range is used instead. Module-local: not part of the public surface.
|
|
7
|
+
const POOL_PRIVILEGED_FLOOR = 1024;
|
|
8
|
+
/**
|
|
9
|
+
* Parse PORTWEAVE_POOL_RANGE from the environment.
|
|
10
|
+
*
|
|
11
|
+
* Format: "<start>-<end>" where both are integers, start >= 1024, and
|
|
12
|
+
* start < end. Malformed, non-integer, privileged, or inverted values fall
|
|
13
|
+
* back to the default and emit a one-line warning to stderr so a typo
|
|
14
|
+
* doesn't silently produce surprising allocations.
|
|
15
|
+
*
|
|
16
|
+
* The fallback semantics match PORTWEAVE_LOCK_TIMEOUT_MS (decision-log #19);
|
|
17
|
+
* the difference is the warning line — silent fallback was discovered to be
|
|
18
|
+
* surprising in the v0 review pass.
|
|
19
|
+
*/
|
|
20
|
+
export function resolvePoolRange(env = process.env, stderr = process.stderr) {
|
|
21
|
+
const raw = env.PORTWEAVE_POOL_RANGE;
|
|
22
|
+
if (raw === undefined || raw.length === 0) {
|
|
23
|
+
return { end: POOL_END_DEFAULT, start: POOL_START_DEFAULT };
|
|
24
|
+
}
|
|
25
|
+
const parts = raw.split('-');
|
|
26
|
+
if (parts.length === 2) {
|
|
27
|
+
const start = Number(parts[0]);
|
|
28
|
+
const end = Number(parts[1]);
|
|
29
|
+
if (Number.isInteger(start) &&
|
|
30
|
+
Number.isInteger(end) &&
|
|
31
|
+
start >= POOL_PRIVILEGED_FLOOR &&
|
|
32
|
+
end > start) {
|
|
33
|
+
return { end, start };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
stderr.write(`[portweave] PORTWEAVE_POOL_RANGE="${raw}" ignored — using default ${POOL_START_DEFAULT.toString()}-${POOL_END_DEFAULT.toString()} (format: <start>-<end>, integers, start >= ${POOL_PRIVILEGED_FLOOR.toString()}, end > start)\n`);
|
|
37
|
+
return { end: POOL_END_DEFAULT, start: POOL_START_DEFAULT };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Find the first ascending port at which `slotCount` contiguous ports avoid
|
|
41
|
+
* every entry in `occupiedSorted` and fit within `range`.
|
|
42
|
+
*
|
|
43
|
+
* Pure — no I/O. The probe loop in allocate.ts calls it repeatedly, each
|
|
44
|
+
* call passing an enlarged "occupied" set as probes fail.
|
|
45
|
+
*
|
|
46
|
+
* @param occupiedSorted - Sorted array of currently-occupied ports.
|
|
47
|
+
* @param slotCount - Number of contiguous ports needed.
|
|
48
|
+
* @param range - Pool range (exclusive upper bound).
|
|
49
|
+
* @returns The start port of the first free block, or null if no block fits.
|
|
50
|
+
*/
|
|
51
|
+
export function findFreeBlock(occupiedSorted, slotCount, range) {
|
|
52
|
+
let start = range.start;
|
|
53
|
+
let oi = 0;
|
|
54
|
+
// Advance oi to the first occupied port >= start
|
|
55
|
+
while (oi < occupiedSorted.length && occupiedSorted[oi] < start) {
|
|
56
|
+
oi++;
|
|
57
|
+
}
|
|
58
|
+
while (start + slotCount <= range.end) {
|
|
59
|
+
const blockEnd = start + slotCount - 1;
|
|
60
|
+
// Find first occupied port within [start, blockEnd]
|
|
61
|
+
while (oi < occupiedSorted.length && occupiedSorted[oi] < start) {
|
|
62
|
+
oi++;
|
|
63
|
+
}
|
|
64
|
+
if (oi >= occupiedSorted.length || occupiedSorted[oi] > blockEnd) {
|
|
65
|
+
// No occupied port in [start, blockEnd] — this block is free
|
|
66
|
+
return start;
|
|
67
|
+
}
|
|
68
|
+
// Jump past the conflict
|
|
69
|
+
start = occupiedSorted[oi] + 1;
|
|
70
|
+
oi++;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/allocator/pool.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAc,CAAA;AAChD,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAc,CAAA;AAC9C,8EAA8E;AAC9E,8EAA8E;AAC9E,yEAAyE;AACzE,+EAA+E;AAC/E,MAAM,qBAAqB,GAAG,IAAI,CAAA;AAOlC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAyB,OAAO,CAAC,GAAG,EACpC,SAA8C,OAAO,CAAC,MAAM;IAE5D,MAAM,GAAG,GAAG,GAAG,CAAC,oBAAoB,CAAA;IACpC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC7D,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC5B,IACE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;YACvB,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC;YACrB,KAAK,IAAI,qBAAqB;YAC9B,GAAG,GAAG,KAAK,EACX,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;QACvB,CAAC;IACH,CAAC;IACD,MAAM,CAAC,KAAK,CACV,qCAAqC,GAAG,6BAA6B,kBAAkB,CAAC,QAAQ,EAAE,IAAI,gBAAgB,CAAC,QAAQ,EAAE,+CAA+C,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,CACnO,CAAA;IACD,OAAO,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAA;AAC7D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,cAAiC,EACjC,SAAiB,EACjB,KAAgB;IAEhB,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;IACvB,IAAI,EAAE,GAAG,CAAC,CAAA;IAEV,iDAAiD;IACjD,OAAO,EAAE,GAAG,cAAc,CAAC,MAAM,IAAI,cAAc,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC;QAChE,EAAE,EAAE,CAAA;IACN,CAAC;IAED,OAAO,KAAK,GAAG,SAAS,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,CAAC,CAAA;QAEtC,oDAAoD;QACpD,OAAO,EAAE,GAAG,cAAc,CAAC,MAAM,IAAI,cAAc,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC;YAChE,EAAE,EAAE,CAAA;QACN,CAAC;QAED,IAAI,EAAE,IAAI,cAAc,CAAC,MAAM,IAAI,cAAc,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC;YACjE,6DAA6D;YAC7D,OAAO,KAAK,CAAA;QACd,CAAC;QAED,yBAAyB;QACzB,KAAK,GAAG,cAAc,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;QAC9B,EAAE,EAAE,CAAA;IACN,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Probe a single port on 127.0.0.1 to determine if it is free or taken.
|
|
3
|
+
*
|
|
4
|
+
* Binds to 127.0.0.1 explicitly — we only care about loopback availability.
|
|
5
|
+
* Binding to 0.0.0.0 would falsely flag ports as free when an
|
|
6
|
+
* interface-specific listener exists on a different address.
|
|
7
|
+
*/
|
|
8
|
+
export declare function probePort(port: number): Promise<'free' | 'taken'>;
|
|
9
|
+
export type ProbeBlockResult = {
|
|
10
|
+
allFree: false;
|
|
11
|
+
firstTakenPort: number;
|
|
12
|
+
} | {
|
|
13
|
+
allFree: true;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Probe every port in [start, start + count).
|
|
17
|
+
*
|
|
18
|
+
* Probes sequentially so we can short-circuit on the first taken port and
|
|
19
|
+
* return it for use as the skip-past value in the outer allocator loop.
|
|
20
|
+
*/
|
|
21
|
+
export declare function probeBlock(start: number, count: number): Promise<ProbeBlockResult>;
|
|
22
|
+
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/allocator/probe.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAejE;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAA;AAErB;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,CAQ3B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createServer } from 'node:net';
|
|
2
|
+
/**
|
|
3
|
+
* Probe a single port on 127.0.0.1 to determine if it is free or taken.
|
|
4
|
+
*
|
|
5
|
+
* Binds to 127.0.0.1 explicitly — we only care about loopback availability.
|
|
6
|
+
* Binding to 0.0.0.0 would falsely flag ports as free when an
|
|
7
|
+
* interface-specific listener exists on a different address.
|
|
8
|
+
*/
|
|
9
|
+
export function probePort(port) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const server = createServer();
|
|
12
|
+
server.once('listening', () => {
|
|
13
|
+
server.close(() => {
|
|
14
|
+
resolve('free');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
// Any error (EADDRINUSE or permissions/sandbox issues) means we cannot
|
|
18
|
+
// bind — treat as taken so the allocator skips this port defensively.
|
|
19
|
+
server.once('error', () => {
|
|
20
|
+
resolve('taken');
|
|
21
|
+
});
|
|
22
|
+
server.listen(port, '127.0.0.1');
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Probe every port in [start, start + count).
|
|
27
|
+
*
|
|
28
|
+
* Probes sequentially so we can short-circuit on the first taken port and
|
|
29
|
+
* return it for use as the skip-past value in the outer allocator loop.
|
|
30
|
+
*/
|
|
31
|
+
export async function probeBlock(start, count) {
|
|
32
|
+
for (let port = start; port < start + count; port++) {
|
|
33
|
+
const result = await probePort(port);
|
|
34
|
+
if (result === 'taken') {
|
|
35
|
+
return { allFree: false, firstTakenPort: port };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { allFree: true };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/allocator/probe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;QAC7B,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;YAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBAChB,OAAO,CAAC,MAAM,CAAC,CAAA;YACjB,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QACF,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACxB,OAAO,CAAC,OAAO,CAAC,CAAA;QAClB,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC;AAMD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAa,EACb,KAAa;IAEb,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAA;QACjD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Allocation } from '../allocator/allocate.ts';
|
|
2
|
+
import type { Config } from '../config/index.ts';
|
|
3
|
+
export interface BannerInput {
|
|
4
|
+
allocation: Allocation;
|
|
5
|
+
config: Config;
|
|
6
|
+
launchingCommand?: string;
|
|
7
|
+
reused: boolean;
|
|
8
|
+
verboseLines?: readonly string[];
|
|
9
|
+
wroteEnvFile?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns a multi-line banner string. The caller writes it once to stderr
|
|
13
|
+
* with a trailing newline appended.
|
|
14
|
+
*
|
|
15
|
+
* Pure — no I/O, no global state. All tests are snapshot-friendly fixtures.
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatAllocationBanner(input: BannerInput): string;
|
|
18
|
+
/**
|
|
19
|
+
* Returns a formatted error line for use in all error paths.
|
|
20
|
+
*
|
|
21
|
+
* `[portweave] error: <message> (<code>)` or
|
|
22
|
+
* `[portweave] error: <message>`
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatErrorLine(message: string, code?: string): string;
|
|
25
|
+
//# sourceMappingURL=banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/cli/banner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAQD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAuDjE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAItE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
const PREFIX = '[portweave]';
|
|
3
|
+
function worktreeName(worktreeRoot) {
|
|
4
|
+
return basename(worktreeRoot);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Returns a multi-line banner string. The caller writes it once to stderr
|
|
8
|
+
* with a trailing newline appended.
|
|
9
|
+
*
|
|
10
|
+
* Pure — no I/O, no global state. All tests are snapshot-friendly fixtures.
|
|
11
|
+
*/
|
|
12
|
+
export function formatAllocationBanner(input) {
|
|
13
|
+
const { allocation, config, launchingCommand, reused, verboseLines, wroteEnvFile, } = input;
|
|
14
|
+
const { key, namespace, ports } = allocation;
|
|
15
|
+
const lines = [];
|
|
16
|
+
// Worktree header
|
|
17
|
+
lines.push(`${PREFIX} worktree: ${worktreeName(key.worktreeRoot)} (namespace: ${namespace})`);
|
|
18
|
+
// Verb line
|
|
19
|
+
const verb = reused ? 'reusing existing allocation:' : 'allocated:';
|
|
20
|
+
lines.push(`${PREFIX} ${verb}`);
|
|
21
|
+
// Service rows: left-pad name to longest service name + 2 spaces
|
|
22
|
+
const serviceNames = config.services.map((s) => s.name);
|
|
23
|
+
const maxNameLen = Math.max(...serviceNames.map((n) => n.length));
|
|
24
|
+
const padWidth = maxNameLen + 2;
|
|
25
|
+
for (const service of config.services) {
|
|
26
|
+
if (!(service.name in ports)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const port = ports[service.name];
|
|
30
|
+
const paddedName = service.name.padEnd(padWidth);
|
|
31
|
+
lines.push(` ${paddedName}→ ${String(port)} (${service.envVar})`);
|
|
32
|
+
}
|
|
33
|
+
// Wrote line — only when run-command actually wrote the env file; show
|
|
34
|
+
// omits this since it's a read-only introspection command.
|
|
35
|
+
if (wroteEnvFile === true) {
|
|
36
|
+
lines.push(`${PREFIX} wrote .portweave/current.env`);
|
|
37
|
+
}
|
|
38
|
+
// Optional verbose lines (already prefixed with [portweave])
|
|
39
|
+
if (verboseLines !== undefined) {
|
|
40
|
+
for (const vl of verboseLines) {
|
|
41
|
+
lines.push(vl);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Launching line (omitted by show-command)
|
|
45
|
+
if (launchingCommand !== undefined) {
|
|
46
|
+
lines.push(`${PREFIX} launching: ${launchingCommand}`);
|
|
47
|
+
}
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns a formatted error line for use in all error paths.
|
|
52
|
+
*
|
|
53
|
+
* `[portweave] error: <message> (<code>)` or
|
|
54
|
+
* `[portweave] error: <message>`
|
|
55
|
+
*/
|
|
56
|
+
export function formatErrorLine(message, code) {
|
|
57
|
+
return code !== undefined
|
|
58
|
+
? `${PREFIX} error: ${message} (${code})`
|
|
59
|
+
: `${PREFIX} error: ${message}`;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/cli/banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAapC,MAAM,MAAM,GAAG,aAAa,CAAA;AAE5B,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAkB;IACvD,MAAM,EACJ,UAAU,EACV,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,YAAY,EACZ,YAAY,GACb,GAAG,KAAK,CAAA;IACT,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,UAAU,CAAA;IAE5C,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,kBAAkB;IAClB,KAAK,CAAC,IAAI,CACR,GAAG,MAAM,cAAc,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,gBAAgB,SAAS,GAAG,CAClF,CAAA;IAED,YAAY;IACZ,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,YAAY,CAAA;IACnE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAA;IAE/B,iEAAiE;IACjE,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IACjE,MAAM,QAAQ,GAAG,UAAU,GAAG,CAAC,CAAA;IAE/B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YAC7B,SAAQ;QACV,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAChC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAChD,KAAK,CAAC,IAAI,CAAC,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IACxE,CAAC;IAED,uEAAuE;IACvE,2DAA2D;IAC3D,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,+BAA+B,CAAC,CAAA;IACtD,CAAC;IAED,6DAA6D;IAC7D,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAChB,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,eAAe,gBAAgB,EAAE,CAAC,CAAA;IACxD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,IAAa;IAC5D,OAAO,IAAI,KAAK,SAAS;QACvB,CAAC,CAAC,GAAG,MAAM,WAAW,OAAO,KAAK,IAAI,GAAG;QACzC,CAAC,CAAC,GAAG,MAAM,WAAW,OAAO,EAAE,CAAA;AACnC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export interface RunIo {
|
|
3
|
+
cwd: () => string;
|
|
4
|
+
env: NodeJS.ProcessEnv;
|
|
5
|
+
stderr: NodeJS.WritableStream;
|
|
6
|
+
stdout: NodeJS.WritableStream;
|
|
7
|
+
}
|
|
8
|
+
export interface RunOptions {
|
|
9
|
+
configPath?: string;
|
|
10
|
+
count?: number;
|
|
11
|
+
verbose: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function runCommand(childArgs: readonly string[], options: RunOptions, io?: RunIo): Promise<number>;
|
|
14
|
+
export declare function registerRunCommand(program: Command): void;
|
|
15
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAaxC,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAA;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB;AAsJD,wBAAsB,UAAU,CAC9B,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,OAAO,EAAE,UAAU,EACnB,EAAE,GAAE,KAAoB,GACvB,OAAO,CAAC,MAAM,CAAC,CAgDjB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoBzD"}
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { allocate } from "../allocator/allocate.js";
|
|
2
|
+
import { synthesizeAnonymousConfig } from "../config/anonymous.js";
|
|
3
|
+
import { loadConfig } from "../config/loader.js";
|
|
4
|
+
import { resolveEnv } from "../env/index.js";
|
|
5
|
+
import { PortweaveError, PW_ERROR_CODES } from "../errors.js";
|
|
6
|
+
import { resolveRegistryPath } from "../registry/paths.js";
|
|
7
|
+
import { err } from "../result.js";
|
|
8
|
+
import { resolveAllocationKey } from "../worktree/key.js";
|
|
9
|
+
import { formatAllocationBanner, formatErrorLine } from "./banner.js";
|
|
10
|
+
import { resolveExitCode, spawnChild } from "./spawn.js";
|
|
11
|
+
const defaultRunIo = {
|
|
12
|
+
cwd: () => process.cwd(),
|
|
13
|
+
env: process.env,
|
|
14
|
+
stderr: process.stderr,
|
|
15
|
+
stdout: process.stdout,
|
|
16
|
+
};
|
|
17
|
+
function writeError(opts) {
|
|
18
|
+
opts.io.stderr.write(formatErrorLine(opts.message, opts.code) + '\n');
|
|
19
|
+
if (opts.verbose === true && opts.stack !== undefined) {
|
|
20
|
+
opts.io.stderr.write(opts.stack + '\n');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function writePortweaveError(error, io, verbose) {
|
|
24
|
+
writeError({
|
|
25
|
+
code: error.code,
|
|
26
|
+
io,
|
|
27
|
+
message: error.message,
|
|
28
|
+
stack: error.stack,
|
|
29
|
+
verbose,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function validateFlags(childArgs, options) {
|
|
33
|
+
if (options.configPath !== undefined && options.count !== undefined) {
|
|
34
|
+
return err(new PortweaveError(PW_ERROR_CODES.CLI_INVALID_FLAGS, '--config and --count are mutually exclusive'));
|
|
35
|
+
}
|
|
36
|
+
if (childArgs.length === 0) {
|
|
37
|
+
return err(new PortweaveError(PW_ERROR_CODES.CLI_INVALID_FLAGS, 'no command provided after --'));
|
|
38
|
+
}
|
|
39
|
+
if (options.count !== undefined &&
|
|
40
|
+
(!Number.isInteger(options.count) || options.count <= 0)) {
|
|
41
|
+
return err(new PortweaveError(PW_ERROR_CODES.CLI_INVALID_FLAGS, `--count must be a positive integer, received ${String(options.count)}`));
|
|
42
|
+
}
|
|
43
|
+
return { ok: true, value: undefined };
|
|
44
|
+
}
|
|
45
|
+
async function resolveConfig(cwd, options, io) {
|
|
46
|
+
if (options.count !== undefined) {
|
|
47
|
+
const result = synthesizeAnonymousConfig(options.count);
|
|
48
|
+
if (!result.ok) {
|
|
49
|
+
writePortweaveError(result.error, io, options.verbose);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return result.value;
|
|
53
|
+
}
|
|
54
|
+
const result = await loadConfig(cwd, { configPath: options.configPath });
|
|
55
|
+
if (!result.ok) {
|
|
56
|
+
writePortweaveError(result.error, io, options.verbose);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return result.value;
|
|
60
|
+
}
|
|
61
|
+
function buildVerboseLines(config, key, env) {
|
|
62
|
+
const registryPaths = resolveRegistryPath(env);
|
|
63
|
+
const configLabel = config.sourcePath ??
|
|
64
|
+
(config.source === 'anonymous' ? '<anonymous-mode>' : '<unknown>');
|
|
65
|
+
return [
|
|
66
|
+
`[portweave] config: ${configLabel}`,
|
|
67
|
+
`[portweave] registry: ${registryPaths.registryFile}`,
|
|
68
|
+
`[portweave] key: ${JSON.stringify({ gitCommonDir: key.gitCommonDir, namespace: key.namespace, worktreeRoot: key.worktreeRoot })}`,
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
async function spawnWithBanner(ctx) {
|
|
72
|
+
const { allocResult, childArgs, config, io, key, options, resolvedEnv } = ctx;
|
|
73
|
+
const { allocation, reused } = allocResult;
|
|
74
|
+
const verboseLines = options.verbose
|
|
75
|
+
? buildVerboseLines(config, key, io.env)
|
|
76
|
+
: undefined;
|
|
77
|
+
io.stderr.write(formatAllocationBanner({
|
|
78
|
+
allocation,
|
|
79
|
+
config,
|
|
80
|
+
launchingCommand: childArgs.join(' '),
|
|
81
|
+
reused,
|
|
82
|
+
verboseLines,
|
|
83
|
+
wroteEnvFile: true,
|
|
84
|
+
}) + '\n');
|
|
85
|
+
// Parent env spread last → io.env wins on conflict (DESIGN.md §7.2 row 9: process > .env > computed). Spec step-7 example is inverted.
|
|
86
|
+
const mergedEnv = { ...resolvedEnv.env, ...io.env };
|
|
87
|
+
const spawnResult = await spawnChild(childArgs, { env: mergedEnv, io });
|
|
88
|
+
if (!spawnResult.ok) {
|
|
89
|
+
writeError({
|
|
90
|
+
code: spawnResult.error.code,
|
|
91
|
+
io,
|
|
92
|
+
message: spawnResult.error.message,
|
|
93
|
+
verbose: options.verbose,
|
|
94
|
+
});
|
|
95
|
+
return 127;
|
|
96
|
+
}
|
|
97
|
+
return resolveExitCode(spawnResult.value);
|
|
98
|
+
}
|
|
99
|
+
export async function runCommand(childArgs, options, io = defaultRunIo) {
|
|
100
|
+
const flagResult = validateFlags(childArgs, options);
|
|
101
|
+
if (!flagResult.ok) {
|
|
102
|
+
writeError({
|
|
103
|
+
code: flagResult.error.code,
|
|
104
|
+
io,
|
|
105
|
+
message: flagResult.error.message,
|
|
106
|
+
});
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
const keyResult = resolveAllocationKey(io.cwd());
|
|
110
|
+
if (!keyResult.ok) {
|
|
111
|
+
writePortweaveError(keyResult.error, io, options.verbose);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
const key = keyResult.value;
|
|
115
|
+
const config = await resolveConfig(io.cwd(), options, io);
|
|
116
|
+
if (config === null) {
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
const allocResult = await allocate(key, config, io.env);
|
|
120
|
+
if (!allocResult.ok) {
|
|
121
|
+
writePortweaveError(allocResult.error, io, options.verbose);
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
const envResult = await resolveEnv(allocResult.value.allocation, config, key.worktreeRoot);
|
|
125
|
+
if (!envResult.ok) {
|
|
126
|
+
writePortweaveError(envResult.error, io, options.verbose);
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
return spawnWithBanner({
|
|
130
|
+
allocResult: allocResult.value,
|
|
131
|
+
childArgs,
|
|
132
|
+
config,
|
|
133
|
+
io,
|
|
134
|
+
key,
|
|
135
|
+
options,
|
|
136
|
+
resolvedEnv: envResult.value,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export function registerRunCommand(program) {
|
|
140
|
+
program
|
|
141
|
+
.command('run')
|
|
142
|
+
.description('Allocate ports and run a command with env vars injected')
|
|
143
|
+
.argument('[childArgs...]', 'command and args to run after --')
|
|
144
|
+
.allowUnknownOption(true)
|
|
145
|
+
.action(async (childArgs) => {
|
|
146
|
+
const opts = program.opts();
|
|
147
|
+
const count = opts.count !== undefined ? Number(opts.count) : undefined;
|
|
148
|
+
const exitCode = await runCommand(childArgs, {
|
|
149
|
+
configPath: opts.config,
|
|
150
|
+
count,
|
|
151
|
+
verbose: opts.verbose === true,
|
|
152
|
+
});
|
|
153
|
+
process.exitCode = exitCode;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|