yolobox 0.1.3 → 0.1.5
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/index.js +220 -51
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,12 +7,74 @@ import { defineCommand } from "citty";
|
|
|
7
7
|
// src/lib/docker.ts
|
|
8
8
|
import { execSync, spawn, spawnSync } from "child_process";
|
|
9
9
|
import { existsSync } from "fs";
|
|
10
|
+
import { join as join2 } from "path";
|
|
11
|
+
|
|
12
|
+
// src/lib/debug.ts
|
|
13
|
+
import { appendFileSync, writeFileSync } from "fs";
|
|
10
14
|
import { join } from "path";
|
|
15
|
+
import pc from "picocolors";
|
|
16
|
+
var _enabled = false;
|
|
17
|
+
var _logPath = null;
|
|
18
|
+
function enable() {
|
|
19
|
+
_enabled = true;
|
|
20
|
+
_logPath = join(process.cwd(), "yolobox-debug.log");
|
|
21
|
+
writeFileSync(
|
|
22
|
+
_logPath,
|
|
23
|
+
`yolobox debug log \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
24
|
+
${"=".repeat(60)}
|
|
25
|
+
|
|
26
|
+
`
|
|
27
|
+
);
|
|
28
|
+
return _logPath;
|
|
29
|
+
}
|
|
30
|
+
function isEnabled() {
|
|
31
|
+
return _enabled;
|
|
32
|
+
}
|
|
33
|
+
function getLogPath() {
|
|
34
|
+
return _logPath;
|
|
35
|
+
}
|
|
36
|
+
function timestamp() {
|
|
37
|
+
return (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
38
|
+
}
|
|
39
|
+
function log(message) {
|
|
40
|
+
if (!_enabled || !_logPath) return;
|
|
41
|
+
appendFileSync(_logPath, `[${timestamp()}] ${message}
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
function error(message) {
|
|
45
|
+
if (!_enabled || !_logPath) return;
|
|
46
|
+
const ts = timestamp();
|
|
47
|
+
appendFileSync(_logPath, `[${ts}] ERROR: ${message}
|
|
48
|
+
`);
|
|
49
|
+
process.stderr.write(`${pc.dim(`[${ts}]`)} ${pc.red(message)}
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
function logCommand(cmd, args, result) {
|
|
53
|
+
if (!_enabled || !_logPath) return;
|
|
54
|
+
const lines = [`$ ${cmd} ${args.join(" ")}`, `exit code: ${result.status}`];
|
|
55
|
+
if (result.stdout.trim()) lines.push(`stdout:
|
|
56
|
+
${result.stdout.trim()}`);
|
|
57
|
+
if (result.stderr.trim()) lines.push(`stderr:
|
|
58
|
+
${result.stderr.trim()}`);
|
|
59
|
+
log(lines.join("\n"));
|
|
60
|
+
if (result.status !== 0) {
|
|
61
|
+
const errorOutput = result.stderr.trim() || result.stdout.trim();
|
|
62
|
+
if (errorOutput) {
|
|
63
|
+
process.stderr.write(`${pc.dim("[debug]")} ${pc.red(errorOutput)}
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/lib/docker.ts
|
|
11
70
|
function isDockerRunning() {
|
|
71
|
+
log("Checking Docker status...");
|
|
12
72
|
try {
|
|
13
73
|
execSync("docker info", { stdio: ["pipe", "pipe", "pipe"] });
|
|
74
|
+
log("Docker is running");
|
|
14
75
|
return true;
|
|
15
76
|
} catch {
|
|
77
|
+
error("Docker is not running or not installed");
|
|
16
78
|
return false;
|
|
17
79
|
}
|
|
18
80
|
}
|
|
@@ -35,12 +97,15 @@ function resolveDockerImage(options) {
|
|
|
35
97
|
};
|
|
36
98
|
}
|
|
37
99
|
function imageExists(imageName) {
|
|
100
|
+
log(`Checking if image exists: ${imageName}`);
|
|
38
101
|
try {
|
|
39
102
|
execSync(`docker image inspect ${imageName}`, {
|
|
40
103
|
stdio: ["pipe", "pipe", "pipe"]
|
|
41
104
|
});
|
|
105
|
+
log(`Image found: ${imageName}`);
|
|
42
106
|
return true;
|
|
43
107
|
} catch {
|
|
108
|
+
log(`Image not found locally: ${imageName}`);
|
|
44
109
|
return false;
|
|
45
110
|
}
|
|
46
111
|
}
|
|
@@ -79,12 +144,42 @@ function pullImage(imageName, onProgress) {
|
|
|
79
144
|
proc.stdout?.on("data", handleOutput);
|
|
80
145
|
proc.stderr?.on("data", handleOutput);
|
|
81
146
|
proc.on("close", (code) => {
|
|
147
|
+
log(
|
|
148
|
+
`Image pull ${code === 0 ? "succeeded" : "failed"}: ${imageName}`
|
|
149
|
+
);
|
|
82
150
|
resolve(code === 0);
|
|
83
151
|
});
|
|
84
152
|
});
|
|
85
153
|
}
|
|
86
154
|
function isYoloboxDevRepo(repoRoot) {
|
|
87
|
-
return existsSync(
|
|
155
|
+
return existsSync(join2(repoRoot, "docker", "Dockerfile"));
|
|
156
|
+
}
|
|
157
|
+
function canDockerAccessPath(hostFilePath, image) {
|
|
158
|
+
log(`Checking Docker file access for: ${hostFilePath}`);
|
|
159
|
+
try {
|
|
160
|
+
const result = spawnSync(
|
|
161
|
+
"docker",
|
|
162
|
+
[
|
|
163
|
+
"run",
|
|
164
|
+
"--rm",
|
|
165
|
+
"-v",
|
|
166
|
+
`${hostFilePath}:/test-file:ro`,
|
|
167
|
+
image,
|
|
168
|
+
"cat",
|
|
169
|
+
"/test-file"
|
|
170
|
+
],
|
|
171
|
+
{ stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
|
|
172
|
+
);
|
|
173
|
+
const ok = result.status === 0;
|
|
174
|
+
if (!ok) {
|
|
175
|
+
log(
|
|
176
|
+
`Docker cannot access path: ${result.stderr?.toString().trim()}`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return ok;
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
88
183
|
}
|
|
89
184
|
function buildDockerArgs(opts) {
|
|
90
185
|
const args = [
|
|
@@ -123,18 +218,33 @@ function buildExecArgs(id, command) {
|
|
|
123
218
|
}
|
|
124
219
|
function startContainer(opts) {
|
|
125
220
|
const args = buildDockerArgs(opts);
|
|
221
|
+
log(`Starting container yolobox-${opts.id}`);
|
|
126
222
|
const result = spawnSync("docker", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
223
|
+
const stdout = result.stdout?.toString() || "";
|
|
224
|
+
const stderr = result.stderr?.toString() || "";
|
|
225
|
+
logCommand("docker", args, {
|
|
226
|
+
status: result.status,
|
|
227
|
+
stdout,
|
|
228
|
+
stderr
|
|
229
|
+
});
|
|
127
230
|
return result.status === 0;
|
|
128
231
|
}
|
|
129
232
|
function restartContainer(id) {
|
|
130
|
-
|
|
233
|
+
log(`Restarting container yolobox-${id}`);
|
|
234
|
+
const args = ["start", `yolobox-${id}`];
|
|
235
|
+
const result = spawnSync("docker", args, {
|
|
131
236
|
stdio: ["pipe", "pipe", "pipe"]
|
|
132
237
|
});
|
|
238
|
+
const stdout = result.stdout?.toString() || "";
|
|
239
|
+
const stderr = result.stderr?.toString() || "";
|
|
240
|
+
logCommand("docker", args, { status: result.status, stdout, stderr });
|
|
133
241
|
return result.status === 0;
|
|
134
242
|
}
|
|
135
243
|
function execInContainer(id, command) {
|
|
136
244
|
const args = buildExecArgs(id, command);
|
|
245
|
+
log(`Exec in container: docker ${args.join(" ")}`);
|
|
137
246
|
const result = spawnSync("docker", args, { stdio: "inherit" });
|
|
247
|
+
log(`Exec exited with code ${result.status}`);
|
|
138
248
|
return result.status ?? 1;
|
|
139
249
|
}
|
|
140
250
|
function timeAgo(dateStr) {
|
|
@@ -150,7 +260,7 @@ function timeAgo(dateStr) {
|
|
|
150
260
|
}
|
|
151
261
|
function getWorktreeBranch(repoPath, id) {
|
|
152
262
|
try {
|
|
153
|
-
const worktreePath =
|
|
263
|
+
const worktreePath = join2(repoPath, ".yolobox", id);
|
|
154
264
|
return execSync(`git -C "${worktreePath}" rev-parse --abbrev-ref HEAD`, {
|
|
155
265
|
encoding: "utf-8"
|
|
156
266
|
}).trim();
|
|
@@ -180,30 +290,43 @@ function listContainers() {
|
|
|
180
290
|
}
|
|
181
291
|
}
|
|
182
292
|
function killContainer(id) {
|
|
183
|
-
|
|
293
|
+
log(`Killing container yolobox-${id}`);
|
|
294
|
+
const stopArgs = ["stop", `yolobox-${id}`];
|
|
295
|
+
const stop = spawnSync("docker", stopArgs, {
|
|
184
296
|
stdio: ["pipe", "pipe", "pipe"]
|
|
185
297
|
});
|
|
298
|
+
logCommand("docker", stopArgs, {
|
|
299
|
+
status: stop.status,
|
|
300
|
+
stdout: stop.stdout?.toString() || "",
|
|
301
|
+
stderr: stop.stderr?.toString() || ""
|
|
302
|
+
});
|
|
186
303
|
if (stop.status !== 0) return false;
|
|
187
|
-
const
|
|
304
|
+
const rmArgs = ["rm", `yolobox-${id}`];
|
|
305
|
+
const rm = spawnSync("docker", rmArgs, {
|
|
188
306
|
stdio: ["pipe", "pipe", "pipe"]
|
|
189
307
|
});
|
|
308
|
+
logCommand("docker", rmArgs, {
|
|
309
|
+
status: rm.status,
|
|
310
|
+
stdout: rm.stdout?.toString() || "",
|
|
311
|
+
stderr: rm.stderr?.toString() || ""
|
|
312
|
+
});
|
|
190
313
|
return rm.status === 0;
|
|
191
314
|
}
|
|
192
315
|
|
|
193
316
|
// src/lib/ui.ts
|
|
194
317
|
import * as p from "@clack/prompts";
|
|
195
|
-
import
|
|
318
|
+
import pc2 from "picocolors";
|
|
196
319
|
function intro2() {
|
|
197
|
-
p.intro(
|
|
320
|
+
p.intro(pc2.bgCyan(pc2.black(` yolobox v${"0.1.5"} `)));
|
|
198
321
|
}
|
|
199
322
|
function success(message) {
|
|
200
323
|
p.log.success(message);
|
|
201
324
|
}
|
|
202
|
-
function
|
|
203
|
-
p.log.error(
|
|
325
|
+
function error2(message) {
|
|
326
|
+
p.log.error(pc2.red(message));
|
|
204
327
|
}
|
|
205
328
|
function warn(message) {
|
|
206
|
-
p.log.warn(
|
|
329
|
+
p.log.warn(pc2.yellow(message));
|
|
207
330
|
}
|
|
208
331
|
function info(message) {
|
|
209
332
|
p.log.info(message);
|
|
@@ -227,14 +350,14 @@ var attach_default = defineCommand({
|
|
|
227
350
|
},
|
|
228
351
|
run: async ({ args }) => {
|
|
229
352
|
if (!isDockerRunning()) {
|
|
230
|
-
|
|
353
|
+
error2("Docker is not running.");
|
|
231
354
|
return process.exit(1);
|
|
232
355
|
}
|
|
233
356
|
let id = args.id;
|
|
234
357
|
if (!id) {
|
|
235
358
|
const containers = listContainers().filter((c) => c.status === "running");
|
|
236
359
|
if (containers.length === 0) {
|
|
237
|
-
|
|
360
|
+
error2("No running yolobox containers found.");
|
|
238
361
|
return process.exit(1);
|
|
239
362
|
}
|
|
240
363
|
if (containers.length === 1) {
|
|
@@ -255,13 +378,13 @@ var attach_default = defineCommand({
|
|
|
255
378
|
const containers = listContainers();
|
|
256
379
|
const match = containers.find((c) => c.id === id);
|
|
257
380
|
if (!match) {
|
|
258
|
-
|
|
381
|
+
error2(`No yolobox container found with ID "${id}".`);
|
|
259
382
|
return process.exit(1);
|
|
260
383
|
}
|
|
261
384
|
if (match.status !== "running") {
|
|
262
385
|
info(`Restarting stopped container "${id}"...`);
|
|
263
386
|
if (!restartContainer(id)) {
|
|
264
|
-
|
|
387
|
+
error2(`Failed to restart container "${id}".`);
|
|
265
388
|
return process.exit(1);
|
|
266
389
|
}
|
|
267
390
|
}
|
|
@@ -281,17 +404,17 @@ import {
|
|
|
281
404
|
mkdirSync,
|
|
282
405
|
readFileSync,
|
|
283
406
|
unlinkSync,
|
|
284
|
-
writeFileSync
|
|
407
|
+
writeFileSync as writeFileSync2
|
|
285
408
|
} from "fs";
|
|
286
409
|
import { homedir } from "os";
|
|
287
|
-
import { join as
|
|
410
|
+
import { join as join3 } from "path";
|
|
288
411
|
var AUTH_DIR_NAME = ".yolobox";
|
|
289
412
|
var AUTH_FILE_NAME = "auth.json";
|
|
290
413
|
function getAuthDir() {
|
|
291
|
-
return
|
|
414
|
+
return join3(homedir(), AUTH_DIR_NAME);
|
|
292
415
|
}
|
|
293
416
|
function getAuthFilePath() {
|
|
294
|
-
return
|
|
417
|
+
return join3(getAuthDir(), AUTH_FILE_NAME);
|
|
295
418
|
}
|
|
296
419
|
function saveToken(token) {
|
|
297
420
|
const dir = getAuthDir();
|
|
@@ -299,7 +422,7 @@ function saveToken(token) {
|
|
|
299
422
|
mkdirSync(dir, { mode: 448, recursive: true });
|
|
300
423
|
}
|
|
301
424
|
const data = JSON.stringify({ claudeOauthToken: token }, null, 2);
|
|
302
|
-
|
|
425
|
+
writeFileSync2(getAuthFilePath(), `${data}
|
|
303
426
|
`, { mode: 384 });
|
|
304
427
|
}
|
|
305
428
|
function loadToken() {
|
|
@@ -380,7 +503,7 @@ var auth_default = defineCommand2({
|
|
|
380
503
|
if (args.token) {
|
|
381
504
|
const token = args.token;
|
|
382
505
|
if (!isValidToken(token)) {
|
|
383
|
-
|
|
506
|
+
error2(
|
|
384
507
|
'Invalid token. Expected a token starting with "sk-ant-".\nRun `claude setup-token` to generate a valid token.'
|
|
385
508
|
);
|
|
386
509
|
process.exit(1);
|
|
@@ -393,7 +516,7 @@ var auth_default = defineCommand2({
|
|
|
393
516
|
const envToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
394
517
|
if (envToken) {
|
|
395
518
|
if (!isValidToken(envToken)) {
|
|
396
|
-
|
|
519
|
+
error2(
|
|
397
520
|
"CLAUDE_CODE_OAUTH_TOKEN is set but does not look like a valid token."
|
|
398
521
|
);
|
|
399
522
|
process.exit(1);
|
|
@@ -468,7 +591,7 @@ function getRepoRoot() {
|
|
|
468
591
|
return exec("git rev-parse --show-toplevel");
|
|
469
592
|
}
|
|
470
593
|
function getGitDir() {
|
|
471
|
-
return exec("git rev-parse --git-dir");
|
|
594
|
+
return exec("git rev-parse --absolute-git-dir");
|
|
472
595
|
}
|
|
473
596
|
function getBranches() {
|
|
474
597
|
const output = exec('git branch --list --format="%(refname:short)"');
|
|
@@ -1287,8 +1410,11 @@ function getExistingWorktreeIds(repoRoot) {
|
|
|
1287
1410
|
// src/lib/container-setup.ts
|
|
1288
1411
|
async function setupContainer(options = {}) {
|
|
1289
1412
|
intro2();
|
|
1413
|
+
if (isEnabled()) {
|
|
1414
|
+
info(`Debug log: ${getLogPath()}`);
|
|
1415
|
+
}
|
|
1290
1416
|
if (!isDockerRunning()) {
|
|
1291
|
-
|
|
1417
|
+
error2("Docker is not running. Start Docker and try again.");
|
|
1292
1418
|
process.exit(1);
|
|
1293
1419
|
}
|
|
1294
1420
|
if (!isInsideGitRepo()) {
|
|
@@ -1296,7 +1422,7 @@ async function setupContainer(options = {}) {
|
|
|
1296
1422
|
message: "No git repo found. Initialize one here?"
|
|
1297
1423
|
});
|
|
1298
1424
|
if (p.isCancel(shouldInit) || !shouldInit) {
|
|
1299
|
-
|
|
1425
|
+
error2("yolobox needs a git repo for worktrees.");
|
|
1300
1426
|
process.exit(1);
|
|
1301
1427
|
}
|
|
1302
1428
|
initRepo();
|
|
@@ -1308,6 +1434,8 @@ async function setupContainer(options = {}) {
|
|
|
1308
1434
|
}
|
|
1309
1435
|
const repoRoot = getRepoRoot();
|
|
1310
1436
|
const gitDir = getGitDir();
|
|
1437
|
+
log(`Repo root: ${repoRoot}`);
|
|
1438
|
+
log(`Git dir: ${gitDir}`);
|
|
1311
1439
|
let id;
|
|
1312
1440
|
if (options.name) {
|
|
1313
1441
|
id = options.name;
|
|
@@ -1321,7 +1449,7 @@ async function setupContainer(options = {}) {
|
|
|
1321
1449
|
const containers = listContainers();
|
|
1322
1450
|
const existing = containers.find((c) => c.id === id);
|
|
1323
1451
|
if (existing && existing.status === "running") {
|
|
1324
|
-
|
|
1452
|
+
error2(
|
|
1325
1453
|
`Container "${id}" is already running. Use "yolobox attach ${id}" to connect.`
|
|
1326
1454
|
);
|
|
1327
1455
|
process.exit(1);
|
|
@@ -1344,9 +1472,13 @@ async function setupContainer(options = {}) {
|
|
|
1344
1472
|
}
|
|
1345
1473
|
ensureGitignore(repoRoot);
|
|
1346
1474
|
const gitIdentity = getGitIdentity();
|
|
1475
|
+
log("Resolving Docker image...");
|
|
1347
1476
|
const imageResolution = resolveDockerImage({
|
|
1348
1477
|
envImage: process.env.YOLOBOX_IMAGE
|
|
1349
1478
|
});
|
|
1479
|
+
log(
|
|
1480
|
+
`Resolved image: ${imageResolution.image} (source: ${imageResolution.source})`
|
|
1481
|
+
);
|
|
1350
1482
|
if (imageResolution.source === "env") {
|
|
1351
1483
|
info(`Using custom Docker image: ${imageResolution.image}`);
|
|
1352
1484
|
} else if (imageResolution.source === "local") {
|
|
@@ -1366,17 +1498,35 @@ async function setupContainer(options = {}) {
|
|
|
1366
1498
|
);
|
|
1367
1499
|
if (!pulled) {
|
|
1368
1500
|
spinner.stop("Failed to pull image", 1);
|
|
1369
|
-
|
|
1501
|
+
error2("Failed to pull Docker image.");
|
|
1370
1502
|
process.exit(1);
|
|
1371
1503
|
}
|
|
1372
1504
|
spinner.stop("Docker image pulled");
|
|
1373
1505
|
}
|
|
1506
|
+
const testFile = path2.join(gitDir, "HEAD");
|
|
1507
|
+
if (!canDockerAccessPath(testFile, imageResolution.image)) {
|
|
1508
|
+
error2("Docker cannot access files in this directory.");
|
|
1509
|
+
if (process.platform === "darwin" && repoRoot.includes("/Library/Mobile Documents/")) {
|
|
1510
|
+
error2(
|
|
1511
|
+
'This repo is in iCloud Drive. Docker Desktop needs "Full Disk Access" to read these files.\n \u2192 Open System Settings > Privacy & Security > Full Disk Access\n \u2192 Enable Docker Desktop, then restart Docker.'
|
|
1512
|
+
);
|
|
1513
|
+
} else if (process.platform === "darwin") {
|
|
1514
|
+
error2(
|
|
1515
|
+
'Docker Desktop may need "Full Disk Access" to read files in this location.\n \u2192 Open System Settings > Privacy & Security > Full Disk Access\n \u2192 Enable Docker Desktop, then restart Docker.'
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
process.exit(1);
|
|
1519
|
+
}
|
|
1374
1520
|
const claudeOauthToken = resolveToken();
|
|
1375
1521
|
if (claudeOauthToken) {
|
|
1376
1522
|
success("Claude auth token configured");
|
|
1377
1523
|
} else {
|
|
1378
1524
|
warn('No Claude auth token. Run "yolobox auth" to set up.');
|
|
1379
1525
|
}
|
|
1526
|
+
log(`Starting container with id=${id}, image=${imageResolution.image}`);
|
|
1527
|
+
log(`Worktree path: ${worktreePath}`);
|
|
1528
|
+
log(`Git identity: ${gitIdentity.name} <${gitIdentity.email}>`);
|
|
1529
|
+
log(`Claude token: ${claudeOauthToken ? "set" : "not set"}`);
|
|
1380
1530
|
const started = startContainer({
|
|
1381
1531
|
id,
|
|
1382
1532
|
worktreePath,
|
|
@@ -1387,7 +1537,13 @@ async function setupContainer(options = {}) {
|
|
|
1387
1537
|
claudeOauthToken: claudeOauthToken ?? void 0
|
|
1388
1538
|
});
|
|
1389
1539
|
if (!started) {
|
|
1390
|
-
|
|
1540
|
+
if (isEnabled()) {
|
|
1541
|
+
error2(
|
|
1542
|
+
`Failed to start container. Check ${getLogPath()} for details.`
|
|
1543
|
+
);
|
|
1544
|
+
} else {
|
|
1545
|
+
error2("Failed to start container. Run with --debug for details.");
|
|
1546
|
+
}
|
|
1391
1547
|
process.exit(1);
|
|
1392
1548
|
}
|
|
1393
1549
|
return { id, repoRoot };
|
|
@@ -1453,14 +1609,14 @@ var kill_default = defineCommand5({
|
|
|
1453
1609
|
},
|
|
1454
1610
|
run: async ({ args }) => {
|
|
1455
1611
|
if (!isDockerRunning()) {
|
|
1456
|
-
|
|
1612
|
+
error2("Docker is not running.");
|
|
1457
1613
|
return process.exit(1);
|
|
1458
1614
|
}
|
|
1459
1615
|
let id = args.id;
|
|
1460
1616
|
if (!id) {
|
|
1461
1617
|
const containers = listContainers();
|
|
1462
1618
|
if (containers.length === 0) {
|
|
1463
|
-
|
|
1619
|
+
error2("No yolobox containers found.");
|
|
1464
1620
|
return process.exit(1);
|
|
1465
1621
|
}
|
|
1466
1622
|
const selected = await p.select({
|
|
@@ -1486,13 +1642,13 @@ var kill_default = defineCommand5({
|
|
|
1486
1642
|
const containers = listContainers();
|
|
1487
1643
|
const match = containers.find((c) => c.id === id);
|
|
1488
1644
|
if (!match) {
|
|
1489
|
-
|
|
1645
|
+
error2(`No yolobox container found with ID "${id}".`);
|
|
1490
1646
|
return process.exit(1);
|
|
1491
1647
|
}
|
|
1492
1648
|
}
|
|
1493
1649
|
const killed = killContainer(id);
|
|
1494
1650
|
if (!killed) {
|
|
1495
|
-
|
|
1651
|
+
error2(`Failed to kill yolobox-${id}. Is it running?`);
|
|
1496
1652
|
process.exit(1);
|
|
1497
1653
|
}
|
|
1498
1654
|
success(`Killed yolobox-${id}`);
|
|
@@ -1522,7 +1678,7 @@ var ls_default = defineCommand6({
|
|
|
1522
1678
|
},
|
|
1523
1679
|
run: async () => {
|
|
1524
1680
|
if (!isDockerRunning()) {
|
|
1525
|
-
|
|
1681
|
+
error2("Docker is not running.");
|
|
1526
1682
|
process.exit(1);
|
|
1527
1683
|
}
|
|
1528
1684
|
const containers = listContainers();
|
|
@@ -1536,12 +1692,12 @@ var ls_default = defineCommand6({
|
|
|
1536
1692
|
const statusW = Math.max(8, ...containers.map((c) => c.status.length)) + 2;
|
|
1537
1693
|
const createdW = Math.max(9, ...containers.map((c) => c.created.length)) + 2;
|
|
1538
1694
|
const header = `${"ID".padEnd(idW)}${"BRANCH".padEnd(branchW)}${"STATUS".padEnd(statusW)}${"CREATED".padEnd(createdW)}PATH`;
|
|
1539
|
-
console.log(
|
|
1695
|
+
console.log(pc2.dim(header));
|
|
1540
1696
|
for (let i = 0; i < containers.length; i++) {
|
|
1541
1697
|
const c = containers[i];
|
|
1542
|
-
const statusColor = c.status === "running" ?
|
|
1698
|
+
const statusColor = c.status === "running" ? pc2.green : pc2.yellow;
|
|
1543
1699
|
console.log(
|
|
1544
|
-
`${c.id.padEnd(idW)}${c.branch.padEnd(branchW)}${statusColor(c.status.padEnd(statusW))}${
|
|
1700
|
+
`${c.id.padEnd(idW)}${c.branch.padEnd(branchW)}${statusColor(c.status.padEnd(statusW))}${pc2.dim(c.created.padEnd(createdW))}${pc2.dim(paths[i])}`
|
|
1545
1701
|
);
|
|
1546
1702
|
}
|
|
1547
1703
|
}
|
|
@@ -1604,7 +1760,7 @@ var nuke_default = defineCommand7({
|
|
|
1604
1760
|
const targets = [...targetMap.values()];
|
|
1605
1761
|
if (targets.length === 0) {
|
|
1606
1762
|
if (!dockerRunning) {
|
|
1607
|
-
|
|
1763
|
+
error2(
|
|
1608
1764
|
"Docker is not running and no local branches or worktrees found."
|
|
1609
1765
|
);
|
|
1610
1766
|
} else {
|
|
@@ -1622,18 +1778,18 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
|
|
|
1622
1778
|
for (const target of targets) {
|
|
1623
1779
|
const parts = [];
|
|
1624
1780
|
if (target.container) {
|
|
1625
|
-
const statusColor = target.container.status === "running" ?
|
|
1781
|
+
const statusColor = target.container.status === "running" ? pc2.green : pc2.dim;
|
|
1626
1782
|
parts.push(`container ${statusColor(target.container.status)}`);
|
|
1627
1783
|
}
|
|
1628
1784
|
if (target.hasBranch) {
|
|
1629
|
-
parts.push(`branch ${
|
|
1785
|
+
parts.push(`branch ${pc2.cyan(`yolo/${target.id}`)}`);
|
|
1630
1786
|
}
|
|
1631
1787
|
if (target.hasWorktree) {
|
|
1632
1788
|
parts.push("worktree");
|
|
1633
1789
|
}
|
|
1634
|
-
const pathSuffix = args.all && target.container ? ` ${
|
|
1790
|
+
const pathSuffix = args.all && target.container ? ` ${pc2.dim(target.container.path)}` : "";
|
|
1635
1791
|
console.log(
|
|
1636
|
-
` ${
|
|
1792
|
+
` ${pc2.bold(target.id)} ${parts.join(pc2.dim(" \xB7 "))}${pathSuffix}`
|
|
1637
1793
|
);
|
|
1638
1794
|
}
|
|
1639
1795
|
console.log();
|
|
@@ -1676,10 +1832,10 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
|
|
|
1676
1832
|
if (!target.container) continue;
|
|
1677
1833
|
const success2 = killContainer(target.id);
|
|
1678
1834
|
if (success2) {
|
|
1679
|
-
success(`Killed container ${
|
|
1835
|
+
success(`Killed container ${pc2.bold(target.id)}`);
|
|
1680
1836
|
containersKilled++;
|
|
1681
1837
|
} else {
|
|
1682
|
-
|
|
1838
|
+
error2(`Failed to kill container ${target.id}`);
|
|
1683
1839
|
killFailures++;
|
|
1684
1840
|
}
|
|
1685
1841
|
}
|
|
@@ -1692,7 +1848,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
|
|
|
1692
1848
|
const removed = removeWorktree(repoRoot, target.id);
|
|
1693
1849
|
if (removed) {
|
|
1694
1850
|
success(
|
|
1695
|
-
`Removed worktree ${
|
|
1851
|
+
`Removed worktree ${pc2.bold(`.yolobox/${target.id}`)}`
|
|
1696
1852
|
);
|
|
1697
1853
|
worktreesRemoved++;
|
|
1698
1854
|
} else {
|
|
@@ -1703,7 +1859,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
|
|
|
1703
1859
|
const branchName = `yolo/${target.id}`;
|
|
1704
1860
|
const deleted = deleteBranch(branchName);
|
|
1705
1861
|
if (deleted) {
|
|
1706
|
-
success(`Deleted branch ${
|
|
1862
|
+
success(`Deleted branch ${pc2.bold(branchName)}`);
|
|
1707
1863
|
branchesDeleted++;
|
|
1708
1864
|
} else {
|
|
1709
1865
|
warn(`Could not delete branch ${branchName}`);
|
|
@@ -1733,7 +1889,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
|
|
|
1733
1889
|
summaryParts.push(
|
|
1734
1890
|
`${killFailures} container${s(killFailures)} failed to stop`
|
|
1735
1891
|
);
|
|
1736
|
-
|
|
1892
|
+
error2(`${summaryParts.join(", ")}.`);
|
|
1737
1893
|
process.exit(1);
|
|
1738
1894
|
} else {
|
|
1739
1895
|
const msg = summaryParts.join(", ");
|
|
@@ -1759,7 +1915,7 @@ var rm_default = defineCommand8({
|
|
|
1759
1915
|
},
|
|
1760
1916
|
run: async ({ args }) => {
|
|
1761
1917
|
if (!isDockerRunning()) {
|
|
1762
|
-
|
|
1918
|
+
error2("Docker is not running.");
|
|
1763
1919
|
return process.exit(1);
|
|
1764
1920
|
}
|
|
1765
1921
|
const repoRoot = getRepoRoot();
|
|
@@ -1767,7 +1923,7 @@ var rm_default = defineCommand8({
|
|
|
1767
1923
|
if (!id) {
|
|
1768
1924
|
const containers = listContainers();
|
|
1769
1925
|
if (containers.length === 0) {
|
|
1770
|
-
|
|
1926
|
+
error2("No yolobox containers found.");
|
|
1771
1927
|
return process.exit(1);
|
|
1772
1928
|
}
|
|
1773
1929
|
const selected = await p.select({
|
|
@@ -1794,7 +1950,7 @@ var rm_default = defineCommand8({
|
|
|
1794
1950
|
const hasWorktree = getExistingWorktreeIds(repoRoot).includes(id);
|
|
1795
1951
|
const hasBranch = getBranches().includes(`yolo/${id}`);
|
|
1796
1952
|
if (!hasContainer && !hasWorktree && !hasBranch) {
|
|
1797
|
-
|
|
1953
|
+
error2(`No yolobox found with ID "${id}".`);
|
|
1798
1954
|
return process.exit(1);
|
|
1799
1955
|
}
|
|
1800
1956
|
}
|
|
@@ -1831,7 +1987,7 @@ var start_default = defineCommand9({
|
|
|
1831
1987
|
run: async ({ args }) => {
|
|
1832
1988
|
if (args.name) {
|
|
1833
1989
|
if (!isDockerRunning()) {
|
|
1834
|
-
|
|
1990
|
+
error2("Docker is not running.");
|
|
1835
1991
|
return process.exit(1);
|
|
1836
1992
|
}
|
|
1837
1993
|
const containers = listContainers();
|
|
@@ -1844,7 +2000,9 @@ var start_default = defineCommand9({
|
|
|
1844
2000
|
}
|
|
1845
2001
|
info(`Restarting stopped container "${args.name}"...`);
|
|
1846
2002
|
if (!restartContainer(args.name)) {
|
|
1847
|
-
|
|
2003
|
+
error2(
|
|
2004
|
+
`Failed to restart container "${args.name}". Run with --debug for details.`
|
|
2005
|
+
);
|
|
1848
2006
|
return process.exit(1);
|
|
1849
2007
|
}
|
|
1850
2008
|
outro2(`Launching shell in ${args.name}...`);
|
|
@@ -1861,10 +2019,21 @@ var start_default = defineCommand9({
|
|
|
1861
2019
|
});
|
|
1862
2020
|
|
|
1863
2021
|
// src/index.ts
|
|
2022
|
+
var debugIndex = process.argv.indexOf("--debug");
|
|
2023
|
+
if (debugIndex !== -1) {
|
|
2024
|
+
process.argv.splice(debugIndex, 1);
|
|
2025
|
+
const logPath = enable();
|
|
2026
|
+
log(`yolobox v${"0.1.5"}`);
|
|
2027
|
+
log(`args: ${process.argv.slice(2).join(" ")}`);
|
|
2028
|
+
log(`cwd: ${process.cwd()}`);
|
|
2029
|
+
log(`node: ${process.version}`);
|
|
2030
|
+
log(`platform: ${process.platform} ${process.arch}`);
|
|
2031
|
+
log(`log file: ${logPath}`);
|
|
2032
|
+
}
|
|
1864
2033
|
var main = defineCommand10({
|
|
1865
2034
|
meta: {
|
|
1866
2035
|
name: "yolobox",
|
|
1867
|
-
version: "0.1.
|
|
2036
|
+
version: "0.1.5",
|
|
1868
2037
|
description: "Run Claude Code in Docker containers. YOLO safely."
|
|
1869
2038
|
},
|
|
1870
2039
|
subCommands: {
|