@hoox-sh/hoox-cli 0.11.5 → 0.11.7
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 +46 -5
- package/dist/index.js +346 -121
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
> **Runtime requirement:** Bun ≥ 1.2. The bin shebang and bundle target are Bun-only; `npm install -g` will install the package but the CLI will not run under Node.js.
|
|
8
8
|
|
|
9
|
-
**v0.11.x** — operator security plane (`doctor --security`, `tunnel check
|
|
9
|
+
**v0.11.x** — monorepo auto-detect + remember (run `hx` from any directory), Linear Rail banner, quieter secrets check, operator security plane (`doctor --security`, `tunnel check`), PYNE (`hoox pyne`), schema validate/generate, wrangler **4.x**.
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
+
- **Monorepo auto-detect & remember**: On every start the CLI finds the hoox monorepo (`HOOX_REPO` → walk up from cwd → `~/.hoox/config/monorepo.json` → `~/.hoox/repo`), saves the path, and `chdir`s into it so `hx` / `hoox` work from `~/Videos` or any other folder after you have run once from the monorepo.
|
|
13
14
|
- **One-shot Onboarding** (`hoox onboard`): Full bootstrap from a fresh clone — collects credentials, configures workers, provisions D1/KV, generates keys, pushes secrets, deploys dashboard. The recommended entry point for new users.
|
|
14
15
|
- **Config & Setup Wizards** (`hoox init`, `hoox setup`): Split into two steps — `init` writes `wrangler.jsonc` and collects integration secrets; `setup` generates keys, applies D1 schema, and pushes secrets to Cloudflare. Run separately when you need fine-grained control.
|
|
15
16
|
- **Infrastructure as Code**: Manage D1, KV, R2, Queues, Vectorize, and Analytics via `hoox infra`.
|
|
@@ -56,6 +57,10 @@ bun run build # produces dist/index.js — required for `hoox` after global in
|
|
|
56
57
|
## Quick Start
|
|
57
58
|
|
|
58
59
|
```bash
|
|
60
|
+
# From the monorepo root (first time — path is remembered)
|
|
61
|
+
cd /path/to/hoox
|
|
62
|
+
hoox doctor # shows Remembered + Runtime root
|
|
63
|
+
|
|
59
64
|
# One-shot onboarding (collects credentials, provisions, deploys)
|
|
60
65
|
hoox onboard
|
|
61
66
|
|
|
@@ -70,8 +75,42 @@ hoox check health
|
|
|
70
75
|
# Deploy to Cloudflare
|
|
71
76
|
hoox deploy all --auto
|
|
72
77
|
|
|
73
|
-
#
|
|
74
|
-
|
|
78
|
+
# Later: works from any directory (uses remembered monorepo)
|
|
79
|
+
cd ~/Videos
|
|
80
|
+
hx check health
|
|
81
|
+
hx doctor # Source: remembered
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Monorepo resolution (any cwd)
|
|
85
|
+
|
|
86
|
+
Resolution order at CLI startup:
|
|
87
|
+
|
|
88
|
+
1. **`HOOX_REPO`** — absolute path override (highest priority)
|
|
89
|
+
2. **Walk up from cwd** — discovers a local checkout (`packages/cli` + repo markers)
|
|
90
|
+
3. **Remembered path** — `~/.hoox/config/monorepo.json` (written when discovered via cwd/env)
|
|
91
|
+
4. **Global clone** — `~/.hoox/repo` (create with `hoox doctor --fix-runtime`)
|
|
92
|
+
|
|
93
|
+
When a root is found, the CLI:
|
|
94
|
+
|
|
95
|
+
- refreshes the remembered path (when source is `cwd` / `env` / `remembered`)
|
|
96
|
+
- sets session `HOOX_REPO` for child processes
|
|
97
|
+
- `chdir`s into the monorepo so relative paths (`wrangler.jsonc`, `workers/`, …) work
|
|
98
|
+
|
|
99
|
+
Markers for a valid monorepo: `packages/cli/package.json` **plus** one of `wrangler.jsonc`, `wrangler.jsonc.example`, `workers/`, or `.gitmodules`.
|
|
100
|
+
|
|
101
|
+
| Env / path | Purpose |
|
|
102
|
+
| ------------------------------ | ------------------------------------------------------ |
|
|
103
|
+
| `HOOX_REPO` | Force monorepo root |
|
|
104
|
+
| `HOOX_HOME` | Override `~/.hoox` (default home for config/data/repo) |
|
|
105
|
+
| `HOOX_CLI_SILENT=1` | Suppress “using monorepo at …” notice |
|
|
106
|
+
| `~/.hoox/config/monorepo.json` | Persisted root `{ "root", "updatedAt" }` |
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Inspect layout
|
|
110
|
+
hoox doctor
|
|
111
|
+
# Remembered /home/you/Git/hoox
|
|
112
|
+
# Runtime root /home/you/Git/hoox
|
|
113
|
+
# Source remembered
|
|
75
114
|
```
|
|
76
115
|
|
|
77
116
|
## Available Commands
|
|
@@ -358,8 +397,9 @@ hoox perf fastpath report --from 1h
|
|
|
358
397
|
### Operator Security & Private Ingress
|
|
359
398
|
|
|
360
399
|
```bash
|
|
361
|
-
# Paths, TUI entry,
|
|
400
|
+
# Paths, TUI entry, remembered monorepo, runtime layout
|
|
362
401
|
hoox doctor
|
|
402
|
+
hoox doctor --fix-runtime # clone monorepo into ~/.hoox/repo if needed
|
|
363
403
|
|
|
364
404
|
# Hygiene + optional management /v1/health probes
|
|
365
405
|
hoox doctor --security
|
|
@@ -504,8 +544,9 @@ packages/cli/
|
|
|
504
544
|
│ │ ├── perf/ # Percentile, probe-sender, observability-reader
|
|
505
545
|
│ │ ├── prerequisites/ # Tool version checks
|
|
506
546
|
│ │ ├── runtime/ # Global ~/.hoox layout
|
|
547
|
+
│ │ ├── workspace/ # Monorepo detect, remember, chdir
|
|
507
548
|
│ │ └── secrets/ # Secret management
|
|
508
|
-
│ ├── ui/ # Interactive menu
|
|
549
|
+
│ ├── ui/ # Interactive menu + Linear Rail banner
|
|
509
550
|
│ └── utils/ # Errors, formatters, theme
|
|
510
551
|
├── bin/
|
|
511
552
|
│ └── hoox.js # CLI binary entry point
|
package/dist/index.js
CHANGED
|
@@ -16200,7 +16200,13 @@ var init_sse = __esm(() => {
|
|
|
16200
16200
|
});
|
|
16201
16201
|
|
|
16202
16202
|
// ../shared/src/path-utils.ts
|
|
16203
|
-
import {
|
|
16203
|
+
import {
|
|
16204
|
+
existsSync as existsSync2,
|
|
16205
|
+
mkdirSync as mkdirSync2,
|
|
16206
|
+
readFileSync as readFileSync2,
|
|
16207
|
+
writeFileSync as writeFileSync2,
|
|
16208
|
+
chmodSync as chmodSync2
|
|
16209
|
+
} from "fs";
|
|
16204
16210
|
import { homedir as homedir2 } from "os";
|
|
16205
16211
|
import { dirname, join as join2, resolve } from "path";
|
|
16206
16212
|
function createHooxPath(path2) {
|
|
@@ -16226,7 +16232,55 @@ function isHooxSetupRoot(dir) {
|
|
|
16226
16232
|
return false;
|
|
16227
16233
|
try {
|
|
16228
16234
|
const root = resolve(dir);
|
|
16229
|
-
|
|
16235
|
+
if (!existsSync2(join2(root, "packages", "cli", "package.json"))) {
|
|
16236
|
+
return false;
|
|
16237
|
+
}
|
|
16238
|
+
return existsSync2(join2(root, "wrangler.jsonc")) || existsSync2(join2(root, "wrangler.jsonc.example")) || existsSync2(join2(root, "workers")) || existsSync2(join2(root, ".gitmodules"));
|
|
16239
|
+
} catch {
|
|
16240
|
+
return false;
|
|
16241
|
+
}
|
|
16242
|
+
}
|
|
16243
|
+
function getRememberedMonorepoPath() {
|
|
16244
|
+
return resolveHooxPath("config/monorepo.json");
|
|
16245
|
+
}
|
|
16246
|
+
function getRememberedMonorepoRoot() {
|
|
16247
|
+
try {
|
|
16248
|
+
const path2 = getRememberedMonorepoPath();
|
|
16249
|
+
if (!existsSync2(path2))
|
|
16250
|
+
return null;
|
|
16251
|
+
const raw = readFileSync2(path2, "utf-8");
|
|
16252
|
+
const data = JSON.parse(raw);
|
|
16253
|
+
if (!data.root || typeof data.root !== "string")
|
|
16254
|
+
return null;
|
|
16255
|
+
const resolved = resolve(data.root);
|
|
16256
|
+
return isHooxSetupRoot(resolved) ? resolved : null;
|
|
16257
|
+
} catch {
|
|
16258
|
+
return null;
|
|
16259
|
+
}
|
|
16260
|
+
}
|
|
16261
|
+
function rememberMonorepoRoot(root) {
|
|
16262
|
+
try {
|
|
16263
|
+
const resolved = resolve(root);
|
|
16264
|
+
if (!isHooxSetupRoot(resolved))
|
|
16265
|
+
return false;
|
|
16266
|
+
const configDir = getHooxConfigDir();
|
|
16267
|
+
mkdirSync2(configDir, { recursive: true, mode: 448 });
|
|
16268
|
+
try {
|
|
16269
|
+
chmodSync2(configDir, 448);
|
|
16270
|
+
} catch {}
|
|
16271
|
+
const payload = {
|
|
16272
|
+
root: resolved,
|
|
16273
|
+
updatedAt: new Date().toISOString()
|
|
16274
|
+
};
|
|
16275
|
+
const path2 = getRememberedMonorepoPath();
|
|
16276
|
+
writeFileSync2(path2, JSON.stringify(payload, null, 2) + `
|
|
16277
|
+
`, {
|
|
16278
|
+
mode: 384
|
|
16279
|
+
});
|
|
16280
|
+
try {
|
|
16281
|
+
chmodSync2(path2, 384);
|
|
16282
|
+
} catch {}
|
|
16283
|
+
return true;
|
|
16230
16284
|
} catch {
|
|
16231
16285
|
return false;
|
|
16232
16286
|
}
|
|
@@ -16246,6 +16300,7 @@ function resolveHooxRuntimeRoot(options) {
|
|
|
16246
16300
|
const env = options?.env ?? process.env;
|
|
16247
16301
|
const cwd = resolve(options?.cwd ?? process.cwd());
|
|
16248
16302
|
const globalRepo = getHooxRepoPath();
|
|
16303
|
+
const remembered = options?.skipRemembered ? null : getRememberedMonorepoRoot();
|
|
16249
16304
|
const envRepo = env.HOOX_REPO?.trim();
|
|
16250
16305
|
if (envRepo) {
|
|
16251
16306
|
const resolved = resolve(envRepo);
|
|
@@ -16256,6 +16311,7 @@ function resolveHooxRuntimeRoot(options) {
|
|
|
16256
16311
|
checked: {
|
|
16257
16312
|
env: resolved,
|
|
16258
16313
|
cwd: findHooxSetupRoot(cwd),
|
|
16314
|
+
remembered,
|
|
16259
16315
|
global: globalRepo
|
|
16260
16316
|
}
|
|
16261
16317
|
};
|
|
@@ -16266,6 +16322,7 @@ function resolveHooxRuntimeRoot(options) {
|
|
|
16266
16322
|
checked: {
|
|
16267
16323
|
env: resolved,
|
|
16268
16324
|
cwd: findHooxSetupRoot(cwd),
|
|
16325
|
+
remembered,
|
|
16269
16326
|
global: globalRepo
|
|
16270
16327
|
}
|
|
16271
16328
|
};
|
|
@@ -16275,20 +16332,27 @@ function resolveHooxRuntimeRoot(options) {
|
|
|
16275
16332
|
return {
|
|
16276
16333
|
root: local,
|
|
16277
16334
|
source: "cwd",
|
|
16278
|
-
checked: { cwd: local, global: globalRepo }
|
|
16335
|
+
checked: { cwd: local, remembered, global: globalRepo }
|
|
16336
|
+
};
|
|
16337
|
+
}
|
|
16338
|
+
if (remembered) {
|
|
16339
|
+
return {
|
|
16340
|
+
root: remembered,
|
|
16341
|
+
source: "remembered",
|
|
16342
|
+
checked: { cwd: null, remembered, global: globalRepo }
|
|
16279
16343
|
};
|
|
16280
16344
|
}
|
|
16281
16345
|
if (isHooxSetupRoot(globalRepo)) {
|
|
16282
16346
|
return {
|
|
16283
16347
|
root: globalRepo,
|
|
16284
16348
|
source: "global",
|
|
16285
|
-
checked: { cwd: null, global: globalRepo }
|
|
16349
|
+
checked: { cwd: null, remembered: null, global: globalRepo }
|
|
16286
16350
|
};
|
|
16287
16351
|
}
|
|
16288
16352
|
return {
|
|
16289
16353
|
root: null,
|
|
16290
16354
|
source: "none",
|
|
16291
|
-
checked: { cwd: null, global: globalRepo }
|
|
16355
|
+
checked: { cwd: null, remembered: null, global: globalRepo }
|
|
16292
16356
|
};
|
|
16293
16357
|
}
|
|
16294
16358
|
function getTuiEntryCandidates(runtimeRoot) {
|
|
@@ -16468,7 +16532,7 @@ function formatRelativeTime2(timestampMs, nowMs) {
|
|
|
16468
16532
|
}
|
|
16469
16533
|
|
|
16470
16534
|
// ../shared/src/dashboard-manifest.ts
|
|
16471
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as
|
|
16535
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
16472
16536
|
import { join as join3 } from "path";
|
|
16473
16537
|
function isDashboardSectionFlatKv(sectionId) {
|
|
16474
16538
|
return !DASHBOARD_SECTIONS_NOT_FLAT_KV.has(sectionId);
|
|
@@ -16702,7 +16766,7 @@ function tryReadFile(path2) {
|
|
|
16702
16766
|
try {
|
|
16703
16767
|
if (!existsSync3(path2))
|
|
16704
16768
|
return null;
|
|
16705
|
-
return
|
|
16769
|
+
return readFileSync3(path2, "utf-8");
|
|
16706
16770
|
} catch {
|
|
16707
16771
|
return null;
|
|
16708
16772
|
}
|
|
@@ -21424,7 +21488,8 @@ var init_ui_store = __esm(() => {
|
|
|
21424
21488
|
sidebarExpanded: true,
|
|
21425
21489
|
modal: null,
|
|
21426
21490
|
commandPaletteOpen: false,
|
|
21427
|
-
previousView: null
|
|
21491
|
+
previousView: null,
|
|
21492
|
+
statusErrorExpanded: false
|
|
21428
21493
|
};
|
|
21429
21494
|
useUIStore = create()(immer2((set3) => ({
|
|
21430
21495
|
...initialState,
|
|
@@ -21455,6 +21520,12 @@ var init_ui_store = __esm(() => {
|
|
|
21455
21520
|
state.activeView = state.previousView;
|
|
21456
21521
|
state.previousView = null;
|
|
21457
21522
|
}
|
|
21523
|
+
}),
|
|
21524
|
+
toggleStatusErrorExpanded: () => set3((state) => {
|
|
21525
|
+
state.statusErrorExpanded = !state.statusErrorExpanded;
|
|
21526
|
+
}),
|
|
21527
|
+
setStatusErrorExpanded: (expanded) => set3((state) => {
|
|
21528
|
+
state.statusErrorExpanded = expanded;
|
|
21458
21529
|
})
|
|
21459
21530
|
})));
|
|
21460
21531
|
});
|
|
@@ -21471,13 +21542,28 @@ function ringPush(arr, item, max) {
|
|
|
21471
21542
|
const next = [...arr, item];
|
|
21472
21543
|
return next.length > max ? next.slice(next.length - max) : next;
|
|
21473
21544
|
}
|
|
21545
|
+
function clearReconnectTimer() {
|
|
21546
|
+
if (reconnectTimer !== null) {
|
|
21547
|
+
clearTimeout(reconnectTimer);
|
|
21548
|
+
reconnectTimer = null;
|
|
21549
|
+
}
|
|
21550
|
+
}
|
|
21551
|
+
function scheduleReconnect(delayMs) {
|
|
21552
|
+
clearReconnectTimer();
|
|
21553
|
+
if (delayMs < 0)
|
|
21554
|
+
return;
|
|
21555
|
+
reconnectTimer = setTimeout(() => {
|
|
21556
|
+
reconnectTimer = null;
|
|
21557
|
+
scheduledReconnectTick();
|
|
21558
|
+
}, delayMs);
|
|
21559
|
+
}
|
|
21474
21560
|
function getBackoffDelay(retryCount) {
|
|
21475
21561
|
if (retryCount <= 0)
|
|
21476
21562
|
return 0;
|
|
21477
21563
|
const index = Math.min(retryCount - 1, BACKOFF_SEQUENCE.length - 1);
|
|
21478
21564
|
return BACKOFF_SEQUENCE[index];
|
|
21479
21565
|
}
|
|
21480
|
-
var BACKOFF_SEQUENCE, MAX_RETRIES2 = 5, tradeStreamAbort = null, logStreamAbort = null, fetchWorkersGeneration = 0, MAX_TRADES = 500, MAX_ALERTS = 100, MAX_LOGS = 1000, initialState2, useServiceStore;
|
|
21566
|
+
var BACKOFF_SEQUENCE, MAX_RETRIES2 = 5, tradeStreamAbort = null, logStreamAbort = null, fetchWorkersGeneration = 0, reconnectTimer = null, scheduledReconnectTick = async () => {}, MAX_TRADES = 500, MAX_ALERTS = 100, MAX_LOGS = 1000, initialState2, useServiceStore;
|
|
21481
21567
|
var init_service_store = __esm(() => {
|
|
21482
21568
|
init_esm();
|
|
21483
21569
|
init_immer2();
|
|
@@ -21507,6 +21593,7 @@ var init_service_store = __esm(() => {
|
|
|
21507
21593
|
const data = await hooxFetch2("/v1/workers");
|
|
21508
21594
|
if (gen !== fetchWorkersGeneration)
|
|
21509
21595
|
return;
|
|
21596
|
+
clearReconnectTimer();
|
|
21510
21597
|
set3((state) => {
|
|
21511
21598
|
state.workers = data;
|
|
21512
21599
|
state.lastUpdated = Date.now();
|
|
@@ -21532,6 +21619,7 @@ var init_service_store = __esm(() => {
|
|
|
21532
21619
|
timestamp: Date.now(),
|
|
21533
21620
|
acknowledged: false
|
|
21534
21621
|
});
|
|
21622
|
+
let nextDelay = 0;
|
|
21535
21623
|
set3((state) => {
|
|
21536
21624
|
state.lastError = msg;
|
|
21537
21625
|
state.disconnectedAt = state.disconnectedAt ?? Date.now();
|
|
@@ -21540,21 +21628,26 @@ var init_service_store = __esm(() => {
|
|
|
21540
21628
|
state.retryCount = state.retryCount + 1;
|
|
21541
21629
|
if (state.retryCount >= MAX_RETRIES2) {
|
|
21542
21630
|
state.connectionStatus = "offline";
|
|
21543
|
-
state.lastError =
|
|
21631
|
+
state.lastError = `${msg} (gave up after ${MAX_RETRIES2} retries)`;
|
|
21632
|
+
state.reconnectDelay = 0;
|
|
21544
21633
|
} else {
|
|
21545
21634
|
state.reconnectDelay = getBackoffDelay(state.retryCount);
|
|
21635
|
+
nextDelay = state.reconnectDelay;
|
|
21546
21636
|
}
|
|
21547
21637
|
} else if (state.connectionStatus === "reconnecting") {
|
|
21548
21638
|
state.retryCount = state.retryCount + 1;
|
|
21549
21639
|
if (state.retryCount >= MAX_RETRIES2) {
|
|
21550
21640
|
state.connectionStatus = "offline";
|
|
21551
|
-
state.lastError =
|
|
21641
|
+
state.lastError = `${msg} (gave up after ${MAX_RETRIES2} retries)`;
|
|
21552
21642
|
state.reconnectDelay = 0;
|
|
21553
21643
|
} else {
|
|
21554
21644
|
state.reconnectDelay = getBackoffDelay(state.retryCount);
|
|
21645
|
+
nextDelay = state.reconnectDelay;
|
|
21555
21646
|
}
|
|
21556
21647
|
}
|
|
21557
21648
|
});
|
|
21649
|
+
if (nextDelay > 0)
|
|
21650
|
+
scheduleReconnect(nextDelay);
|
|
21558
21651
|
}
|
|
21559
21652
|
},
|
|
21560
21653
|
streamTrades: async () => {
|
|
@@ -21616,6 +21709,17 @@ var init_service_store = __esm(() => {
|
|
|
21616
21709
|
const merged = [...state.alerts, ...alerts];
|
|
21617
21710
|
state.alerts = merged.length > MAX_ALERTS ? merged.slice(merged.length - MAX_ALERTS) : merged;
|
|
21618
21711
|
}),
|
|
21712
|
+
acknowledgeAlert: (id) => set3((state) => {
|
|
21713
|
+
const hit = state.alerts.find((a) => a.id === id);
|
|
21714
|
+
if (hit)
|
|
21715
|
+
hit.acknowledged = true;
|
|
21716
|
+
}),
|
|
21717
|
+
dismissAlert: (id) => set3((state) => {
|
|
21718
|
+
state.alerts = state.alerts.filter((a) => a.id !== id);
|
|
21719
|
+
}),
|
|
21720
|
+
clearLogs: () => set3((state) => {
|
|
21721
|
+
state.logs = [];
|
|
21722
|
+
}),
|
|
21619
21723
|
setConnectionStatus: (status) => set3((state) => {
|
|
21620
21724
|
if (status === "connected") {
|
|
21621
21725
|
state.retryCount = 0;
|
|
@@ -21646,49 +21750,69 @@ var init_service_store = __esm(() => {
|
|
|
21646
21750
|
state.metrics = metrics;
|
|
21647
21751
|
state.lastUpdated = metrics.lastUpdated;
|
|
21648
21752
|
}),
|
|
21649
|
-
handleConnectionSuccess: () =>
|
|
21650
|
-
|
|
21651
|
-
state
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
|
|
21663
|
-
|
|
21664
|
-
state.
|
|
21665
|
-
|
|
21666
|
-
state.
|
|
21667
|
-
|
|
21668
|
-
state.
|
|
21669
|
-
state.
|
|
21670
|
-
state.reconnectDelay
|
|
21671
|
-
} else {
|
|
21672
|
-
state.
|
|
21753
|
+
handleConnectionSuccess: () => {
|
|
21754
|
+
clearReconnectTimer();
|
|
21755
|
+
set3((state) => {
|
|
21756
|
+
state.lastSuccessfulFetch = Date.now();
|
|
21757
|
+
state.retryCount = 0;
|
|
21758
|
+
state.lastError = null;
|
|
21759
|
+
state.lastErrorDetails = null;
|
|
21760
|
+
state.reconnectDelay = 0;
|
|
21761
|
+
state.disconnectedAt = null;
|
|
21762
|
+
state.connectionStatus = "connected";
|
|
21763
|
+
});
|
|
21764
|
+
},
|
|
21765
|
+
handleConnectionFailure: (errorMessage) => {
|
|
21766
|
+
let nextDelay = 0;
|
|
21767
|
+
set3((state) => {
|
|
21768
|
+
state.lastError = errorMessage;
|
|
21769
|
+
state.disconnectedAt = state.disconnectedAt ?? Date.now();
|
|
21770
|
+
if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
|
|
21771
|
+
state.connectionStatus = "reconnecting";
|
|
21772
|
+
state.retryCount = 1;
|
|
21773
|
+
state.reconnectDelay = getBackoffDelay(1);
|
|
21774
|
+
nextDelay = state.reconnectDelay;
|
|
21775
|
+
} else if (state.connectionStatus === "reconnecting") {
|
|
21776
|
+
state.retryCount = state.retryCount + 1;
|
|
21777
|
+
if (state.retryCount >= MAX_RETRIES2) {
|
|
21778
|
+
state.connectionStatus = "offline";
|
|
21779
|
+
state.lastError = `${errorMessage} (gave up after ${MAX_RETRIES2} retries)`;
|
|
21780
|
+
state.reconnectDelay = 0;
|
|
21781
|
+
} else {
|
|
21782
|
+
state.reconnectDelay = getBackoffDelay(state.retryCount);
|
|
21783
|
+
nextDelay = state.reconnectDelay;
|
|
21784
|
+
}
|
|
21673
21785
|
}
|
|
21674
|
-
}
|
|
21675
|
-
|
|
21676
|
-
|
|
21677
|
-
|
|
21678
|
-
|
|
21679
|
-
|
|
21680
|
-
state
|
|
21681
|
-
state.disconnectedAt = null;
|
|
21682
|
-
}),
|
|
21683
|
-
forceRetry: () => set3((state) => {
|
|
21684
|
-
if (state.connectionStatus === "offline") {
|
|
21685
|
-
state.connectionStatus = "polling";
|
|
21786
|
+
});
|
|
21787
|
+
if (nextDelay > 0)
|
|
21788
|
+
scheduleReconnect(nextDelay);
|
|
21789
|
+
},
|
|
21790
|
+
resetRetries: () => {
|
|
21791
|
+
clearReconnectTimer();
|
|
21792
|
+
set3((state) => {
|
|
21686
21793
|
state.retryCount = 0;
|
|
21687
21794
|
state.lastError = null;
|
|
21688
21795
|
state.lastErrorDetails = null;
|
|
21689
21796
|
state.reconnectDelay = 0;
|
|
21797
|
+
state.disconnectedAt = null;
|
|
21798
|
+
});
|
|
21799
|
+
},
|
|
21800
|
+
forceRetry: () => {
|
|
21801
|
+
clearReconnectTimer();
|
|
21802
|
+
const wasOffline = get2().connectionStatus === "offline";
|
|
21803
|
+
set3((state) => {
|
|
21804
|
+
if (state.connectionStatus === "offline") {
|
|
21805
|
+
state.connectionStatus = "polling";
|
|
21806
|
+
state.retryCount = 0;
|
|
21807
|
+
state.lastError = null;
|
|
21808
|
+
state.lastErrorDetails = null;
|
|
21809
|
+
state.reconnectDelay = 0;
|
|
21810
|
+
}
|
|
21811
|
+
});
|
|
21812
|
+
if (wasOffline) {
|
|
21813
|
+
scheduleReconnect(0);
|
|
21690
21814
|
}
|
|
21691
|
-
}
|
|
21815
|
+
},
|
|
21692
21816
|
setLastErrorDetails: (details) => set3((state) => {
|
|
21693
21817
|
state.lastErrorDetails = details;
|
|
21694
21818
|
state.lastError = details === null ? null : summarizeCliError(details);
|
|
@@ -21711,6 +21835,12 @@ var init_service_store = __esm(() => {
|
|
|
21711
21835
|
}, MAX_ALERTS);
|
|
21712
21836
|
})
|
|
21713
21837
|
})));
|
|
21838
|
+
scheduledReconnectTick = async () => {
|
|
21839
|
+
const status = useServiceStore.getState().connectionStatus;
|
|
21840
|
+
if (status === "reconnecting" || status === "polling") {
|
|
21841
|
+
await useServiceStore.getState().fetchWorkers();
|
|
21842
|
+
}
|
|
21843
|
+
};
|
|
21714
21844
|
});
|
|
21715
21845
|
|
|
21716
21846
|
// ../../node_modules/.bun/zustand@5.0.14+158594928fb811b4/node_modules/zustand/esm/middleware.mjs
|
|
@@ -21897,39 +22027,69 @@ var init_middleware = __esm(() => {
|
|
|
21897
22027
|
});
|
|
21898
22028
|
|
|
21899
22029
|
// ../shared/src/stores/config-store.ts
|
|
21900
|
-
import { chmodSync as
|
|
21901
|
-
import { homedir as homedir3 } from "os";
|
|
22030
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync3, existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
21902
22031
|
import { join as join4 } from "path";
|
|
22032
|
+
function getTuiPreferencesPath() {
|
|
22033
|
+
return join4(getHooxHome(), ".tui-state", "preferences.json");
|
|
22034
|
+
}
|
|
22035
|
+
function getLegacyConfigJsonPath() {
|
|
22036
|
+
return join4(getHooxHome(), "config.json");
|
|
22037
|
+
}
|
|
22038
|
+
function extractZustandPrefsPayload(raw) {
|
|
22039
|
+
try {
|
|
22040
|
+
const parsed = JSON.parse(raw);
|
|
22041
|
+
if (parsed && typeof parsed === "object" && parsed.state && typeof parsed.state === "object" && !Array.isArray(parsed.state) && (("theme" in parsed.state) || ("refreshIntervalMs" in parsed.state)) && !("apiToken" in parsed)) {
|
|
22042
|
+
return raw;
|
|
22043
|
+
}
|
|
22044
|
+
} catch {}
|
|
22045
|
+
return null;
|
|
22046
|
+
}
|
|
21903
22047
|
var bunConfigStorage, DEFAULT_SHORTCUTS, DEFAULT_LOG_FILTER, DEFAULT_NOTIFICATIONS, defaults, useConfigStore;
|
|
21904
22048
|
var init_config_store = __esm(() => {
|
|
21905
22049
|
init_esm();
|
|
21906
22050
|
init_immer2();
|
|
21907
22051
|
init_middleware();
|
|
21908
22052
|
init_config();
|
|
22053
|
+
init_path_utils();
|
|
21909
22054
|
bunConfigStorage = {
|
|
21910
22055
|
getItem: async (_name) => {
|
|
21911
22056
|
try {
|
|
21912
|
-
const
|
|
21913
|
-
const
|
|
21914
|
-
if (
|
|
21915
|
-
return
|
|
21916
|
-
|
|
22057
|
+
const prefsPath = getTuiPreferencesPath();
|
|
22058
|
+
const prefsFile = Bun.file(prefsPath);
|
|
22059
|
+
if (await prefsFile.exists()) {
|
|
22060
|
+
return await prefsFile.text();
|
|
22061
|
+
}
|
|
22062
|
+
const legacyPath = getLegacyConfigJsonPath();
|
|
22063
|
+
if (existsSync4(legacyPath)) {
|
|
22064
|
+
const legacyRaw = readFileSync4(legacyPath, "utf-8");
|
|
22065
|
+
const envelope = extractZustandPrefsPayload(legacyRaw);
|
|
22066
|
+
if (envelope) {
|
|
22067
|
+
await bunConfigStorage.setItem(_name, envelope);
|
|
22068
|
+
return envelope;
|
|
22069
|
+
}
|
|
22070
|
+
}
|
|
22071
|
+
return null;
|
|
21917
22072
|
} catch {
|
|
21918
22073
|
return null;
|
|
21919
22074
|
}
|
|
21920
22075
|
},
|
|
21921
22076
|
setItem: async (_name, value) => {
|
|
21922
|
-
const
|
|
21923
|
-
|
|
21924
|
-
|
|
22077
|
+
const home = getHooxHome();
|
|
22078
|
+
const stateDir = join4(home, ".tui-state");
|
|
22079
|
+
if (!existsSync4(home)) {
|
|
22080
|
+
mkdirSync3(home, { recursive: true, mode: HOOX_DIR_MODE });
|
|
22081
|
+
}
|
|
22082
|
+
if (!existsSync4(stateDir)) {
|
|
22083
|
+
mkdirSync3(stateDir, { recursive: true, mode: HOOX_DIR_MODE });
|
|
21925
22084
|
}
|
|
21926
22085
|
try {
|
|
21927
|
-
|
|
22086
|
+
chmodSync3(home, HOOX_DIR_MODE);
|
|
22087
|
+
chmodSync3(stateDir, HOOX_DIR_MODE);
|
|
21928
22088
|
} catch {}
|
|
21929
|
-
const filePath =
|
|
22089
|
+
const filePath = getTuiPreferencesPath();
|
|
21930
22090
|
await Bun.write(filePath, value);
|
|
21931
22091
|
try {
|
|
21932
|
-
|
|
22092
|
+
chmodSync3(filePath, HOOX_CONFIG_FILE_MODE);
|
|
21933
22093
|
} catch {}
|
|
21934
22094
|
},
|
|
21935
22095
|
removeItem: async (_name) => {}
|
|
@@ -22001,7 +22161,7 @@ var init_config_store = __esm(() => {
|
|
|
22001
22161
|
state.notifications[channel] = !state.notifications[channel];
|
|
22002
22162
|
})
|
|
22003
22163
|
})), {
|
|
22004
|
-
name: "hoox-
|
|
22164
|
+
name: "hoox-tui-prefs",
|
|
22005
22165
|
storage: createJSONStorage(() => bunConfigStorage),
|
|
22006
22166
|
partialize: (state) => ({
|
|
22007
22167
|
theme: state.theme,
|
|
@@ -22049,6 +22209,7 @@ __export(exports_src, {
|
|
|
22049
22209
|
resolveHooxRuntimeRoot: () => resolveHooxRuntimeRoot,
|
|
22050
22210
|
resolveHooxPath: () => resolveHooxPath,
|
|
22051
22211
|
resolveDependencies: () => resolveDependencies,
|
|
22212
|
+
rememberMonorepoRoot: () => rememberMonorepoRoot,
|
|
22052
22213
|
readConfigSync: () => readConfigSync,
|
|
22053
22214
|
readConfig: () => readConfig,
|
|
22054
22215
|
parseDashboardManifest: () => parseDashboardManifest,
|
|
@@ -22073,6 +22234,8 @@ __export(exports_src, {
|
|
|
22073
22234
|
headersToObject: () => headersToObject,
|
|
22074
22235
|
hasOperatorClientCredentials: () => hasOperatorClientCredentials,
|
|
22075
22236
|
getTuiEntryCandidates: () => getTuiEntryCandidates,
|
|
22237
|
+
getRememberedMonorepoRoot: () => getRememberedMonorepoRoot,
|
|
22238
|
+
getRememberedMonorepoPath: () => getRememberedMonorepoPath,
|
|
22076
22239
|
getRelativeHooxPath: () => getRelativeHooxPath,
|
|
22077
22240
|
getHooxWranglerPath: () => getHooxWranglerPath,
|
|
22078
22241
|
getHooxStatePath: () => getHooxStatePath,
|
|
@@ -25557,15 +25720,15 @@ __export(exports_fs_secure, {
|
|
|
25557
25720
|
SECRET_FILE_MODE: () => SECRET_FILE_MODE,
|
|
25558
25721
|
SECRET_DIR_MODE: () => SECRET_DIR_MODE
|
|
25559
25722
|
});
|
|
25560
|
-
import { chmodSync as
|
|
25723
|
+
import { chmodSync as chmodSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
25561
25724
|
function secureChmod(path4, mode) {
|
|
25562
25725
|
try {
|
|
25563
|
-
|
|
25726
|
+
chmodSync4(path4, mode);
|
|
25564
25727
|
} catch {}
|
|
25565
25728
|
}
|
|
25566
25729
|
function ensureSecretDir(dir) {
|
|
25567
25730
|
try {
|
|
25568
|
-
|
|
25731
|
+
mkdirSync4(dir, { recursive: true, mode: SECRET_DIR_MODE });
|
|
25569
25732
|
} catch {}
|
|
25570
25733
|
secureChmod(dir, SECRET_DIR_MODE);
|
|
25571
25734
|
}
|
|
@@ -26171,7 +26334,7 @@ var exports_schema_service = {};
|
|
|
26171
26334
|
__export(exports_schema_service, {
|
|
26172
26335
|
SchemaService: () => SchemaService
|
|
26173
26336
|
});
|
|
26174
|
-
import { readFileSync as
|
|
26337
|
+
import { readFileSync as readFileSync7, existsSync as existsSync11 } from "fs";
|
|
26175
26338
|
import { resolve as resolve4 } from "path";
|
|
26176
26339
|
|
|
26177
26340
|
class SchemaService {
|
|
@@ -26207,7 +26370,7 @@ class SchemaService {
|
|
|
26207
26370
|
const errors4 = [];
|
|
26208
26371
|
const isHooxProject = existsSync11(resolve4(this.projectRoot, "wrangler.jsonc")) || existsSync11(resolve4(this.projectRoot, "workers"));
|
|
26209
26372
|
try {
|
|
26210
|
-
const wranglerContent =
|
|
26373
|
+
const wranglerContent = readFileSync7(workerWranglerPath, "utf-8");
|
|
26211
26374
|
errors4.push(...validateWranglerJsonc(name, manifest, wranglerContent));
|
|
26212
26375
|
} catch (e) {
|
|
26213
26376
|
const isErrnoException = e instanceof Error && "code" in e;
|
|
@@ -26221,7 +26384,7 @@ class SchemaService {
|
|
|
26221
26384
|
});
|
|
26222
26385
|
}
|
|
26223
26386
|
try {
|
|
26224
|
-
const rootContent =
|
|
26387
|
+
const rootContent = readFileSync7(rootWranglerPath, "utf-8");
|
|
26225
26388
|
errors4.push(...validateRootSecrets(name, manifest, rootContent));
|
|
26226
26389
|
} catch (e) {
|
|
26227
26390
|
const errMessage = e instanceof Error ? e.message : String(e);
|
|
@@ -26232,7 +26395,7 @@ class SchemaService {
|
|
|
26232
26395
|
});
|
|
26233
26396
|
}
|
|
26234
26397
|
try {
|
|
26235
|
-
const devVarsContent =
|
|
26398
|
+
const devVarsContent = readFileSync7(devVarsPath, "utf-8");
|
|
26236
26399
|
errors4.push(...validateDevVars(name, manifest, devVarsContent));
|
|
26237
26400
|
} catch (e) {
|
|
26238
26401
|
const errMessage = e instanceof Error ? e.message : String(e);
|
|
@@ -26491,7 +26654,7 @@ var init_trace_service = __esm(() => {
|
|
|
26491
26654
|
});
|
|
26492
26655
|
|
|
26493
26656
|
// src/index.ts
|
|
26494
|
-
import { readFileSync as
|
|
26657
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
26495
26658
|
|
|
26496
26659
|
// ../../node_modules/.bun/commander@15.0.0/node_modules/commander/lib/error.js
|
|
26497
26660
|
class CommanderError extends Error {
|
|
@@ -29621,15 +29784,15 @@ async function runInitCommand(options, globalOpts, isNonInteractive) {
|
|
|
29621
29784
|
}
|
|
29622
29785
|
// src/services/setup/setup-service.ts
|
|
29623
29786
|
import {
|
|
29624
|
-
chmodSync as
|
|
29787
|
+
chmodSync as chmodSync5,
|
|
29625
29788
|
existsSync as existsSync7,
|
|
29626
|
-
mkdirSync as
|
|
29789
|
+
mkdirSync as mkdirSync5,
|
|
29627
29790
|
readdirSync as readdirSync3,
|
|
29628
|
-
readFileSync as
|
|
29791
|
+
readFileSync as readFileSync5,
|
|
29629
29792
|
statSync as statSync2,
|
|
29630
|
-
writeFileSync as
|
|
29793
|
+
writeFileSync as writeFileSync3
|
|
29631
29794
|
} from "fs";
|
|
29632
|
-
import { homedir as
|
|
29795
|
+
import { homedir as homedir3 } from "os";
|
|
29633
29796
|
import { join as join7 } from "path";
|
|
29634
29797
|
var ALL_WORKERS = [
|
|
29635
29798
|
"d1-worker",
|
|
@@ -29699,7 +29862,7 @@ function parseEnvFile(text2) {
|
|
|
29699
29862
|
}
|
|
29700
29863
|
function safeChmod(path4, mode) {
|
|
29701
29864
|
try {
|
|
29702
|
-
|
|
29865
|
+
chmodSync5(path4, mode);
|
|
29703
29866
|
} catch {}
|
|
29704
29867
|
}
|
|
29705
29868
|
function loadExistingKeysFromDisk() {
|
|
@@ -29708,7 +29871,7 @@ function loadExistingKeysFromDisk() {
|
|
|
29708
29871
|
return null;
|
|
29709
29872
|
let text2;
|
|
29710
29873
|
try {
|
|
29711
|
-
text2 =
|
|
29874
|
+
text2 = readFileSync5(setupEnv, "utf-8");
|
|
29712
29875
|
} catch {
|
|
29713
29876
|
return null;
|
|
29714
29877
|
}
|
|
@@ -29773,9 +29936,9 @@ class SetupService {
|
|
|
29773
29936
|
API_SERVICE_KEY_BINDING: internalKey
|
|
29774
29937
|
};
|
|
29775
29938
|
if (!existsSync7(KEYS_DIR)) {
|
|
29776
|
-
|
|
29939
|
+
mkdirSync5(KEYS_DIR, { recursive: true });
|
|
29777
29940
|
} else if (!statSync2(KEYS_DIR).isDirectory()) {
|
|
29778
|
-
|
|
29941
|
+
mkdirSync5(KEYS_DIR, { recursive: true });
|
|
29779
29942
|
}
|
|
29780
29943
|
safeChmod(KEYS_DIR, 448);
|
|
29781
29944
|
const setupEnvPath = join7(KEYS_DIR, "setup.env");
|
|
@@ -29798,7 +29961,7 @@ class SetupService {
|
|
|
29798
29961
|
const path4 = `workers/${workerFsDir(worker)}/.dev.vars`;
|
|
29799
29962
|
const meshNames = secretNames;
|
|
29800
29963
|
const meshSet = new Set(meshNames);
|
|
29801
|
-
const existing = existsSync7(path4) ? parseEnvFile(
|
|
29964
|
+
const existing = existsSync7(path4) ? parseEnvFile(readFileSync5(path4, "utf-8")) : new Map;
|
|
29802
29965
|
for (const secretName of meshNames) {
|
|
29803
29966
|
existing.set(secretName, keys[secretName]);
|
|
29804
29967
|
}
|
|
@@ -29958,7 +30121,7 @@ class SetupService {
|
|
|
29958
30121
|
const wranglerPath = join7("workers", workerFsDir(worker), "wrangler.jsonc");
|
|
29959
30122
|
let wranglerContent;
|
|
29960
30123
|
try {
|
|
29961
|
-
wranglerContent =
|
|
30124
|
+
wranglerContent = readFileSync5(wranglerPath, "utf-8");
|
|
29962
30125
|
} catch {
|
|
29963
30126
|
return { ok: false, error: errMsg };
|
|
29964
30127
|
}
|
|
@@ -29976,7 +30139,7 @@ class SetupService {
|
|
|
29976
30139
|
let fixed = wranglerContent.replace(new RegExp(`(\\s*//[^\\n]*\\n)?\\s*"${escapeRegex2(secretName)}"\\s*:\\s*null\\s*,?\\s*\\n?`, "g"), `
|
|
29977
30140
|
`);
|
|
29978
30141
|
fixed = fixed.replace(/"vars"\s*:\s*\{\s*\}/, '"vars": {}');
|
|
29979
|
-
|
|
30142
|
+
writeFileSync3(wranglerPath, fixed, "utf-8");
|
|
29980
30143
|
this.onProgress({
|
|
29981
30144
|
type: "info",
|
|
29982
30145
|
step: "secrets",
|
|
@@ -30095,7 +30258,7 @@ class SetupService {
|
|
|
30095
30258
|
}
|
|
30096
30259
|
const fallbacks = [
|
|
30097
30260
|
"/usr/local/bin/wrangler",
|
|
30098
|
-
join7(
|
|
30261
|
+
join7(homedir3(), ".bun", "bin", "wrangler")
|
|
30099
30262
|
];
|
|
30100
30263
|
for (const fb of fallbacks) {
|
|
30101
30264
|
try {
|
|
@@ -30159,7 +30322,7 @@ class SetupService {
|
|
|
30159
30322
|
for (const name of workerDirs) {
|
|
30160
30323
|
const dir = `workers/${workerFsDir(name)}`;
|
|
30161
30324
|
try {
|
|
30162
|
-
const gitEntry =
|
|
30325
|
+
const gitEntry = readFileSync5(join7(dir, ".git"), "utf-8").trim();
|
|
30163
30326
|
if (gitEntry.startsWith("gitdir:") || gitEntry.length > 0) {
|
|
30164
30327
|
if (!this.isWorkerTreePopulated(dir, name))
|
|
30165
30328
|
missing.push(name);
|
|
@@ -30308,7 +30471,7 @@ class SetupService {
|
|
|
30308
30471
|
const wranglerPath = `workers/${workerFsDir(worker)}/wrangler.jsonc`;
|
|
30309
30472
|
let content;
|
|
30310
30473
|
try {
|
|
30311
|
-
content =
|
|
30474
|
+
content = readFileSync5(wranglerPath, "utf-8");
|
|
30312
30475
|
} catch {
|
|
30313
30476
|
continue;
|
|
30314
30477
|
}
|
|
@@ -30340,7 +30503,7 @@ class SetupService {
|
|
|
30340
30503
|
}
|
|
30341
30504
|
if (modified) {
|
|
30342
30505
|
content = content.replace(/"vars"\s*:\s*\{\s*\}/, '"vars": {}');
|
|
30343
|
-
|
|
30506
|
+
writeFileSync3(wranglerPath, content, "utf-8");
|
|
30344
30507
|
fixed.push(worker);
|
|
30345
30508
|
}
|
|
30346
30509
|
}
|
|
@@ -30495,7 +30658,7 @@ class SetupService {
|
|
|
30495
30658
|
const devVarsPath = `workers/${workerFsDir(worker)}/.dev.vars`;
|
|
30496
30659
|
let devVarsContent;
|
|
30497
30660
|
try {
|
|
30498
|
-
devVarsContent =
|
|
30661
|
+
devVarsContent = readFileSync5(devVarsPath, "utf-8");
|
|
30499
30662
|
} catch {
|
|
30500
30663
|
missing.push(`${worker}:.dev.vars (missing file)`);
|
|
30501
30664
|
continue;
|
|
@@ -31300,8 +31463,8 @@ init_errors4();
|
|
|
31300
31463
|
import {
|
|
31301
31464
|
statSync as statSync3,
|
|
31302
31465
|
existsSync as existsSync8,
|
|
31303
|
-
readFileSync as
|
|
31304
|
-
writeFileSync as
|
|
31466
|
+
readFileSync as readFileSync6,
|
|
31467
|
+
writeFileSync as writeFileSync4,
|
|
31305
31468
|
renameSync
|
|
31306
31469
|
} from "fs";
|
|
31307
31470
|
import { resolve as resolve2 } from "path";
|
|
@@ -32082,7 +32245,7 @@ async function doUpdateInternalUrls(fmt) {
|
|
|
32082
32245
|
process.exitCode = 1 /* ERROR */;
|
|
32083
32246
|
return;
|
|
32084
32247
|
}
|
|
32085
|
-
const content =
|
|
32248
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
32086
32249
|
const errors4 = [];
|
|
32087
32250
|
const parsed = parse6(content, errors4);
|
|
32088
32251
|
if (errors4.length > 0) {
|
|
@@ -32108,12 +32271,12 @@ async function doUpdateInternalUrls(fmt) {
|
|
|
32108
32271
|
formatSuccess("All service URLs already up to date.", fmt);
|
|
32109
32272
|
return;
|
|
32110
32273
|
}
|
|
32111
|
-
const fresh =
|
|
32274
|
+
const fresh = readFileSync6(filePath, "utf-8");
|
|
32112
32275
|
const edits = modify(fresh, ["vars"], vars, {
|
|
32113
32276
|
formattingOptions: { tabSize: 2, insertSpaces: true }
|
|
32114
32277
|
});
|
|
32115
32278
|
const tmpPath = `${filePath}.tmp-${process.pid}`;
|
|
32116
|
-
|
|
32279
|
+
writeFileSync4(tmpPath, applyEdits(fresh, edits), "utf-8");
|
|
32117
32280
|
renameSync(tmpPath, filePath);
|
|
32118
32281
|
formatSuccess(`Updated ${changesCount} service URL(s) in dashboard wrangler.jsonc`, fmt);
|
|
32119
32282
|
} catch (err) {
|
|
@@ -37752,13 +37915,13 @@ import { createRequire } from "module";
|
|
|
37752
37915
|
import { resolve as resolve6, dirname as dirname3, join as join10 } from "path";
|
|
37753
37916
|
import { fileURLToPath } from "url";
|
|
37754
37917
|
import { existsSync as existsSync12, realpathSync } from "fs";
|
|
37755
|
-
import { homedir as
|
|
37918
|
+
import { homedir as homedir4 } from "os";
|
|
37756
37919
|
|
|
37757
37920
|
// src/services/perf/endpoint-resolver.ts
|
|
37758
|
-
import { readFileSync as
|
|
37921
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
37759
37922
|
function readSubdomainPrefix() {
|
|
37760
37923
|
try {
|
|
37761
|
-
const raw =
|
|
37924
|
+
const raw = readFileSync8("wrangler.jsonc", "utf8");
|
|
37762
37925
|
const stripped = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
|
|
37763
37926
|
const parsed = JSON.parse(stripped);
|
|
37764
37927
|
const prefix = parsed.global?.subdomain_prefix;
|
|
@@ -37805,7 +37968,7 @@ function collectTuiCandidates() {
|
|
|
37805
37968
|
const cwdRequire = createRequire(join10(process.cwd(), "package.json"));
|
|
37806
37969
|
pushPackageEntries(candidates, cwdRequire);
|
|
37807
37970
|
} catch {}
|
|
37808
|
-
const bunGlobal = join10(
|
|
37971
|
+
const bunGlobal = join10(homedir4(), ".bun", "install", "global", "node_modules", TUI_PKG);
|
|
37809
37972
|
pushPackageRootEntries(candidates, bunGlobal);
|
|
37810
37973
|
const npmGlobal = process.env.npm_config_prefix?.trim();
|
|
37811
37974
|
if (npmGlobal) {
|
|
@@ -38058,7 +38221,7 @@ TUI exited with code ${code}
|
|
|
38058
38221
|
init_dist4();
|
|
38059
38222
|
init_src();
|
|
38060
38223
|
import { join as join12 } from "path";
|
|
38061
|
-
import { homedir as
|
|
38224
|
+
import { homedir as homedir5 } from "os";
|
|
38062
38225
|
|
|
38063
38226
|
// src/services/runtime/runtime-service.ts
|
|
38064
38227
|
init_src();
|
|
@@ -38163,6 +38326,62 @@ ${install.stderr || install.stdout}`.trim());
|
|
|
38163
38326
|
const tuiEntry = firstExisting(getTuiEntryCandidates(repoPath));
|
|
38164
38327
|
return { repoPath, cloned, installed, tuiEntry };
|
|
38165
38328
|
}
|
|
38329
|
+
// src/commands/doctor/doctor-command.ts
|
|
38330
|
+
init_src();
|
|
38331
|
+
|
|
38332
|
+
// src/services/workspace/workspace-context.ts
|
|
38333
|
+
init_src();
|
|
38334
|
+
import { resolve as resolve7 } from "path";
|
|
38335
|
+
var lastWorkspaceContext = null;
|
|
38336
|
+
function getLastWorkspaceContext() {
|
|
38337
|
+
return lastWorkspaceContext;
|
|
38338
|
+
}
|
|
38339
|
+
function ensureWorkspaceContext(options) {
|
|
38340
|
+
const previousCwd = resolve7(options?.cwd ?? process.cwd());
|
|
38341
|
+
const inTest = process.env.BUN_TEST === "1" || false || typeof process.env.BUN_TEST !== "undefined";
|
|
38342
|
+
const allowChdir = options?.force === true || !inTest;
|
|
38343
|
+
const runtime = resolveHooxRuntimeRoot({ cwd: previousCwd });
|
|
38344
|
+
const quiet = options?.quiet === true || process.env.HOOX_CLI_SILENT === "1" || process.argv.includes("--json") || process.argv.includes("--quiet");
|
|
38345
|
+
if (!runtime.root) {
|
|
38346
|
+
const empty = {
|
|
38347
|
+
root: null,
|
|
38348
|
+
source: runtime.source,
|
|
38349
|
+
chdir: false,
|
|
38350
|
+
previousCwd,
|
|
38351
|
+
runtime
|
|
38352
|
+
};
|
|
38353
|
+
lastWorkspaceContext = empty;
|
|
38354
|
+
return empty;
|
|
38355
|
+
}
|
|
38356
|
+
if (runtime.source === "cwd" || runtime.source === "env") {
|
|
38357
|
+
rememberMonorepoRoot(runtime.root);
|
|
38358
|
+
} else if (runtime.source === "remembered") {
|
|
38359
|
+
rememberMonorepoRoot(runtime.root);
|
|
38360
|
+
}
|
|
38361
|
+
if (!process.env.HOOX_REPO) {
|
|
38362
|
+
process.env.HOOX_REPO = runtime.root;
|
|
38363
|
+
}
|
|
38364
|
+
let didChdir = false;
|
|
38365
|
+
const target = resolve7(runtime.root);
|
|
38366
|
+
if (allowChdir && resolve7(process.cwd()) !== target) {
|
|
38367
|
+
process.chdir(target);
|
|
38368
|
+
didChdir = true;
|
|
38369
|
+
if (!quiet) {
|
|
38370
|
+
const label = runtime.source === "remembered" ? "remembered monorepo" : runtime.source === "global" ? "global runtime" : runtime.source === "env" ? "HOOX_REPO" : "monorepo";
|
|
38371
|
+
process.stderr.write(`hoox: using ${label} at ${target}
|
|
38372
|
+
`);
|
|
38373
|
+
}
|
|
38374
|
+
}
|
|
38375
|
+
const ctx = {
|
|
38376
|
+
root: target,
|
|
38377
|
+
source: runtime.source,
|
|
38378
|
+
chdir: didChdir,
|
|
38379
|
+
previousCwd,
|
|
38380
|
+
runtime
|
|
38381
|
+
};
|
|
38382
|
+
lastWorkspaceContext = ctx;
|
|
38383
|
+
return ctx;
|
|
38384
|
+
}
|
|
38166
38385
|
// src/services/operator-security/operator-security-service.ts
|
|
38167
38386
|
init_src();
|
|
38168
38387
|
function collectSecurityHygiene(env = process.env, options) {
|
|
@@ -38413,14 +38632,19 @@ function printCheckIcon(severity) {
|
|
|
38413
38632
|
function printStatus() {
|
|
38414
38633
|
const status = getRuntimeStatus();
|
|
38415
38634
|
const { runtime } = status;
|
|
38635
|
+
const remembered = getRememberedMonorepoRoot();
|
|
38636
|
+
const session = getLastWorkspaceContext();
|
|
38637
|
+
const sourceLabel = session?.source ?? runtime.source;
|
|
38638
|
+
const rootLabel = session?.root ?? runtime.root;
|
|
38416
38639
|
process.stdout.write(theme.heading(`
|
|
38417
38640
|
Hoox doctor
|
|
38418
38641
|
|
|
38419
38642
|
`));
|
|
38420
38643
|
process.stdout.write(`${theme.dim("HOOX_HOME")} ${status.hooxHome}
|
|
38421
38644
|
` + `${theme.dim("Global repo")} ${status.repoPath}
|
|
38422
|
-
` + `${theme.dim("
|
|
38423
|
-
` + `${theme.dim("
|
|
38645
|
+
` + `${theme.dim("Remembered")} ${remembered ?? theme.dim("(none \u2014 run hx once from the monorepo)")}
|
|
38646
|
+
` + `${theme.dim("Runtime root")} ${rootLabel ?? theme.warning("(none)")}
|
|
38647
|
+
` + `${theme.dim("Source")} ${sourceLabel}
|
|
38424
38648
|
` + `${theme.dim("TUI entry")} ${status.tuiEntry ?? theme.warning("(not found)")}
|
|
38425
38649
|
|
|
38426
38650
|
`);
|
|
@@ -38439,10 +38663,10 @@ Hoox doctor
|
|
|
38439
38663
|
detail: status.isSetupRoot ? "monorepo markers present" : status.repoPresent ? "path exists but is not a setup monorepo" : runtime.root ? "optional (runtime resolved elsewhere)" : "missing \u2014 run: hoox doctor --fix-runtime"
|
|
38440
38664
|
},
|
|
38441
38665
|
{
|
|
38442
|
-
ok:
|
|
38666
|
+
ok: rootLabel !== null,
|
|
38443
38667
|
required: true,
|
|
38444
38668
|
label: "Resolved runtime root",
|
|
38445
|
-
detail:
|
|
38669
|
+
detail: rootLabel === null ? "set HOOX_REPO, run hx once inside the monorepo (remembers path), or: hoox doctor --fix-runtime" : `${sourceLabel}: ${rootLabel}`
|
|
38446
38670
|
},
|
|
38447
38671
|
{
|
|
38448
38672
|
ok: status.tuiEntry !== null,
|
|
@@ -38478,7 +38702,7 @@ Hoox doctor
|
|
|
38478
38702
|
return failed === 0 ? 0 /* SUCCESS */ : 1 /* ERROR */;
|
|
38479
38703
|
}
|
|
38480
38704
|
function printSecurityHygieneSection(env = process.env) {
|
|
38481
|
-
const configPath = join12(
|
|
38705
|
+
const configPath = join12(homedir5(), ".hoox", "config.json");
|
|
38482
38706
|
const lines = collectSecurityHygiene(env, { configPath });
|
|
38483
38707
|
process.stdout.write(theme.heading(`Security hygiene
|
|
38484
38708
|
`));
|
|
@@ -38496,7 +38720,7 @@ async function runSecurityDoctor(options) {
|
|
|
38496
38720
|
env.HOOX_API_URL = options.apiUrl;
|
|
38497
38721
|
}
|
|
38498
38722
|
const profile = resolveOperatorTransportProfile(env);
|
|
38499
|
-
const configPath = join12(
|
|
38723
|
+
const configPath = join12(homedir5(), ".hoox", "config.json");
|
|
38500
38724
|
const hygiene = collectSecurityHygiene(env, { configPath });
|
|
38501
38725
|
const isLocal = profile.apiBase.includes("localhost") || profile.apiBase.includes("127.0.0.1");
|
|
38502
38726
|
const shouldProbe = options.probe === true || options.probe !== false && !isLocal;
|
|
@@ -38741,7 +38965,7 @@ function registerDisclaimerCommand(program2) {
|
|
|
38741
38965
|
}));
|
|
38742
38966
|
}
|
|
38743
38967
|
// src/commands/agent/agent-command.ts
|
|
38744
|
-
import { readFileSync as
|
|
38968
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
38745
38969
|
init_errors4();
|
|
38746
38970
|
var PROVIDERS = [
|
|
38747
38971
|
{
|
|
@@ -38775,7 +38999,7 @@ function checkDevVars() {
|
|
|
38775
38999
|
const candidates = ["workers/agent-worker/.dev.vars", ".dev.vars"];
|
|
38776
39000
|
for (const filePath of candidates) {
|
|
38777
39001
|
try {
|
|
38778
|
-
const content =
|
|
39002
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
38779
39003
|
for (const line of content.split(`
|
|
38780
39004
|
`)) {
|
|
38781
39005
|
const trimmed = line.trim();
|
|
@@ -39306,12 +39530,12 @@ EXAMPLES:
|
|
|
39306
39530
|
}
|
|
39307
39531
|
// src/commands/pyne/pyne-command.ts
|
|
39308
39532
|
init_config2();
|
|
39309
|
-
import { existsSync as existsSync14, readFileSync as
|
|
39310
|
-
import { resolve as
|
|
39533
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
|
|
39534
|
+
import { resolve as resolve8 } from "path";
|
|
39311
39535
|
init_errors4();
|
|
39312
39536
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
39313
39537
|
function getPyneWorkerDir() {
|
|
39314
|
-
return
|
|
39538
|
+
return resolve8(process.cwd(), "workers/pyne-worker");
|
|
39315
39539
|
}
|
|
39316
39540
|
function requirePyneDir() {
|
|
39317
39541
|
const dir = getPyneWorkerDir();
|
|
@@ -39325,14 +39549,14 @@ function requirePyneDir() {
|
|
|
39325
39549
|
function loadDevVars(workerDir) {
|
|
39326
39550
|
const out = {};
|
|
39327
39551
|
const candidates = [
|
|
39328
|
-
|
|
39329
|
-
|
|
39552
|
+
resolve8(workerDir, ".dev.vars"),
|
|
39553
|
+
resolve8(process.cwd(), ".dev.vars")
|
|
39330
39554
|
];
|
|
39331
39555
|
for (const filePath of candidates) {
|
|
39332
39556
|
try {
|
|
39333
39557
|
if (!existsSync14(filePath))
|
|
39334
39558
|
continue;
|
|
39335
|
-
const content =
|
|
39559
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
39336
39560
|
for (const line of content.split(`
|
|
39337
39561
|
`)) {
|
|
39338
39562
|
const trimmed = line.trim();
|
|
@@ -39547,11 +39771,11 @@ EXAMPLES:
|
|
|
39547
39771
|
} else if (scriptPath === "-") {
|
|
39548
39772
|
body.script = await new Response(Bun.stdin).text();
|
|
39549
39773
|
} else {
|
|
39550
|
-
const abs =
|
|
39774
|
+
const abs = resolve8(process.cwd(), scriptPath);
|
|
39551
39775
|
if (!existsSync14(abs)) {
|
|
39552
39776
|
throw new CLIError(`Script not found: ${abs}`, 1 /* ERROR */);
|
|
39553
39777
|
}
|
|
39554
|
-
body.script =
|
|
39778
|
+
body.script = readFileSync10(abs, "utf-8");
|
|
39555
39779
|
}
|
|
39556
39780
|
const res = await pyneFetch(baseUrl, "/run", {
|
|
39557
39781
|
method: "POST",
|
|
@@ -39598,11 +39822,11 @@ EXAMPLES:
|
|
|
39598
39822
|
if (!apiKey) {
|
|
39599
39823
|
throw new CLIError("API key required for authenticated endpoints", 2 /* INVALID_USAGE */);
|
|
39600
39824
|
}
|
|
39601
|
-
const abs =
|
|
39825
|
+
const abs = resolve8(process.cwd(), scriptPath);
|
|
39602
39826
|
if (!existsSync14(abs)) {
|
|
39603
39827
|
throw new CLIError(`Script not found: ${abs}`, 1 /* ERROR */);
|
|
39604
39828
|
}
|
|
39605
|
-
const script =
|
|
39829
|
+
const script = readFileSync10(abs, "utf-8");
|
|
39606
39830
|
const baseName = abs.split(/[/\\]/).pop() ?? "script.pine";
|
|
39607
39831
|
const scriptId = opts.id ?? baseName.replace(/\.pine$/i, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
39608
39832
|
const res = await pyneFetch(baseUrl, "/scripts", {
|
|
@@ -39706,7 +39930,7 @@ Pass script flags after -- :
|
|
|
39706
39930
|
pyneCmd.command("sync-vendor").summary("Sync pynescript into python_modules/ for Cloudflare deploy").description(`Runs workers/pyne-worker/scripts/sync_vendor.sh.
|
|
39707
39931
|
Required before wrangler deploy after pulling new PYNE APIs.`).action(withErrorHandling(async () => {
|
|
39708
39932
|
const dir = requirePyneDir();
|
|
39709
|
-
const script =
|
|
39933
|
+
const script = resolve8(dir, "scripts/sync_vendor.sh");
|
|
39710
39934
|
if (!existsSync14(script)) {
|
|
39711
39935
|
throw new CLIError(`sync_vendor.sh not found at ${script}`, 1 /* ERROR */);
|
|
39712
39936
|
}
|
|
@@ -39715,7 +39939,7 @@ Required before wrangler deploy after pulling new PYNE APIs.`).action(withErrorH
|
|
|
39715
39939
|
pyneCmd.command("deploy").summary("sync-vendor then wrangler deploy pyne-worker").option("--skip-sync", "Skip scripts/sync_vendor.sh").action(withErrorHandling(async (opts) => {
|
|
39716
39940
|
const dir = requirePyneDir();
|
|
39717
39941
|
if (!opts.skipSync) {
|
|
39718
|
-
const script =
|
|
39942
|
+
const script = resolve8(dir, "scripts/sync_vendor.sh");
|
|
39719
39943
|
if (existsSync14(script)) {
|
|
39720
39944
|
const code = await spawnInPyneDir(["bash", script], dir);
|
|
39721
39945
|
if (code !== 0) {
|
|
@@ -40277,7 +40501,7 @@ ${theme.dim("Live tail duration reached. Exiting.")}
|
|
|
40277
40501
|
`);
|
|
40278
40502
|
}
|
|
40279
40503
|
} catch {}
|
|
40280
|
-
await new Promise((
|
|
40504
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
40281
40505
|
await poll();
|
|
40282
40506
|
};
|
|
40283
40507
|
await poll();
|
|
@@ -41265,7 +41489,7 @@ function registerCompletionCommand(program2) {
|
|
|
41265
41489
|
// src/ui/banner.ts
|
|
41266
41490
|
init_ansis();
|
|
41267
41491
|
init_theme();
|
|
41268
|
-
import { existsSync as existsSync15, readFileSync as
|
|
41492
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
41269
41493
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
41270
41494
|
import { dirname as dirname4, join as join13 } from "path";
|
|
41271
41495
|
var ORANGE = ansis_default.hex("#ff7f2a");
|
|
@@ -41283,7 +41507,7 @@ function findCliVersion() {
|
|
|
41283
41507
|
const pkgPath = join13(dir, "package.json");
|
|
41284
41508
|
if (existsSync15(pkgPath)) {
|
|
41285
41509
|
try {
|
|
41286
|
-
const pkg = JSON.parse(
|
|
41510
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
41287
41511
|
if (pkg.name === PKG_NAME)
|
|
41288
41512
|
return pkg.version;
|
|
41289
41513
|
} catch {}
|
|
@@ -41918,7 +42142,7 @@ if (typeof Bun === "undefined") {
|
|
|
41918
42142
|
`);
|
|
41919
42143
|
process.exit(1);
|
|
41920
42144
|
}
|
|
41921
|
-
var pkgVersion = JSON.parse(
|
|
42145
|
+
var pkgVersion = JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf-8")).version;
|
|
41922
42146
|
var program2 = new Command;
|
|
41923
42147
|
program2.name("hoox").description("Hoox CLI \u2014 manage Cloudflare Workers, infrastructure, secrets, and deployments").version(`${pkgVersion}
|
|
41924
42148
|
|
|
@@ -42069,6 +42293,7 @@ registerPerfCommand(program2);
|
|
|
42069
42293
|
registerCompletionCommand(program2);
|
|
42070
42294
|
async function main() {
|
|
42071
42295
|
try {
|
|
42296
|
+
ensureWorkspaceContext();
|
|
42072
42297
|
const hasArgs = process.argv.slice(2).length > 0;
|
|
42073
42298
|
if (hasArgs) {
|
|
42074
42299
|
await program2.parseAsync(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoox-sh/hoox-cli",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.7",
|
|
4
4
|
"description": "Hoox CLI — manage Cloudflare Workers, infrastructure, secrets, and deployments (@hoox-sh/hoox-cli)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@hoox-sh/hoox-tui": "workspace:*"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@hoox-sh/hoox-shared": "^1.
|
|
29
|
+
"@hoox-sh/hoox-shared": "^1.3.0",
|
|
30
30
|
"@types/node": "^26.1.1",
|
|
31
31
|
"bun-types": "^1.3.14",
|
|
32
32
|
"typescript": "^6.0.3"
|