@hoox-sh/hoox-cli 0.11.3 → 0.11.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.
Files changed (2) hide show
  1. package/dist/index.js +259 -108
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25548,6 +25548,33 @@ var init_cloudflare_service = __esm(() => {
25548
25548
  init_wrangler_output();
25549
25549
  });
25550
25550
 
25551
+ // src/utils/fs-secure.ts
25552
+ var exports_fs_secure = {};
25553
+ __export(exports_fs_secure, {
25554
+ secureSecretFile: () => secureSecretFile,
25555
+ secureChmod: () => secureChmod,
25556
+ ensureSecretDir: () => ensureSecretDir,
25557
+ SECRET_FILE_MODE: () => SECRET_FILE_MODE,
25558
+ SECRET_DIR_MODE: () => SECRET_DIR_MODE
25559
+ });
25560
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync3 } from "fs";
25561
+ function secureChmod(path4, mode) {
25562
+ try {
25563
+ chmodSync3(path4, mode);
25564
+ } catch {}
25565
+ }
25566
+ function ensureSecretDir(dir) {
25567
+ try {
25568
+ mkdirSync3(dir, { recursive: true, mode: SECRET_DIR_MODE });
25569
+ } catch {}
25570
+ secureChmod(dir, SECRET_DIR_MODE);
25571
+ }
25572
+ function secureSecretFile(path4) {
25573
+ secureChmod(path4, SECRET_FILE_MODE);
25574
+ }
25575
+ var SECRET_FILE_MODE = 384, SECRET_DIR_MODE = 448;
25576
+ var init_fs_secure = () => {};
25577
+
25551
25578
  // src/services/prerequisites/prerequisites-service.ts
25552
25579
  class PrerequisitesService {
25553
25580
  MINIMUM_WRANGLER = "3.88.0";
@@ -28583,6 +28610,10 @@ function formatSuccess(message, opts) {
28583
28610
  function formatError2(error51, opts = {}) {
28584
28611
  const message = typeof error51 === "string" ? error51 : error51.message;
28585
28612
  const cliError = error51 instanceof CLIError ? error51 : null;
28613
+ const write = (s) => {
28614
+ process.stderr.write(s);
28615
+ process.stdout.write(s);
28616
+ };
28586
28617
  if (opts.json) {
28587
28618
  const output = {
28588
28619
  success: false,
@@ -28596,12 +28627,12 @@ function formatError2(error51, opts = {}) {
28596
28627
  if (opts.suggestions && opts.suggestions.length > 0) {
28597
28628
  output.suggestions = opts.suggestions;
28598
28629
  }
28599
- process.stdout.write(JSON.stringify(output) + `
28630
+ write(JSON.stringify(output) + `
28600
28631
  `);
28601
28632
  return;
28602
28633
  }
28603
28634
  if (opts.quiet) {
28604
- process.stdout.write(`${message}
28635
+ write(`${message}
28605
28636
  `);
28606
28637
  return;
28607
28638
  }
@@ -28611,13 +28642,13 @@ function formatError2(error51, opts = {}) {
28611
28642
  titleParts.push(theme.textSubtle(`[${cliError.code}]`));
28612
28643
  }
28613
28644
  titleParts.push(theme.text(message));
28614
- process.stdout.write(`${titleParts.join(" ")}
28645
+ write(`${titleParts.join(" ")}
28615
28646
  `);
28616
28647
  if (cliError?.details) {
28617
28648
  const detailLines = cliError.details.split(`
28618
28649
  `).filter((l) => l.length > 0);
28619
28650
  for (const line of detailLines) {
28620
- process.stdout.write(` ${theme.textMuted(line)}
28651
+ write(` ${theme.textMuted(line)}
28621
28652
  `);
28622
28653
  }
28623
28654
  }
@@ -28625,24 +28656,24 @@ function formatError2(error51, opts = {}) {
28625
28656
  const hasSuggestions = !!opts.suggestions && opts.suggestions.length > 0;
28626
28657
  if (hasHint || hasSuggestions) {
28627
28658
  if (inCard) {
28628
- process.stdout.write(` ${theme.border(icons.pipe)}
28659
+ write(` ${theme.border(icons.pipe)}
28629
28660
  `);
28630
28661
  }
28631
28662
  if (hasHint) {
28632
- process.stdout.write(` ${inCard ? theme.border(icons.pipe) : " "} ${theme.dim("\u21B3 hint:")} ${theme.value(cliError.hint)}
28663
+ write(` ${inCard ? theme.border(icons.pipe) : " "} ${theme.dim("\u21B3 hint:")} ${theme.value(cliError.hint)}
28633
28664
  `);
28634
28665
  }
28635
28666
  if (hasSuggestions) {
28636
28667
  const sugText = opts.suggestions.map((s) => `'${s}'`).join(", ");
28637
- process.stdout.write(` ${inCard ? theme.border(icons.pipe) : " "} ${theme.dim("did you mean:")} ${theme.accent(sugText)} ${theme.dim("?")}
28668
+ write(` ${inCard ? theme.border(icons.pipe) : " "} ${theme.dim("did you mean:")} ${theme.accent(sugText)} ${theme.dim("?")}
28638
28669
  `);
28639
28670
  }
28640
28671
  if (inCard) {
28641
- process.stdout.write(` ${theme.border(icons.pipe)}
28672
+ write(` ${theme.border(icons.pipe)}
28642
28673
  `);
28643
28674
  }
28644
28675
  } else if (inCard && !cliError?.details) {
28645
- process.stdout.write(` ${theme.border(icons.pipe)}
28676
+ write(` ${theme.border(icons.pipe)}
28646
28677
  `);
28647
28678
  }
28648
28679
  }
@@ -29189,6 +29220,8 @@ async function createDevVars(config2, secrets, opts) {
29189
29220
  const dir = `${worker.path}`;
29190
29221
  try {
29191
29222
  await Bun.write(filePath, content);
29223
+ const { secureSecretFile: secureSecretFile2 } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
29224
+ secureSecretFile2(filePath);
29192
29225
  if (!opts?.quiet) {
29193
29226
  formatSuccess(`Created ${filePath}`, opts);
29194
29227
  }
@@ -29196,6 +29229,8 @@ async function createDevVars(config2, secrets, opts) {
29196
29229
  const { mkdir } = await import("fs/promises");
29197
29230
  await mkdir(dir, { recursive: true });
29198
29231
  await Bun.write(filePath, content);
29232
+ const { secureSecretFile: secureSecretFile2 } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
29233
+ secureSecretFile2(filePath);
29199
29234
  if (!opts?.quiet) {
29200
29235
  formatSuccess(`Created ${filePath}`, opts);
29201
29236
  }
@@ -29586,9 +29621,10 @@ async function runInitCommand(options, globalOpts, isNonInteractive) {
29586
29621
  }
29587
29622
  // src/services/setup/setup-service.ts
29588
29623
  import {
29589
- chmodSync as chmodSync3,
29624
+ chmodSync as chmodSync4,
29590
29625
  existsSync as existsSync7,
29591
- mkdirSync as mkdirSync3,
29626
+ mkdirSync as mkdirSync4,
29627
+ readdirSync as readdirSync3,
29592
29628
  readFileSync as readFileSync3,
29593
29629
  statSync as statSync2,
29594
29630
  writeFileSync as writeFileSync2
@@ -29663,9 +29699,38 @@ function parseEnvFile(text2) {
29663
29699
  }
29664
29700
  function safeChmod(path4, mode) {
29665
29701
  try {
29666
- chmodSync3(path4, mode);
29702
+ chmodSync4(path4, mode);
29667
29703
  } catch {}
29668
29704
  }
29705
+ function loadExistingKeysFromDisk() {
29706
+ const setupEnv = join7(KEYS_DIR, "setup.env");
29707
+ if (!existsSync7(setupEnv))
29708
+ return null;
29709
+ let text2;
29710
+ try {
29711
+ text2 = readFileSync3(setupEnv, "utf-8");
29712
+ } catch {
29713
+ return null;
29714
+ }
29715
+ const map2 = parseEnvFile(text2);
29716
+ const required2 = [
29717
+ "INTERNAL_KEY_BINDING",
29718
+ "AGENT_INTERNAL_KEY",
29719
+ "SESSION_SECRET",
29720
+ "WEBHOOK_API_KEY_BINDING",
29721
+ "TELEGRAM_INTERNAL_KEY_BINDING",
29722
+ "TRADE_INTERNAL_KEY",
29723
+ "API_SERVICE_KEY_BINDING"
29724
+ ];
29725
+ const keys = {};
29726
+ for (const name of required2) {
29727
+ const v = map2.get(name);
29728
+ if (!v)
29729
+ return null;
29730
+ keys[name] = v;
29731
+ }
29732
+ return keys;
29733
+ }
29669
29734
 
29670
29735
  class SetupService {
29671
29736
  onProgress;
@@ -29676,7 +29741,20 @@ class SetupService {
29676
29741
  }
29677
29742
  async generateKeys(skip = false) {
29678
29743
  if (skip) {
29679
- this.onProgress({ type: "info", step: "keys", message: "Skipped" });
29744
+ const existing = loadExistingKeysFromDisk();
29745
+ if (existing) {
29746
+ this.onProgress({
29747
+ type: "info",
29748
+ step: "keys",
29749
+ message: "Using existing keys from .keys/setup.env (--skip-keys)"
29750
+ });
29751
+ return existing;
29752
+ }
29753
+ this.onProgress({
29754
+ type: "info",
29755
+ step: "keys",
29756
+ message: "Skipped (no existing .keys/setup.env)"
29757
+ });
29680
29758
  return null;
29681
29759
  }
29682
29760
  this.onProgress({
@@ -29695,9 +29773,9 @@ class SetupService {
29695
29773
  API_SERVICE_KEY_BINDING: internalKey
29696
29774
  };
29697
29775
  if (!existsSync7(KEYS_DIR)) {
29698
- mkdirSync3(KEYS_DIR, { recursive: true });
29776
+ mkdirSync4(KEYS_DIR, { recursive: true });
29699
29777
  } else if (!statSync2(KEYS_DIR).isDirectory()) {
29700
- mkdirSync3(KEYS_DIR, { recursive: true });
29778
+ mkdirSync4(KEYS_DIR, { recursive: true });
29701
29779
  }
29702
29780
  safeChmod(KEYS_DIR, 448);
29703
29781
  const setupEnvPath = join7(KEYS_DIR, "setup.env");
@@ -30071,36 +30149,38 @@ class SetupService {
30071
30149
  ];
30072
30150
  return markers.some((p3) => existsSync7(p3));
30073
30151
  }
30074
- async ensureWorkers() {
30075
- this.onProgress({
30076
- type: "step-start",
30077
- step: "workers",
30078
- message: "Checking worker submodules..."
30079
- });
30152
+ listMissingWorkers() {
30080
30153
  const workerDirs = [
30081
30154
  ...ALL_WORKERS,
30082
30155
  ...TOOLING_WORKERS,
30083
30156
  "dashboard"
30084
30157
  ];
30085
30158
  const missing = [];
30086
- const gitmodulesPath = ".gitmodules";
30087
30159
  for (const name of workerDirs) {
30088
30160
  const dir = `workers/${workerFsDir(name)}`;
30089
30161
  try {
30090
30162
  const gitEntry = readFileSync3(join7(dir, ".git"), "utf-8").trim();
30091
30163
  if (gitEntry.startsWith("gitdir:") || gitEntry.length > 0) {
30092
- if (!this.isWorkerTreePopulated(dir, name)) {
30164
+ if (!this.isWorkerTreePopulated(dir, name))
30093
30165
  missing.push(name);
30094
- }
30095
30166
  } else if (!this.isWorkerTreePopulated(dir, name)) {
30096
30167
  missing.push(name);
30097
30168
  }
30098
30169
  } catch {
30099
- if (!this.isWorkerTreePopulated(dir, name)) {
30170
+ if (!this.isWorkerTreePopulated(dir, name))
30100
30171
  missing.push(name);
30101
- }
30102
30172
  }
30103
30173
  }
30174
+ return missing;
30175
+ }
30176
+ async ensureWorkers() {
30177
+ this.onProgress({
30178
+ type: "step-start",
30179
+ step: "workers",
30180
+ message: "Checking worker submodules..."
30181
+ });
30182
+ const missing = this.listMissingWorkers();
30183
+ const gitmodulesPath = ".gitmodules";
30104
30184
  if (missing.length === 0) {
30105
30185
  this.onProgress({
30106
30186
  type: "step-complete",
@@ -30155,14 +30235,17 @@ class SetupService {
30155
30235
  ];
30156
30236
  const toBuild = [];
30157
30237
  for (const pkg of packages) {
30238
+ let built = false;
30158
30239
  try {
30159
- const jsFiles = readFileSync3(pkg.dist, "utf-8");
30160
- if (!jsFiles.includes(".js")) {
30161
- toBuild.push(pkg.name);
30240
+ if (existsSync7(pkg.dist) && statSync2(pkg.dist).isDirectory()) {
30241
+ const entries = readdirSync3(pkg.dist);
30242
+ built = entries.some((e) => e.endsWith(".js"));
30162
30243
  }
30163
30244
  } catch {
30164
- toBuild.push(pkg.name);
30245
+ built = false;
30165
30246
  }
30247
+ if (!built)
30248
+ toBuild.push(pkg.name);
30166
30249
  }
30167
30250
  if (toBuild.length === 0) {
30168
30251
  this.onProgress({
@@ -30479,13 +30562,21 @@ class SetupService {
30479
30562
  results.success = false;
30480
30563
  return results;
30481
30564
  }
30482
- const cloned = await this.ensureWorkers();
30483
- if (cloned.length > 0) {
30565
+ const missingAfterClone = await this.ensureWorkers();
30566
+ const stillMissing = this.listMissingWorkers();
30567
+ if (stillMissing.length > 0) {
30568
+ steps.push({
30569
+ step: "workers",
30570
+ success: false,
30571
+ message: `Missing worker trees: ${stillMissing.join(", ")}. Run: git submodule update --init --recursive`
30572
+ });
30484
30573
  this.onProgress({
30485
- type: "warn",
30574
+ type: "step-error",
30486
30575
  step: "workers",
30487
- message: "Run `hoox setup` again after clone completes."
30576
+ message: missingAfterClone.length > 0 ? "Workers still missing after clone attempt \u2014 aborting setup." : "Workers missing \u2014 aborting setup."
30488
30577
  });
30578
+ results.success = false;
30579
+ return results;
30489
30580
  }
30490
30581
  const built = await this.ensurePackages();
30491
30582
  if (!built) {
@@ -30494,6 +30585,8 @@ class SetupService {
30494
30585
  success: false,
30495
30586
  message: "Package build failed \u2014 check dependencies"
30496
30587
  });
30588
+ results.success = false;
30589
+ return results;
30497
30590
  }
30498
30591
  this.fixWranglerConfigs();
30499
30592
  const keys = await this.generateKeys(options.skipKeys);
@@ -30644,6 +30737,14 @@ async function runOnboard(options, fmt) {
30644
30737
  if (process.exitCode && process.exitCode !== 0) {
30645
30738
  return;
30646
30739
  }
30740
+ const configReady = await Bun.file("wrangler.jsonc").exists();
30741
+ if (!configReady) {
30742
+ if (!quiet) {
30743
+ formatError2(new CLIError("Onboard stopped: workspace config was not written (init cancelled or incomplete).", 1 /* ERROR */, undefined, false, "Re-run `hoox onboard` or `hoox init` to finish configuration, then `hoox setup`."), fmt);
30744
+ }
30745
+ process.exitCode = 1 /* ERROR */;
30746
+ return;
30747
+ }
30647
30748
  if (options.token) {
30648
30749
  process.env.CLOUDFLARE_API_TOKEN = options.token;
30649
30750
  }
@@ -33130,6 +33231,8 @@ async function handleInit(opts) {
33130
33231
  await Bun.write(tmpPath, envContent);
33131
33232
  await Bun.write(".env.local", envContent);
33132
33233
  await Bun.file(tmpPath).delete().catch(() => {});
33234
+ const { secureSecretFile: secureSecretFile2 } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
33235
+ secureSecretFile2(".env.local");
33133
33236
  if (!opts.quiet)
33134
33237
  formatSuccess(".env.local written", opts);
33135
33238
  log.step("Writing .dev.vars files...");
@@ -33144,6 +33247,8 @@ async function handleInit(opts) {
33144
33247
  ].join(`
33145
33248
  `);
33146
33249
  await Bun.write(`${workerPath}/.dev.vars`, content);
33250
+ const { secureSecretFile: chmodDev } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
33251
+ chmodDev(`${workerPath}/.dev.vars`);
33147
33252
  if (!opts.quiet)
33148
33253
  formatSuccess(`Created ${workerPath}/.dev.vars`, opts);
33149
33254
  }
@@ -33220,6 +33325,8 @@ async function handleGenerateDevVars(opts) {
33220
33325
  ].join(`
33221
33326
  `);
33222
33327
  await Bun.write(`${workerPath}/.dev.vars`, content);
33328
+ const { secureSecretFile: secureSecretFile2 } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
33329
+ secureSecretFile2(`${workerPath}/.dev.vars`);
33223
33330
  count++;
33224
33331
  }
33225
33332
  formatSuccess(`Generated .dev.vars for ${count} worker(s)`, opts);
@@ -33467,7 +33574,7 @@ class SecretsService {
33467
33574
  const path5 = configPath ?? "wrangler.jsonc";
33468
33575
  const file2 = Bun.file(path5);
33469
33576
  if (!await file2.exists()) {
33470
- throw new Error(`Config file not found: ${path5}`);
33577
+ throw new Error(`Config file not found: ${path5}. Run 'hoox onboard' (or 'hoox init') to create one.`);
33471
33578
  }
33472
33579
  const text2 = await file2.text();
33473
33580
  const config2 = parse6(text2);
@@ -33550,6 +33657,8 @@ class SecretsService {
33550
33657
  const devVarsPath = `${worker.path}/.dev.vars`;
33551
33658
  try {
33552
33659
  await Bun.write(devVarsPath, content);
33660
+ const { secureSecretFile: secureSecretFile2 } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
33661
+ secureSecretFile2(devVarsPath);
33553
33662
  return { ok: true, value: devVarsPath };
33554
33663
  } catch (err) {
33555
33664
  const message = err instanceof Error ? err.message : String(err);
@@ -33797,6 +33906,8 @@ async function updateDevVars(filePath, key, value) {
33797
33906
  await Bun.write(filePath, `${key}=${value}
33798
33907
  `);
33799
33908
  }
33909
+ const { secureSecretFile: secureSecretFile2 } = await Promise.resolve().then(() => (init_fs_secure(), exports_fs_secure));
33910
+ secureSecretFile2(filePath);
33800
33911
  }
33801
33912
  function registerSecretsSubcommands(secretsCmd, service = "config") {
33802
33913
  secretsCmd.command("list [worker]").summary("List secrets for all workers or a specific worker").description(`List the secrets declared in wrangler.jsonc for workers.
@@ -33870,7 +33981,7 @@ EXAMPLES:
33870
33981
  const svc = await SecretsService.create();
33871
33982
  const declared = svc.listSecrets(workerName);
33872
33983
  if (!declared.includes(secretName) && declared.length > 0) {
33873
- throw new CLIError(`Secret "${secretName}" is not declared for worker "${workerName}". ` + `Declared secrets: ${declared.join(", ")}`, 2 /* INVALID_USAGE */);
33984
+ throw new CLIError(`Secret "${secretName}" is not declared for worker "${workerName}". Declared secrets: ${declared.join(", ")}`, 2 /* INVALID_USAGE */);
33874
33985
  }
33875
33986
  const value = await promptSecret(`Enter value for "${secretName}"`);
33876
33987
  if (!value) {
@@ -34053,7 +34164,8 @@ EXAMPLES:
34053
34164
  }
34054
34165
 
34055
34166
  // src/commands/config/keys-subcommands.ts
34056
- import { existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
34167
+ import { existsSync as existsSync9 } from "fs";
34168
+ init_fs_secure();
34057
34169
  init_theme();
34058
34170
  function generateKey(bytes = 32) {
34059
34171
  const buf = new Uint8Array(bytes);
@@ -34079,9 +34191,7 @@ EXAMPLES:
34079
34191
  hoox config keys generate`).action(withErrorHandling(async (_2, cmd) => {
34080
34192
  const opts = getFormatOptions(cmd);
34081
34193
  const keysDir = ".keys";
34082
- if (!existsSync9(keysDir)) {
34083
- mkdirSync4(keysDir, { recursive: true });
34084
- }
34194
+ ensureSecretDir(keysDir);
34085
34195
  const meshKey = generateKey();
34086
34196
  const keys = {
34087
34197
  INTERNAL_KEY_BINDING: meshKey,
@@ -34096,7 +34206,13 @@ EXAMPLES:
34096
34206
  const filePath = `${keysDir}/${name.toLowerCase()}.env`;
34097
34207
  await Bun.write(filePath, `${name}=${value}
34098
34208
  `);
34209
+ secureSecretFile(filePath);
34099
34210
  }
34211
+ await Bun.write(`${keysDir}/setup.env`, `# Generated by hoox keys generate
34212
+ ` + Object.entries(keys).map(([k, v]) => `${k}=${v}`).join(`
34213
+ `) + `
34214
+ `);
34215
+ secureSecretFile(`${keysDir}/setup.env`);
34100
34216
  formatSuccess(`Generated ${Object.keys(keys).length} keys in ${keysDir}/`, opts);
34101
34217
  if (!opts.quiet && !opts.json) {
34102
34218
  process.stdout.write(`${theme.warning("!")} Keep these keys secret. Add ${keysDir}/ to .gitignore.
@@ -34708,6 +34824,22 @@ async function runInfraChecks(cf) {
34708
34824
  });
34709
34825
  return { name: "Infrastructure", checks: checks3 };
34710
34826
  }
34827
+ function parseRemoteSecretNames(raw) {
34828
+ try {
34829
+ const parsed = JSON.parse(raw);
34830
+ if (!Array.isArray(parsed))
34831
+ return null;
34832
+ const names = new Set;
34833
+ for (const item of parsed) {
34834
+ if (item && typeof item === "object" && typeof item.name === "string") {
34835
+ names.add(item.name);
34836
+ }
34837
+ }
34838
+ return names;
34839
+ } catch {
34840
+ return null;
34841
+ }
34842
+ }
34711
34843
  async function runSecretsChecks(secretsService, configService, cf) {
34712
34844
  const checks3 = [];
34713
34845
  const enabledWorkers = configService.listEnabledWorkers();
@@ -34716,11 +34848,11 @@ async function runSecretsChecks(secretsService, configService, cf) {
34716
34848
  for (const workerName of enabledWorkers) {
34717
34849
  const result = await secretsService.checkLocalSecrets(workerName);
34718
34850
  if (result.missing.length > 0) {
34719
- errors4.push(`Worker "${workerName}" missing secrets: ${result.missing.join(", ")}`);
34851
+ errors4.push(`Worker "${workerName}" missing secrets: ${result.missing.join(", ")}. ` + `Fix: hoox setup --skip-db --skip-dashboard (mesh keys) or ` + `hoox secrets set ${workerName} <NAME>`);
34720
34852
  }
34721
34853
  for (const secret of result.secrets) {
34722
- if (secret.set && secret.source) {
34723
- warnings.push(`Worker "${workerName}": ${secret.name} is set locally (source: ${secret.source})`);
34854
+ if (!secret.set && secret.source) {
34855
+ warnings.push(`Worker "${workerName}": ${secret.name} is present in ${secret.source} but empty/placeholder`);
34724
34856
  }
34725
34857
  }
34726
34858
  }
@@ -34733,14 +34865,22 @@ async function runSecretsChecks(secretsService, configService, cf) {
34733
34865
  const remoteErrors = [];
34734
34866
  const remoteWarnings = [];
34735
34867
  for (const workerName of enabledWorkers) {
34736
- const secrets = secretsService.listSecrets(workerName);
34737
- if (secrets.length === 0)
34868
+ const expected = secretsService.listSecrets(workerName);
34869
+ if (expected.length === 0)
34738
34870
  continue;
34739
34871
  const result = await cf.secretList(workerName);
34740
34872
  if (!result.ok) {
34741
34873
  remoteErrors.push(`Worker "${workerName}": ${result.error}`);
34742
- } else {
34743
- remoteWarnings.push(`Worker "${workerName}": remote secrets listed OK (${secrets.length} expected)`);
34874
+ continue;
34875
+ }
34876
+ const remoteNames = parseRemoteSecretNames(result.value);
34877
+ if (!remoteNames) {
34878
+ remoteWarnings.push(`Worker "${workerName}": could not parse remote secret list (raw length ${result.value.length})`);
34879
+ continue;
34880
+ }
34881
+ const missingRemote = expected.filter((n3) => !remoteNames.has(n3));
34882
+ if (missingRemote.length > 0) {
34883
+ remoteErrors.push(`Worker "${workerName}" missing on Cloudflare: ${missingRemote.join(", ")}. ` + `Fix: hoox secrets sync ${workerName}`);
34744
34884
  }
34745
34885
  }
34746
34886
  checks3.push({
@@ -41157,68 +41297,78 @@ function findCliVersion() {
41157
41297
  }
41158
41298
  var VERSION = findCliVersion();
41159
41299
  var DISCLAIMER2 = "DISCLAIMER: Trading cryptocurrencies involves substantial risk of loss. Use at your own risk.";
41160
- var WORDMARK = [
41161
- " _ _ ___ _____ _ ",
41162
- "| | | |/ _ \\ / _ \\ \\/ / ",
41163
- "| |_| | (_) | (_) > < ",
41164
- " \\___/ \\___/ \\___/_/\\_\\ "
41165
- ];
41166
- var WORD_W = WORDMARK[0].length;
41167
- function colorWordmark(phase, mode) {
41168
- return WORDMARK.map((line, row) => {
41169
- if (mode === "assemble") {
41170
- const local = Math.max(0, Math.min(1, phase));
41171
- const cut = Math.floor(local * line.length);
41172
- let out = "";
41173
- for (let col = 0;col < line.length; col++) {
41174
- const ch = line[col];
41175
- if (ch === " ") {
41176
- out += " ";
41177
- continue;
41178
- }
41179
- if (col > cut)
41180
- out += ZINC_FAINT("\xB7");
41181
- else if (local < 0.85)
41182
- out += ZINC(ch);
41183
- else
41184
- out += theme.heading(ch);
41185
- }
41186
- return out;
41187
- }
41188
- if (mode === "pulse") {
41189
- let out = "";
41190
- for (let col = 0;col < line.length; col++) {
41191
- const ch = line[col];
41192
- if (ch === " ") {
41193
- out += " ";
41194
- continue;
41195
- }
41196
- const t2 = (phase * 1.5 + col * 0.05 + row * 0.06) % 1;
41197
- if (t2 < 0.12)
41198
- out += AMBER(ch);
41199
- else if (t2 < 0.25)
41200
- out += ORANGE(ch);
41201
- else if (t2 < 0.4)
41202
- out += INDIGO_SOFT(ch);
41203
- else
41204
- out += INDIGO.bold(ch);
41205
- }
41206
- return out;
41207
- }
41208
- return theme.heading(line);
41209
- });
41210
- }
41300
+ var LETTERS = ["H", "O", "O", "X"];
41301
+ var RULE_W = 38;
41211
41302
  var PAD = " ";
41303
+ function colorDiamond(phase, mode) {
41304
+ if (mode === "assemble") {
41305
+ if (phase < 0.05)
41306
+ return " ";
41307
+ if (phase < 0.2)
41308
+ return ZINC_FAINT("\u25C6");
41309
+ return AMBER("\u25C6");
41310
+ }
41311
+ if (mode === "pulse") {
41312
+ const t2 = phase * 2 % 1;
41313
+ return t2 < 0.5 ? AMBER("\u25C6") : ORANGE("\u25C6");
41314
+ }
41315
+ return AMBER("\u25C6");
41316
+ }
41317
+ function colorLetter(letter, index, phase, mode) {
41318
+ if (mode === "assemble") {
41319
+ const start = 0.12;
41320
+ const span = 0.88;
41321
+ const letterPhase = (phase - start) / span;
41322
+ const revealAt = index / LETTERS.length;
41323
+ if (letterPhase < revealAt)
41324
+ return " ";
41325
+ if (letterPhase < revealAt + 0.12)
41326
+ return ZINC(letter);
41327
+ return INDIGO.bold(letter);
41328
+ }
41329
+ if (mode === "pulse") {
41330
+ const t2 = (phase * 1.4 + index * 0.18) % 1;
41331
+ if (t2 < 0.15)
41332
+ return INDIGO_SOFT(letter);
41333
+ if (t2 < 0.3)
41334
+ return AMBER(letter);
41335
+ return INDIGO.bold(letter);
41336
+ }
41337
+ return INDIGO.bold(letter);
41338
+ }
41339
+ function colorDot(afterLetterIndex, phase, mode) {
41340
+ if (mode === "assemble") {
41341
+ const start = 0.12;
41342
+ const span = 0.88;
41343
+ const letterPhase = (phase - start) / span;
41344
+ const revealAt = (afterLetterIndex + 1) / LETTERS.length;
41345
+ if (letterPhase < revealAt - 0.02)
41346
+ return " ";
41347
+ return ZINC_FAINT("\xB7");
41348
+ }
41349
+ return ZINC_FAINT("\xB7");
41350
+ }
41351
+ function composeTitleLine(phase, mode) {
41352
+ const diamond = colorDiamond(phase, mode);
41353
+ const parts = [diamond, " "];
41354
+ for (let i2 = 0;i2 < LETTERS.length; i2++) {
41355
+ parts.push(colorLetter(LETTERS[i2], i2, phase, mode));
41356
+ if (i2 < LETTERS.length - 1) {
41357
+ parts.push(" ");
41358
+ parts.push(colorDot(i2, phase, mode));
41359
+ parts.push(" ");
41360
+ }
41361
+ }
41362
+ return PAD + parts.join("");
41363
+ }
41364
+ function composeMetaLine() {
41365
+ return PAD + ZINC_SOFT(TAGLINE) + " " + ZINC_FAINT("\xB7") + " " + INDIGO_SOFT(`v${VERSION}`);
41366
+ }
41212
41367
  function composeFrame(phase, mode) {
41213
- const word = colorWordmark(phase, mode);
41214
- const contentW = WORD_W;
41215
- const body = word.map((line) => PAD + line);
41216
- const rule = PAD + ZINC_FAINT("\u2500".repeat(contentW));
41217
- const metaInner = ZINC_SOFT(TAGLINE) + " " + ZINC_FAINT("\xB7") + " " + INDIGO_SOFT(`v${VERSION}`);
41218
- const metaVis = stripAnsi(metaInner).length;
41219
- const metaPad = Math.max(0, Math.floor((contentW - metaVis) / 2));
41220
- const meta3 = PAD + " ".repeat(metaPad) + metaInner;
41221
- return [rule, ...body, rule, meta3].join(`
41368
+ const title = composeTitleLine(phase, mode);
41369
+ const rule = PAD + ZINC_SOFT("\xB7") + " " + ZINC_FAINT("\u2500".repeat(RULE_W - 2));
41370
+ const meta3 = composeMetaLine();
41371
+ return [title, rule, meta3].join(`
41222
41372
  `);
41223
41373
  }
41224
41374
  function canAnimate() {
@@ -41850,7 +42000,8 @@ program2.exitOverride((err) => {
41850
42000
  const match = err.message.match(/'([^']+)'/);
41851
42001
  const badArg = match?.[1] ?? "";
41852
42002
  const suggestion = suggestForCommand(program2, badArg);
41853
- formatError2(new CLIError(err.message, 2 /* INVALID_USAGE */, undefined, false), suggestion ? { ...errFmtOpts, suggestions: [suggestion] } : errFmtOpts);
42003
+ const cleanMsg = err.message.replace(/\n?\(Did you mean[^)]*\)\s*/gi, "").trim();
42004
+ formatError2(new CLIError(cleanMsg, 2 /* INVALID_USAGE */, undefined, false), suggestion ? { ...errFmtOpts, suggestions: [suggestion] } : errFmtOpts);
41854
42005
  process.exitCode = 2 /* INVALID_USAGE */;
41855
42006
  throw err;
41856
42007
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoox-sh/hoox-cli",
3
- "version": "0.11.3",
3
+ "version": "0.11.5",
4
4
  "description": "Hoox CLI — manage Cloudflare Workers, infrastructure, secrets, and deployments (@hoox-sh/hoox-cli)",
5
5
  "type": "module",
6
6
  "bin": {