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
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { existsSync, readFileSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import os__default, { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'pathe';
|
|
4
|
+
import { writeFileAtomic } from '../chunks/fs-operations.mjs';
|
|
5
|
+
import { Buffer } from 'node:buffer';
|
|
6
|
+
import crypto from 'node:crypto';
|
|
7
|
+
|
|
8
|
+
const TOKEN_PREFIX = "ccjk_";
|
|
9
|
+
const TOKEN_LENGTH = 64;
|
|
10
|
+
const TOKEN_VERSION = 1;
|
|
11
|
+
function generateDeviceToken() {
|
|
12
|
+
const randomBytes = crypto.randomBytes(TOKEN_LENGTH / 2);
|
|
13
|
+
const randomHex = randomBytes.toString("hex");
|
|
14
|
+
return `${TOKEN_PREFIX}${TOKEN_VERSION}${randomHex}`;
|
|
15
|
+
}
|
|
16
|
+
function isValidTokenFormat(token) {
|
|
17
|
+
if (!token || typeof token !== "string") {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (!token.startsWith(TOKEN_PREFIX)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const expectedLength = TOKEN_PREFIX.length + 1 + TOKEN_LENGTH;
|
|
24
|
+
if (token.length !== expectedLength) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const version = token[TOKEN_PREFIX.length];
|
|
28
|
+
if (!/^\d$/.test(version)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const hexPart = token.slice(TOKEN_PREFIX.length + 1);
|
|
32
|
+
if (!/^[a-f0-9]+$/i.test(hexPart)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function getDeviceInfo() {
|
|
38
|
+
return {
|
|
39
|
+
name: os__default.hostname(),
|
|
40
|
+
platform: os__default.platform(),
|
|
41
|
+
osVersion: os__default.release(),
|
|
42
|
+
arch: os__default.arch(),
|
|
43
|
+
username: os__default.userInfo().username,
|
|
44
|
+
machineId: generateMachineId()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function generateMachineId() {
|
|
48
|
+
const components = [
|
|
49
|
+
os__default.hostname(),
|
|
50
|
+
os__default.platform(),
|
|
51
|
+
os__default.arch(),
|
|
52
|
+
os__default.cpus()[0]?.model || "unknown",
|
|
53
|
+
os__default.userInfo().username,
|
|
54
|
+
// Add network interface MAC addresses for uniqueness
|
|
55
|
+
...Object.values(os__default.networkInterfaces()).flat().filter((iface) => iface && !iface.internal && iface.mac !== "00:00:00:00:00:00").map((iface) => iface?.mac).filter(Boolean).slice(0, 3)
|
|
56
|
+
// Limit to first 3 MACs
|
|
57
|
+
];
|
|
58
|
+
const combined = components.join("|");
|
|
59
|
+
return crypto.createHash("sha256").update(combined).digest("hex").slice(0, 32);
|
|
60
|
+
}
|
|
61
|
+
function deriveEncryptionKey() {
|
|
62
|
+
const machineId = generateMachineId();
|
|
63
|
+
const salt = "ccjk-notification-token-v1";
|
|
64
|
+
return crypto.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
|
|
65
|
+
}
|
|
66
|
+
function encryptToken(token) {
|
|
67
|
+
const key = deriveEncryptionKey();
|
|
68
|
+
const iv = crypto.randomBytes(16);
|
|
69
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
70
|
+
let encrypted = cipher.update(token, "utf8", "hex");
|
|
71
|
+
encrypted += cipher.final("hex");
|
|
72
|
+
const authTag = cipher.getAuthTag();
|
|
73
|
+
return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
|
|
74
|
+
}
|
|
75
|
+
function decryptToken(encryptedToken) {
|
|
76
|
+
try {
|
|
77
|
+
const parts = encryptedToken.split(":");
|
|
78
|
+
if (parts.length !== 3) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const [ivHex, authTagHex, encrypted] = parts;
|
|
82
|
+
const key = deriveEncryptionKey();
|
|
83
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
84
|
+
const authTag = Buffer.from(authTagHex, "hex");
|
|
85
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
86
|
+
decipher.setAuthTag(authTag);
|
|
87
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
88
|
+
decrypted += decipher.final("utf8");
|
|
89
|
+
return decrypted;
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function maskToken(token) {
|
|
95
|
+
if (!token || token.length < 12) {
|
|
96
|
+
return "***";
|
|
97
|
+
}
|
|
98
|
+
const prefix = token.slice(0, TOKEN_PREFIX.length + 1);
|
|
99
|
+
const suffix = token.slice(-4);
|
|
100
|
+
return `${prefix}***...***${suffix}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const CLOUD_API_BASE_URL = "https://api.claudehome.cn";
|
|
104
|
+
const DEFAULT_TIMEOUT = 3e4;
|
|
105
|
+
const POLL_TIMEOUT = 6e4;
|
|
106
|
+
const TOKEN_FILE_PATH = join(homedir(), ".ccjk", "cloud-token.json");
|
|
107
|
+
class CCJKCloudClient {
|
|
108
|
+
baseUrl;
|
|
109
|
+
deviceToken = null;
|
|
110
|
+
deviceId = null;
|
|
111
|
+
/**
|
|
112
|
+
* Create a new CCJKCloudClient instance
|
|
113
|
+
*
|
|
114
|
+
* @param baseUrl - Cloud API base URL (default: https://api.claudehome.cn)
|
|
115
|
+
*/
|
|
116
|
+
constructor(baseUrl = CLOUD_API_BASE_URL) {
|
|
117
|
+
this.baseUrl = baseUrl;
|
|
118
|
+
this.loadToken();
|
|
119
|
+
}
|
|
120
|
+
// ==========================================================================
|
|
121
|
+
// Token Management
|
|
122
|
+
// ==========================================================================
|
|
123
|
+
/**
|
|
124
|
+
* Load token from storage file
|
|
125
|
+
*/
|
|
126
|
+
loadToken() {
|
|
127
|
+
try {
|
|
128
|
+
if (existsSync(TOKEN_FILE_PATH)) {
|
|
129
|
+
const data = readFileSync(TOKEN_FILE_PATH, "utf-8");
|
|
130
|
+
const storage = JSON.parse(data);
|
|
131
|
+
this.deviceToken = storage.deviceToken;
|
|
132
|
+
this.deviceId = storage.deviceId || null;
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
this.deviceToken = null;
|
|
136
|
+
this.deviceId = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Save token to storage file
|
|
141
|
+
*/
|
|
142
|
+
saveToken(storage) {
|
|
143
|
+
try {
|
|
144
|
+
const dir = join(homedir(), ".ccjk");
|
|
145
|
+
if (!existsSync(dir)) {
|
|
146
|
+
mkdirSync(dir, { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
writeFileAtomic(TOKEN_FILE_PATH, JSON.stringify(storage, null, 2));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
throw new Error(`Failed to save token: ${error instanceof Error ? error.message : String(error)}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Check if device is bound
|
|
155
|
+
*/
|
|
156
|
+
isBound() {
|
|
157
|
+
return this.deviceToken !== null && this.deviceToken.length > 0;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get current device token
|
|
161
|
+
*/
|
|
162
|
+
getDeviceToken() {
|
|
163
|
+
return this.deviceToken;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get current device ID
|
|
167
|
+
*/
|
|
168
|
+
getDeviceId() {
|
|
169
|
+
return this.deviceId;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Clear stored token (unbind device)
|
|
173
|
+
*/
|
|
174
|
+
clearToken() {
|
|
175
|
+
this.deviceToken = null;
|
|
176
|
+
this.deviceId = null;
|
|
177
|
+
try {
|
|
178
|
+
if (existsSync(TOKEN_FILE_PATH)) {
|
|
179
|
+
unlinkSync(TOKEN_FILE_PATH);
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// ==========================================================================
|
|
185
|
+
// Device Binding
|
|
186
|
+
// ==========================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Bind device using a binding code
|
|
189
|
+
*
|
|
190
|
+
* The binding code is obtained from the CCJK mobile app or web dashboard.
|
|
191
|
+
* Once bound, the device can send and receive notifications.
|
|
192
|
+
*
|
|
193
|
+
* @param code - Binding code from mobile app
|
|
194
|
+
* @param deviceInfo - Optional device information (auto-detected if not provided)
|
|
195
|
+
* @returns Bind response with device token
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* const client = new CCJKCloudClient()
|
|
200
|
+
* const result = await client.bind('ABC123')
|
|
201
|
+
* if (result.success) {
|
|
202
|
+
* console.log('Device bound successfully!')
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
async bind(code, deviceInfo) {
|
|
207
|
+
const info = deviceInfo ? { ...getDeviceInfo(), ...deviceInfo } : getDeviceInfo();
|
|
208
|
+
const response = await this.request("/bind/use", {
|
|
209
|
+
method: "POST",
|
|
210
|
+
body: JSON.stringify({
|
|
211
|
+
code,
|
|
212
|
+
deviceInfo: info
|
|
213
|
+
})
|
|
214
|
+
});
|
|
215
|
+
if (response.success && response.data) {
|
|
216
|
+
this.deviceToken = response.data.deviceToken;
|
|
217
|
+
this.deviceId = response.data.deviceId;
|
|
218
|
+
this.saveToken({
|
|
219
|
+
deviceToken: response.data.deviceToken,
|
|
220
|
+
deviceId: response.data.deviceId,
|
|
221
|
+
bindingCode: code,
|
|
222
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
223
|
+
deviceInfo: info
|
|
224
|
+
});
|
|
225
|
+
return {
|
|
226
|
+
success: true,
|
|
227
|
+
deviceToken: response.data.deviceToken,
|
|
228
|
+
deviceId: response.data.deviceId
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
error: response.error || "Failed to bind device",
|
|
234
|
+
code: response.code
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
// ==========================================================================
|
|
238
|
+
// Notification Sending
|
|
239
|
+
// ==========================================================================
|
|
240
|
+
/**
|
|
241
|
+
* Send a notification to the user
|
|
242
|
+
*
|
|
243
|
+
* @param options - Notification options
|
|
244
|
+
* @returns Notification response
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const client = new CCJKCloudClient()
|
|
249
|
+
* await client.notify({
|
|
250
|
+
* title: 'Build Complete',
|
|
251
|
+
* body: 'Your project has been built successfully!',
|
|
252
|
+
* type: 'success'
|
|
253
|
+
* })
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
async notify(options) {
|
|
257
|
+
if (!this.deviceToken) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
error: 'Device not bound. Please run "ccjk notification bind <code>" first.',
|
|
261
|
+
code: "NOT_BOUND"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const response = await this.request("/notify", {
|
|
265
|
+
method: "POST",
|
|
266
|
+
body: JSON.stringify({
|
|
267
|
+
title: options.title,
|
|
268
|
+
body: options.body,
|
|
269
|
+
type: options.type || "info",
|
|
270
|
+
taskId: options.taskId,
|
|
271
|
+
metadata: options.metadata,
|
|
272
|
+
actions: options.actions
|
|
273
|
+
})
|
|
274
|
+
});
|
|
275
|
+
if (response.success && response.data) {
|
|
276
|
+
return {
|
|
277
|
+
success: true,
|
|
278
|
+
notificationId: response.data.notificationId
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
success: false,
|
|
283
|
+
error: response.error || "Failed to send notification",
|
|
284
|
+
code: response.code
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
// ==========================================================================
|
|
288
|
+
// Reply Polling
|
|
289
|
+
// ==========================================================================
|
|
290
|
+
/**
|
|
291
|
+
* Wait for a reply from the user
|
|
292
|
+
*
|
|
293
|
+
* Uses long-polling to wait for a user reply. The timeout parameter
|
|
294
|
+
* controls how long to wait before returning null.
|
|
295
|
+
*
|
|
296
|
+
* @param timeout - Timeout in milliseconds (default: 30000)
|
|
297
|
+
* @returns User reply or null if timeout
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* const client = new CCJKCloudClient()
|
|
302
|
+
* const reply = await client.waitForReply(60000) // Wait up to 60 seconds
|
|
303
|
+
* if (reply) {
|
|
304
|
+
* console.log('User replied:', reply.content)
|
|
305
|
+
* }
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
async waitForReply(timeout = POLL_TIMEOUT) {
|
|
309
|
+
if (!this.deviceToken) {
|
|
310
|
+
throw new Error('Device not bound. Please run "ccjk notification bind <code>" first.');
|
|
311
|
+
}
|
|
312
|
+
const response = await this.request(`/reply/poll?timeout=${timeout}`, {
|
|
313
|
+
method: "GET",
|
|
314
|
+
timeout
|
|
315
|
+
});
|
|
316
|
+
if (response.success && response.data?.reply) {
|
|
317
|
+
return {
|
|
318
|
+
content: response.data.reply.content,
|
|
319
|
+
timestamp: new Date(response.data.reply.timestamp),
|
|
320
|
+
notificationId: response.data.reply.notificationId,
|
|
321
|
+
actionId: response.data.reply.actionId
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
// ==========================================================================
|
|
327
|
+
// Ask and Wait
|
|
328
|
+
// ==========================================================================
|
|
329
|
+
/**
|
|
330
|
+
* Ask the user a question and wait for their reply
|
|
331
|
+
*
|
|
332
|
+
* This is a convenience method that combines notify() and waitForReply().
|
|
333
|
+
* It sends a notification with the question and waits for the user to respond.
|
|
334
|
+
*
|
|
335
|
+
* @param question - Question to ask the user
|
|
336
|
+
* @param options - Additional notification options
|
|
337
|
+
* @param timeout - Timeout in milliseconds (default: 60000)
|
|
338
|
+
* @returns User reply
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* const client = new CCJKCloudClient()
|
|
343
|
+
* const reply = await client.ask('Deploy to production?', {
|
|
344
|
+
* actions: [
|
|
345
|
+
* { id: 'yes', label: 'Yes', value: 'yes' },
|
|
346
|
+
* { id: 'no', label: 'No', value: 'no' }
|
|
347
|
+
* ]
|
|
348
|
+
* })
|
|
349
|
+
* if (reply.actionId === 'yes') {
|
|
350
|
+
* // Proceed with deployment
|
|
351
|
+
* }
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
async ask(question, options, timeout = POLL_TIMEOUT) {
|
|
355
|
+
const notifyResult = await this.notify({
|
|
356
|
+
title: options?.title || "CCJK Question",
|
|
357
|
+
body: question,
|
|
358
|
+
type: "info",
|
|
359
|
+
...options
|
|
360
|
+
});
|
|
361
|
+
if (!notifyResult.success) {
|
|
362
|
+
throw new Error(notifyResult.error || "Failed to send question");
|
|
363
|
+
}
|
|
364
|
+
const reply = await this.waitForReply(timeout);
|
|
365
|
+
if (!reply) {
|
|
366
|
+
throw new Error("No reply received within timeout");
|
|
367
|
+
}
|
|
368
|
+
return reply;
|
|
369
|
+
}
|
|
370
|
+
// ==========================================================================
|
|
371
|
+
// Status Check
|
|
372
|
+
// ==========================================================================
|
|
373
|
+
/**
|
|
374
|
+
* Get binding status and device information
|
|
375
|
+
*
|
|
376
|
+
* @returns Binding status information
|
|
377
|
+
*/
|
|
378
|
+
async getStatus() {
|
|
379
|
+
if (!this.deviceToken) {
|
|
380
|
+
return { bound: false };
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
if (existsSync(TOKEN_FILE_PATH)) {
|
|
384
|
+
const data = readFileSync(TOKEN_FILE_PATH, "utf-8");
|
|
385
|
+
const storage = JSON.parse(data);
|
|
386
|
+
return {
|
|
387
|
+
bound: true,
|
|
388
|
+
deviceId: storage.deviceId,
|
|
389
|
+
deviceInfo: storage.deviceInfo,
|
|
390
|
+
lastUsed: storage.lastUsedAt
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
bound: true,
|
|
397
|
+
deviceId: this.deviceId || void 0
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
// ==========================================================================
|
|
401
|
+
// HTTP Request Helper
|
|
402
|
+
// ==========================================================================
|
|
403
|
+
/**
|
|
404
|
+
* Make an HTTP request to the cloud service
|
|
405
|
+
*/
|
|
406
|
+
async request(path, options) {
|
|
407
|
+
const url = `${this.baseUrl}${path}`;
|
|
408
|
+
const timeout = options.timeout || DEFAULT_TIMEOUT;
|
|
409
|
+
const controller = new AbortController();
|
|
410
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
411
|
+
try {
|
|
412
|
+
const headers = {
|
|
413
|
+
"Content-Type": "application/json"
|
|
414
|
+
};
|
|
415
|
+
if (this.deviceToken) {
|
|
416
|
+
headers["X-Device-Token"] = this.deviceToken;
|
|
417
|
+
}
|
|
418
|
+
const response = await fetch(url, {
|
|
419
|
+
method: options.method,
|
|
420
|
+
headers,
|
|
421
|
+
body: options.body,
|
|
422
|
+
signal: controller.signal
|
|
423
|
+
});
|
|
424
|
+
clearTimeout(timeoutId);
|
|
425
|
+
const data = await response.json();
|
|
426
|
+
if (!response.ok) {
|
|
427
|
+
return {
|
|
428
|
+
success: false,
|
|
429
|
+
error: data.error || `HTTP ${response.status}: ${response.statusText}`,
|
|
430
|
+
code: data.code || `HTTP_${response.status}`
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (this.deviceToken && existsSync(TOKEN_FILE_PATH)) {
|
|
434
|
+
try {
|
|
435
|
+
const storageData = readFileSync(TOKEN_FILE_PATH, "utf-8");
|
|
436
|
+
const storage = JSON.parse(storageData);
|
|
437
|
+
storage.lastUsedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
438
|
+
writeFileAtomic(TOKEN_FILE_PATH, JSON.stringify(storage, null, 2));
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return data;
|
|
443
|
+
} catch (error) {
|
|
444
|
+
clearTimeout(timeoutId);
|
|
445
|
+
if (error instanceof Error) {
|
|
446
|
+
if (error.name === "AbortError") {
|
|
447
|
+
return {
|
|
448
|
+
success: false,
|
|
449
|
+
error: "Request timeout",
|
|
450
|
+
code: "TIMEOUT"
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
success: false,
|
|
455
|
+
error: error.message,
|
|
456
|
+
code: "NETWORK_ERROR"
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
error: String(error),
|
|
462
|
+
code: "UNKNOWN_ERROR"
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
let cloudClientInstance = null;
|
|
468
|
+
function getCloudNotificationClient() {
|
|
469
|
+
if (!cloudClientInstance) {
|
|
470
|
+
cloudClientInstance = new CCJKCloudClient();
|
|
471
|
+
}
|
|
472
|
+
return cloudClientInstance;
|
|
473
|
+
}
|
|
474
|
+
async function bindDevice(code, deviceInfo) {
|
|
475
|
+
const client = getCloudNotificationClient();
|
|
476
|
+
return client.bind(code, deviceInfo);
|
|
477
|
+
}
|
|
478
|
+
async function sendNotification(options) {
|
|
479
|
+
const client = getCloudNotificationClient();
|
|
480
|
+
return client.notify(options);
|
|
481
|
+
}
|
|
482
|
+
function isDeviceBound() {
|
|
483
|
+
const client = getCloudNotificationClient();
|
|
484
|
+
return client.isBound();
|
|
485
|
+
}
|
|
486
|
+
async function getBindingStatus() {
|
|
487
|
+
const client = getCloudNotificationClient();
|
|
488
|
+
return client.getStatus();
|
|
489
|
+
}
|
|
490
|
+
function unbindDevice() {
|
|
491
|
+
const client = getCloudNotificationClient();
|
|
492
|
+
client.clearToken();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export { isValidTokenFormat as a, bindDevice as b, generateDeviceToken as c, decryptToken as d, encryptToken as e, getDeviceInfo as f, getBindingStatus as g, isDeviceBound as i, maskToken as m, sendNotification as s, unbindDevice as u };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccjk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "11.1.
|
|
4
|
+
"version": "11.1.2",
|
|
5
5
|
"packageManager": "pnpm@10.17.1",
|
|
6
6
|
"description": "CLI toolkit for Claude Code and Codex setup. Simplifies MCP service installation, API configuration, workflow management, and multi-provider support with guided interactive setup.",
|
|
7
7
|
"author": {
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"build": "unbuild",
|
|
87
87
|
"start": "node bin/ccjk.mjs",
|
|
88
88
|
"typecheck": "tsc --noEmit",
|
|
89
|
-
"prepublishOnly": "node scripts/validate-prepublish.mjs && pnpm build && pnpm test:run",
|
|
89
|
+
"prepublishOnly": "node scripts/validate-prepublish.mjs && pnpm contract:check && pnpm build && pnpm test:run",
|
|
90
90
|
"lint": "eslint",
|
|
91
91
|
"lint:fix": "eslint --fix",
|
|
92
92
|
"test": "vitest",
|
|
@@ -137,7 +137,8 @@
|
|
|
137
137
|
"benchmark:server": "npx http-server docs/v2 -p 8080 -o dashboard.html",
|
|
138
138
|
"benchmark:open": "open docs/v2/dashboard.html || xdg-open docs/v2/dashboard.html || start docs/v2/dashboard.html",
|
|
139
139
|
"i18n:check": "tsx scripts/check-i18n.ts",
|
|
140
|
-
"i18n:report": "tsx scripts/check-i18n.ts --report"
|
|
140
|
+
"i18n:report": "tsx scripts/check-i18n.ts --report",
|
|
141
|
+
"contract:check": "node scripts/check-remote-contract.mjs"
|
|
141
142
|
},
|
|
142
143
|
"dependencies": {
|
|
143
144
|
"@anthropic-ai/sdk": "^0.52.0",
|
|
@@ -1,53 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
3
|
"description": "CCJK settings template — auto-migrated on upgrade",
|
|
4
|
-
|
|
5
4
|
"model": "default",
|
|
6
|
-
|
|
7
5
|
"env": {
|
|
8
6
|
"ANTHROPIC_MODEL": "",
|
|
9
7
|
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "",
|
|
10
8
|
"ANTHROPIC_DEFAULT_SONNET_MODEL": "",
|
|
11
9
|
"ANTHROPIC_DEFAULT_OPUS_MODEL": ""
|
|
12
10
|
},
|
|
13
|
-
|
|
14
11
|
"language": "",
|
|
15
|
-
|
|
16
12
|
"showTurnDuration": false,
|
|
17
|
-
|
|
18
13
|
"respectGitignore": true,
|
|
19
|
-
|
|
20
14
|
"auto": {
|
|
21
15
|
"mcp": 0
|
|
22
16
|
},
|
|
23
|
-
|
|
24
17
|
"agent": "",
|
|
25
|
-
|
|
26
18
|
"thinking": {
|
|
27
19
|
"enabled": false,
|
|
28
20
|
"budgetTokens": 10240
|
|
29
21
|
},
|
|
30
|
-
|
|
31
22
|
"fileSuggestion": {
|
|
32
23
|
"type": "command",
|
|
33
24
|
"command": "git"
|
|
34
25
|
},
|
|
35
|
-
|
|
36
26
|
"allowUnsandboxedCommands": false,
|
|
37
|
-
|
|
38
27
|
"disallowedTools": [],
|
|
39
|
-
|
|
40
28
|
"attribution": {},
|
|
41
|
-
|
|
42
29
|
"index": {
|
|
43
30
|
"maxFiles": 10000,
|
|
44
31
|
"maxFileSize": 2097152
|
|
45
32
|
},
|
|
46
|
-
|
|
47
33
|
"allowBrowser": false,
|
|
48
|
-
|
|
49
34
|
"statusLine": {},
|
|
50
|
-
|
|
51
35
|
"plansDirectory": ".claude/plans",
|
|
52
36
|
"hooks": {
|
|
53
37
|
"PreToolUse": [],
|
|
@@ -85,13 +69,37 @@
|
|
|
85
69
|
"Read(*)",
|
|
86
70
|
"Edit(*)",
|
|
87
71
|
"Write(*)",
|
|
88
|
-
"NotebookEdit(*)"
|
|
72
|
+
"NotebookEdit(*)",
|
|
73
|
+
"Bash(cd *)",
|
|
74
|
+
"Bash(for *)",
|
|
75
|
+
"Bash(while *)",
|
|
76
|
+
"Bash(if *)",
|
|
77
|
+
"Bash(source *)",
|
|
78
|
+
"Bash(export *)",
|
|
79
|
+
"Bash(unset *)",
|
|
80
|
+
"Bash(rm *)",
|
|
81
|
+
"Bash(rmdir *)",
|
|
82
|
+
"Bash(tee *)",
|
|
83
|
+
"Bash(env *)",
|
|
84
|
+
"Bash(time *)",
|
|
85
|
+
"Bash(printf *)",
|
|
86
|
+
"Bash(sed *)",
|
|
87
|
+
"Bash(awk *)",
|
|
88
|
+
"Bash(tr *)",
|
|
89
|
+
"Bash(xargs *)",
|
|
90
|
+
"Bash(cut *)",
|
|
91
|
+
"Bash(curl *)",
|
|
92
|
+
"Bash(node *)",
|
|
93
|
+
"Bash(python *)",
|
|
94
|
+
"Bash(python3 *)",
|
|
95
|
+
"Bash(pip *)",
|
|
96
|
+
"Bash(pip3 *)",
|
|
97
|
+
"WebFetch(*)",
|
|
98
|
+
"MCP(*)"
|
|
89
99
|
]
|
|
90
100
|
},
|
|
91
|
-
|
|
92
101
|
"chat": {
|
|
93
102
|
"alwaysApprove": []
|
|
94
103
|
},
|
|
95
|
-
|
|
96
104
|
"experimental": {}
|
|
97
|
-
}
|
|
105
|
+
}
|