miki-moni 0.3.1
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/LICENSE +21 -0
- package/README.md +283 -0
- package/README.zh-CN.md +275 -0
- package/README.zh-TW.md +275 -0
- package/bin/miki.mjs +49 -0
- package/dist/web/assets/favicon-DFpLtP36.svg +13 -0
- package/dist/web/assets/index--89DkyV1.css +1 -0
- package/dist/web/assets/index-CyPlxvOn.js +64 -0
- package/dist/web/index.html +20 -0
- package/dist/web/pair-info.html +138 -0
- package/dist/web-phone/assets/app-CyQWCdKZ.js +64 -0
- package/dist/web-phone/assets/index-D5BUh7Uf.js +1 -0
- package/dist/web-phone/assets/index-D8vY_9ld.css +1 -0
- package/dist/web-phone/index.html +20 -0
- package/hooks/miki-emit.ps1 +56 -0
- package/package.json +89 -0
- package/shared/i18n.ts +915 -0
- package/src/cli/i18n-cli.ts +149 -0
- package/src/cli/miki.ts +168 -0
- package/src/cli/pair.ts +534 -0
- package/src/cli/prompt.ts +6 -0
- package/src/cli/pushable-iter.ts +45 -0
- package/src/cli/setup-self-host.ts +292 -0
- package/src/cli/setup-wizard.ts +130 -0
- package/src/cli/wrap.ts +742 -0
- package/src/config.ts +121 -0
- package/src/crypto.ts +66 -0
- package/src/data-dir.ts +31 -0
- package/src/ext-registry.ts +47 -0
- package/src/hook-handler.ts +86 -0
- package/src/index.ts +279 -0
- package/src/install-hooks.ts +107 -0
- package/src/notifier.ts +21 -0
- package/src/pairing.ts +100 -0
- package/src/protocol-ext.ts +46 -0
- package/src/relay-client.ts +468 -0
- package/src/relay-protocol.ts +57 -0
- package/src/server.ts +1134 -0
- package/src/session-resolver.ts +437 -0
- package/src/session-store.ts +131 -0
- package/src/types.ts +33 -0
- package/src/vscode-bridge.ts +407 -0
- package/src/wrap-process.ts +183 -0
- package/tools/tray.ps1 +286 -0
- package/worker/package.json +24 -0
- package/worker/src/daemon-relay.ts +348 -0
- package/worker/src/env.ts +11 -0
- package/worker/src/handshake.ts +63 -0
- package/worker/src/index.ts +81 -0
- package/worker/src/pairing-code.ts +39 -0
- package/worker/src/pairing-coordinator.ts +145 -0
- package/worker/wrangler-selfhost.toml +36 -0
- package/worker/wrangler.toml +29 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Tiny i18n module for CLI / setup wizard strings.
|
|
2
|
+
//
|
|
3
|
+
// Kept separate from web/i18n.ts (which serves the dashboard); CLI strings
|
|
4
|
+
// rarely change and don't need a full framework. Add new keys at the bottom
|
|
5
|
+
// of LOCALES and call `t(key)` from CLI code.
|
|
6
|
+
//
|
|
7
|
+
// Locale resolution order:
|
|
8
|
+
// 1. explicit setLocale() call (used after wizard picks one)
|
|
9
|
+
// 2. config.locale loaded from ~/.miki-moni/config.json
|
|
10
|
+
// 3. process.env.MIKI_LOCALE (override for one-off CLI runs)
|
|
11
|
+
// 4. "en" default
|
|
12
|
+
|
|
13
|
+
import type { Locale } from "../config.js";
|
|
14
|
+
|
|
15
|
+
type Bundle = Record<string, string>;
|
|
16
|
+
|
|
17
|
+
const LOCALES: Record<Locale, Bundle> = {
|
|
18
|
+
"en": {
|
|
19
|
+
"wizard.welcome": "✨ Welcome to miki-moni! First-time setup.",
|
|
20
|
+
"wizard.pick.language": "Which language?",
|
|
21
|
+
"wizard.pick.relay": "How should phones reach the daemon?",
|
|
22
|
+
"wizard.choice.hosted": "Hosted relay (recommended) — use relay.f1telemetrystationpro.org, zero setup",
|
|
23
|
+
"wizard.choice.hosted.desc": "Free shared relay. Zero-knowledge — relay never sees your content.",
|
|
24
|
+
"wizard.choice.selfhost": "Self-host — auto-deploy to your own Cloudflare account",
|
|
25
|
+
"wizard.choice.selfhost.desc": "Needs CF account + wrangler. ~5 minutes. Fully independent.",
|
|
26
|
+
"wizard.choice.local": "Local-only — only 127.0.0.1:8765 dashboard, no phone",
|
|
27
|
+
"wizard.choice.local.desc": "Most secure, but no phone / cross-machine access.",
|
|
28
|
+
"selfhost.intro.1": "I'll set up two things on your own Cloudflare account:",
|
|
29
|
+
"selfhost.intro.2": "1. Relay server — the encrypted forwarding backend",
|
|
30
|
+
"selfhost.intro.3": "2. Phone app — the web page your phone / second laptop opens",
|
|
31
|
+
"selfhost.intro.4": "Both free; CF's free tier covers personal use.",
|
|
32
|
+
"selfhost.wrangler.missing": "✗ wrangler (Cloudflare's deploy tool) not found.",
|
|
33
|
+
"selfhost.wrangler.install": " Install: npm install -g wrangler",
|
|
34
|
+
"selfhost.wrangler.retry": " Then re-run: miki setup",
|
|
35
|
+
"selfhost.step1": "[1/3] Sign in to Cloudflare",
|
|
36
|
+
"selfhost.step1.browser": " Your browser will open for OAuth login…",
|
|
37
|
+
"selfhost.step1.ok": "✓ Cloudflare login successful",
|
|
38
|
+
"selfhost.step2": "[2/3] Deploying Relay server …",
|
|
39
|
+
"selfhost.step2.multiacc": "⚠ Multiple Cloudflare accounts detected. Pick one:",
|
|
40
|
+
"selfhost.step2.pickacc": "Which CF account?",
|
|
41
|
+
"selfhost.step2.retry": "→ Retrying with account",
|
|
42
|
+
"selfhost.step2.fail": "✗ Relay deploy failed. See wrangler output above; usually it's CF account permissions.",
|
|
43
|
+
"selfhost.step2.ok": "✓ Relay deployed to",
|
|
44
|
+
"selfhost.step2.urlfail": "but wrangler didn't print the workers.dev URL.",
|
|
45
|
+
"selfhost.step2.urlinput": "Worker URL (https://...workers.dev):",
|
|
46
|
+
"selfhost.step3": "[3/3] Deploying Phone app …",
|
|
47
|
+
"selfhost.step3.fail": "✗ Phone app deploy failed.",
|
|
48
|
+
"selfhost.step3.ok": "✓ Phone app deployed to",
|
|
49
|
+
"selfhost.done.title": "✓ Done! Settings saved to ~/.miki-moni/config.json:",
|
|
50
|
+
"banner.title": "📱 Phone pairing — scan QR, open URL, or type the 16-char code:",
|
|
51
|
+
"banner.local": "Local: ",
|
|
52
|
+
"banner.local.hint": "← Open on the same machine, no relay",
|
|
53
|
+
"banner.permanent": "(QR / URL / Code are permanent — rotate with `miki pair --rotate`)",
|
|
54
|
+
},
|
|
55
|
+
"zh-TW": {
|
|
56
|
+
"wizard.welcome": "✨ 歡迎使用 miki-moni!首次設定。",
|
|
57
|
+
"wizard.pick.language": "請選語言:",
|
|
58
|
+
"wizard.pick.relay": "配對手機要透過哪條路徑連回 daemon?",
|
|
59
|
+
"wizard.choice.hosted": "Hosted relay(推薦) — 用 relay.f1telemetrystationpro.org,零設定",
|
|
60
|
+
"wizard.choice.hosted.desc": "免費共用 relay。Zero-knowledge — relay 看不到你內容。99% 使用者選這個。",
|
|
61
|
+
"wizard.choice.selfhost": "Self-host — 自動部署到你的 Cloudflare 帳號",
|
|
62
|
+
"wizard.choice.selfhost.desc": "需要 CF 帳號 + wrangler。約 5 分鐘。完全自主,不依賴作者基礎設施。",
|
|
63
|
+
"wizard.choice.local": "Local-only — 只用 127.0.0.1:8765 dashboard,不配手機",
|
|
64
|
+
"wizard.choice.local.desc": "完全本機。安全度最高,但手機 / 跨機器無法用。",
|
|
65
|
+
"selfhost.intro.1": "我會幫你在你自己的 Cloudflare 帳號上開兩個東西:",
|
|
66
|
+
"selfhost.intro.2": "1. Relay server — 轉發加密訊息的後端",
|
|
67
|
+
"selfhost.intro.3": "2. Phone app — 手機 / 第二台電腦開的網頁",
|
|
68
|
+
"selfhost.intro.4": "完全免費,CF 免費層額度足夠個人用。",
|
|
69
|
+
"selfhost.wrangler.missing": "✗ 找不到 wrangler(Cloudflare 的部署工具)。",
|
|
70
|
+
"selfhost.wrangler.install": " 請先跑:npm install -g wrangler",
|
|
71
|
+
"selfhost.wrangler.retry": " 然後重來:miki setup",
|
|
72
|
+
"selfhost.step1": "[1/3] 登入 Cloudflare",
|
|
73
|
+
"selfhost.step1.browser": " 瀏覽器即將開啟,請完成 OAuth 登入…",
|
|
74
|
+
"selfhost.step1.ok": "✓ Cloudflare 登入成功",
|
|
75
|
+
"selfhost.step2": "[2/3] 部署 Relay server …",
|
|
76
|
+
"selfhost.step2.multiacc": "⚠ 偵測到你有多個 Cloudflare 帳號,請選一個來部署:",
|
|
77
|
+
"selfhost.step2.pickacc": "用哪個 CF 帳號?",
|
|
78
|
+
"selfhost.step2.retry": "→ 用帳號重新部署",
|
|
79
|
+
"selfhost.step2.fail": "✗ Relay 部署失敗。看上面的 wrangler 訊息找原因;常見是 CF 帳號權限不足。",
|
|
80
|
+
"selfhost.step2.ok": "✓ Relay 已部署到",
|
|
81
|
+
"selfhost.step2.urlfail": "但 wrangler 沒印出 workers.dev URL。",
|
|
82
|
+
"selfhost.step2.urlinput": "Worker URL (https://...workers.dev):",
|
|
83
|
+
"selfhost.step3": "[3/3] 部署 Phone app …",
|
|
84
|
+
"selfhost.step3.fail": "✗ Phone app 部署失敗。",
|
|
85
|
+
"selfhost.step3.ok": "✓ Phone app 已部署到",
|
|
86
|
+
"selfhost.done.title": "✓ 完成!設定已存進 ~/.miki-moni/config.json:",
|
|
87
|
+
"banner.title": "📱 Phone pairing — scan QR, open URL, or type the 16-char code:",
|
|
88
|
+
"banner.local": "Local: ",
|
|
89
|
+
"banner.local.hint": "← 同台電腦在這看 dashboard,不走 relay",
|
|
90
|
+
"banner.permanent": "(QR / URL / Code 是永久 — `miki pair --rotate` 可換)",
|
|
91
|
+
},
|
|
92
|
+
"zh-CN": {
|
|
93
|
+
"wizard.welcome": "✨ 欢迎使用 miki-moni!首次设置。",
|
|
94
|
+
"wizard.pick.language": "请选语言:",
|
|
95
|
+
"wizard.pick.relay": "手机配对要走哪条路径连回 daemon?",
|
|
96
|
+
"wizard.choice.hosted": "Hosted relay(推荐) — 用 relay.f1telemetrystationpro.org,零设置",
|
|
97
|
+
"wizard.choice.hosted.desc": "免费共用 relay。Zero-knowledge — relay 看不到你内容。",
|
|
98
|
+
"wizard.choice.selfhost": "Self-host — 自动部署到你自己的 Cloudflare 账号",
|
|
99
|
+
"wizard.choice.selfhost.desc": "需要 CF 账号 + wrangler。约 5 分钟。完全自主。",
|
|
100
|
+
"wizard.choice.local": "Local-only — 只用 127.0.0.1:8765 dashboard,不连手机",
|
|
101
|
+
"wizard.choice.local.desc": "最安全,但手机 / 跨设备无法用。",
|
|
102
|
+
"selfhost.intro.1": "我会在你自己的 Cloudflare 账号上开两个东西:",
|
|
103
|
+
"selfhost.intro.2": "1. Relay server — 转发加密消息的后端",
|
|
104
|
+
"selfhost.intro.3": "2. Phone app — 手机 / 第二台电脑打开的网页",
|
|
105
|
+
"selfhost.intro.4": "完全免费,CF 免费层额度够个人用。",
|
|
106
|
+
"selfhost.wrangler.missing": "✗ 找不到 wrangler(Cloudflare 的部署工具)。",
|
|
107
|
+
"selfhost.wrangler.install": " 请先跑:npm install -g wrangler",
|
|
108
|
+
"selfhost.wrangler.retry": " 然后重来:miki setup",
|
|
109
|
+
"selfhost.step1": "[1/3] 登录 Cloudflare",
|
|
110
|
+
"selfhost.step1.browser": " 浏览器即将打开,请完成 OAuth 登录…",
|
|
111
|
+
"selfhost.step1.ok": "✓ Cloudflare 登录成功",
|
|
112
|
+
"selfhost.step2": "[2/3] 部署 Relay server …",
|
|
113
|
+
"selfhost.step2.multiacc": "⚠ 检测到你有多个 Cloudflare 账号,请选一个:",
|
|
114
|
+
"selfhost.step2.pickacc": "用哪个 CF 账号?",
|
|
115
|
+
"selfhost.step2.retry": "→ 用账号重新部署",
|
|
116
|
+
"selfhost.step2.fail": "✗ Relay 部署失败。看上面 wrangler 输出找原因;通常是 CF 账号权限不足。",
|
|
117
|
+
"selfhost.step2.ok": "✓ Relay 已部署到",
|
|
118
|
+
"selfhost.step2.urlfail": "但 wrangler 没打印 workers.dev URL。",
|
|
119
|
+
"selfhost.step2.urlinput": "Worker URL (https://...workers.dev):",
|
|
120
|
+
"selfhost.step3": "[3/3] 部署 Phone app …",
|
|
121
|
+
"selfhost.step3.fail": "✗ Phone app 部署失败。",
|
|
122
|
+
"selfhost.step3.ok": "✓ Phone app 已部署到",
|
|
123
|
+
"selfhost.done.title": "✓ 完成!设置已存进 ~/.miki-moni/config.json:",
|
|
124
|
+
"banner.title": "📱 手机配对 — 扫 QR、打开 URL,或输入 16 位码:",
|
|
125
|
+
"banner.local": "Local: ",
|
|
126
|
+
"banner.local.hint": "← 同台电脑在这看 dashboard,不走 relay",
|
|
127
|
+
"banner.permanent": "(QR / URL / Code 永久 — `miki pair --rotate` 可换)",
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
let active: Locale = (process.env.MIKI_LOCALE as Locale) ?? "en";
|
|
132
|
+
|
|
133
|
+
export function setLocale(l: Locale): void {
|
|
134
|
+
active = l;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function getLocale(): Locale {
|
|
138
|
+
return active;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function t(key: string): string {
|
|
142
|
+
return LOCALES[active]?.[key] ?? LOCALES.en[key] ?? key;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const LOCALE_CHOICES: Array<{ name: string; value: Locale }> = [
|
|
146
|
+
{ name: "English", value: "en" },
|
|
147
|
+
{ name: "繁體中文 (Traditional Chinese)", value: "zh-TW" },
|
|
148
|
+
{ name: "简体中文 (Simplified Chinese)", value: "zh-CN" },
|
|
149
|
+
];
|
package/src/cli/miki.ts
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `miki` — miki-moni CLI dispatcher.
|
|
3
|
+
//
|
|
4
|
+
// Subcommands:
|
|
5
|
+
// miki claude [args] Wrap a Claude Code session (auto-spawns daemon if down)
|
|
6
|
+
// miki start Run the daemon explicitly (foreground)
|
|
7
|
+
// miki pair Show / rotate / list the phone pairing QR
|
|
8
|
+
// miki install-hooks Install Claude Code hooks so non-wrapped panels show up in dashboard
|
|
9
|
+
|
|
10
|
+
import qrcode from "qrcode-terminal";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { loadOrInitConfig, saveConfig } from "../config.js";
|
|
13
|
+
import { CONFIG_FILE, PORT_FILE } from "../data-dir.js";
|
|
14
|
+
import { generateNewPairingToken, pairingQrPayload } from "../pairing.js";
|
|
15
|
+
import { runSetupWizard, shouldRunWizard } from "./setup-wizard.js";
|
|
16
|
+
import { setLocale, t } from "./i18n-cli.js";
|
|
17
|
+
|
|
18
|
+
/** Load the user's preferred locale from config (set by wizard step 0) so
|
|
19
|
+
* subsequent CLI output uses it. Best-effort: silent fall back to "en". */
|
|
20
|
+
async function applySavedLocale(): Promise<void> {
|
|
21
|
+
try {
|
|
22
|
+
const cfg = await loadOrInitConfig(CONFIG_FILE);
|
|
23
|
+
if (cfg.locale) setLocale(cfg.locale);
|
|
24
|
+
} catch { /* first-run, fine */ }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Last port the daemon listened on (written to PORT_FILE on startup). If
|
|
28
|
+
* this is the first ever run there's no file yet, so fall back to the
|
|
29
|
+
* conventional default; the daemon's own "listening on" line will show the
|
|
30
|
+
* real port a second later if it differs. */
|
|
31
|
+
function lastKnownPort(): number {
|
|
32
|
+
try {
|
|
33
|
+
const n = Number(readFileSync(PORT_FILE, "utf8").trim());
|
|
34
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
35
|
+
} catch { /* first run */ }
|
|
36
|
+
return 8765;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Print "scan / open / type" banner whenever the user starts the daemon. Read
|
|
40
|
+
* the persistent pair token from config; auto-generate one on first ever run
|
|
41
|
+
* so a fresh `npm install -g miki-moni && miki start` already has a working
|
|
42
|
+
* QR to wave at a phone. */
|
|
43
|
+
async function printPairBanner(): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
let cfg = await loadOrInitConfig(CONFIG_FILE);
|
|
46
|
+
const workerUrl = cfg.remote?.worker_url;
|
|
47
|
+
if (!workerUrl) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
let token = cfg.remote!.pair_token;
|
|
51
|
+
if (!token) {
|
|
52
|
+
token = generateNewPairingToken();
|
|
53
|
+
cfg = {
|
|
54
|
+
...cfg,
|
|
55
|
+
remote: { ...cfg.remote!, pair_token: token },
|
|
56
|
+
};
|
|
57
|
+
await saveConfig(CONFIG_FILE, cfg);
|
|
58
|
+
}
|
|
59
|
+
const payload = pairingQrPayload({
|
|
60
|
+
worker_url: workerUrl,
|
|
61
|
+
pairing_token: token,
|
|
62
|
+
daemon_pubkey: cfg.device.pubkey,
|
|
63
|
+
device_name: cfg.device.name,
|
|
64
|
+
phone_pwa_url: cfg.remote?.phone_pwa_url,
|
|
65
|
+
});
|
|
66
|
+
const grouped = token.match(/.{1,4}/g)!.join("-");
|
|
67
|
+
const localUrl = `http://127.0.0.1:${lastKnownPort()}`;
|
|
68
|
+
console.log("");
|
|
69
|
+
console.log(t("banner.title"));
|
|
70
|
+
console.log("");
|
|
71
|
+
qrcode.generate(payload, { small: true });
|
|
72
|
+
console.log("");
|
|
73
|
+
console.log(" URL: " + payload);
|
|
74
|
+
console.log(" Code: " + grouped);
|
|
75
|
+
console.log(" " + t("banner.local") + localUrl + " " + t("banner.local.hint"));
|
|
76
|
+
console.log(" " + t("banner.permanent"));
|
|
77
|
+
console.log("");
|
|
78
|
+
} catch (e) {
|
|
79
|
+
// Best-effort: never block daemon startup on banner failure.
|
|
80
|
+
console.error("miki: pair banner skipped: " + (e as Error).message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** First-run setup wizard. Runs once on a fresh install before the daemon
|
|
85
|
+
* starts; subsequent runs no-op silently. Wizard writes Config.remote — so
|
|
86
|
+
* the rest of the pipeline (banner, RelayClient) "just works" after. */
|
|
87
|
+
async function maybeRunSetupWizard(): Promise<void> {
|
|
88
|
+
try {
|
|
89
|
+
const cfg = await loadOrInitConfig(CONFIG_FILE);
|
|
90
|
+
if (!await shouldRunWizard(cfg)) return;
|
|
91
|
+
const next = await runSetupWizard(cfg);
|
|
92
|
+
await saveConfig(CONFIG_FILE, next);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
console.error("miki setup: " + (e as Error).message);
|
|
95
|
+
// Don't crash daemon startup — they can re-run `miki setup` later.
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function main(): Promise<void> {
|
|
100
|
+
await applySavedLocale();
|
|
101
|
+
const sub = process.argv[2];
|
|
102
|
+
if (sub === "claude") {
|
|
103
|
+
await import("./wrap.js");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (sub === "start" || sub === "daemon") {
|
|
107
|
+
await maybeRunSetupWizard();
|
|
108
|
+
await printPairBanner();
|
|
109
|
+
await import("../index.js");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (sub === "setup") {
|
|
113
|
+
// Explicit re-trigger of the wizard. Removes the local-only marker so the
|
|
114
|
+
// user can change their mind, then runs it.
|
|
115
|
+
const fs = await import("node:fs/promises");
|
|
116
|
+
const path = await import("node:path");
|
|
117
|
+
const { HUB_HOME } = await import("../data-dir.js");
|
|
118
|
+
try { await fs.unlink(path.join(HUB_HOME, "wizard-local-only")); } catch { /* not there, fine */ }
|
|
119
|
+
let cfg = await loadOrInitConfig(CONFIG_FILE);
|
|
120
|
+
// Preserve pair_token across wizard re-runs — existing paired phones use
|
|
121
|
+
// it; only the relay URL is being reconfigured.
|
|
122
|
+
const preservedToken = cfg.remote?.pair_token;
|
|
123
|
+
cfg = { ...cfg };
|
|
124
|
+
delete (cfg as any).remote;
|
|
125
|
+
let next = await runSetupWizard(cfg);
|
|
126
|
+
if (preservedToken && next.remote) {
|
|
127
|
+
next = { ...next, remote: { ...next.remote, pair_token: preservedToken } };
|
|
128
|
+
}
|
|
129
|
+
await saveConfig(CONFIG_FILE, next);
|
|
130
|
+
console.log("✓ Setup complete. Starting daemon…");
|
|
131
|
+
console.log("");
|
|
132
|
+
await printPairBanner();
|
|
133
|
+
await import("../index.js");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (sub === "pair") {
|
|
137
|
+
// Strip "pair" so pair.ts's parseArgs (which reads slice(2)) sees the
|
|
138
|
+
// remaining flags (--rotate, --new, --list, --revoke, etc.) as if it
|
|
139
|
+
// were invoked directly.
|
|
140
|
+
process.argv = [process.argv[0]!, process.argv[1]!, ...process.argv.slice(3)];
|
|
141
|
+
await import("./pair.js");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (sub === "install-hooks") {
|
|
145
|
+
await import("../install-hooks.js");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
console.error("usage: miki <subcommand>");
|
|
149
|
+
console.error("");
|
|
150
|
+
console.error("subcommands:");
|
|
151
|
+
console.error(" claude [-c | -r <uuid> | --fresh] [--model X] [--bypass-permissions]");
|
|
152
|
+
console.error(" Wrap a Claude Code session. Auto-spawns the daemon if not");
|
|
153
|
+
console.error(" already running. Dashboard at http://localhost:8765");
|
|
154
|
+
console.error(" start Run the miki-moni daemon in the foreground.");
|
|
155
|
+
console.error(" Prints the phone-pairing QR + URL + 16-char code on startup.");
|
|
156
|
+
console.error(" First run also walks through a setup wizard.");
|
|
157
|
+
console.error(" setup Re-run the first-run wizard (hosted vs self-host vs local-only).");
|
|
158
|
+
console.error(" pair Show the permanent phone-pairing QR. Subcommands:");
|
|
159
|
+
console.error(" miki pair show current QR (auto-creates on first run)");
|
|
160
|
+
console.error(" miki pair --rotate regenerate token");
|
|
161
|
+
console.error(" miki pair --list list paired phones");
|
|
162
|
+
console.error(" miki pair --revoke <id> revoke a paired phone");
|
|
163
|
+
console.error(" install-hooks");
|
|
164
|
+
console.error(" Install Claude Code hooks into ~/.claude/settings.json so");
|
|
165
|
+
console.error(" non-wrapped VSCode panels also show up in the dashboard.");
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
main();
|