@my-life-buddies/cli 0.13.0 → 0.13.3
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 +11 -1
- package/dist/bin/buddy.js +21 -6
- package/dist/bin/buddy.js.map +2 -2
- package/dist/bin/core.js +221 -140
- package/dist/bin/core.js.map +4 -4
- package/dist/web/app.css +139 -30
- package/dist/web/app.js +184 -54
- package/dist/web/index.html +14 -16
- package/package.json +3 -3
package/dist/bin/core.js
CHANGED
|
@@ -2465,10 +2465,14 @@ var MlbDevRegistrationClient = class {
|
|
|
2465
2465
|
#baseUrl;
|
|
2466
2466
|
#developerToken;
|
|
2467
2467
|
#fetch;
|
|
2468
|
+
#tokenStoreFor;
|
|
2469
|
+
#refreshToken;
|
|
2468
2470
|
constructor(options) {
|
|
2469
2471
|
this.#baseUrl = normalizePlatformUrl(options.baseUrl);
|
|
2470
2472
|
this.#developerToken = safeCredential(options.developerToken, "developerToken");
|
|
2471
2473
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
2474
|
+
this.#tokenStoreFor = options.tokenStoreFor;
|
|
2475
|
+
this.#refreshToken = options.refreshToken ?? false;
|
|
2472
2476
|
if (typeof this.#fetch !== "function") {
|
|
2473
2477
|
throw new TypeError("A Fetch API implementation is required");
|
|
2474
2478
|
}
|
|
@@ -2538,6 +2542,13 @@ var MlbDevRegistrationClient = class {
|
|
|
2538
2542
|
message: "buddy.agent.json \u4E2D\u7684 buddyId \u65E0\u6548\u3002"
|
|
2539
2543
|
});
|
|
2540
2544
|
}
|
|
2545
|
+
options.signal.throwIfAborted();
|
|
2546
|
+
const store = this.#tokenStoreFor?.(request.devAgentId);
|
|
2547
|
+
const cached = this.#refreshToken ? void 0 : await store?.load();
|
|
2548
|
+
if (cached !== void 0) {
|
|
2549
|
+
options.signal.throwIfAborted();
|
|
2550
|
+
return Object.freeze({ devAgentId: request.devAgentId, appServerUrl: new URL(this.#baseUrl), agentToken: safeCredential(cached, "agentToken") });
|
|
2551
|
+
}
|
|
2541
2552
|
const body = object2(await this.#request(
|
|
2542
2553
|
`/cli/buddies/${encodeURIComponent(request.devAgentId)}`,
|
|
2543
2554
|
{
|
|
@@ -2552,10 +2563,13 @@ var MlbDevRegistrationClient = class {
|
|
|
2552
2563
|
message: "\u642D\u5B50\u8BE6\u60C5\u54CD\u5E94\u7F3A\u5C11\u5339\u914D\u7684 id \u6216 Buddy token\u3002"
|
|
2553
2564
|
});
|
|
2554
2565
|
}
|
|
2566
|
+
const token = safeCredential(body.token, "agentToken");
|
|
2567
|
+
options.signal.throwIfAborted();
|
|
2568
|
+
await store?.save(token);
|
|
2555
2569
|
return Object.freeze({
|
|
2556
2570
|
devAgentId: request.devAgentId,
|
|
2557
2571
|
appServerUrl: new URL(this.#baseUrl),
|
|
2558
|
-
agentToken:
|
|
2572
|
+
agentToken: token
|
|
2559
2573
|
});
|
|
2560
2574
|
}
|
|
2561
2575
|
async listOnline(options) {
|
|
@@ -2942,7 +2956,7 @@ function validateRegistration(value, expectedAgentId) {
|
|
|
2942
2956
|
throw new BuddyDevError({
|
|
2943
2957
|
code: "DEV_APP_SERVER_RESPONSE_INVALID",
|
|
2944
2958
|
phase: "registration",
|
|
2945
|
-
message: "DEV \u6302\u53F7\u6CA1\u6709\u8FD4\u56DE\u5339\u914D\u7684 Buddy \u8EAB\u4EFD\u4E0E\
|
|
2959
|
+
message: "DEV \u6302\u53F7\u6CA1\u6709\u8FD4\u56DE\u5339\u914D\u7684 Buddy \u8EAB\u4EFD\u4E0E\u5F00\u53D1 Token\u3002"
|
|
2946
2960
|
});
|
|
2947
2961
|
}
|
|
2948
2962
|
return value;
|
|
@@ -3291,6 +3305,183 @@ function createBuddyDevStarter(options = {}) {
|
|
|
3291
3305
|
});
|
|
3292
3306
|
}
|
|
3293
3307
|
|
|
3308
|
+
// packages/cli-core/src/dev/token-store.ts
|
|
3309
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
3310
|
+
|
|
3311
|
+
// packages/cli-core/src/cloud/credential-store.ts
|
|
3312
|
+
import { spawn } from "node:child_process";
|
|
3313
|
+
var DEFAULT_SERVICE = "com.xiaohongshu.buddy.developer-cli";
|
|
3314
|
+
var DEFAULT_ACCOUNT = "developer-session";
|
|
3315
|
+
var MAX_CREDENTIAL_BYTES = 32 * 1024;
|
|
3316
|
+
function safeLabel(value, fallback, name) {
|
|
3317
|
+
const result = value ?? fallback;
|
|
3318
|
+
if (!result || result !== result.trim() || /[\u0000-\u001f\u007f]/u.test(result) || result.length > 256) {
|
|
3319
|
+
throw new TypeError(`${name} is invalid`);
|
|
3320
|
+
}
|
|
3321
|
+
return result;
|
|
3322
|
+
}
|
|
3323
|
+
function serialize(token) {
|
|
3324
|
+
const value = JSON.stringify(token);
|
|
3325
|
+
if (Buffer.byteLength(value, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
3326
|
+
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u4E91\u7AEF\u4F1A\u8BDD\u51ED\u636E\u8FC7\u5927\uFF0C\u672A\u4FDD\u5B58");
|
|
3327
|
+
}
|
|
3328
|
+
return value;
|
|
3329
|
+
}
|
|
3330
|
+
function parse(source) {
|
|
3331
|
+
if (Buffer.byteLength(source, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
3332
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3333
|
+
}
|
|
3334
|
+
let value;
|
|
3335
|
+
try {
|
|
3336
|
+
value = JSON.parse(source);
|
|
3337
|
+
} catch {
|
|
3338
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3339
|
+
}
|
|
3340
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3341
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3342
|
+
}
|
|
3343
|
+
const record2 = value;
|
|
3344
|
+
if (Object.keys(record2).length !== 3 || typeof record2.accessToken !== "string" || typeof record2.subject !== "string" || !Number.isSafeInteger(record2.expiresAt)) {
|
|
3345
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3346
|
+
}
|
|
3347
|
+
return Object.freeze({
|
|
3348
|
+
accessToken: record2.accessToken,
|
|
3349
|
+
subject: record2.subject,
|
|
3350
|
+
expiresAt: record2.expiresAt
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
async function runCredentialCommand(input) {
|
|
3354
|
+
return await new Promise((resolve17, reject) => {
|
|
3355
|
+
const child = spawn(input.command, [...input.args], {
|
|
3356
|
+
shell: false,
|
|
3357
|
+
windowsHide: true,
|
|
3358
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3359
|
+
});
|
|
3360
|
+
const stdout = [];
|
|
3361
|
+
const stderr = [];
|
|
3362
|
+
let outputBytes = 0;
|
|
3363
|
+
const collect = (target, chunk) => {
|
|
3364
|
+
outputBytes += chunk.byteLength;
|
|
3365
|
+
if (outputBytes <= MAX_CREDENTIAL_BYTES) target.push(chunk);
|
|
3366
|
+
};
|
|
3367
|
+
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3368
|
+
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3369
|
+
child.once("error", reject);
|
|
3370
|
+
child.once("close", (code2) => resolve17({
|
|
3371
|
+
code: code2 ?? 1,
|
|
3372
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3373
|
+
stderr: Buffer.concat(stderr).toString("utf8")
|
|
3374
|
+
}));
|
|
3375
|
+
child.stdin.on("error", () => void 0);
|
|
3376
|
+
child.stdin.end(input.input ?? "");
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
var SystemCredentialStore = class {
|
|
3380
|
+
#platform;
|
|
3381
|
+
#service;
|
|
3382
|
+
#account;
|
|
3383
|
+
#run;
|
|
3384
|
+
constructor(options = {}) {
|
|
3385
|
+
this.#platform = options.platform ?? process.platform;
|
|
3386
|
+
this.#service = safeLabel(options.service, DEFAULT_SERVICE, "service");
|
|
3387
|
+
this.#account = safeLabel(options.account, DEFAULT_ACCOUNT, "account");
|
|
3388
|
+
this.#run = options.commandRunner ?? runCredentialCommand;
|
|
3389
|
+
if (this.#platform !== "darwin") {
|
|
3390
|
+
throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D CLI V1 \u7684\u5B89\u5168\u51ED\u636E\u5B58\u50A8\u76EE\u524D\u53EA\u652F\u6301 macOS Keychain");
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
async load() {
|
|
3394
|
+
const result = await this.#run({
|
|
3395
|
+
command: "security",
|
|
3396
|
+
args: ["find-generic-password", "-s", this.#service, "-a", this.#account, "-w"]
|
|
3397
|
+
});
|
|
3398
|
+
if (result.code === 44) return void 0;
|
|
3399
|
+
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u8BFB\u53D6 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u51ED\u636E");
|
|
3400
|
+
const raw = result.stdout.trimEnd();
|
|
3401
|
+
return /^(?:[a-fA-F0-9]{2})+$/u.test(raw) ? Buffer.from(raw, "hex").toString("utf8") : raw;
|
|
3402
|
+
}
|
|
3403
|
+
async save(source) {
|
|
3404
|
+
if (!source || /[\u0000-\u001f\u007f]/u.test(source)) throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3405
|
+
const line = ["add-generic-password", "-U", "-s", this.#service, "-a", this.#account, "-w", source].map((part) => `"${part.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"')}"`).join(" ") + "\n";
|
|
3406
|
+
if (Buffer.byteLength(line, "utf8") >= 4096) {
|
|
3407
|
+
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u51ED\u636E\u8D85\u8FC7 macOS \u5199\u5165\u957F\u5EA6\u9650\u5236\uFF0C\u672A\u4FDD\u5B58\uFF1B\u8BF7\u8054\u7CFB\u5E73\u53F0\u5904\u7406");
|
|
3408
|
+
}
|
|
3409
|
+
const result = await this.#run({
|
|
3410
|
+
command: "security",
|
|
3411
|
+
args: ["-i", "-q"],
|
|
3412
|
+
input: line
|
|
3413
|
+
});
|
|
3414
|
+
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u628A\u642D\u642D\u51ED\u636E\u5199\u5165 macOS \u94A5\u5319\u4E32");
|
|
3415
|
+
const saved = await this.load();
|
|
3416
|
+
if (!saved || saved !== source) throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u51ED\u636E\u5199\u5165\u540E\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
3417
|
+
}
|
|
3418
|
+
async clear() {
|
|
3419
|
+
const result = await this.#run({
|
|
3420
|
+
command: "security",
|
|
3421
|
+
args: ["delete-generic-password", "-s", this.#service, "-a", this.#account]
|
|
3422
|
+
});
|
|
3423
|
+
if (result.code !== 0 && result.code !== 44) {
|
|
3424
|
+
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u6E05\u9664 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u51ED\u636E");
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
};
|
|
3428
|
+
var SystemCloudTokenStore = class {
|
|
3429
|
+
#store;
|
|
3430
|
+
constructor(options = {}) {
|
|
3431
|
+
this.#store = new SystemCredentialStore(options);
|
|
3432
|
+
}
|
|
3433
|
+
async load() {
|
|
3434
|
+
const raw = await this.#store.load();
|
|
3435
|
+
return raw === void 0 ? void 0 : parse(raw);
|
|
3436
|
+
}
|
|
3437
|
+
async save(token) {
|
|
3438
|
+
await this.#store.save(serialize(token));
|
|
3439
|
+
}
|
|
3440
|
+
async clear() {
|
|
3441
|
+
await this.#store.clear();
|
|
3442
|
+
}
|
|
3443
|
+
};
|
|
3444
|
+
|
|
3445
|
+
// packages/cli-core/src/dev/token-store.ts
|
|
3446
|
+
var SystemBuddyTokenStore = class {
|
|
3447
|
+
#store;
|
|
3448
|
+
constructor(options) {
|
|
3449
|
+
if (![options.subject, options.buddyId].every((value) => value.trim() && !/[\u0000-\u001f\u007f]/u.test(value))) throw new TypeError("\u5F00\u53D1 Token \u7684\u8D26\u53F7\u548C\u642D\u5B50 ID \u65E0\u6548");
|
|
3450
|
+
const scope = JSON.stringify([normalizePlatformUrl(options.platformUrl).origin, options.subject, options.buddyId]);
|
|
3451
|
+
this.#store = new SystemCredentialStore({
|
|
3452
|
+
service: "com.xiaohongshu.buddy.developer-cli.dev-token",
|
|
3453
|
+
account: createHash5("sha256").update(scope).digest("hex"),
|
|
3454
|
+
platform: options.platform,
|
|
3455
|
+
commandRunner: options.commandRunner
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
async load() {
|
|
3459
|
+
const raw = await this.#store.load();
|
|
3460
|
+
if (raw === void 0) return void 0;
|
|
3461
|
+
let value;
|
|
3462
|
+
try {
|
|
3463
|
+
value = JSON.parse(raw);
|
|
3464
|
+
} catch {
|
|
3465
|
+
throw corrupt();
|
|
3466
|
+
}
|
|
3467
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).length !== 1 || !("token" in value)) throw corrupt();
|
|
3468
|
+
return validateToken(value.token);
|
|
3469
|
+
}
|
|
3470
|
+
async save(token) {
|
|
3471
|
+
await this.#store.save(JSON.stringify({ token: validateToken(token) }));
|
|
3472
|
+
}
|
|
3473
|
+
async clear() {
|
|
3474
|
+
await this.#store.clear();
|
|
3475
|
+
}
|
|
3476
|
+
};
|
|
3477
|
+
function corrupt() {
|
|
3478
|
+
return new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u672C\u673A\u5F00\u53D1\u51ED\u636E\u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 --refresh-token \u91CD\u65B0\u9886\u53D6");
|
|
3479
|
+
}
|
|
3480
|
+
function validateToken(value) {
|
|
3481
|
+
if (typeof value !== "string" || !value.trim() || /[\u0000-\u001f\u007f]/u.test(value) || value.length > 1024) throw corrupt();
|
|
3482
|
+
return value;
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3294
3485
|
// packages/cli-core/src/commands/dev.ts
|
|
3295
3486
|
var DevCommandUsageError = class extends Error {
|
|
3296
3487
|
constructor(message) {
|
|
@@ -3299,12 +3490,13 @@ var DevCommandUsageError = class extends Error {
|
|
|
3299
3490
|
}
|
|
3300
3491
|
};
|
|
3301
3492
|
var DEV_COMMAND_HELP = `\u7528\u6CD5:
|
|
3302
|
-
buddy-cli dev [--app-server <App Server URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
3493
|
+
buddy-cli dev [--app-server <App Server URL>] [--directory <\u9879\u76EE\u76EE\u5F55>] [--refresh-token]
|
|
3303
3494
|
|
|
3304
3495
|
\u8BF4\u660E:
|
|
3305
3496
|
\u672A\u914D\u7F6E\u5730\u5740\u65F6\u8FDE\u63A5 MLB \u6D4B\u8BD5\u73AF\u5883 ${DEFAULT_MLB_TEST_URL}\uFF08HTTP \u4EC5\u9650\u6D4B\u8BD5\uFF09\u3002
|
|
3306
3497
|
CLI \u7EDF\u4E00\u542F\u52A8\u5E76\u7BA1\u7406\u5DE5\u7A0B\u4E2D\u7684\u5B98\u65B9 Runtime\u3002
|
|
3307
|
-
DEV \u4F1A\u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u5F00\u53D1\u8005\u4F1A\u8BDD\uFF0C\u518D\u8BFB\u53D6\u5F53\u524D buddy.agent.json \u4E2D\u7684 buddyId \u5BF9\u5E94\u7684
|
|
3498
|
+
DEV \u4F1A\u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u5F00\u53D1\u8005\u4F1A\u8BDD\uFF0C\u518D\u8BFB\u53D6\u5F53\u524D buddy.agent.json \u4E2D\u7684 buddyId \u5BF9\u5E94\u7684\u5F00\u53D1 Token\uFF0C\u4FDD\u5B58\u5230\u7CFB\u7EDF\u94A5\u5319\u4E32\u4F9B\u540E\u7EED\u590D\u7528\u3002
|
|
3499
|
+
--refresh-token \u91CD\u65B0\u9886\u53D6\u5DF2\u6709\u5F00\u53D1 Token\uFF0C\u7528\u4E8E\u670D\u52A1\u7AEF\u91CD\u7F6E\u540E\u7684\u672C\u673A\u66F4\u65B0\uFF1B\u4E0D\u4F1A\u751F\u6210\u65B0\u5BC6\u94A5\u3002`;
|
|
3308
3500
|
function valueAfter(args, index, flag2) {
|
|
3309
3501
|
const value = args[index + 1];
|
|
3310
3502
|
if (value === void 0 || !value.trim()) throw new DevCommandUsageError(`${flag2} \u7F3A\u5C11\u503C`);
|
|
@@ -3319,6 +3511,7 @@ function parseDevArguments(args) {
|
|
|
3319
3511
|
let directorySet = false;
|
|
3320
3512
|
let appServer;
|
|
3321
3513
|
let help = false;
|
|
3514
|
+
let refreshToken = false;
|
|
3322
3515
|
for (let index = 0; index < args.length; index += 1) {
|
|
3323
3516
|
const flag2 = args[index];
|
|
3324
3517
|
if (flag2 === "--help" || flag2 === "-h") {
|
|
@@ -3326,6 +3519,11 @@ function parseDevArguments(args) {
|
|
|
3326
3519
|
help = true;
|
|
3327
3520
|
continue;
|
|
3328
3521
|
}
|
|
3522
|
+
if (flag2 === "--refresh-token") {
|
|
3523
|
+
if (refreshToken) throw new DevCommandUsageError("--refresh-token \u4E0D\u80FD\u91CD\u590D");
|
|
3524
|
+
refreshToken = true;
|
|
3525
|
+
continue;
|
|
3526
|
+
}
|
|
3329
3527
|
const value = valueAfter(args, index, flag2 ?? "\u53C2\u6570");
|
|
3330
3528
|
index += 1;
|
|
3331
3529
|
switch (flag2) {
|
|
@@ -3341,7 +3539,7 @@ function parseDevArguments(args) {
|
|
|
3341
3539
|
throw new DevCommandUsageError(`\u672A\u77E5 dev \u53C2\u6570 ${flag2}`);
|
|
3342
3540
|
}
|
|
3343
3541
|
}
|
|
3344
|
-
return { directory, appServer, help };
|
|
3542
|
+
return { directory, appServer, help, refreshToken };
|
|
3345
3543
|
}
|
|
3346
3544
|
function appServerUrl(override, env) {
|
|
3347
3545
|
try {
|
|
@@ -3375,7 +3573,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
3375
3573
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
3376
3574
|
const environment = options.env ?? process.env;
|
|
3377
3575
|
const url = appServerUrl(parsed.appServer, environment);
|
|
3378
|
-
const authorizationInput = { appServerUrl: url };
|
|
3576
|
+
const authorizationInput = { appServerUrl: url, ...parsed.refreshToken ? { refreshToken: true } : {} };
|
|
3379
3577
|
if (!options.authorize) {
|
|
3380
3578
|
throw new DevCommandUsageError("\u5F53\u524D CLI Host \u5C1A\u672A\u914D\u7F6E\u642D\u642D\u8D26\u53F7\u767B\u5F55 Adapter");
|
|
3381
3579
|
}
|
|
@@ -3478,7 +3676,7 @@ var MemoryCloudTokenStore = class {
|
|
|
3478
3676
|
return this.#token === void 0 ? void 0 : frozenClone(this.#token);
|
|
3479
3677
|
}
|
|
3480
3678
|
async save(token) {
|
|
3481
|
-
this.#token =
|
|
3679
|
+
this.#token = validateToken2(token);
|
|
3482
3680
|
}
|
|
3483
3681
|
async clear() {
|
|
3484
3682
|
this.#token = void 0;
|
|
@@ -3534,7 +3732,7 @@ var CloudAuthenticator = class {
|
|
|
3534
3732
|
}
|
|
3535
3733
|
if (stored === void 0) return void 0;
|
|
3536
3734
|
try {
|
|
3537
|
-
const token =
|
|
3735
|
+
const token = validateToken2(stored);
|
|
3538
3736
|
if (token.expiresAt > this.#now() + this.#minimumValidityMs) return token;
|
|
3539
3737
|
} catch {
|
|
3540
3738
|
}
|
|
@@ -3555,7 +3753,7 @@ var CloudAuthenticator = class {
|
|
|
3555
3753
|
"\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u7684 state \u4E0D\u5339\u914D\uFF1B\u51ED\u636E\u672A\u4FDD\u5B58"
|
|
3556
3754
|
);
|
|
3557
3755
|
}
|
|
3558
|
-
const token =
|
|
3756
|
+
const token = validateToken2(resultRecord.token);
|
|
3559
3757
|
if (token.expiresAt <= this.#now() + this.#minimumValidityMs) {
|
|
3560
3758
|
throw new CloudLifecycleError("AUTH_TOKEN_EXPIRED", "\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u4E86\u5DF2\u8FC7\u671F\u6216\u5373\u5C06\u8FC7\u671F\u7684\u4F1A\u8BDD");
|
|
3561
3759
|
}
|
|
@@ -3563,7 +3761,7 @@ var CloudAuthenticator = class {
|
|
|
3563
3761
|
return token;
|
|
3564
3762
|
}
|
|
3565
3763
|
};
|
|
3566
|
-
function
|
|
3764
|
+
function validateToken2(value) {
|
|
3567
3765
|
try {
|
|
3568
3766
|
const input = record(value, "cloud token");
|
|
3569
3767
|
exactKeys(input, ["accessToken", "subject", "expiresAt"], [], "cloud token");
|
|
@@ -3866,8 +4064,8 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3866
4064
|
};
|
|
3867
4065
|
|
|
3868
4066
|
// packages/cli-core/src/cloud/browser-login.ts
|
|
3869
|
-
import { createHash as
|
|
3870
|
-
import { spawn } from "node:child_process";
|
|
4067
|
+
import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
4068
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3871
4069
|
import { createServer } from "node:http";
|
|
3872
4070
|
|
|
3873
4071
|
// packages/cli-core/src/cloud/login-result.ts
|
|
@@ -3927,7 +4125,7 @@ function browserCommand(url) {
|
|
|
3927
4125
|
async function openExternalUrl(url) {
|
|
3928
4126
|
const launch = browserCommand(url);
|
|
3929
4127
|
await new Promise((resolve17, reject) => {
|
|
3930
|
-
const child =
|
|
4128
|
+
const child = spawn2(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
3931
4129
|
child.once("error", reject);
|
|
3932
4130
|
child.once("exit", (code2) => code2 === 0 ? resolve17() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
3933
4131
|
});
|
|
@@ -3974,7 +4172,7 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3974
4172
|
if (request.signal?.aborted) throw request.signal.reason;
|
|
3975
4173
|
const callbackSecret = randomBytes(24).toString("hex");
|
|
3976
4174
|
const codeVerifier = randomBytes(32).toString("base64url");
|
|
3977
|
-
const codeChallenge =
|
|
4175
|
+
const codeChallenge = createHash6("sha256").update(codeVerifier).digest("base64url");
|
|
3978
4176
|
let requestId = "";
|
|
3979
4177
|
let settle;
|
|
3980
4178
|
let reject;
|
|
@@ -4110,126 +4308,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
4110
4308
|
}
|
|
4111
4309
|
};
|
|
4112
4310
|
|
|
4113
|
-
// packages/cli-core/src/cloud/credential-store.ts
|
|
4114
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
4115
|
-
var DEFAULT_SERVICE = "com.xiaohongshu.buddy.developer-cli";
|
|
4116
|
-
var DEFAULT_ACCOUNT = "developer-session";
|
|
4117
|
-
var MAX_CREDENTIAL_BYTES = 32 * 1024;
|
|
4118
|
-
function safeLabel(value, fallback, name) {
|
|
4119
|
-
const result = value ?? fallback;
|
|
4120
|
-
if (!result || result !== result.trim() || /[\u0000-\u001f\u007f]/u.test(result) || result.length > 256) {
|
|
4121
|
-
throw new TypeError(`${name} is invalid`);
|
|
4122
|
-
}
|
|
4123
|
-
return result;
|
|
4124
|
-
}
|
|
4125
|
-
function serialize(token) {
|
|
4126
|
-
const value = JSON.stringify(token);
|
|
4127
|
-
if (Buffer.byteLength(value, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
4128
|
-
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u4E91\u7AEF\u4F1A\u8BDD\u51ED\u636E\u8FC7\u5927\uFF0C\u672A\u4FDD\u5B58");
|
|
4129
|
-
}
|
|
4130
|
-
return value;
|
|
4131
|
-
}
|
|
4132
|
-
function parse(source) {
|
|
4133
|
-
if (Buffer.byteLength(source, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
4134
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4135
|
-
}
|
|
4136
|
-
let value;
|
|
4137
|
-
try {
|
|
4138
|
-
value = JSON.parse(source);
|
|
4139
|
-
} catch {
|
|
4140
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4141
|
-
}
|
|
4142
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4143
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4144
|
-
}
|
|
4145
|
-
const record2 = value;
|
|
4146
|
-
if (Object.keys(record2).length !== 3 || typeof record2.accessToken !== "string" || typeof record2.subject !== "string" || !Number.isSafeInteger(record2.expiresAt)) {
|
|
4147
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4148
|
-
}
|
|
4149
|
-
return Object.freeze({
|
|
4150
|
-
accessToken: record2.accessToken,
|
|
4151
|
-
subject: record2.subject,
|
|
4152
|
-
expiresAt: record2.expiresAt
|
|
4153
|
-
});
|
|
4154
|
-
}
|
|
4155
|
-
async function runCredentialCommand(input) {
|
|
4156
|
-
return await new Promise((resolve17, reject) => {
|
|
4157
|
-
const child = spawn2(input.command, [...input.args], {
|
|
4158
|
-
shell: false,
|
|
4159
|
-
windowsHide: true,
|
|
4160
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4161
|
-
});
|
|
4162
|
-
const stdout = [];
|
|
4163
|
-
const stderr = [];
|
|
4164
|
-
let outputBytes = 0;
|
|
4165
|
-
const collect = (target, chunk) => {
|
|
4166
|
-
outputBytes += chunk.byteLength;
|
|
4167
|
-
if (outputBytes <= MAX_CREDENTIAL_BYTES) target.push(chunk);
|
|
4168
|
-
};
|
|
4169
|
-
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
4170
|
-
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
4171
|
-
child.once("error", reject);
|
|
4172
|
-
child.once("close", (code2) => resolve17({
|
|
4173
|
-
code: code2 ?? 1,
|
|
4174
|
-
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
4175
|
-
stderr: Buffer.concat(stderr).toString("utf8")
|
|
4176
|
-
}));
|
|
4177
|
-
child.stdin.on("error", () => void 0);
|
|
4178
|
-
child.stdin.end(input.input ?? "");
|
|
4179
|
-
});
|
|
4180
|
-
}
|
|
4181
|
-
var SystemCloudTokenStore = class {
|
|
4182
|
-
#platform;
|
|
4183
|
-
#service;
|
|
4184
|
-
#account;
|
|
4185
|
-
#run;
|
|
4186
|
-
constructor(options = {}) {
|
|
4187
|
-
this.#platform = options.platform ?? process.platform;
|
|
4188
|
-
this.#service = safeLabel(options.service, DEFAULT_SERVICE, "service");
|
|
4189
|
-
this.#account = safeLabel(options.account, DEFAULT_ACCOUNT, "account");
|
|
4190
|
-
this.#run = options.commandRunner ?? runCredentialCommand;
|
|
4191
|
-
if (this.#platform !== "darwin") {
|
|
4192
|
-
throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D CLI V1 \u7684\u5B89\u5168\u767B\u5F55\u5B58\u50A8\u76EE\u524D\u53EA\u652F\u6301 macOS Keychain");
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
async load() {
|
|
4196
|
-
const result = await this.#run({
|
|
4197
|
-
command: "security",
|
|
4198
|
-
args: ["find-generic-password", "-s", this.#service, "-a", this.#account, "-w"]
|
|
4199
|
-
});
|
|
4200
|
-
if (result.code === 44) return void 0;
|
|
4201
|
-
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u8BFB\u53D6 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u767B\u5F55\u72B6\u6001");
|
|
4202
|
-
const raw = result.stdout.trimEnd();
|
|
4203
|
-
return parse(/^(?:[a-fA-F0-9]{2})+$/u.test(raw) ? Buffer.from(raw, "hex").toString("utf8") : raw);
|
|
4204
|
-
}
|
|
4205
|
-
async save(token) {
|
|
4206
|
-
const source = serialize(token);
|
|
4207
|
-
const line = ["add-generic-password", "-U", "-s", this.#service, "-a", this.#account, "-w", source].map((part) => `"${part.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"')}"`).join(" ") + "\n";
|
|
4208
|
-
if (Buffer.byteLength(line, "utf8") >= 4096) {
|
|
4209
|
-
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u767B\u5F55\u51ED\u636E\u8D85\u8FC7 macOS \u5199\u5165\u957F\u5EA6\u9650\u5236\uFF0C\u672A\u4FDD\u5B58\uFF1B\u8BF7\u8054\u7CFB\u5E73\u53F0\u5904\u7406");
|
|
4210
|
-
}
|
|
4211
|
-
const result = await this.#run({
|
|
4212
|
-
command: "security",
|
|
4213
|
-
args: ["-i", "-q"],
|
|
4214
|
-
input: line
|
|
4215
|
-
});
|
|
4216
|
-
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u628A\u642D\u642D\u767B\u5F55\u72B6\u6001\u5199\u5165 macOS \u94A5\u5319\u4E32");
|
|
4217
|
-
const saved = await this.load();
|
|
4218
|
-
if (!saved || serialize(saved) !== source) throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u767B\u5F55\u51ED\u636E\u5199\u5165\u540E\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
|
|
4219
|
-
}
|
|
4220
|
-
async clear() {
|
|
4221
|
-
const result = await this.#run({
|
|
4222
|
-
command: "security",
|
|
4223
|
-
args: ["delete-generic-password", "-s", this.#service, "-a", this.#account]
|
|
4224
|
-
});
|
|
4225
|
-
if (result.code !== 0 && result.code !== 44) {
|
|
4226
|
-
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u6E05\u9664 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u767B\u5F55\u72B6\u6001");
|
|
4227
|
-
}
|
|
4228
|
-
}
|
|
4229
|
-
};
|
|
4230
|
-
|
|
4231
4311
|
// packages/cli-core/src/cloud/mock-client.ts
|
|
4232
|
-
import { createHash as
|
|
4312
|
+
import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
|
|
4233
4313
|
var MockDeveloperPlatformClient = class {
|
|
4234
4314
|
#now;
|
|
4235
4315
|
#buddies = /* @__PURE__ */ new Map();
|
|
@@ -4257,7 +4337,7 @@ var MockDeveloperPlatformClient = class {
|
|
|
4257
4337
|
const current = this.#require(request.id);
|
|
4258
4338
|
validateBuddyMetaUpdate(request.meta);
|
|
4259
4339
|
const onlyAvatar = Object.keys(request.meta).every((key) => key === "avatar");
|
|
4260
|
-
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${
|
|
4340
|
+
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${createHash7("sha256").update(request.avatar.bytes).digest("hex").slice(0, 16)}` : request.meta.avatar === null ? null : current.meta?.avatar ?? null;
|
|
4261
4341
|
const next = this.#value({ ...current, meta: { ...onlyAvatar ? current.meta : request.meta, avatar }, updatedAt: this.#now() });
|
|
4262
4342
|
this.#buddies.set(request.id, next);
|
|
4263
4343
|
return structuredClone(next);
|
|
@@ -5319,7 +5399,7 @@ async function scanExternalSkillPortability(options) {
|
|
|
5319
5399
|
}
|
|
5320
5400
|
|
|
5321
5401
|
// packages/cli-core/src/push/preflight.ts
|
|
5322
|
-
import { createHash as
|
|
5402
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
5323
5403
|
import { lstat as lstat10, open as open5, readdir as readdir5, realpath as realpath4 } from "node:fs/promises";
|
|
5324
5404
|
import { isAbsolute as isAbsolute3, join as join13, relative as relative2, resolve as resolve15, sep as sep2, win32 as win323 } from "node:path";
|
|
5325
5405
|
|
|
@@ -5419,7 +5499,7 @@ function error(options) {
|
|
|
5419
5499
|
throw new PushPreflightError(options);
|
|
5420
5500
|
}
|
|
5421
5501
|
function sha256(value) {
|
|
5422
|
-
return `sha256:${
|
|
5502
|
+
return `sha256:${createHash8("sha256").update(value).digest("hex")}`;
|
|
5423
5503
|
}
|
|
5424
5504
|
function positiveLimit(value, name, hardMaximum) {
|
|
5425
5505
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -5665,7 +5745,7 @@ async function hashRegularFile(options) {
|
|
|
5665
5745
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
5666
5746
|
});
|
|
5667
5747
|
}
|
|
5668
|
-
const hash3 =
|
|
5748
|
+
const hash3 = createHash8("sha256");
|
|
5669
5749
|
const captured = [];
|
|
5670
5750
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
5671
5751
|
let bytes = 0;
|
|
@@ -6124,7 +6204,7 @@ async function createPushPreflight(options) {
|
|
|
6124
6204
|
}
|
|
6125
6205
|
|
|
6126
6206
|
// packages/cli-core/src/push/bundle.ts
|
|
6127
|
-
import { createHash as
|
|
6207
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
6128
6208
|
import { lstat as lstat11, readFile as readFile11, realpath as realpath5 } from "node:fs/promises";
|
|
6129
6209
|
import { join as join14, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
|
|
6130
6210
|
import { Writable } from "node:stream";
|
|
@@ -6133,7 +6213,7 @@ import { createGzip } from "node:zlib";
|
|
|
6133
6213
|
import { pack } from "tar-stream";
|
|
6134
6214
|
var MEDIA_TYPE = "application/gzip";
|
|
6135
6215
|
function digest2(bytes) {
|
|
6136
|
-
return `sha256:${
|
|
6216
|
+
return `sha256:${createHash9("sha256").update(bytes).digest("hex")}`;
|
|
6137
6217
|
}
|
|
6138
6218
|
function changed(path, message, cause) {
|
|
6139
6219
|
throw new PushPreflightError({
|
|
@@ -6182,7 +6262,7 @@ async function createPushSourceBundle(plan) {
|
|
|
6182
6262
|
schemaVersion: "1",
|
|
6183
6263
|
files: files.map((file) => ({ path: file.path, bytes: file.bytes, digest: file.digest }))
|
|
6184
6264
|
});
|
|
6185
|
-
const calculatedBundleDigest = `sha256:${
|
|
6265
|
+
const calculatedBundleDigest = `sha256:${createHash9("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
6186
6266
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
6187
6267
|
changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
|
|
6188
6268
|
}
|
|
@@ -6840,6 +6920,7 @@ export {
|
|
|
6840
6920
|
PromptCancelledError,
|
|
6841
6921
|
PushPreflightError,
|
|
6842
6922
|
SkillPortabilityScanError,
|
|
6923
|
+
SystemBuddyTokenStore,
|
|
6843
6924
|
SystemCloudTokenStore,
|
|
6844
6925
|
abortedDevError,
|
|
6845
6926
|
applyBuddyDraftInitialization,
|