@tokenbooks/wt 0.2.2 → 0.3.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 +59 -28
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/new.js +30 -5
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/prune.d.ts +8 -0
- package/dist/commands/prune.js +210 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/commands/prune.spec.d.ts +1 -0
- package/dist/commands/prune.spec.js +173 -0
- package/dist/commands/prune.spec.js.map +1 -0
- package/dist/commands/remove.js +11 -1
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/setup.js +144 -7
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/env-patcher.js +8 -8
- package/dist/core/env-patcher.js.map +1 -1
- package/dist/core/git.d.ts +8 -0
- package/dist/core/git.js +32 -0
- package/dist/core/git.js.map +1 -1
- package/dist/core/managed-redis.d.ts +19 -0
- package/dist/core/managed-redis.js +266 -0
- package/dist/core/managed-redis.js.map +1 -0
- package/dist/core/slot-allocator.d.ts +10 -0
- package/dist/core/slot-allocator.js +98 -0
- package/dist/core/slot-allocator.js.map +1 -1
- package/dist/output.js +14 -3
- package/dist/output.js.map +1 -1
- package/dist/schemas/config.schema.d.ts +45 -27
- package/dist/schemas/config.schema.js +22 -6
- package/dist/schemas/config.schema.js.map +1 -1
- package/dist/schemas/registry.schema.d.ts +4 -2
- package/dist/schemas/registry.schema.js +2 -1
- package/dist/schemas/registry.schema.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -1
- package/skills/wt/SKILL.md +31 -12
package/dist/core/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/core/git.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/core/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ServiceConfig, WtConfig } from '../types';
|
|
2
|
+
interface EnsureManagedRedisContainerOptions {
|
|
3
|
+
readonly mainRoot: string;
|
|
4
|
+
readonly slot: number;
|
|
5
|
+
readonly branchName: string;
|
|
6
|
+
readonly worktreePath: string;
|
|
7
|
+
readonly port: number;
|
|
8
|
+
readonly sourceUrl: string | null;
|
|
9
|
+
readonly log?: (message: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function usesManagedRedis(config: WtConfig): boolean;
|
|
12
|
+
export declare function getManagedRedisService(config: WtConfig): ServiceConfig | null;
|
|
13
|
+
export declare function getAllocationServices(config: WtConfig): readonly ServiceConfig[];
|
|
14
|
+
export declare function getManagedRedisContainerName(mainRoot: string, slot: number): string;
|
|
15
|
+
export declare function readManagedRedisSourceUrl(mainRoot: string, config: WtConfig): string | null;
|
|
16
|
+
export declare function patchManagedRedisUrl(sourceUrl: string, port: number): string;
|
|
17
|
+
export declare function ensureManagedRedisContainer(options: EnsureManagedRedisContainerOptions): string;
|
|
18
|
+
export declare function removeManagedRedisContainer(mainRoot: string, slot: number, log?: (message: string) => void): boolean;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.usesManagedRedis = usesManagedRedis;
|
|
37
|
+
exports.getManagedRedisService = getManagedRedisService;
|
|
38
|
+
exports.getAllocationServices = getAllocationServices;
|
|
39
|
+
exports.getManagedRedisContainerName = getManagedRedisContainerName;
|
|
40
|
+
exports.readManagedRedisSourceUrl = readManagedRedisSourceUrl;
|
|
41
|
+
exports.patchManagedRedisUrl = patchManagedRedisUrl;
|
|
42
|
+
exports.ensureManagedRedisContainer = ensureManagedRedisContainer;
|
|
43
|
+
exports.removeManagedRedisContainer = removeManagedRedisContainer;
|
|
44
|
+
const crypto = __importStar(require("node:crypto"));
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const node_child_process_1 = require("node:child_process");
|
|
48
|
+
const MANAGED_REDIS_SERVICE_NAME = 'redis';
|
|
49
|
+
const DEFAULT_REDIS_PORT = 6379;
|
|
50
|
+
const DEFAULT_REDIS_IMAGE = 'redis:8-alpine';
|
|
51
|
+
const DOCKER_LABEL_PREFIX = 'dev.tokenbooks.wt';
|
|
52
|
+
function slugify(input) {
|
|
53
|
+
return input
|
|
54
|
+
.toLowerCase()
|
|
55
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
56
|
+
.replace(/^-+|-+$/g, '')
|
|
57
|
+
.slice(0, 32) || 'repo';
|
|
58
|
+
}
|
|
59
|
+
function repoHash(mainRoot) {
|
|
60
|
+
return crypto
|
|
61
|
+
.createHash('sha1')
|
|
62
|
+
.update(mainRoot)
|
|
63
|
+
.digest('hex')
|
|
64
|
+
.slice(0, 8);
|
|
65
|
+
}
|
|
66
|
+
function runDocker(args) {
|
|
67
|
+
try {
|
|
68
|
+
return (0, node_child_process_1.execFileSync)('docker', args, {
|
|
69
|
+
encoding: 'utf-8',
|
|
70
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
71
|
+
}).trim();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
|
|
75
|
+
throw new Error('Docker CLI not found on PATH.');
|
|
76
|
+
}
|
|
77
|
+
const stderr = err && typeof err === 'object' && 'stderr' in err
|
|
78
|
+
? String(err.stderr ?? '').trim()
|
|
79
|
+
: '';
|
|
80
|
+
throw new Error(stderr || `Docker command failed: docker ${args.join(' ')}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function inspectManagedRedisContainer(containerName) {
|
|
84
|
+
try {
|
|
85
|
+
const raw = runDocker(['container', 'inspect', containerName]);
|
|
86
|
+
const parsed = JSON.parse(raw);
|
|
87
|
+
return parsed[0] ?? null;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
91
|
+
if (message.includes('No such container')) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function parseManagedRedisPassword(sourceUrl) {
|
|
98
|
+
if (!sourceUrl) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
let parsed;
|
|
102
|
+
try {
|
|
103
|
+
parsed = new URL(sourceUrl);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (parsed.protocol !== 'redis:') {
|
|
109
|
+
throw new Error(`Managed Redis only supports redis:// URLs, got: ${parsed.protocol}//`);
|
|
110
|
+
}
|
|
111
|
+
if (parsed.username && parsed.username !== 'default') {
|
|
112
|
+
throw new Error(`Managed Redis does not support non-default ACL usernames in REDIS_URL, got: ${parsed.username}`);
|
|
113
|
+
}
|
|
114
|
+
return parsed.password || undefined;
|
|
115
|
+
}
|
|
116
|
+
function passwordHash(password) {
|
|
117
|
+
return crypto
|
|
118
|
+
.createHash('sha256')
|
|
119
|
+
.update(password ?? '')
|
|
120
|
+
.digest('hex');
|
|
121
|
+
}
|
|
122
|
+
function usesManagedRedis(config) {
|
|
123
|
+
return config.envFiles.some((envFile) => envFile.patches.some((patch) => patch.type === 'redis'));
|
|
124
|
+
}
|
|
125
|
+
function getManagedRedisService(config) {
|
|
126
|
+
if (!usesManagedRedis(config)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const configured = config.services.find((service) => service.name === MANAGED_REDIS_SERVICE_NAME);
|
|
130
|
+
if (configured) {
|
|
131
|
+
return configured;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
name: MANAGED_REDIS_SERVICE_NAME,
|
|
135
|
+
defaultPort: DEFAULT_REDIS_PORT,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function getAllocationServices(config) {
|
|
139
|
+
const redisService = getManagedRedisService(config);
|
|
140
|
+
if (!redisService) {
|
|
141
|
+
return config.services;
|
|
142
|
+
}
|
|
143
|
+
if (config.services.some((service) => service.name === MANAGED_REDIS_SERVICE_NAME)) {
|
|
144
|
+
return config.services;
|
|
145
|
+
}
|
|
146
|
+
return [...config.services, redisService];
|
|
147
|
+
}
|
|
148
|
+
function getManagedRedisContainerName(mainRoot, slot) {
|
|
149
|
+
const repoName = slugify(path.basename(mainRoot));
|
|
150
|
+
return `wt-${repoName}-${repoHash(mainRoot)}-slot-${slot}-redis`;
|
|
151
|
+
}
|
|
152
|
+
function readManagedRedisSourceUrl(mainRoot, config) {
|
|
153
|
+
for (const envFile of config.envFiles) {
|
|
154
|
+
const redisVars = envFile.patches
|
|
155
|
+
.filter((patch) => patch.type === 'redis')
|
|
156
|
+
.map((patch) => patch.var);
|
|
157
|
+
if (redisVars.length === 0) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const sourcePath = path.join(mainRoot, envFile.source);
|
|
161
|
+
if (!fs.existsSync(sourcePath)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
165
|
+
for (const redisVar of redisVars) {
|
|
166
|
+
const match = content.match(new RegExp(`^${redisVar}=["']?([^"'\\n]+)`, 'm'));
|
|
167
|
+
if (match?.[1]) {
|
|
168
|
+
return match[1];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
function patchManagedRedisUrl(sourceUrl, port) {
|
|
175
|
+
const parsed = new URL(sourceUrl);
|
|
176
|
+
if (parsed.protocol !== 'redis:') {
|
|
177
|
+
throw new Error(`Managed Redis only supports redis:// URLs, got: ${parsed.protocol}//`);
|
|
178
|
+
}
|
|
179
|
+
parsed.hostname = '127.0.0.1';
|
|
180
|
+
parsed.port = String(port);
|
|
181
|
+
parsed.pathname = '/0';
|
|
182
|
+
parsed.search = '';
|
|
183
|
+
parsed.hash = '';
|
|
184
|
+
return parsed.toString();
|
|
185
|
+
}
|
|
186
|
+
function ensureManagedRedisContainer(options) {
|
|
187
|
+
const containerName = getManagedRedisContainerName(options.mainRoot, options.slot);
|
|
188
|
+
const inspect = inspectManagedRedisContainer(containerName);
|
|
189
|
+
const password = parseManagedRedisPassword(options.sourceUrl);
|
|
190
|
+
const expectedPasswordHash = passwordHash(password);
|
|
191
|
+
const expectedPort = String(options.port);
|
|
192
|
+
if (inspect) {
|
|
193
|
+
const actualPort = inspect.NetworkSettings?.Ports?.['6379/tcp']?.[0]?.HostPort;
|
|
194
|
+
const actualPasswordHash = inspect.Config?.Labels?.[`${DOCKER_LABEL_PREFIX}.redis-password-sha256`];
|
|
195
|
+
const actualRepoRoot = inspect.Config?.Labels?.[`${DOCKER_LABEL_PREFIX}.repo-root`];
|
|
196
|
+
const shouldRecreate = actualPort !== expectedPort
|
|
197
|
+
|| actualPasswordHash !== expectedPasswordHash
|
|
198
|
+
|| actualRepoRoot !== options.mainRoot
|
|
199
|
+
|| inspect.Config?.Image !== DEFAULT_REDIS_IMAGE;
|
|
200
|
+
if (shouldRecreate) {
|
|
201
|
+
runDocker(['rm', '-f', containerName]);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
if (!inspect.State?.Running) {
|
|
205
|
+
runDocker(['start', containerName]);
|
|
206
|
+
options.log?.(`Started Redis container '${containerName}'.`);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
options.log?.(`Reusing Redis container '${containerName}'.`);
|
|
210
|
+
}
|
|
211
|
+
return containerName;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const args = [
|
|
215
|
+
'run',
|
|
216
|
+
'-d',
|
|
217
|
+
'--name',
|
|
218
|
+
containerName,
|
|
219
|
+
'--restart',
|
|
220
|
+
'unless-stopped',
|
|
221
|
+
'--label',
|
|
222
|
+
`${DOCKER_LABEL_PREFIX}.managed=true`,
|
|
223
|
+
'--label',
|
|
224
|
+
`${DOCKER_LABEL_PREFIX}.repo-root=${options.mainRoot}`,
|
|
225
|
+
'--label',
|
|
226
|
+
`${DOCKER_LABEL_PREFIX}.service=redis`,
|
|
227
|
+
'--label',
|
|
228
|
+
`${DOCKER_LABEL_PREFIX}.purpose=git-worktree-redis`,
|
|
229
|
+
'--label',
|
|
230
|
+
`${DOCKER_LABEL_PREFIX}.slot=${options.slot}`,
|
|
231
|
+
'--label',
|
|
232
|
+
`${DOCKER_LABEL_PREFIX}.branch=${options.branchName}`,
|
|
233
|
+
'--label',
|
|
234
|
+
`${DOCKER_LABEL_PREFIX}.worktree=${options.worktreePath}`,
|
|
235
|
+
'--label',
|
|
236
|
+
`${DOCKER_LABEL_PREFIX}.redis-password-sha256=${expectedPasswordHash}`,
|
|
237
|
+
'-p',
|
|
238
|
+
`127.0.0.1:${options.port}:6379`,
|
|
239
|
+
DEFAULT_REDIS_IMAGE,
|
|
240
|
+
'redis-server',
|
|
241
|
+
'--save',
|
|
242
|
+
'',
|
|
243
|
+
'--appendonly',
|
|
244
|
+
'no',
|
|
245
|
+
'--port',
|
|
246
|
+
'6379',
|
|
247
|
+
];
|
|
248
|
+
if (password) {
|
|
249
|
+
args.push('--requirepass', password);
|
|
250
|
+
}
|
|
251
|
+
runDocker(args);
|
|
252
|
+
options.log?.(`Started Redis container '${containerName}' on port ${options.port}.`);
|
|
253
|
+
return containerName;
|
|
254
|
+
}
|
|
255
|
+
function removeManagedRedisContainer(mainRoot, slot, log) {
|
|
256
|
+
const containerName = getManagedRedisContainerName(mainRoot, slot);
|
|
257
|
+
const inspect = inspectManagedRedisContainer(containerName);
|
|
258
|
+
if (!inspect) {
|
|
259
|
+
log?.(`Skipping Redis container cleanup; not found: ${containerName}`);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
runDocker(['rm', '-f', containerName]);
|
|
263
|
+
log?.(`Removed Redis container '${containerName}'.`);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=managed-redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"managed-redis.js","sourceRoot":"","sources":["../../src/core/managed-redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,16 @@ export declare function calculatePorts(slot: number, services: readonly ServiceC
|
|
|
9
9
|
* Returns "baseName_wtN" where N is the slot number.
|
|
10
10
|
*/
|
|
11
11
|
export declare function calculateDbName(slot: number, baseName: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Validate that generated ports stay within range and never collide across
|
|
14
|
+
* the main worktree (slot 0) and configured worktree slots.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validatePortPlan(services: readonly ServiceConfig[], maxSlots: number, stride: number): void;
|
|
17
|
+
export declare function findUnavailableServicePorts(ports: Record<string, number>): Promise<Array<{
|
|
18
|
+
service: string;
|
|
19
|
+
port: number;
|
|
20
|
+
}>>;
|
|
21
|
+
export declare function findAvailablePortSafeSlot(registry: Registry, maxSlots: number, services: readonly ServiceConfig[], stride: number): Promise<number | null>;
|
|
12
22
|
/**
|
|
13
23
|
* Find the next available slot in the registry.
|
|
14
24
|
* Scans slots 1..maxSlots and returns the first unoccupied one.
|
|
@@ -1,8 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.calculatePorts = calculatePorts;
|
|
4
37
|
exports.calculateDbName = calculateDbName;
|
|
38
|
+
exports.validatePortPlan = validatePortPlan;
|
|
39
|
+
exports.findUnavailableServicePorts = findUnavailableServicePorts;
|
|
40
|
+
exports.findAvailablePortSafeSlot = findAvailablePortSafeSlot;
|
|
5
41
|
exports.findAvailableSlot = findAvailableSlot;
|
|
42
|
+
const net = __importStar(require("node:net"));
|
|
6
43
|
/**
|
|
7
44
|
* Calculate port assignments for a given slot.
|
|
8
45
|
* Formula: slot * stride + service.defaultPort
|
|
@@ -21,6 +58,67 @@ function calculatePorts(slot, services, stride) {
|
|
|
21
58
|
function calculateDbName(slot, baseName) {
|
|
22
59
|
return `${baseName}_wt${slot}`;
|
|
23
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Validate that generated ports stay within range and never collide across
|
|
63
|
+
* the main worktree (slot 0) and configured worktree slots.
|
|
64
|
+
*/
|
|
65
|
+
function validatePortPlan(services, maxSlots, stride) {
|
|
66
|
+
const seen = new Map();
|
|
67
|
+
for (let slot = 0; slot <= maxSlots; slot++) {
|
|
68
|
+
for (const service of services) {
|
|
69
|
+
const port = slot * stride + service.defaultPort;
|
|
70
|
+
if (port > 65535) {
|
|
71
|
+
throw new Error(`Port ${port} for service '${service.name}' in slot ${slot} exceeds 65535. ` +
|
|
72
|
+
'Reduce maxSlots, portStride, or the default port.');
|
|
73
|
+
}
|
|
74
|
+
const owner = `slot ${slot} (${service.name})`;
|
|
75
|
+
const existing = seen.get(port);
|
|
76
|
+
if (existing) {
|
|
77
|
+
throw new Error(`Port ${port} collides between ${existing} and ${owner}. ` +
|
|
78
|
+
'Adjust service default ports or increase portStride.');
|
|
79
|
+
}
|
|
80
|
+
seen.set(port, owner);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function isPortAvailable(port) {
|
|
85
|
+
return await new Promise((resolve) => {
|
|
86
|
+
const server = net.createServer();
|
|
87
|
+
const finish = (available) => {
|
|
88
|
+
server.removeAllListeners();
|
|
89
|
+
resolve(available);
|
|
90
|
+
};
|
|
91
|
+
server.once('error', () => finish(false));
|
|
92
|
+
server.once('listening', () => {
|
|
93
|
+
server.close(() => finish(true));
|
|
94
|
+
});
|
|
95
|
+
server.listen(port, '127.0.0.1');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function findUnavailableServicePorts(ports) {
|
|
99
|
+
const entries = Object.entries(ports);
|
|
100
|
+
const checks = await Promise.all(entries.map(async ([service, port]) => ({
|
|
101
|
+
service,
|
|
102
|
+
port,
|
|
103
|
+
available: await isPortAvailable(port),
|
|
104
|
+
})));
|
|
105
|
+
return checks
|
|
106
|
+
.filter((item) => !item.available)
|
|
107
|
+
.map(({ service, port }) => ({ service, port }));
|
|
108
|
+
}
|
|
109
|
+
async function findAvailablePortSafeSlot(registry, maxSlots, services, stride) {
|
|
110
|
+
for (let slot = 1; slot <= maxSlots; slot++) {
|
|
111
|
+
if (String(slot) in registry.allocations) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const ports = calculatePorts(slot, services, stride);
|
|
115
|
+
const unavailable = await findUnavailableServicePorts(ports);
|
|
116
|
+
if (unavailable.length === 0) {
|
|
117
|
+
return slot;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
24
122
|
/**
|
|
25
123
|
* Find the next available slot in the registry.
|
|
26
124
|
* Scans slots 1..maxSlots and returns the first unoccupied one.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-allocator.js","sourceRoot":"","sources":["../../src/core/slot-allocator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slot-allocator.js","sourceRoot":"","sources":["../../src/core/slot-allocator.ts"],"names":[],"mappings":"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"}
|
package/dist/output.js
CHANGED
|
@@ -64,15 +64,24 @@ function formatAllocationTable(allocations) {
|
|
|
64
64
|
.map(([name, port]) => `${name}:${port}`)
|
|
65
65
|
.join(' ');
|
|
66
66
|
const status = fs.existsSync(alloc.worktreePath) ? 'ok' : 'stale';
|
|
67
|
-
return padRow([slot, alloc.branchName, alloc.dbName,
|
|
67
|
+
return padRow([slot, alloc.branchName, alloc.dbName, formatRedisCell(alloc), portStr, status]);
|
|
68
68
|
});
|
|
69
69
|
return [header, separator, ...rows].join('\n');
|
|
70
70
|
}
|
|
71
71
|
/** Pad columns to fixed widths for table output */
|
|
72
72
|
function padRow(cols) {
|
|
73
|
-
const widths = [6, 30, 20,
|
|
73
|
+
const widths = [6, 30, 20, 18, 35, 8];
|
|
74
74
|
return cols.map((col, i) => col.padEnd(widths[i] ?? 10)).join('');
|
|
75
75
|
}
|
|
76
|
+
function formatRedisCell(alloc) {
|
|
77
|
+
if (alloc.redisContainerName) {
|
|
78
|
+
return alloc.ports.redis ? `port:${alloc.ports.redis}` : alloc.redisContainerName;
|
|
79
|
+
}
|
|
80
|
+
if (alloc.redisDb !== undefined) {
|
|
81
|
+
return `db:${alloc.redisDb}`;
|
|
82
|
+
}
|
|
83
|
+
return '-';
|
|
84
|
+
}
|
|
76
85
|
/** Print a setup summary for human output */
|
|
77
86
|
function formatSetupSummary(slot, alloc) {
|
|
78
87
|
const portLines = Object.entries(alloc.ports)
|
|
@@ -82,7 +91,9 @@ function formatSetupSummary(slot, alloc) {
|
|
|
82
91
|
`Worktree configured (slot ${slot}):`,
|
|
83
92
|
` Branch: ${alloc.branchName}`,
|
|
84
93
|
` Database: ${alloc.dbName}`,
|
|
85
|
-
|
|
94
|
+
alloc.redisContainerName
|
|
95
|
+
? ` Redis: ${alloc.redisContainerName}${alloc.ports.redis ? ` (port ${alloc.ports.redis})` : ''}`
|
|
96
|
+
: ` Redis DB: ${alloc.redisDb ?? '-'}`,
|
|
86
97
|
` Ports:`,
|
|
87
98
|
portLines,
|
|
88
99
|
` Path: ${alloc.worktreePath}`,
|
package/dist/output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,gCAEC;AAGD,0BAEC;AAGD,sBAEC;AAGD,sDAmBC;
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
/** Schema for a single env var patch rule */
|
|
3
|
-
export declare const patchSchema: z.ZodObject<{
|
|
3
|
+
export declare const patchSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4
4
|
var: z.ZodString;
|
|
5
|
-
type: z.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
type: z.ZodLiteral<"database">;
|
|
6
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
7
|
+
var: z.ZodString;
|
|
8
|
+
type: z.ZodLiteral<"redis">;
|
|
9
|
+
service: z.ZodString;
|
|
10
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
11
|
+
var: z.ZodString;
|
|
12
|
+
type: z.ZodLiteral<"port">;
|
|
13
|
+
service: z.ZodString;
|
|
14
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
15
|
+
var: z.ZodString;
|
|
16
|
+
type: z.ZodLiteral<"url">;
|
|
17
|
+
service: z.ZodString;
|
|
18
|
+
}, z.core.$strip>], "type">;
|
|
13
19
|
/** Schema for a service with a default port */
|
|
14
20
|
export declare const serviceSchema: z.ZodObject<{
|
|
15
21
|
name: z.ZodString;
|
|
@@ -18,16 +24,22 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
18
24
|
/** Schema for an env file to copy and patch */
|
|
19
25
|
export declare const envFileSchema: z.ZodObject<{
|
|
20
26
|
source: z.ZodString;
|
|
21
|
-
patches: z.ZodArray<z.ZodObject<{
|
|
27
|
+
patches: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
22
28
|
var: z.ZodString;
|
|
23
|
-
type: z.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
type: z.ZodLiteral<"database">;
|
|
30
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
31
|
+
var: z.ZodString;
|
|
32
|
+
type: z.ZodLiteral<"redis">;
|
|
33
|
+
service: z.ZodString;
|
|
34
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
35
|
+
var: z.ZodString;
|
|
36
|
+
type: z.ZodLiteral<"port">;
|
|
37
|
+
service: z.ZodString;
|
|
38
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
39
|
+
var: z.ZodString;
|
|
40
|
+
type: z.ZodLiteral<"url">;
|
|
41
|
+
service: z.ZodString;
|
|
42
|
+
}, z.core.$strip>], "type">>;
|
|
31
43
|
}, z.core.$strip>;
|
|
32
44
|
/** Schema for wt.config.json */
|
|
33
45
|
export declare const configSchema: z.ZodObject<{
|
|
@@ -41,16 +53,22 @@ export declare const configSchema: z.ZodObject<{
|
|
|
41
53
|
}, z.core.$strip>>;
|
|
42
54
|
envFiles: z.ZodArray<z.ZodObject<{
|
|
43
55
|
source: z.ZodString;
|
|
44
|
-
patches: z.ZodArray<z.ZodObject<{
|
|
56
|
+
patches: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
57
|
+
var: z.ZodString;
|
|
58
|
+
type: z.ZodLiteral<"database">;
|
|
59
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
60
|
+
var: z.ZodString;
|
|
61
|
+
type: z.ZodLiteral<"redis">;
|
|
62
|
+
service: z.ZodString;
|
|
63
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
64
|
+
var: z.ZodString;
|
|
65
|
+
type: z.ZodLiteral<"port">;
|
|
66
|
+
service: z.ZodString;
|
|
67
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
45
68
|
var: z.ZodString;
|
|
46
|
-
type: z.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
port: "port";
|
|
50
|
-
url: "url";
|
|
51
|
-
}>;
|
|
52
|
-
service: z.ZodOptional<z.ZodString>;
|
|
53
|
-
}, z.core.$strip>>;
|
|
69
|
+
type: z.ZodLiteral<"url">;
|
|
70
|
+
service: z.ZodString;
|
|
71
|
+
}, z.core.$strip>], "type">>;
|
|
54
72
|
}, z.core.$strip>>;
|
|
55
73
|
postSetup: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
56
74
|
autoInstall: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -4,11 +4,27 @@ exports.configSchema = exports.envFileSchema = exports.serviceSchema = exports.p
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const PATCH_TYPES = ['database', 'redis', 'port', 'url'];
|
|
6
6
|
/** Schema for a single env var patch rule */
|
|
7
|
-
exports.patchSchema = zod_1.z.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
})
|
|
7
|
+
exports.patchSchema = zod_1.z.discriminatedUnion('type', [
|
|
8
|
+
zod_1.z.object({
|
|
9
|
+
var: zod_1.z.string().min(1),
|
|
10
|
+
type: zod_1.z.literal(PATCH_TYPES[0]),
|
|
11
|
+
}),
|
|
12
|
+
zod_1.z.object({
|
|
13
|
+
var: zod_1.z.string().min(1),
|
|
14
|
+
type: zod_1.z.literal(PATCH_TYPES[1]),
|
|
15
|
+
service: zod_1.z.string().min(1),
|
|
16
|
+
}),
|
|
17
|
+
zod_1.z.object({
|
|
18
|
+
var: zod_1.z.string().min(1),
|
|
19
|
+
type: zod_1.z.literal(PATCH_TYPES[2]),
|
|
20
|
+
service: zod_1.z.string().min(1),
|
|
21
|
+
}),
|
|
22
|
+
zod_1.z.object({
|
|
23
|
+
var: zod_1.z.string().min(1),
|
|
24
|
+
type: zod_1.z.literal(PATCH_TYPES[3]),
|
|
25
|
+
service: zod_1.z.string().min(1),
|
|
26
|
+
}),
|
|
27
|
+
]);
|
|
12
28
|
/** Schema for a service with a default port */
|
|
13
29
|
exports.serviceSchema = zod_1.z.object({
|
|
14
30
|
name: zod_1.z.string().min(1),
|
|
@@ -24,7 +40,7 @@ exports.configSchema = zod_1.z.object({
|
|
|
24
40
|
baseDatabaseName: zod_1.z.string().min(1),
|
|
25
41
|
baseWorktreePath: zod_1.z.string().min(1).default('.worktrees'),
|
|
26
42
|
portStride: zod_1.z.number().int().positive().default(100),
|
|
27
|
-
maxSlots: zod_1.z.number().int().min(1).
|
|
43
|
+
maxSlots: zod_1.z.number().int().min(1).default(50),
|
|
28
44
|
services: zod_1.z.array(exports.serviceSchema).min(1),
|
|
29
45
|
envFiles: zod_1.z.array(exports.envFileSchema),
|
|
30
46
|
postSetup: zod_1.z.array(zod_1.z.string()).default([]),
|