@jango-blockchained/hoox-cli 0.3.4
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 +403 -0
- package/bin/hoox.js +12 -0
- package/package.json +60 -0
- package/src/commands/check/check-command.test.ts +468 -0
- package/src/commands/check/check-command.ts +1144 -0
- package/src/commands/check/index.ts +10 -0
- package/src/commands/check/prerequisites-command.test.ts +19 -0
- package/src/commands/check/prerequisites-command.ts +92 -0
- package/src/commands/check/types.ts +103 -0
- package/src/commands/clone/clone-command.test.ts +442 -0
- package/src/commands/clone/clone-command.ts +440 -0
- package/src/commands/clone/index.ts +1 -0
- package/src/commands/config/config-command.test.ts +583 -0
- package/src/commands/config/config-command.ts +901 -0
- package/src/commands/config/env-command.test.ts +43 -0
- package/src/commands/config/env-command.ts +314 -0
- package/src/commands/config/index.ts +3 -0
- package/src/commands/config/kv-command.test.ts +14 -0
- package/src/commands/config/kv-command.ts +329 -0
- package/src/commands/dashboard/dashboard-command.test.ts +47 -0
- package/src/commands/dashboard/dashboard-command.ts +127 -0
- package/src/commands/dashboard/index.ts +1 -0
- package/src/commands/db/db-command.test.ts +21 -0
- package/src/commands/db/db-command.ts +314 -0
- package/src/commands/db/index.ts +1 -0
- package/src/commands/deploy/deploy-command.test.ts +304 -0
- package/src/commands/deploy/deploy-command.ts +1053 -0
- package/src/commands/deploy/index.ts +2 -0
- package/src/commands/deploy/telegram-service.ts +61 -0
- package/src/commands/deploy/types.ts +34 -0
- package/src/commands/dev/dev-command.test.ts +383 -0
- package/src/commands/dev/dev-command.ts +407 -0
- package/src/commands/dev/index.ts +1 -0
- package/src/commands/infra/index.ts +5 -0
- package/src/commands/infra/infra-command.test.ts +719 -0
- package/src/commands/infra/infra-command.ts +940 -0
- package/src/commands/infra/types.ts +23 -0
- package/src/commands/init/index.ts +1 -0
- package/src/commands/init/init-command.test.ts +827 -0
- package/src/commands/init/init-command.ts +627 -0
- package/src/commands/init/types.ts +185 -0
- package/src/commands/monitor/index.ts +2 -0
- package/src/commands/monitor/monitor-command.test.ts +235 -0
- package/src/commands/monitor/monitor-command.ts +245 -0
- package/src/commands/monitor/monitor-service.ts +50 -0
- package/src/commands/monitor/types.ts +13 -0
- package/src/commands/repair/index.ts +2 -0
- package/src/commands/repair/repair-command.test.ts +204 -0
- package/src/commands/repair/repair-command.ts +199 -0
- package/src/commands/repair/repair-service.ts +102 -0
- package/src/commands/repair/types.ts +13 -0
- package/src/commands/test/index.ts +2 -0
- package/src/commands/test/test-command.test.ts +319 -0
- package/src/commands/test/test-command.ts +412 -0
- package/src/commands/waf/index.ts +2 -0
- package/src/commands/waf/types.ts +48 -0
- package/src/commands/waf/waf-command.test.ts +506 -0
- package/src/commands/waf/waf-command.ts +548 -0
- package/src/index.ts +198 -0
- package/src/services/cloudflare/cloudflare-service.test.ts +654 -0
- package/src/services/cloudflare/cloudflare-service.ts +435 -0
- package/src/services/cloudflare/index.ts +2 -0
- package/src/services/cloudflare/types.ts +29 -0
- package/src/services/config/config-service.test.ts +395 -0
- package/src/services/config/config-service.ts +207 -0
- package/src/services/config/index.ts +9 -0
- package/src/services/config/types.ts +66 -0
- package/src/services/db/db-service.test.ts +51 -0
- package/src/services/db/db-service.ts +140 -0
- package/src/services/db/index.ts +1 -0
- package/src/services/docker/docker-service.ts +155 -0
- package/src/services/docker/index.ts +1 -0
- package/src/services/env/env-service.test.ts +210 -0
- package/src/services/env/env-service.ts +156 -0
- package/src/services/env/index.ts +1 -0
- package/src/services/kv/index.ts +1 -0
- package/src/services/kv/kv-sync-service.test.ts +38 -0
- package/src/services/kv/kv-sync-service.ts +151 -0
- package/src/services/prerequisites/index.ts +1 -0
- package/src/services/prerequisites/prerequisites-service.test.ts +89 -0
- package/src/services/prerequisites/prerequisites-service.ts +269 -0
- package/src/services/prerequisites/types.ts +48 -0
- package/src/services/secrets/index.ts +12 -0
- package/src/services/secrets/secrets-service.test.ts +486 -0
- package/src/services/secrets/secrets-service.ts +293 -0
- package/src/services/secrets/types.ts +57 -0
- package/src/ui/banner.ts +52 -0
- package/src/ui/index.ts +8 -0
- package/src/ui/menu.ts +473 -0
- package/src/utils/errors.test.ts +69 -0
- package/src/utils/errors.ts +23 -0
- package/src/utils/formatters.test.ts +180 -0
- package/src/utils/formatters.ts +252 -0
- package/src/utils/theme.ts +94 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hoox clone` command — clone worker repos as git submodules.
|
|
3
|
+
*
|
|
4
|
+
* Modes:
|
|
5
|
+
* hoox clone — List workers with clone status (cloned / not cloned)
|
|
6
|
+
* hoox clone --all — Clone all workers from wrangler.jsonc
|
|
7
|
+
* hoox clone <name> — Clone a specific worker by name
|
|
8
|
+
*
|
|
9
|
+
* Repo URLs are derived from: https://github.com/<org>/<worker-name>.git
|
|
10
|
+
* The org is auto-detected from `git remote get-url origin` or set via --org.
|
|
11
|
+
*
|
|
12
|
+
* Uses @clack/prompts spinner for progress feedback.
|
|
13
|
+
*/
|
|
14
|
+
import { Command } from "commander";
|
|
15
|
+
import { spinner } from "@clack/prompts";
|
|
16
|
+
import { ConfigService } from "../../services/config/index.js";
|
|
17
|
+
import { icons } from "../../utils/theme.js";
|
|
18
|
+
import {
|
|
19
|
+
formatSuccess,
|
|
20
|
+
formatError,
|
|
21
|
+
formatTable,
|
|
22
|
+
formatJson,
|
|
23
|
+
} from "../../utils/formatters.js";
|
|
24
|
+
import { CLIError, ExitCode } from "../../utils/errors.js";
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Types
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
/** Per-worker clone status entry. */
|
|
31
|
+
interface CloneStatus {
|
|
32
|
+
worker: string;
|
|
33
|
+
path: string;
|
|
34
|
+
cloned: boolean;
|
|
35
|
+
repo?: string;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Helpers
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
/** Extract --json / --quiet from the global program options. */
|
|
44
|
+
function getFormatOptions(cmd: Command) {
|
|
45
|
+
const opts = cmd.optsWithGlobals();
|
|
46
|
+
return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the base GitHub URL for worker repos.
|
|
51
|
+
* Tries `git remote get-url origin` first, falls back to a sensible default.
|
|
52
|
+
*
|
|
53
|
+
* Examples:
|
|
54
|
+
* https://github.com/org/repo.git → https://github.com/org
|
|
55
|
+
* git@github.com:org/repo.git → https://github.com/org
|
|
56
|
+
*/
|
|
57
|
+
async function resolveRepoBase(): Promise<string> {
|
|
58
|
+
try {
|
|
59
|
+
const proc = Bun.spawn(["git", "remote", "get-url", "origin"], {
|
|
60
|
+
stdout: "pipe",
|
|
61
|
+
stderr: "pipe",
|
|
62
|
+
});
|
|
63
|
+
const exitCode = await proc.exited;
|
|
64
|
+
if (exitCode === 0) {
|
|
65
|
+
const url = (await new Response(proc.stdout).text()).trim();
|
|
66
|
+
const match = url.match(/github\.com[:/]([^/]+)\/.+?(?:\.git)?$/);
|
|
67
|
+
if (match) {
|
|
68
|
+
return `https://github.com/${match[1]}`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Fall through to default
|
|
73
|
+
}
|
|
74
|
+
return "https://github.com/jango-blockchained";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Build a repo URL from the org base and worker name. */
|
|
78
|
+
function getRepoUrl(orgBase: string, workerName: string): string {
|
|
79
|
+
return `${orgBase}/${workerName}.git`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Check whether a worker is already cloned (has a `.git` entry).
|
|
84
|
+
* For git submodules this is a file (not a directory) pointing to the
|
|
85
|
+
* superproject's modules directory.
|
|
86
|
+
*/
|
|
87
|
+
async function isWorkerCloned(workerPath: string): Promise<boolean> {
|
|
88
|
+
const dotGit = Bun.file(`${workerPath}/.git`);
|
|
89
|
+
return dotGit.exists();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Run `git submodule add <repo> <path>` to clone a single worker.
|
|
94
|
+
* Returns a CloneStatus with the outcome.
|
|
95
|
+
*/
|
|
96
|
+
async function cloneWorker(
|
|
97
|
+
repoUrl: string,
|
|
98
|
+
targetPath: string,
|
|
99
|
+
name: string,
|
|
100
|
+
cwd: string
|
|
101
|
+
): Promise<CloneStatus> {
|
|
102
|
+
const proc = Bun.spawn(["git", "submodule", "add", repoUrl, targetPath], {
|
|
103
|
+
cwd,
|
|
104
|
+
stdout: "pipe",
|
|
105
|
+
stderr: "pipe",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const exitCode = await proc.exited;
|
|
109
|
+
|
|
110
|
+
if (exitCode !== 0) {
|
|
111
|
+
const stderr = await new Response(proc.stderr).text();
|
|
112
|
+
return {
|
|
113
|
+
worker: name,
|
|
114
|
+
path: targetPath,
|
|
115
|
+
cloned: false,
|
|
116
|
+
repo: repoUrl,
|
|
117
|
+
error:
|
|
118
|
+
stderr.trim() || `git submodule add failed with exit code ${exitCode}`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
worker: name,
|
|
124
|
+
path: targetPath,
|
|
125
|
+
cloned: true,
|
|
126
|
+
repo: repoUrl,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Run `git submodule update --init --recursive`. */
|
|
131
|
+
async function updateSubmodules(cwd: string): Promise<void> {
|
|
132
|
+
const proc = Bun.spawn(
|
|
133
|
+
["git", "submodule", "update", "--init", "--recursive"],
|
|
134
|
+
{
|
|
135
|
+
cwd,
|
|
136
|
+
stdout: "pipe",
|
|
137
|
+
stderr: "pipe",
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const exitCode = await proc.exited;
|
|
142
|
+
|
|
143
|
+
if (exitCode !== 0) {
|
|
144
|
+
const stderr = await new Response(proc.stderr).text();
|
|
145
|
+
throw new Error(
|
|
146
|
+
stderr.trim() || `git submodule update failed with exit code ${exitCode}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Build the list of worker clone statuses, checking the filesystem for
|
|
153
|
+
* whether each worker has already been cloned.
|
|
154
|
+
*/
|
|
155
|
+
async function buildStatusList(
|
|
156
|
+
configService: ConfigService,
|
|
157
|
+
repoBase: string
|
|
158
|
+
): Promise<CloneStatus[]> {
|
|
159
|
+
const workers = configService.listWorkers();
|
|
160
|
+
const statuses: CloneStatus[] = [];
|
|
161
|
+
|
|
162
|
+
for (const name of workers) {
|
|
163
|
+
const workerConfig = configService.getWorker(name);
|
|
164
|
+
if (!workerConfig) continue;
|
|
165
|
+
|
|
166
|
+
const cloned = await isWorkerCloned(workerConfig.path);
|
|
167
|
+
statuses.push({
|
|
168
|
+
worker: name,
|
|
169
|
+
path: workerConfig.path,
|
|
170
|
+
cloned,
|
|
171
|
+
repo: getRepoUrl(repoBase, name),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return statuses;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Command registration
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Register the `hoox clone` command on the given commander program.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* import { registerCloneCommand } from "./commands/clone/index.js";
|
|
188
|
+
* registerCloneCommand(program);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export function registerCloneCommand(program: Command): void {
|
|
192
|
+
program
|
|
193
|
+
.command("clone")
|
|
194
|
+
.summary("Clone worker repositories as git submodules")
|
|
195
|
+
.description(
|
|
196
|
+
`Clone worker repositories from GitHub as git submodules.
|
|
197
|
+
|
|
198
|
+
Workers in the Hoox monorepo are stored as git submodules. This command helps you clone them.
|
|
199
|
+
|
|
200
|
+
MODES:
|
|
201
|
+
hoox clone List clone status of all workers
|
|
202
|
+
hoox clone --all Clone all workers
|
|
203
|
+
hoox clone <name> Clone a specific worker
|
|
204
|
+
|
|
205
|
+
OPTIONS:
|
|
206
|
+
--all Clone all worker repositories
|
|
207
|
+
--org <org> GitHub organization (auto-detected from git remote by default)
|
|
208
|
+
|
|
209
|
+
ARGUMENTS:
|
|
210
|
+
name Optional worker name to clone (e.g., trade-worker, agent-worker)
|
|
211
|
+
|
|
212
|
+
EXAMPLES:
|
|
213
|
+
hoox clone List all workers' clone status
|
|
214
|
+
hoox clone --all Clone all workers
|
|
215
|
+
hoox clone trade-worker Clone specific worker
|
|
216
|
+
hoox clone --org my-org Clone from specific org`
|
|
217
|
+
)
|
|
218
|
+
.option("--all", "Clone all worker repositories")
|
|
219
|
+
.option(
|
|
220
|
+
"--org <org>",
|
|
221
|
+
"GitHub organization (derived from git remote by default)"
|
|
222
|
+
)
|
|
223
|
+
.argument("[name]", "Worker name to clone (omit to list status)")
|
|
224
|
+
.action(
|
|
225
|
+
async (
|
|
226
|
+
name: string | undefined,
|
|
227
|
+
options: { all?: boolean; org?: string }
|
|
228
|
+
) => {
|
|
229
|
+
const fmt = getFormatOptions(program);
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const configService = new ConfigService();
|
|
233
|
+
await configService.load();
|
|
234
|
+
|
|
235
|
+
const workers = configService.listWorkers();
|
|
236
|
+
|
|
237
|
+
// -------------------------------------------------------------------
|
|
238
|
+
// Mode 1: No name, no --all → list clone status
|
|
239
|
+
// -------------------------------------------------------------------
|
|
240
|
+
if (!options.all && !name) {
|
|
241
|
+
const repoBase = options.org
|
|
242
|
+
? `https://github.com/${options.org}`
|
|
243
|
+
: await resolveRepoBase();
|
|
244
|
+
|
|
245
|
+
const statuses = await buildStatusList(configService, repoBase);
|
|
246
|
+
|
|
247
|
+
if (fmt.json) {
|
|
248
|
+
formatJson(statuses, fmt);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (fmt.quiet) {
|
|
253
|
+
for (const s of statuses) {
|
|
254
|
+
process.stdout.write(
|
|
255
|
+
`${s.worker}: ${s.cloned ? "cloned" : "not cloned"}\n`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const rows = statuses.map((s) => ({
|
|
262
|
+
Worker: s.worker,
|
|
263
|
+
Status: s.cloned ? "cloned" : "not cloned",
|
|
264
|
+
Repo: s.repo ?? "-",
|
|
265
|
+
}));
|
|
266
|
+
formatTable(rows, fmt);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// -------------------------------------------------------------------
|
|
271
|
+
// Mode 2: --all
|
|
272
|
+
// -------------------------------------------------------------------
|
|
273
|
+
if (options.all) {
|
|
274
|
+
if (workers.length === 0) {
|
|
275
|
+
formatSuccess("No workers defined in wrangler.jsonc", fmt);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const repoBase = options.org
|
|
280
|
+
? `https://github.com/${options.org}`
|
|
281
|
+
: await resolveRepoBase();
|
|
282
|
+
|
|
283
|
+
const s = spinner();
|
|
284
|
+
s.start(`Cloning ${workers.length} worker(s)...`);
|
|
285
|
+
|
|
286
|
+
const results: CloneStatus[] = [];
|
|
287
|
+
|
|
288
|
+
for (let i = 0; i < workers.length; i++) {
|
|
289
|
+
const name = workers[i];
|
|
290
|
+
const workerConfig = configService.getWorker(name);
|
|
291
|
+
if (!workerConfig) continue;
|
|
292
|
+
|
|
293
|
+
// Skip already cloned workers
|
|
294
|
+
const alreadyCloned = await isWorkerCloned(workerConfig.path);
|
|
295
|
+
if (alreadyCloned) {
|
|
296
|
+
s.message(
|
|
297
|
+
`[${i + 1}/${workers.length}] ${icons.info} ${name} already cloned — skipping`
|
|
298
|
+
);
|
|
299
|
+
results.push({
|
|
300
|
+
worker: name,
|
|
301
|
+
path: workerConfig.path,
|
|
302
|
+
cloned: true,
|
|
303
|
+
repo: getRepoUrl(repoBase, name),
|
|
304
|
+
});
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
s.message(`[${i + 1}/${workers.length}] Cloning ${name}...`);
|
|
309
|
+
|
|
310
|
+
const result = await cloneWorker(
|
|
311
|
+
getRepoUrl(repoBase, name),
|
|
312
|
+
workerConfig.path,
|
|
313
|
+
name,
|
|
314
|
+
process.cwd()
|
|
315
|
+
);
|
|
316
|
+
results.push(result);
|
|
317
|
+
|
|
318
|
+
if (result.cloned) {
|
|
319
|
+
s.message(
|
|
320
|
+
`[${i + 1}/${workers.length}] ${icons.success} ${name} cloned`
|
|
321
|
+
);
|
|
322
|
+
} else {
|
|
323
|
+
s.message(
|
|
324
|
+
`[${i + 1}/${workers.length}] ${icons.error} ${name} failed: ${result.error}`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Run submodule update after all clones
|
|
330
|
+
try {
|
|
331
|
+
s.message("Updating submodules...");
|
|
332
|
+
await updateSubmodules(process.cwd());
|
|
333
|
+
} catch (updateErr) {
|
|
334
|
+
const msg =
|
|
335
|
+
updateErr instanceof Error
|
|
336
|
+
? updateErr.message
|
|
337
|
+
: String(updateErr);
|
|
338
|
+
s.message(`Warning: submodule update failed: ${msg}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const succeeded = results.filter((r) => r.cloned).length;
|
|
342
|
+
const failed = results.filter((r) => !r.cloned).length;
|
|
343
|
+
|
|
344
|
+
if (failed > 0) {
|
|
345
|
+
s.stop(
|
|
346
|
+
`Clone complete: ${succeeded} succeeded, ${failed} failed`
|
|
347
|
+
);
|
|
348
|
+
process.exitCode = ExitCode.ERROR;
|
|
349
|
+
} else {
|
|
350
|
+
s.stop(`All ${succeeded} worker(s) cloned successfully`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Print summary table (unless quiet)
|
|
354
|
+
if (!fmt.quiet) {
|
|
355
|
+
const rows = results.map((r) => ({
|
|
356
|
+
Worker: r.worker,
|
|
357
|
+
Status: r.cloned ? "cloned" : "failed",
|
|
358
|
+
Repo: r.repo ?? "-",
|
|
359
|
+
}));
|
|
360
|
+
formatTable(rows, { json: fmt.json, quiet: false });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// -------------------------------------------------------------------
|
|
367
|
+
// Mode 3: <name> — clone a single worker
|
|
368
|
+
// -------------------------------------------------------------------
|
|
369
|
+
if (name) {
|
|
370
|
+
const workerConfig = configService.getWorker(name);
|
|
371
|
+
|
|
372
|
+
if (!workerConfig) {
|
|
373
|
+
formatError(
|
|
374
|
+
new CLIError(
|
|
375
|
+
`Worker "${name}" not found in wrangler.jsonc`,
|
|
376
|
+
ExitCode.ERROR
|
|
377
|
+
),
|
|
378
|
+
fmt
|
|
379
|
+
);
|
|
380
|
+
process.exitCode = ExitCode.ERROR;
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Already cloned → early exit with success
|
|
385
|
+
const alreadyCloned = await isWorkerCloned(workerConfig.path);
|
|
386
|
+
if (alreadyCloned) {
|
|
387
|
+
formatSuccess(
|
|
388
|
+
`Worker "${name}" is already cloned at ${workerConfig.path}`,
|
|
389
|
+
fmt
|
|
390
|
+
);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const repoBase = options.org
|
|
395
|
+
? `https://github.com/${options.org}`
|
|
396
|
+
: await resolveRepoBase();
|
|
397
|
+
|
|
398
|
+
const s = spinner();
|
|
399
|
+
s.start(`Cloning ${name}...`);
|
|
400
|
+
|
|
401
|
+
const result = await cloneWorker(
|
|
402
|
+
getRepoUrl(repoBase, name),
|
|
403
|
+
workerConfig.path,
|
|
404
|
+
name,
|
|
405
|
+
process.cwd()
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
if (result.cloned) {
|
|
409
|
+
// Update submodules after single clone too
|
|
410
|
+
try {
|
|
411
|
+
s.message("Updating submodules...");
|
|
412
|
+
await updateSubmodules(process.cwd());
|
|
413
|
+
} catch {
|
|
414
|
+
// Non-fatal — the clone itself succeeded
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
s.stop(`Successfully cloned ${name}`);
|
|
418
|
+
formatSuccess(`Cloned ${name} from ${result.repo}`, fmt);
|
|
419
|
+
} else {
|
|
420
|
+
s.stop(`Failed to clone ${name}`);
|
|
421
|
+
formatError(
|
|
422
|
+
new CLIError(
|
|
423
|
+
`Failed to clone "${name}": ${result.error}`,
|
|
424
|
+
ExitCode.ERROR
|
|
425
|
+
),
|
|
426
|
+
fmt
|
|
427
|
+
);
|
|
428
|
+
process.exitCode = ExitCode.ERROR;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
} catch (err) {
|
|
434
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
435
|
+
formatError(message, fmt);
|
|
436
|
+
process.exitCode = ExitCode.ERROR;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerCloneCommand } from "./clone-command.js";
|