ccjk 11.1.0 → 11.1.2
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 +36 -0
- package/README.zh-CN.md +49 -0
- package/dist/chunks/features.mjs +105 -16
- package/dist/chunks/menu.mjs +22 -24
- package/dist/chunks/notification.mjs +9 -494
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/remote.mjs +346 -27
- package/dist/cli.mjs +23 -2
- package/dist/i18n/locales/en/notification.json +1 -0
- package/dist/i18n/locales/en/remote.json +54 -0
- package/dist/i18n/locales/zh-CN/notification.json +1 -0
- package/dist/i18n/locales/zh-CN/remote.json +54 -0
- package/dist/shared/ccjk.CGcy7cNM.mjs +495 -0
- package/package.json +4 -3
- package/templates/claude-code/common/settings.json +28 -20
package/dist/chunks/remote.mjs
CHANGED
|
@@ -1,44 +1,208 @@
|
|
|
1
|
-
import { l as logger } from '../shared/ccjk.Cu_R2MbQ.mjs';
|
|
2
|
-
import { i18n } from './index2.mjs';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
1
|
import { spawn } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import ora from 'ora';
|
|
5
5
|
import { homedir } from 'os';
|
|
6
6
|
import { join } from 'path';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import '
|
|
7
|
+
import { i18n } from './index2.mjs';
|
|
8
|
+
import { i as isDeviceBound, g as getBindingStatus, b as bindDevice } from '../shared/ccjk.CGcy7cNM.mjs';
|
|
9
|
+
import { l as logger } from '../shared/ccjk.Cu_R2MbQ.mjs';
|
|
10
10
|
import 'node:fs';
|
|
11
11
|
import 'node:process';
|
|
12
12
|
import 'node:url';
|
|
13
13
|
import 'i18next';
|
|
14
14
|
import 'i18next-fs-backend';
|
|
15
15
|
import 'pathe';
|
|
16
|
+
import 'node:os';
|
|
17
|
+
import './fs-operations.mjs';
|
|
18
|
+
import 'node:crypto';
|
|
19
|
+
import 'node:fs/promises';
|
|
20
|
+
import 'node:buffer';
|
|
21
|
+
import 'ansis';
|
|
16
22
|
|
|
17
23
|
const DAEMON_CONFIG_PATH = join(homedir(), ".ccjk", "daemon.json");
|
|
24
|
+
async function setupRemote(options = {}) {
|
|
25
|
+
const { nonInteractive = false, json = false, serverUrl, authToken, bindingCode } = options;
|
|
26
|
+
if (!json) {
|
|
27
|
+
console.log(i18n.t("remote:setup.title"));
|
|
28
|
+
}
|
|
29
|
+
let config = await ensureRemoteEnabled({ quiet: true, nonInteractive, serverUrl });
|
|
30
|
+
if (!config) {
|
|
31
|
+
if (json) {
|
|
32
|
+
const result = {
|
|
33
|
+
success: false,
|
|
34
|
+
error: i18n.t("remote:setup.failed_enable")
|
|
35
|
+
};
|
|
36
|
+
console.log(JSON.stringify(result, null, 2));
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
config = await ensureAuthTokenConfigured(config, { nonInteractive, authToken });
|
|
42
|
+
if (!config) {
|
|
43
|
+
if (json) {
|
|
44
|
+
const result = {
|
|
45
|
+
success: false,
|
|
46
|
+
error: i18n.t("remote:setup.failed_auth")
|
|
47
|
+
};
|
|
48
|
+
console.log(JSON.stringify(result, null, 2));
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!isDeviceBound()) {
|
|
54
|
+
const code = await resolveBindingCode(bindingCode, nonInteractive);
|
|
55
|
+
if (!code) {
|
|
56
|
+
if (json) {
|
|
57
|
+
const result2 = {
|
|
58
|
+
success: false,
|
|
59
|
+
error: i18n.t("remote:setup.failed_bind_missing_code")
|
|
60
|
+
};
|
|
61
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const result = await bindDevice(code);
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
if (json) {
|
|
69
|
+
const setupResult2 = {
|
|
70
|
+
success: false,
|
|
71
|
+
error: result.error || i18n.t("remote:setup.binding_failed")
|
|
72
|
+
};
|
|
73
|
+
console.log(JSON.stringify(setupResult2, null, 2));
|
|
74
|
+
} else {
|
|
75
|
+
console.log(i18n.t("remote:setup.binding_failed"));
|
|
76
|
+
if (result.error) {
|
|
77
|
+
console.log(` ${result.error}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!json) {
|
|
84
|
+
console.log(i18n.t("remote:setup.binding_success"));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await startDaemon({ silent: json });
|
|
88
|
+
const daemonRunning = await isDaemonRunning();
|
|
89
|
+
const bindStatus = await getBindingStatus();
|
|
90
|
+
const setupResult = {
|
|
91
|
+
success: daemonRunning && bindStatus.bound,
|
|
92
|
+
daemonRunning,
|
|
93
|
+
bound: bindStatus.bound,
|
|
94
|
+
serverUrl: config.serverUrl,
|
|
95
|
+
machineId: config.machineId
|
|
96
|
+
};
|
|
97
|
+
if (json) {
|
|
98
|
+
console.log(JSON.stringify(setupResult, null, 2));
|
|
99
|
+
if (!setupResult.success) {
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (daemonRunning && bindStatus.bound) {
|
|
105
|
+
console.log(i18n.t("remote:setup.ready"));
|
|
106
|
+
await remoteStatus();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
console.log(i18n.t("remote:setup.partial"));
|
|
110
|
+
await remoteStatus();
|
|
111
|
+
}
|
|
18
112
|
async function enableRemote() {
|
|
19
113
|
console.log(i18n.t("remote:enable.title"));
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
22
|
-
console.log(i18n.t("remote:enable.already_enabled"));
|
|
114
|
+
const enabledConfig = await ensureRemoteEnabled();
|
|
115
|
+
if (!enabledConfig) {
|
|
23
116
|
return;
|
|
24
117
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
118
|
+
console.log(i18n.t("remote:enable.success"));
|
|
119
|
+
console.log(i18n.t("remote:enable.next_steps"));
|
|
120
|
+
}
|
|
121
|
+
async function ensureRemoteEnabled(options = {}) {
|
|
122
|
+
const { quiet = false, nonInteractive = false, serverUrl } = options;
|
|
123
|
+
const config = loadDaemonConfig();
|
|
124
|
+
if (config.enabled) {
|
|
125
|
+
if (!quiet) {
|
|
126
|
+
console.log(i18n.t("remote:enable.already_enabled"));
|
|
31
127
|
}
|
|
32
|
-
|
|
128
|
+
return config;
|
|
129
|
+
}
|
|
130
|
+
let resolvedServerUrl = serverUrl?.trim();
|
|
131
|
+
if (!resolvedServerUrl && nonInteractive) {
|
|
132
|
+
console.log(i18n.t("remote:setup.non_interactive_missing_server_url"));
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (resolvedServerUrl && !isValidRemoteServerUrl(resolvedServerUrl)) {
|
|
136
|
+
console.log(i18n.t("remote:setup.invalid_server_url"));
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (!resolvedServerUrl) {
|
|
140
|
+
const promptResult = await inquirer.prompt([
|
|
141
|
+
{
|
|
142
|
+
type: "input",
|
|
143
|
+
name: "serverUrl",
|
|
144
|
+
message: i18n.t("remote:enable.server_url"),
|
|
145
|
+
validate: (value) => {
|
|
146
|
+
const trimmed = value?.trim() || "";
|
|
147
|
+
if (!trimmed) {
|
|
148
|
+
return i18n.t("remote:setup.server_url_required");
|
|
149
|
+
}
|
|
150
|
+
if (!isValidRemoteServerUrl(trimmed)) {
|
|
151
|
+
return i18n.t("remote:setup.invalid_server_url");
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
]);
|
|
157
|
+
resolvedServerUrl = promptResult.serverUrl.trim();
|
|
158
|
+
}
|
|
33
159
|
const machineId = `machine-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
34
160
|
const newConfig = {
|
|
35
161
|
enabled: true,
|
|
36
|
-
serverUrl,
|
|
162
|
+
serverUrl: resolvedServerUrl,
|
|
37
163
|
machineId
|
|
38
164
|
};
|
|
39
165
|
saveDaemonConfig(newConfig);
|
|
40
|
-
|
|
41
|
-
|
|
166
|
+
return newConfig;
|
|
167
|
+
}
|
|
168
|
+
async function ensureAuthTokenConfigured(config, options = {}) {
|
|
169
|
+
const { nonInteractive = false, authToken } = options;
|
|
170
|
+
if (config.authToken && config.authToken.trim().length > 0 && !authToken) {
|
|
171
|
+
return config;
|
|
172
|
+
}
|
|
173
|
+
let resolvedAuthToken = authToken?.trim();
|
|
174
|
+
if (!resolvedAuthToken && config.authToken) {
|
|
175
|
+
resolvedAuthToken = config.authToken.trim();
|
|
176
|
+
}
|
|
177
|
+
if (!resolvedAuthToken && nonInteractive) {
|
|
178
|
+
console.log(i18n.t("remote:setup.non_interactive_missing_auth_token"));
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
if (!resolvedAuthToken) {
|
|
182
|
+
console.log(i18n.t("remote:setup.auth_token_prompt"));
|
|
183
|
+
const promptResult = await inquirer.prompt([
|
|
184
|
+
{
|
|
185
|
+
type: "password",
|
|
186
|
+
name: "authToken",
|
|
187
|
+
mask: "*",
|
|
188
|
+
message: i18n.t("remote:setup.auth_token"),
|
|
189
|
+
validate: (value) => {
|
|
190
|
+
const trimmed = value?.trim() || "";
|
|
191
|
+
if (!trimmed) {
|
|
192
|
+
return i18n.t("remote:setup.auth_token_required");
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
]);
|
|
198
|
+
resolvedAuthToken = promptResult.authToken.trim();
|
|
199
|
+
}
|
|
200
|
+
const updated = {
|
|
201
|
+
...config,
|
|
202
|
+
authToken: resolvedAuthToken
|
|
203
|
+
};
|
|
204
|
+
saveDaemonConfig(updated);
|
|
205
|
+
return updated;
|
|
42
206
|
}
|
|
43
207
|
async function disableRemote() {
|
|
44
208
|
console.log(i18n.t("remote:disable.title"));
|
|
@@ -63,6 +227,103 @@ async function remoteStatus() {
|
|
|
63
227
|
console.log(` ${i18n.t("remote:status.daemon")}: ${daemonRunning ? "\u{1F7E2} Running" : "\u{1F534} Stopped"}`);
|
|
64
228
|
}
|
|
65
229
|
}
|
|
230
|
+
async function doctorRemote(options = {}) {
|
|
231
|
+
const { json = false } = options;
|
|
232
|
+
if (!json) {
|
|
233
|
+
console.log(i18n.t("remote:doctor.title"));
|
|
234
|
+
}
|
|
235
|
+
const config = loadDaemonConfig();
|
|
236
|
+
const checks = [];
|
|
237
|
+
checks.push({
|
|
238
|
+
label: i18n.t("remote:doctor.checks.enabled"),
|
|
239
|
+
ok: config.enabled,
|
|
240
|
+
detail: config.enabled ? i18n.t("remote:doctor.enabled_yes") : i18n.t("remote:doctor.enabled_no"),
|
|
241
|
+
fixHint: config.enabled ? void 0 : i18n.t("remote:doctor.fix_enable")
|
|
242
|
+
});
|
|
243
|
+
const hasServerUrl = !!config.serverUrl;
|
|
244
|
+
checks.push({
|
|
245
|
+
label: i18n.t("remote:doctor.checks.server_url"),
|
|
246
|
+
ok: hasServerUrl,
|
|
247
|
+
detail: hasServerUrl ? config.serverUrl : i18n.t("remote:doctor.missing"),
|
|
248
|
+
fixHint: hasServerUrl ? void 0 : i18n.t("remote:doctor.fix_setup")
|
|
249
|
+
});
|
|
250
|
+
const hasAuthToken = !!config.authToken?.trim();
|
|
251
|
+
checks.push({
|
|
252
|
+
label: i18n.t("remote:doctor.checks.auth_token"),
|
|
253
|
+
ok: hasAuthToken,
|
|
254
|
+
detail: hasAuthToken ? i18n.t("remote:doctor.present") : i18n.t("remote:doctor.missing"),
|
|
255
|
+
fixHint: hasAuthToken ? void 0 : i18n.t("remote:doctor.fix_setup")
|
|
256
|
+
});
|
|
257
|
+
const hasMachineId = !!config.machineId;
|
|
258
|
+
checks.push({
|
|
259
|
+
label: i18n.t("remote:doctor.checks.machine_id"),
|
|
260
|
+
ok: hasMachineId,
|
|
261
|
+
detail: hasMachineId ? config.machineId : i18n.t("remote:doctor.missing"),
|
|
262
|
+
fixHint: hasMachineId ? void 0 : i18n.t("remote:doctor.fix_setup")
|
|
263
|
+
});
|
|
264
|
+
const bound = isDeviceBound();
|
|
265
|
+
checks.push({
|
|
266
|
+
label: i18n.t("remote:doctor.checks.binding"),
|
|
267
|
+
ok: bound,
|
|
268
|
+
detail: bound ? i18n.t("remote:doctor.bound_yes") : i18n.t("remote:doctor.bound_no"),
|
|
269
|
+
fixHint: bound ? void 0 : i18n.t("remote:doctor.fix_bind")
|
|
270
|
+
});
|
|
271
|
+
let cloudReachable = false;
|
|
272
|
+
if (hasServerUrl) {
|
|
273
|
+
cloudReachable = await isServerReachable(config.serverUrl);
|
|
274
|
+
}
|
|
275
|
+
checks.push({
|
|
276
|
+
label: i18n.t("remote:doctor.checks.cloud_health"),
|
|
277
|
+
ok: hasServerUrl && cloudReachable,
|
|
278
|
+
detail: hasServerUrl ? cloudReachable ? i18n.t("remote:doctor.reachable") : i18n.t("remote:doctor.unreachable") : i18n.t("remote:doctor.skipped"),
|
|
279
|
+
fixHint: hasServerUrl && !cloudReachable ? i18n.t("remote:doctor.fix_network") : void 0
|
|
280
|
+
});
|
|
281
|
+
const daemonRunning = await isDaemonRunning();
|
|
282
|
+
checks.push({
|
|
283
|
+
label: i18n.t("remote:doctor.checks.daemon"),
|
|
284
|
+
ok: daemonRunning,
|
|
285
|
+
detail: daemonRunning ? i18n.t("remote:doctor.daemon_running") : i18n.t("remote:doctor.daemon_stopped"),
|
|
286
|
+
fixHint: daemonRunning ? void 0 : i18n.t("remote:doctor.fix_daemon")
|
|
287
|
+
});
|
|
288
|
+
let remoteStatusDetail = i18n.t("remote:doctor.skipped");
|
|
289
|
+
if (bound) {
|
|
290
|
+
const bindStatus = await getBindingStatus();
|
|
291
|
+
remoteStatusDetail = bindStatus.bound ? i18n.t("remote:doctor.bound_yes") : i18n.t("remote:doctor.bound_no");
|
|
292
|
+
}
|
|
293
|
+
const hasFailures = checks.some((check) => !check.ok);
|
|
294
|
+
const doctorResult = {
|
|
295
|
+
success: !hasFailures,
|
|
296
|
+
checks: checks.map((check) => ({
|
|
297
|
+
label: check.label,
|
|
298
|
+
ok: check.ok,
|
|
299
|
+
detail: check.detail,
|
|
300
|
+
fixHint: check.fixHint
|
|
301
|
+
})),
|
|
302
|
+
bindingStatus: remoteStatusDetail
|
|
303
|
+
};
|
|
304
|
+
if (json) {
|
|
305
|
+
console.log(JSON.stringify(doctorResult, null, 2));
|
|
306
|
+
if (hasFailures) {
|
|
307
|
+
process.exitCode = 1;
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
checks.forEach((check) => {
|
|
312
|
+
const icon = check.ok ? "\u2705" : "\u274C";
|
|
313
|
+
const detail = check.detail ? ` (${check.detail})` : "";
|
|
314
|
+
console.log(` ${icon} ${check.label}${detail}`);
|
|
315
|
+
if (!check.ok && check.fixHint) {
|
|
316
|
+
console.log(` ${i18n.t("remote:doctor.fix_prefix")}${check.fixHint}`);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
console.log(` \u2139\uFE0F ${i18n.t("remote:doctor.binding_status")}: ${remoteStatusDetail}`);
|
|
320
|
+
if (hasFailures) {
|
|
321
|
+
console.log(i18n.t("remote:doctor.summary_failed"));
|
|
322
|
+
process.exitCode = 1;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
console.log(i18n.t("remote:doctor.summary_ok"));
|
|
326
|
+
}
|
|
66
327
|
async function showQRCode() {
|
|
67
328
|
const config = loadDaemonConfig();
|
|
68
329
|
if (!config.enabled) {
|
|
@@ -84,11 +345,14 @@ async function showQRCode() {
|
|
|
84
345
|
console.log(` Server: ${config.serverUrl}`);
|
|
85
346
|
console.log(` Machine ID: ${config.machineId}`);
|
|
86
347
|
}
|
|
87
|
-
async function startDaemon() {
|
|
88
|
-
const
|
|
348
|
+
async function startDaemon(options = {}) {
|
|
349
|
+
const { silent = false } = options;
|
|
350
|
+
const spinner = silent ? null : ora(i18n.t("remote:daemon.starting")).start();
|
|
89
351
|
try {
|
|
90
352
|
if (await isDaemonRunning()) {
|
|
91
|
-
spinner
|
|
353
|
+
if (spinner) {
|
|
354
|
+
spinner.warn(i18n.t("remote:daemon.already_running"));
|
|
355
|
+
}
|
|
92
356
|
return;
|
|
93
357
|
}
|
|
94
358
|
const daemon = spawn("ccjk-daemon", ["start"], {
|
|
@@ -98,15 +362,52 @@ async function startDaemon() {
|
|
|
98
362
|
daemon.unref();
|
|
99
363
|
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
100
364
|
if (await isDaemonRunning()) {
|
|
101
|
-
spinner
|
|
365
|
+
if (spinner) {
|
|
366
|
+
spinner.succeed(i18n.t("remote:daemon.started"));
|
|
367
|
+
}
|
|
102
368
|
} else {
|
|
103
|
-
spinner
|
|
369
|
+
if (spinner) {
|
|
370
|
+
spinner.fail(i18n.t("remote:daemon.start_failed"));
|
|
371
|
+
}
|
|
372
|
+
process.exitCode = 1;
|
|
104
373
|
}
|
|
105
374
|
} catch (error) {
|
|
106
|
-
spinner
|
|
375
|
+
if (spinner) {
|
|
376
|
+
spinner.fail(i18n.t("remote:daemon.start_error"));
|
|
377
|
+
}
|
|
107
378
|
logger.error("Failed to start daemon:", error);
|
|
379
|
+
process.exitCode = 1;
|
|
108
380
|
}
|
|
109
381
|
}
|
|
382
|
+
async function resolveBindingCode(inputCode, nonInteractive) {
|
|
383
|
+
const code = inputCode?.trim();
|
|
384
|
+
if (code) {
|
|
385
|
+
return code;
|
|
386
|
+
}
|
|
387
|
+
if (nonInteractive) {
|
|
388
|
+
console.log(i18n.t("remote:setup.non_interactive_missing_binding_code"));
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
console.log(i18n.t("remote:setup.binding_prompt"));
|
|
392
|
+
const promptResult = await inquirer.prompt([
|
|
393
|
+
{
|
|
394
|
+
type: "input",
|
|
395
|
+
name: "bindingCode",
|
|
396
|
+
message: i18n.t("remote:setup.binding_code"),
|
|
397
|
+
validate: (value) => {
|
|
398
|
+
const trimmed = value?.trim() || "";
|
|
399
|
+
if (!trimmed) {
|
|
400
|
+
return i18n.t("remote:setup.binding_code_required");
|
|
401
|
+
}
|
|
402
|
+
if (trimmed.length < 4) {
|
|
403
|
+
return i18n.t("remote:setup.binding_code_invalid");
|
|
404
|
+
}
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
]);
|
|
409
|
+
return promptResult.bindingCode.trim();
|
|
410
|
+
}
|
|
110
411
|
async function stopDaemon() {
|
|
111
412
|
const spinner = ora(i18n.t("remote:daemon.stopping")).start();
|
|
112
413
|
try {
|
|
@@ -137,6 +438,24 @@ async function isDaemonRunning() {
|
|
|
137
438
|
return false;
|
|
138
439
|
}
|
|
139
440
|
}
|
|
441
|
+
async function isServerReachable(baseUrl) {
|
|
442
|
+
try {
|
|
443
|
+
const response = await fetch(`${baseUrl.replace(/\/$/, "")}/health`, {
|
|
444
|
+
signal: AbortSignal.timeout(3e3)
|
|
445
|
+
});
|
|
446
|
+
return response.ok;
|
|
447
|
+
} catch {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function isValidRemoteServerUrl(url) {
|
|
452
|
+
try {
|
|
453
|
+
const parsed = new URL(url);
|
|
454
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
455
|
+
} catch {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
140
459
|
function loadDaemonConfig() {
|
|
141
460
|
try {
|
|
142
461
|
if (existsSync(DAEMON_CONFIG_PATH)) {
|
|
@@ -154,7 +473,7 @@ function saveDaemonConfig(config) {
|
|
|
154
473
|
try {
|
|
155
474
|
const dir = join(homedir(), ".ccjk");
|
|
156
475
|
if (!existsSync(dir)) {
|
|
157
|
-
|
|
476
|
+
mkdirSync(dir, { recursive: true });
|
|
158
477
|
}
|
|
159
478
|
writeFileSync(DAEMON_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
160
479
|
} catch (error) {
|
|
@@ -163,4 +482,4 @@ function saveDaemonConfig(config) {
|
|
|
163
482
|
}
|
|
164
483
|
}
|
|
165
484
|
|
|
166
|
-
export { disableRemote, enableRemote, remoteStatus, showQRCode, startDaemon, stopDaemon };
|
|
485
|
+
export { disableRemote, doctorRemote, enableRemote, remoteStatus, setupRemote, showQRCode, startDaemon, stopDaemon };
|
package/dist/cli.mjs
CHANGED
|
@@ -1461,16 +1461,37 @@ ${ansis.yellow("By Status:")}`);
|
|
|
1461
1461
|
description: "Remote control management",
|
|
1462
1462
|
tier: "core",
|
|
1463
1463
|
options: [
|
|
1464
|
+
{ flags: "setup", description: "One-command remote setup" },
|
|
1465
|
+
{ flags: "doctor", description: "Diagnose remote setup" },
|
|
1464
1466
|
{ flags: "enable", description: "Enable remote control" },
|
|
1465
1467
|
{ flags: "disable", description: "Disable remote control" },
|
|
1466
1468
|
{ flags: "status", description: "Show remote status" },
|
|
1467
|
-
{ flags: "qr", description: "Show pairing QR code" }
|
|
1469
|
+
{ flags: "qr", description: "Show pairing QR code" },
|
|
1470
|
+
{ flags: "--json", description: "Output as JSON" },
|
|
1471
|
+
{ flags: "--non-interactive", description: "Fail instead of prompting" },
|
|
1472
|
+
{ flags: "--server-url <url>", description: "Remote server URL for setup" },
|
|
1473
|
+
{ flags: "--auth-token <token>", description: "Remote auth token for setup" },
|
|
1474
|
+
{ flags: "--binding-code <code>", description: "Binding code for setup" }
|
|
1468
1475
|
],
|
|
1469
1476
|
loader: async () => {
|
|
1470
|
-
const { enableRemote, disableRemote, remoteStatus, showQRCode } = await import('./chunks/remote.mjs');
|
|
1477
|
+
const { doctorRemote, enableRemote, disableRemote, remoteStatus, setupRemote, showQRCode } = await import('./chunks/remote.mjs');
|
|
1471
1478
|
return async (options, ...args) => {
|
|
1472
1479
|
const action = args[0];
|
|
1473
1480
|
switch (action) {
|
|
1481
|
+
case "setup":
|
|
1482
|
+
await setupRemote({
|
|
1483
|
+
json: options.json,
|
|
1484
|
+
nonInteractive: options.nonInteractive,
|
|
1485
|
+
serverUrl: options.serverUrl,
|
|
1486
|
+
authToken: options.authToken,
|
|
1487
|
+
bindingCode: options.bindingCode
|
|
1488
|
+
});
|
|
1489
|
+
break;
|
|
1490
|
+
case "doctor":
|
|
1491
|
+
await doctorRemote({
|
|
1492
|
+
json: options.json
|
|
1493
|
+
});
|
|
1494
|
+
break;
|
|
1474
1495
|
case "enable":
|
|
1475
1496
|
await enableRemote();
|
|
1476
1497
|
break;
|
|
@@ -250,6 +250,7 @@
|
|
|
250
250
|
"cloud.confirmUnbind": "Are you sure you want to unbind this device?",
|
|
251
251
|
"cloud.unbindCancelled": "Unbind cancelled",
|
|
252
252
|
"cloud.bindHint": "Use 'ccjk notification bind' to bind this device",
|
|
253
|
+
"cloud.migrateToRemoteSetup": "Tip: 'ccjk remote setup' is now the recommended one-command onboarding flow.",
|
|
253
254
|
"cloud.deviceIdLabel": "Device ID:",
|
|
254
255
|
"cloud.deviceNameLabel": "Device Name:",
|
|
255
256
|
"cloud.platformLabel": "Platform:",
|
|
@@ -6,6 +6,60 @@
|
|
|
6
6
|
"success": "✅ Remote control enabled successfully!",
|
|
7
7
|
"next_steps": "\nNext steps:\n 1. Run 'ccjk remote qr' to get pairing QR code\n 2. Scan QR code with CCJK mobile app\n 3. Start daemon with 'ccjk daemon start'"
|
|
8
8
|
},
|
|
9
|
+
"setup": {
|
|
10
|
+
"title": "🚀 Remote Setup",
|
|
11
|
+
"auth_token_prompt": "Enter your CCJK auth token (used by daemon for remote channel):",
|
|
12
|
+
"auth_token": "Auth token:",
|
|
13
|
+
"auth_token_required": "Auth token is required",
|
|
14
|
+
"server_url_required": "Server URL is required",
|
|
15
|
+
"invalid_server_url": "Invalid server URL. Use http:// or https://",
|
|
16
|
+
"non_interactive_missing_server_url": "Missing server URL in non-interactive mode. Pass --server-url.",
|
|
17
|
+
"non_interactive_missing_auth_token": "Missing auth token in non-interactive mode. Pass --auth-token.",
|
|
18
|
+
"non_interactive_missing_binding_code": "Missing binding code in non-interactive mode. Pass --binding-code.",
|
|
19
|
+
"binding_prompt": "Enter binding code from CCJK app/web dashboard:",
|
|
20
|
+
"binding_code": "Binding code:",
|
|
21
|
+
"binding_code_required": "Binding code is required",
|
|
22
|
+
"binding_code_invalid": "Binding code looks invalid",
|
|
23
|
+
"binding_success": "✅ Device binding succeeded",
|
|
24
|
+
"binding_failed": "❌ Device binding failed",
|
|
25
|
+
"failed_enable": "Failed to enable remote control",
|
|
26
|
+
"failed_auth": "Failed to configure auth token",
|
|
27
|
+
"failed_bind_missing_code": "Failed to bind device: binding code missing",
|
|
28
|
+
"ready": "✅ Remote setup complete. You can now use web/app to control CLI.",
|
|
29
|
+
"partial": "⚠️ Remote setup partially completed. Check status below."
|
|
30
|
+
},
|
|
31
|
+
"doctor": {
|
|
32
|
+
"title": "🩺 Remote Doctor",
|
|
33
|
+
"checks": {
|
|
34
|
+
"enabled": "Remote enabled",
|
|
35
|
+
"server_url": "Server URL configured",
|
|
36
|
+
"auth_token": "Auth token configured",
|
|
37
|
+
"machine_id": "Machine ID configured",
|
|
38
|
+
"binding": "Device bound",
|
|
39
|
+
"cloud_health": "Server health reachable",
|
|
40
|
+
"daemon": "Daemon running"
|
|
41
|
+
},
|
|
42
|
+
"enabled_yes": "enabled",
|
|
43
|
+
"enabled_no": "disabled",
|
|
44
|
+
"bound_yes": "bound",
|
|
45
|
+
"bound_no": "not bound",
|
|
46
|
+
"daemon_running": "running",
|
|
47
|
+
"daemon_stopped": "stopped",
|
|
48
|
+
"reachable": "reachable",
|
|
49
|
+
"unreachable": "unreachable",
|
|
50
|
+
"present": "present",
|
|
51
|
+
"missing": "missing",
|
|
52
|
+
"skipped": "skipped",
|
|
53
|
+
"fix_prefix": "Fix: ",
|
|
54
|
+
"binding_status": "Binding status",
|
|
55
|
+
"fix_setup": "Run 'ccjk remote setup'",
|
|
56
|
+
"fix_enable": "Run 'ccjk remote enable'",
|
|
57
|
+
"fix_bind": "Run 'ccjk remote setup' and enter binding code",
|
|
58
|
+
"fix_network": "Check server URL/network or run 'ccjk remote enable' to update URL",
|
|
59
|
+
"fix_daemon": "Run 'ccjk daemon start'",
|
|
60
|
+
"summary_ok": "✅ Remote environment is healthy.",
|
|
61
|
+
"summary_failed": "⚠️ Remote environment has issues. Fix items above first."
|
|
62
|
+
},
|
|
9
63
|
"disable": {
|
|
10
64
|
"title": "🔌 Disable Remote Control",
|
|
11
65
|
"already_disabled": "Remote control is already disabled",
|
|
@@ -250,6 +250,7 @@
|
|
|
250
250
|
"cloud.confirmUnbind": "确定要解绑此设备吗?",
|
|
251
251
|
"cloud.unbindCancelled": "已取消解绑",
|
|
252
252
|
"cloud.bindHint": "使用 'ccjk notification bind' 绑定此设备",
|
|
253
|
+
"cloud.migrateToRemoteSetup": "提示:现在推荐使用 'ccjk remote setup' 一键完成远程初始化。",
|
|
253
254
|
"cloud.deviceIdLabel": "设备 ID:",
|
|
254
255
|
"cloud.deviceNameLabel": "设备名称:",
|
|
255
256
|
"cloud.platformLabel": "平台:",
|
|
@@ -6,6 +6,60 @@
|
|
|
6
6
|
"success": "✅ 远程控制启用成功!",
|
|
7
7
|
"next_steps": "\n下一步:\n 1. 运行 'ccjk remote qr' 获取配对二维码\n 2. 使用 CCJK 移动应用扫描二维码\n 3. 运行 'ccjk daemon start' 启动守护进程"
|
|
8
8
|
},
|
|
9
|
+
"setup": {
|
|
10
|
+
"title": "🚀 远程一键设置",
|
|
11
|
+
"auth_token_prompt": "请输入 CCJK 认证 Token(daemon 远程通道使用):",
|
|
12
|
+
"auth_token": "认证 Token:",
|
|
13
|
+
"auth_token_required": "必须输入认证 Token",
|
|
14
|
+
"server_url_required": "必须输入服务器地址",
|
|
15
|
+
"invalid_server_url": "服务器地址无效,请使用 http:// 或 https://",
|
|
16
|
+
"non_interactive_missing_server_url": "非交互模式缺少服务器地址,请传入 --server-url。",
|
|
17
|
+
"non_interactive_missing_auth_token": "非交互模式缺少认证 Token,请传入 --auth-token。",
|
|
18
|
+
"non_interactive_missing_binding_code": "非交互模式缺少绑定码,请传入 --binding-code。",
|
|
19
|
+
"binding_prompt": "请输入来自 CCJK App/Web 控制台的绑定码:",
|
|
20
|
+
"binding_code": "绑定码:",
|
|
21
|
+
"binding_code_required": "必须输入绑定码",
|
|
22
|
+
"binding_code_invalid": "绑定码格式看起来不正确",
|
|
23
|
+
"binding_success": "✅ 设备绑定成功",
|
|
24
|
+
"binding_failed": "❌ 设备绑定失败",
|
|
25
|
+
"failed_enable": "启用远程控制失败",
|
|
26
|
+
"failed_auth": "配置认证 Token 失败",
|
|
27
|
+
"failed_bind_missing_code": "设备绑定失败:缺少绑定码",
|
|
28
|
+
"ready": "✅ 远程设置完成,现在可通过 Web/App 控制 CLI。",
|
|
29
|
+
"partial": "⚠️ 远程设置部分完成,请查看以下状态。"
|
|
30
|
+
},
|
|
31
|
+
"doctor": {
|
|
32
|
+
"title": "🩺 远程体检",
|
|
33
|
+
"checks": {
|
|
34
|
+
"enabled": "远程功能已启用",
|
|
35
|
+
"server_url": "服务器地址已配置",
|
|
36
|
+
"auth_token": "认证 Token 已配置",
|
|
37
|
+
"machine_id": "机器 ID 已配置",
|
|
38
|
+
"binding": "设备已绑定",
|
|
39
|
+
"cloud_health": "服务器健康可达",
|
|
40
|
+
"daemon": "守护进程运行中"
|
|
41
|
+
},
|
|
42
|
+
"enabled_yes": "已启用",
|
|
43
|
+
"enabled_no": "未启用",
|
|
44
|
+
"bound_yes": "已绑定",
|
|
45
|
+
"bound_no": "未绑定",
|
|
46
|
+
"daemon_running": "运行中",
|
|
47
|
+
"daemon_stopped": "未运行",
|
|
48
|
+
"reachable": "可访问",
|
|
49
|
+
"unreachable": "不可访问",
|
|
50
|
+
"present": "已配置",
|
|
51
|
+
"missing": "缺失",
|
|
52
|
+
"skipped": "已跳过",
|
|
53
|
+
"fix_prefix": "修复建议:",
|
|
54
|
+
"binding_status": "绑定状态",
|
|
55
|
+
"fix_setup": "运行 'ccjk remote setup'",
|
|
56
|
+
"fix_enable": "运行 'ccjk remote enable'",
|
|
57
|
+
"fix_bind": "运行 'ccjk remote setup' 并输入绑定码",
|
|
58
|
+
"fix_network": "检查服务器地址/网络,或运行 'ccjk remote enable' 更新地址",
|
|
59
|
+
"fix_daemon": "运行 'ccjk daemon start'",
|
|
60
|
+
"summary_ok": "✅ 远程环境健康。",
|
|
61
|
+
"summary_failed": "⚠️ 远程环境存在问题,请先修复上述项。"
|
|
62
|
+
},
|
|
9
63
|
"disable": {
|
|
10
64
|
"title": "🔌 禁用远程控制",
|
|
11
65
|
"already_disabled": "远程控制已禁用",
|