@my-life-buddies/cli 0.17.1 → 0.17.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/dist/bin/core.js CHANGED
@@ -2681,6 +2681,271 @@ Runtime \u542F\u52A8\u65F6\u8BFB\u53D6\u6839\u76EE\u5F55 \`data-access.json\`\uF
2681
2681
 
2682
2682
  \u5B8C\u6210\u914D\u7F6E\u3001\u6CE8\u518C\u3001\u6784\u5EFA\u3001\u542F\u52A8\u53CA\u5173\u952E\u89C4\u5219\u7684\u6280\u672F\u68C0\u67E5\u3002\u9700\u8981\u5B9A\u5236\u7684\u884C\u4E3A\u5FC5\u987B\u68C0\u67E5\u771F\u5B9E\u6267\u884C\u8DEF\u5F84\uFF0C\u4E0D\u80FD\u53EA\u641C\u7D22 Prompt \u4E2D\u662F\u5426\u51FA\u73B0\u67D0\u53E5\u8BDD\u3002\u968F\u540E\u8FDB\u5165\u5DF2\u6709\u7684\u771F\u5B9E\u9884\u89C8\u4EA4\u63A5\uFF0C\u7531\u5F00\u53D1\u8005\u4EB2\u81EA\u4F53\u9A8C\u5E76\u53CD\u9988\uFF1B\u4E0D\u81EA\u52A8\u5C55\u5F00\u591A\u573A\u666F\u8BD5\u804A\u3001\u8C03\u6559\u3001\u63D0\u5BA1\u6216\u53D1\u5E03\u3002`;
2683
2683
 
2684
+ // packages/cli-core/src/platform-url.ts
2685
+ var DEFAULT_MLB_TEST_URL = "http://hey-dada.life:8788";
2686
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
2687
+ function normalizePlatformUrl(value) {
2688
+ const url = new URL(value);
2689
+ if (url.username || url.password || url.search || url.hash) {
2690
+ throw new TypeError("\u5E73\u53F0\u5730\u5740\u4E0D\u80FD\u5305\u542B credentials\u3001query \u6216 fragment");
2691
+ }
2692
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && (LOOPBACK_HOSTS.has(url.hostname) || url.origin === DEFAULT_MLB_TEST_URL))) {
2693
+ throw new TypeError(`\u5E73\u53F0\u5730\u5740\u5FC5\u987B\u4F7F\u7528 HTTPS\uFF1BHTTP \u4EC5\u5141\u8BB8\u672C\u673A\u6216\u56FA\u5B9A\u6D4B\u8BD5\u5730\u5740 ${DEFAULT_MLB_TEST_URL}`);
2694
+ }
2695
+ if (!url.pathname.endsWith("/")) url.pathname += "/";
2696
+ return url;
2697
+ }
2698
+ function resolvePlatformUrl(options = {}) {
2699
+ const env = options.env ?? process.env;
2700
+ const configured = options.development ? env.BUDDY_DEV_APP_SERVER_URL ?? env.BUDDY_APP_SERVER_URL : env.BUDDY_APP_SERVER_URL ?? env.BUDDY_DEV_APP_SERVER_URL;
2701
+ return normalizePlatformUrl(options.override ?? configured ?? DEFAULT_MLB_TEST_URL);
2702
+ }
2703
+
2704
+ // packages/cli-core/src/cloud/browser-login.ts
2705
+ import { createHash as createHash4, randomBytes } from "node:crypto";
2706
+ import { spawn } from "node:child_process";
2707
+ import { createServer } from "node:http";
2708
+
2709
+ // packages/cli-core/src/cloud/login-result.ts
2710
+ import { readFileSync } from "node:fs";
2711
+ var brand = new URL("../../resources/brand/", import.meta.url);
2712
+ var tokens = readFileSync(new URL("tokens.css", brand), "utf8");
2713
+ var styles = readFileSync(new URL("login-result.css", brand), "utf8");
2714
+ var logo = readFileSync(new URL("logo.png", brand)).toString("base64");
2715
+ function escapeHtml(value) {
2716
+ return value.replace(/[&<>"']/gu, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;" })[character]);
2717
+ }
2718
+ function renderLoginResult(title, message, webUrl, tone) {
2719
+ const label = { success: "\u5DF2\u6388\u6743", neutral: "\u5DF2\u53D6\u6D88", error: "\u672A\u5B8C\u6210" }[tone];
2720
+ return `<!doctype html>
2721
+ <html lang="zh-CN"><head>
2722
+ <meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
2723
+ <meta name="color-scheme" content="light dark"><title>${escapeHtml(title)} \xB7 \u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</title>
2724
+ <style>${tokens}
2725
+ ${styles}</style></head>
2726
+ <body><div class="result-shell">
2727
+ <header class="result-brand"><img src="data:image/png;base64,${logo}" alt="" width="44" height="44"><span>\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</span></header>
2728
+ <main class="result-card" aria-labelledby="result-title">
2729
+ <div class="result-heading"><p class="result-eyebrow">buddy-cli \u767B\u5F55</p><span class="result-status" data-tone="${tone}">${label}</span></div>
2730
+ <h1 id="result-title">${escapeHtml(title)}</h1>
2731
+ <p class="result-message">${escapeHtml(message)}</p>
2732
+ <div class="result-next"><strong>${tone === "success" ? "\u56DE\u5230\u7EC8\u7AEF\uFF0C\u7EE7\u7EED\u521B\u4F5C" : "\u56DE\u5230\u7EC8\u7AEF\uFF0C\u91CD\u65B0\u5F00\u59CB"}</strong>
2733
+ <p>${tone === "success" ? "\u67E5\u770B CLI \u7684\u767B\u5F55\u7ED3\u679C\uFF0C\u7EE7\u7EED\u521B\u5EFA\u3001\u5F00\u53D1\u548C\u9884\u89C8\u4F60\u7684\u642D\u5B50\u3002" : "\u9700\u8981\u7EE7\u7EED\u65F6\uFF0C\u5728\u7EC8\u7AEF\u91CD\u65B0\u8FD0\u884C\u767B\u5F55\u547D\u4EE4\u3002"}</p>
2734
+ ${tone === "success" ? "" : "<code>buddy-cli login</code>"}</div>
2735
+ <a class="result-link" href="${escapeHtml(webUrl.href)}">\u8FD4\u56DE\u5F00\u653E\u5E73\u53F0</a>
2736
+ </main><footer>\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</footer></div></body></html>`;
2737
+ }
2738
+
2739
+ // packages/cli-core/src/cloud/browser-login.ts
2740
+ var DEFAULT_LOGIN_TIMEOUT_MS = 5 * 6e4;
2741
+ var DEFAULT_EXCHANGE_TIMEOUT_MS = 15e3;
2742
+ var DEFAULT_DEVELOPER_WEB_URL = "https://hey-dada.life/developer/";
2743
+ var REQUEST_ID = /^[A-Za-z0-9_-]{43}$/u;
2744
+ var AUTH_CODE = /^[A-Za-z0-9_-]{43}$/u;
2745
+ function positive(value, fallback, name) {
2746
+ const result = value ?? fallback;
2747
+ if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError(`${name} must be a positive safe integer`);
2748
+ return result;
2749
+ }
2750
+ function webBase(value) {
2751
+ const url = new URL(value);
2752
+ const loopback = ["localhost", "127.0.0.1", "[::1]", "::1"].includes(url.hostname);
2753
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback) || url.username || url.password || url.search || url.hash) {
2754
+ throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u5FC5\u987B\u4F7F\u7528 HTTPS\uFF1B\u672C\u673A\u5F00\u53D1\u53EF\u4EE5\u4F7F\u7528 loopback HTTP");
2755
+ }
2756
+ if (!url.pathname.endsWith("/")) url.pathname += "/";
2757
+ return url;
2758
+ }
2759
+ function browserCommand(url) {
2760
+ if (process.platform === "darwin") return { command: "open", args: [url.href] };
2761
+ throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D CLI V1 \u672C\u5730\u767B\u5F55\u76EE\u524D\u53EA\u652F\u6301 macOS");
2762
+ }
2763
+ async function openExternalUrl(url) {
2764
+ const launch = browserCommand(url);
2765
+ await new Promise((resolve19, reject) => {
2766
+ const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
2767
+ child.once("error", reject);
2768
+ child.once("exit", (code2) => code2 === 0 ? resolve19() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
2769
+ });
2770
+ }
2771
+ async function write(res, status, title, message, webUrl, tone = "error") {
2772
+ res.writeHead(status, {
2773
+ "cache-control": "no-store",
2774
+ "connection": "close",
2775
+ "content-security-policy": "default-src 'none'; img-src data:; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'",
2776
+ "content-type": "text/html; charset=utf-8",
2777
+ "referrer-policy": "no-referrer",
2778
+ "x-content-type-options": "nosniff"
2779
+ });
2780
+ await new Promise((resolve19, reject) => {
2781
+ res.once("error", reject);
2782
+ res.once("close", resolve19);
2783
+ res.end(renderLoginResult(title, message, webUrl, tone), resolve19);
2784
+ });
2785
+ }
2786
+ async function readJson(response) {
2787
+ const value = await response.json().catch(() => void 0);
2788
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2789
+ throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u6388\u6743\u7ED3\u679C");
2790
+ }
2791
+ return value;
2792
+ }
2793
+ var LoopbackBrowserLoginAdapter = class {
2794
+ #platformUrl;
2795
+ #developerWebUrl;
2796
+ #openBrowser;
2797
+ #onLoginUrl;
2798
+ #timeoutMs;
2799
+ #fetch;
2800
+ constructor(options) {
2801
+ this.#platformUrl = normalizePlatformUrl(options.platformUrl);
2802
+ this.#developerWebUrl = webBase(options.developerWebUrl ?? DEFAULT_DEVELOPER_WEB_URL);
2803
+ this.#openBrowser = options.openBrowser ?? openExternalUrl;
2804
+ this.#onLoginUrl = options.onLoginUrl;
2805
+ this.#timeoutMs = positive(options.timeoutMs, DEFAULT_LOGIN_TIMEOUT_MS, "timeoutMs");
2806
+ this.#fetch = options.fetch ?? globalThis.fetch;
2807
+ if (typeof this.#fetch !== "function") throw new TypeError("A Fetch API implementation is required");
2808
+ }
2809
+ async login(request) {
2810
+ if (request.signal?.aborted) throw request.signal.reason;
2811
+ const callbackSecret = randomBytes(24).toString("hex");
2812
+ const codeVerifier = randomBytes(32).toString("base64url");
2813
+ const codeChallenge = createHash4("sha256").update(codeVerifier).digest("base64url");
2814
+ let requestId = "";
2815
+ let settle;
2816
+ let reject;
2817
+ let settled = false;
2818
+ const result = new Promise((resolve19, rejectResult) => {
2819
+ settle = (value) => {
2820
+ if (!settled) {
2821
+ settled = true;
2822
+ resolve19(value);
2823
+ }
2824
+ };
2825
+ reject = (reason) => {
2826
+ if (!settled) {
2827
+ settled = true;
2828
+ rejectResult(reason);
2829
+ }
2830
+ };
2831
+ });
2832
+ void result.catch(() => void 0);
2833
+ const server = createServer((req, res) => {
2834
+ void this.#receiveCallback(req, res, {
2835
+ callbackSecret,
2836
+ codeVerifier,
2837
+ requestId,
2838
+ state: request.state
2839
+ }).then(settle, reject);
2840
+ });
2841
+ await new Promise((resolve19, rejectListen) => {
2842
+ server.once("error", rejectListen);
2843
+ server.listen(0, "127.0.0.1", () => resolve19());
2844
+ });
2845
+ const address = server.address();
2846
+ if (!address || typeof address === "string") {
2847
+ server.close();
2848
+ throw new CloudLifecycleError("PLATFORM_UNAVAILABLE", "\u65E0\u6CD5\u542F\u52A8\u672C\u673A\u767B\u5F55\u56DE\u8C03");
2849
+ }
2850
+ const redirectUri = `http://127.0.0.1:${address.port}/callback/${callbackSecret}`;
2851
+ const timer = setTimeout(() => reject(new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u6D4F\u89C8\u5668\u767B\u5F55\u7B49\u5F85\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5")), this.#timeoutMs);
2852
+ timer.unref?.();
2853
+ const abort = () => reject(request.signal?.reason ?? new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u767B\u5F55\u5DF2\u53D6\u6D88"));
2854
+ request.signal?.addEventListener("abort", abort, { once: true });
2855
+ try {
2856
+ const created = await this.#post("api/cli/authorizations", {
2857
+ state: request.state,
2858
+ codeChallenge,
2859
+ redirectUri,
2860
+ platformOrigin: this.#platformUrl.origin
2861
+ }, request.signal);
2862
+ if (typeof created.requestId !== "string" || !REQUEST_ID.test(created.requestId)) {
2863
+ throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u6388\u6743\u7F16\u53F7");
2864
+ }
2865
+ requestId = created.requestId;
2866
+ const loginUrl = new URL(this.#developerWebUrl);
2867
+ loginUrl.hash = `#/cli-authorize?requestId=${encodeURIComponent(requestId)}`;
2868
+ this.#onLoginUrl?.(loginUrl);
2869
+ await this.#openBrowser(loginUrl).catch(() => {
2870
+ throw new CloudLifecycleError("PLATFORM_UNAVAILABLE", "\u65E0\u6CD5\u81EA\u52A8\u6253\u5F00\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\uFF0C\u8BF7\u68C0\u67E5\u7CFB\u7EDF\u9ED8\u8BA4\u6D4F\u89C8\u5668\u540E\u91CD\u8BD5");
2871
+ });
2872
+ return await result;
2873
+ } finally {
2874
+ clearTimeout(timer);
2875
+ request.signal?.removeEventListener("abort", abort);
2876
+ await new Promise((resolve19) => {
2877
+ server.close(() => resolve19());
2878
+ server.closeIdleConnections();
2879
+ server.closeAllConnections();
2880
+ });
2881
+ }
2882
+ }
2883
+ async #receiveCallback(req, res, expected) {
2884
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
2885
+ if (req.method !== "GET" || url.pathname !== `/callback/${expected.callbackSecret}`) {
2886
+ await write(res, 404, "\u9875\u9762\u4E0D\u5B58\u5728", "\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u65B0\u53D1\u8D77\u767B\u5F55\u3002", this.#developerWebUrl);
2887
+ throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u6D4F\u89C8\u5668\u56DE\u8C03\u65E0\u6548\uFF0C\u8BF7\u91CD\u8BD5");
2888
+ }
2889
+ if (url.searchParams.get("state") !== expected.state) {
2890
+ await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
2891
+ throw new CloudLifecycleError("AUTH_STATE_MISMATCH", "\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u7684 state \u4E0D\u5339\u914D\uFF1B\u51ED\u636E\u672A\u4FDD\u5B58");
2892
+ }
2893
+ if (url.searchParams.get("error") === "access_denied") {
2894
+ await write(res, 200, "\u5DF2\u53D6\u6D88\u6388\u6743", "\u6CA1\u6709\u5411 buddy-cli \u63D0\u4F9B\u8D26\u53F7\u6743\u9650\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u3002", this.#developerWebUrl, "neutral");
2895
+ throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u7528\u6237\u53D6\u6D88\u4E86 CLI \u6388\u6743");
2896
+ }
2897
+ const code2 = url.searchParams.get("code");
2898
+ if (!code2 || !AUTH_CODE.test(code2) || !expected.requestId) {
2899
+ await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u7ED3\u679C\u65E0\u6548\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
2900
+ throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6388\u6743\u7801");
2901
+ }
2902
+ try {
2903
+ const exchanged = await this.#post("api/cli/exchange", {
2904
+ requestId: expected.requestId,
2905
+ code: code2,
2906
+ codeVerifier: expected.codeVerifier
2907
+ });
2908
+ if (exchanged.platformOrigin !== this.#platformUrl.origin || typeof exchanged.token !== "string" || typeof exchanged.userId !== "string" || !Number.isSafeInteger(exchanged.expiresAt)) {
2909
+ throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u7684\u8D26\u53F7\u51ED\u636E\u4E0D\u5B8C\u6574");
2910
+ }
2911
+ await write(res, 200, "\u767B\u5F55\u6210\u529F", "buddy-cli \u5DF2\u83B7\u5F97\u6388\u6743\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u5E76\u8FD4\u56DE\u7EC8\u7AEF\u3002", this.#developerWebUrl, "success");
2912
+ return {
2913
+ state: expected.state,
2914
+ token: {
2915
+ accessToken: exchanged.token,
2916
+ subject: exchanged.userId,
2917
+ expiresAt: exchanged.expiresAt
2918
+ }
2919
+ };
2920
+ } catch (error2) {
2921
+ await write(res, 502, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u4EA4\u6362\u5931\u8D25\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
2922
+ throw error2;
2923
+ }
2924
+ }
2925
+ async #post(path, payload, signal) {
2926
+ let response;
2927
+ try {
2928
+ const signals = [AbortSignal.timeout(DEFAULT_EXCHANGE_TIMEOUT_MS), ...signal ? [signal] : []];
2929
+ response = await this.#fetch(new URL(path, this.#developerWebUrl), {
2930
+ method: "POST",
2931
+ headers: { Accept: "application/json", "Content-Type": "application/json" },
2932
+ body: JSON.stringify(payload),
2933
+ redirect: "error",
2934
+ signal: AbortSignal.any(signals)
2935
+ });
2936
+ } catch {
2937
+ if (signal?.aborted) throw signal.reason;
2938
+ throw new CloudLifecycleError("PLATFORM_UNAVAILABLE", "\u65E0\u6CD5\u8FDE\u63A5\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u5B8C\u6210 CLI \u767B\u5F55");
2939
+ }
2940
+ const data = await readJson(response);
2941
+ if (!response.ok) {
2942
+ const message = typeof data.error === "string" ? data.error : "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u62D2\u7EDD\u4E86\u8FD9\u6B21 CLI \u6388\u6743";
2943
+ throw new CloudLifecycleError(response.status === 400 || response.status === 409 || response.status === 410 ? "AUTH_LOGIN_CANCELLED" : "PLATFORM_UNAVAILABLE", message);
2944
+ }
2945
+ return data;
2946
+ }
2947
+ };
2948
+
2684
2949
  // packages/cli-core/src/help.ts
2685
2950
  var CLI_HELP = `\u642D\u642D Developer Platform CLI
2686
2951
 
@@ -2704,6 +2969,8 @@ var CLI_HELP = `\u642D\u642D Developer Platform CLI
2704
2969
  --version \u67E5\u770B\u5F53\u524D CLI \u7248\u672C
2705
2970
 
2706
2971
  \u76F4\u63A5\u8FD0\u884C buddy-cli \u53EF\u8BC6\u522B\u5F53\u524D\u5DE5\u7A0B\u6216\u5F15\u5BFC\u521B\u5EFA\u3002
2972
+ \u9ED8\u8BA4\u5E73\u53F0\uFF1A${DEFAULT_MLB_TEST_URL}\uFF1B\u5F00\u53D1\u8005\u540E\u53F0\uFF1A${DEFAULT_DEVELOPER_WEB_URL}\u3002
2973
+ \u5E73\u53F0\u8FC1\u79FB\u540E\u91CD\u65B0 login\uFF1B\u65E7\u5DE5\u7A0B\u7528 dev --refresh-token \u6216 preview --refresh-token \u66F4\u65B0\u5F00\u53D1\u51ED\u636E\u548C .env \u5730\u5740\u3002
2707
2974
  \u6B63\u5E38\u547D\u4EE4\u5148\u8054\u7F51\u81EA\u68C0\uFF0C\u65E7\u7248\u81EA\u52A8\u66F4\u65B0\u540E\u7EE7\u7EED\u6267\u884C\uFF1B\u5931\u8D25\u65F6\u505C\u6B62\u3002\u5E2E\u52A9\u548C --version \u79BB\u7EBF\u53EF\u7528\u3002
2708
2975
  \u53C2\u6570\u4E0E\u793A\u4F8B: buddy-cli <\u547D\u4EE4> --help\uFF0C\u4F8B\u5982 buddy-cli init --help\u3002
2709
2976
  Coding Agent \u5F00\u59CB\u5DE5\u4F5C\u524D\u8BF7\u8BFB\u53D6 buddy-cli guide\u3002`;
@@ -2740,6 +3007,8 @@ Skill \u7684\u5177\u4F53\u6D41\u7A0B\u8BFB\u53D6 HANDOFF.md \u4E2D\u7684\u6587\u
2740
3007
 
2741
3008
  ## CLI \u7248\u672C\u4E0E\u6267\u884C\u89C4\u5219
2742
3009
 
3010
+ \u5E73\u53F0\u5165\u53E3\u4E3A ${DEFAULT_MLB_TEST_URL}\uFF0C\u5F00\u53D1\u8005\u540E\u53F0\u4E3A ${DEFAULT_DEVELOPER_WEB_URL}\u3002\u5DF2\u6709\u5DE5\u7A0B\u4ECE\u65E7\u670D\u52A1\u5668\u8FC1\u79FB\u65F6\uFF0C\u5148\u79FB\u9664\u6216\u66F4\u65B0\u7EC8\u7AEF\u4E2D\u7684 BUDDY_APP_SERVER_URL\u3001BUDDY_DEV_APP_SERVER_URL\u3001BUDDY_DEVELOPER_WEB_URL \u8986\u76D6\uFF0C\u91CD\u65B0 login\uFF0C\u518D\u6267\u884C dev --refresh-token \u6216 preview --refresh-token\uFF1BDEV \u542F\u52A8\u65F6\u66F4\u65B0 .env\uFF0C\u4FDD\u7559 package.json.buddyId \u4E0E\u4E1A\u52A1\u914D\u7F6E\u3002
3011
+
2743
3012
  \u4F7F\u7528\u5B98\u65B9 buddy-cli \u5165\u53E3\u3002\u6BCF\u6761\u6B63\u5E38\u547D\u4EE4\uFF08\u5305\u62EC guide\uFF09\u6267\u884C\u524D\u90FD\u4F1A\u67E5\u8BE2\u516C\u5171 npm latest\uFF1B\u7248\u672C\u8FC7\u65E7\u65F6\u81EA\u52A8\u66F4\u65B0\uFF0C\u518D\u7531\u65B0\u7248\u6267\u884C\u540C\u4E00\u547D\u4EE4\u3002\u7B49\u5F85\u5F53\u524D\u547D\u4EE4\u5B8C\u6210\uFF0C\u4E0D\u91CD\u590D\u53D1\u8D77\uFF0C\u4E0D\u53E6\u5F00\u8FDB\u7A0B\u540C\u65F6\u91CD\u8DD1\u3002
2744
3013
  \u5168\u5C40\u5B89\u88C5\u66F4\u65B0\u5BF9\u5E94 npm \u73AF\u5883\u7684\u5B89\u88C5\uFF1B\u6E90\u7801\u3001npm link\u3001\u9879\u76EE\u5185\u5B89\u88C5\u548C npx \u901A\u8FC7\u7F13\u5B58\u7684\u65B0\u7248\u7EE7\u7EED\u6267\u884C\uFF0C\u4E0D\u6539\u5199\u6E90\u7801\u6216\u9879\u76EE\u4F9D\u8D56\u3002\u53C2\u6570\u3001\u5DE5\u4F5C\u76EE\u5F55\u3001\u7EC8\u7AEF\u8F93\u5165\u8F93\u51FA\u548C\u9000\u51FA\u72B6\u6001\u4FDD\u7559\u3002\u4E00\u6B21\u8C03\u7528\u6700\u591A\u81EA\u52A8\u5207\u6362\u4E00\u6B21\uFF1B\u5F53\u524D\u7248\u672C\u9AD8\u4E8E latest \u65F6\u4E0D\u964D\u7EA7\uFF0C\u5DF2\u8FD0\u884C\u7684 DEV \u4E0D\u4F1A\u56E0\u53D1\u5E03\u65B0 CLI \u81EA\u52A8\u91CD\u542F\u3002
2745
3014
  \u5E2E\u52A9\u3001\u5404\u547D\u4EE4 --help \u548C --version \u79BB\u7EBF\u53EF\u7528\uFF1Bupdate \u662F\u5355\u72EC\u68C0\u67E5\u6216\u66F4\u65B0\u5165\u53E3\u3002\u67E5\u8BE2\u6216\u66F4\u65B0\u5931\u8D25\u65F6\u539F\u4E1A\u52A1\u64CD\u4F5C\u4E0D\u4F1A\u5F00\u59CB\uFF0C\u6839\u636E\u9519\u8BEF\u5904\u7406\u540E\u91CD\u8BD5\uFF0C\u4E0D\u8C03\u7528\u65E7\u7248\u6E90\u7801\u7ED5\u8FC7\u68C0\u67E5\u3002\u65E7 CLI \u9700\u5148\u901A\u8FC7\u5B98\u65B9 npm \u5B89\u88C5\u547D\u4EE4\u5347\u7EA7\u4E00\u6B21\uFF0C\u624D\u80FD\u83B7\u5F97\u6B64\u673A\u5236\u3002
@@ -2760,37 +3029,17 @@ Coding Agent \u5E94\u76F4\u63A5\u6CBF\u7528\u7528\u6237\u660E\u786E\u63D0\u4F9B\
2760
3029
  \u975E\u4EA4\u4E92\u8C03\u7528\u987B\u5728\u5F00\u53D1\u8005\u660E\u786E\u786E\u8BA4\u5B9E\u9645\u5934\u50CF\u548C\u8D44\u6599\u540E\u4F20 --avatar <\u5DF2\u786E\u8BA4\u56FE\u7247> --confirm-meta\u3002\u672A\u786E\u8BA4\u4E0D\u5F97\u4F20\u8BE5\u6807\u5FD7\uFF1B\u7F3A\u5C11\u56FE\u7247\u6216\u786E\u8BA4\u65F6\u9000\u51FA\u7801\u4E3A 2\uFF0C\u8349\u7A3F\u4FDD\u7559\uFF0C\u53EF\u4EE5\u5728\u539F\u76EE\u5F55\u7EE7\u7EED\u3002
2761
3030
  --mode create \u5B8C\u6210\u8D44\u6599\u548C\u5934\u50CF\u767B\u8BB0\u540E\uFF0C\u52A8\u6001\u83B7\u53D6\u72EC\u7ACB Creator Skill \u5E76\u751F\u6210\u63A5\u7EED\u8BF4\u660E\uFF0C\u9879\u76EE .buddy/creator/ \u4FDD\u5B58\u521B\u4F5C\u8D44\u6599\u4E0E skill-lock.json \u7248\u672C\u8BB0\u5F55\u3002\u4F7F\u7528 --creator-version <\u51C6\u786E\u7248\u672C> \u663E\u5F0F\u5347\u7EA7\uFF0C\u5DF2\u6709\u9879\u76EE\u9ED8\u8BA4\u590D\u7528\u56FA\u5B9A\u7248\u672C\uFF1B\u672C\u5730\u8054\u8C03\u53EF\u8BBE\u7F6E BUDDY_CREATOR_SKILL_PATH \u7EDD\u5BF9\u8DEF\u5F84\u3002
2762
3031
  \u7531\u5F53\u524D Coding Agent \u8BFB\u53D6 .buddy/creator/HANDOFF.md \u5E76\u6267\u884C\u5176\u4E2D\u6307\u5411\u7684 Skill\uFF1B\u9700\u8981 Python 3.9+\uFF0C\u4E0D\u542F\u52A8\u65E7\u91C7\u8BBF Web \u6216\u72EC\u7ACB\u6A21\u578B\u3002
2763
- \u56DB\u518C\u786E\u8BA4\u540E\u7EE7\u7EED\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u9A8C\u6536\uFF1B\u5168\u90E8\u786E\u8BA4\u5E76\u901A\u8FC7\u4EA4\u4ED8\u6821\u9A8C\u540E\uFF0C\u7531 Coding Agent \u81EA\u52A8\u8854\u63A5\u5F00\u53D1\uFF0C\u9700\u8981\u521D\u59CB\u5316\u65F6\u6267\u884C init --mode direct \u751F\u6210\u5B98\u65B9 Runtime \u5DE5\u7A0B\u3002\u540C\u76EE\u5F55\u6062\u590D\u65F6\u4ECE package.json \u8BFB\u53D6 buddyId\uFF0C\u5E76\u4F7F\u7528\u5E73\u53F0\u6700\u65B0 Meta\u3002
2764
- Coding Agent \u5E94\u4F20\u5165\u6309\u4E0A\u8FF0\u89C4\u5219\u786E\u5B9A\u7684\u8D44\u6599\uFF0C\u811A\u672C\u4F7F\u7528\u5F00\u53D1\u8005\u63D0\u4F9B\u7684\u53C2\u6570\uFF0C\u907F\u514D\u7B49\u5F85\u7EC8\u7AEF\u8F93\u5165\u3002CLI \u6821\u9A8C\u53C2\u6570\uFF0C\u4E0D\u5224\u65AD\u5BF9\u8BDD\u4E2D\u662F\u5426\u5DF2\u7ECF\u786E\u8BA4\uFF1B\u53C2\u6570\u9F50\u5168\u4E0D\u7B49\u4E8E\u7528\u6237\u786E\u8BA4\u3002
2765
- Coding Agent \u6839\u636E\u5DF2\u786E\u8BA4\u624B\u518C\u6216\u9700\u6C42\u63A8\u8FDB\u5B9E\u73B0\uFF1B\u53EA\u4E3A\u5173\u952E\u7F3A\u53E3\u8BE2\u95EE\uFF0C\u4E0D\u56FA\u5B9A\u8FFD\u52A0\u7B56\u7565\u786E\u8BA4\u3002
2766
- \u6240\u6709\u642D\u5B50\u7EDF\u4E00\u4F7F\u7528\u5B98\u65B9 Runtime\uFF1B\u9700\u6C42\u660E\u786E\u65F6\u4F7F\u7528 --mode direct\uFF0C\u9700\u8981\u68B3\u7406\u9700\u6C42\u65F6\u4F7F\u7528 --mode create\u3002
2767
-
2768
- ${IMPLEMENTATION_GUIDE}
2769
-
2770
- ${DEVELOPMENT_HANDOFF}`;
2771
-
2772
- // packages/cli-core/src/commands/dev.ts
2773
- import { resolve as resolve14 } from "node:path";
2774
-
2775
- // packages/cli-core/src/platform-url.ts
2776
- var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
2777
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
2778
- function normalizePlatformUrl(value) {
2779
- const url = new URL(value);
2780
- if (url.username || url.password || url.search || url.hash) {
2781
- throw new TypeError("\u5E73\u53F0\u5730\u5740\u4E0D\u80FD\u5305\u542B credentials\u3001query \u6216 fragment");
2782
- }
2783
- if (url.protocol !== "https:" && !(url.protocol === "http:" && (LOOPBACK_HOSTS.has(url.hostname) || url.origin === DEFAULT_MLB_TEST_URL))) {
2784
- throw new TypeError(`\u5E73\u53F0\u5730\u5740\u5FC5\u987B\u4F7F\u7528 HTTPS\uFF1BHTTP \u4EC5\u5141\u8BB8\u672C\u673A\u6216\u56FA\u5B9A\u6D4B\u8BD5\u5730\u5740 ${DEFAULT_MLB_TEST_URL}`);
2785
- }
2786
- if (!url.pathname.endsWith("/")) url.pathname += "/";
2787
- return url;
2788
- }
2789
- function resolvePlatformUrl(options = {}) {
2790
- const env = options.env ?? process.env;
2791
- const configured = options.development ? env.BUDDY_DEV_APP_SERVER_URL ?? env.BUDDY_APP_SERVER_URL : env.BUDDY_APP_SERVER_URL ?? env.BUDDY_DEV_APP_SERVER_URL;
2792
- return normalizePlatformUrl(options.override ?? configured ?? DEFAULT_MLB_TEST_URL);
2793
- }
3032
+ \u56DB\u518C\u786E\u8BA4\u540E\u7EE7\u7EED\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u9A8C\u6536\uFF1B\u5168\u90E8\u786E\u8BA4\u5E76\u901A\u8FC7\u4EA4\u4ED8\u6821\u9A8C\u540E\uFF0C\u7531 Coding Agent \u81EA\u52A8\u8854\u63A5\u5F00\u53D1\uFF0C\u9700\u8981\u521D\u59CB\u5316\u65F6\u6267\u884C init --mode direct \u751F\u6210\u5B98\u65B9 Runtime \u5DE5\u7A0B\u3002\u540C\u76EE\u5F55\u6062\u590D\u65F6\u4ECE package.json \u8BFB\u53D6 buddyId\uFF0C\u5E76\u4F7F\u7528\u5E73\u53F0\u6700\u65B0 Meta\u3002
3033
+ Coding Agent \u5E94\u4F20\u5165\u6309\u4E0A\u8FF0\u89C4\u5219\u786E\u5B9A\u7684\u8D44\u6599\uFF0C\u811A\u672C\u4F7F\u7528\u5F00\u53D1\u8005\u63D0\u4F9B\u7684\u53C2\u6570\uFF0C\u907F\u514D\u7B49\u5F85\u7EC8\u7AEF\u8F93\u5165\u3002CLI \u6821\u9A8C\u53C2\u6570\uFF0C\u4E0D\u5224\u65AD\u5BF9\u8BDD\u4E2D\u662F\u5426\u5DF2\u7ECF\u786E\u8BA4\uFF1B\u53C2\u6570\u9F50\u5168\u4E0D\u7B49\u4E8E\u7528\u6237\u786E\u8BA4\u3002
3034
+ Coding Agent \u6839\u636E\u5DF2\u786E\u8BA4\u624B\u518C\u6216\u9700\u6C42\u63A8\u8FDB\u5B9E\u73B0\uFF1B\u53EA\u4E3A\u5173\u952E\u7F3A\u53E3\u8BE2\u95EE\uFF0C\u4E0D\u56FA\u5B9A\u8FFD\u52A0\u7B56\u7565\u786E\u8BA4\u3002
3035
+ \u6240\u6709\u642D\u5B50\u7EDF\u4E00\u4F7F\u7528\u5B98\u65B9 Runtime\uFF1B\u9700\u6C42\u660E\u786E\u65F6\u4F7F\u7528 --mode direct\uFF0C\u9700\u8981\u68B3\u7406\u9700\u6C42\u65F6\u4F7F\u7528 --mode create\u3002
3036
+
3037
+ ${IMPLEMENTATION_GUIDE}
3038
+
3039
+ ${DEVELOPMENT_HANDOFF}`;
3040
+
3041
+ // packages/cli-core/src/commands/dev.ts
3042
+ import { resolve as resolve14 } from "node:path";
2794
3043
 
2795
3044
  // packages/cli-core/src/dev/project-env.ts
2796
3045
  import { constants } from "node:fs";
@@ -2864,7 +3113,7 @@ async function projectBuddyToken(projectRoot, gatewayUrl) {
2864
3113
  try {
2865
3114
  gateway = normalizePlatformUrl(values.MLB_GATEWAY_URL);
2866
3115
  } catch {
2867
- throw invalid(".env \u4E2D\u7684 MLB_GATEWAY_URL \u65E0\u6548\u3002");
3116
+ throw invalid(".env \u4E2D\u7684 MLB_GATEWAY_URL \u65E0\u6548\uFF1B\u8BF7\u786E\u8BA4\u5E73\u53F0\u540E\u4F7F\u7528 --refresh-token \u91CD\u65B0\u9886\u53D6\u5F00\u53D1\u4EE4\u724C\u5E76\u66F4\u65B0\u5730\u5740\u3002");
2868
3117
  }
2869
3118
  if (gateway.href !== gatewayUrl.href) throw invalid(".env \u7684\u5E73\u53F0\u5730\u5740\u4E0E\u672C\u6B21\u542F\u52A8\u4E0D\u540C\uFF1B\u8BF7\u786E\u8BA4\u5E73\u53F0\u540E\u4F7F\u7528 --refresh-token \u91CD\u65B0\u9886\u53D6\u5F00\u53D1\u4EE4\u724C\u3002");
2870
3119
  }
@@ -3210,19 +3459,19 @@ function buildAgentEnvironment(options) {
3210
3459
  }
3211
3460
 
3212
3461
  // packages/cli-core/src/dev/orchestrator.ts
3213
- import { createHash as createHash6 } from "node:crypto";
3462
+ import { createHash as createHash7 } from "node:crypto";
3214
3463
  import { readFile as readFile16 } from "node:fs/promises";
3215
3464
  import { join as join17, resolve as resolve12 } from "node:path";
3216
3465
 
3217
3466
  // packages/cli-core/src/dev/reload.ts
3218
3467
  import { watch } from "node:fs";
3219
- import { createHash as createHash5 } from "node:crypto";
3468
+ import { createHash as createHash6 } from "node:crypto";
3220
3469
  import { readFile as readFile13, readdir as readdir4 } from "node:fs/promises";
3221
3470
  import { join as join15, resolve as resolve9 } from "node:path";
3222
3471
  import { setTimeout as sleep } from "node:timers/promises";
3223
3472
 
3224
3473
  // packages/cli-core/src/dev/instance-lock.ts
3225
- import { createHash as createHash4 } from "node:crypto";
3474
+ import { createHash as createHash5 } from "node:crypto";
3226
3475
  import { homedir as homedir3 } from "node:os";
3227
3476
  import { join as join14 } from "node:path";
3228
3477
  function alreadyRunningError() {
@@ -3232,14 +3481,14 @@ function alreadyRunningError() {
3232
3481
  async function acquireDevInstanceLock(options) {
3233
3482
  return acquireProcessLock({
3234
3483
  directory: options.directory ?? join14(homedir3(), ".buddy", "dev-locks"),
3235
- key: createHash4("sha256").update(`${new URL(options.gatewayUrl).origin}
3484
+ key: createHash5("sha256").update(`${new URL(options.gatewayUrl).origin}
3236
3485
  ${options.devAgentId}`).digest("hex"),
3237
3486
  busyError: alreadyRunningError
3238
3487
  });
3239
3488
  }
3240
3489
 
3241
3490
  // packages/cli-core/src/dev/reload.ts
3242
- var digest2 = (contents) => createHash5("sha256").update(contents).digest("hex");
3491
+ var digest2 = (contents) => createHash6("sha256").update(contents).digest("hex");
3243
3492
  async function snapshot(root, prefix = "", result = /* @__PURE__ */ new Map()) {
3244
3493
  for (const entry2 of await readdir4(join15(root, prefix), { withFileTypes: true })) {
3245
3494
  const path = prefix ? `${prefix}/${entry2.name}` : entry2.name;
@@ -3573,7 +3822,7 @@ async function requireLatestRuntime(root, options = {}) {
3573
3822
  }
3574
3823
 
3575
3824
  // packages/cli-core/src/dev/launch.ts
3576
- import { lstatSync, readFileSync } from "node:fs";
3825
+ import { lstatSync, readFileSync as readFileSync2 } from "node:fs";
3577
3826
  import { createRequire as createRequire3 } from "node:module";
3578
3827
  import { dirname as dirname4, resolve as resolve11 } from "node:path";
3579
3828
 
@@ -3643,10 +3892,10 @@ async function officialAgentLaunchPlan(input) {
3643
3892
  const packagePath = resolve11(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
3644
3893
  try {
3645
3894
  await requireLatestRuntime(input.projectRoot, input.runtimeUpdate);
3646
- const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
3895
+ const expected = runtimeDependencyVersion(JSON.parse(readFileSync2(packagePath, "utf8")));
3647
3896
  if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
3648
3897
  const runtimePath = createRequire3(packagePath).resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`);
3649
- const installed2 = JSON.parse(readFileSync(runtimePath, "utf8")).version;
3898
+ const installed2 = JSON.parse(readFileSync2(runtimePath, "utf8")).version;
3650
3899
  if (installed2 !== expected) throw new Error(`Runtime \u5DF2\u5B89\u88C5 ${installed2}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
3651
3900
  await validateRuntimeDatasets(input.projectRoot, await readDataAccessRequirements(input.projectRoot));
3652
3901
  } catch (cause) {
@@ -4148,7 +4397,7 @@ async function startBuddyDevProcess(options, hooks) {
4148
4397
  client: options.registrationClient,
4149
4398
  devAgentId,
4150
4399
  configSource,
4151
- configDigest: `sha256:${createHash6("sha256").update(configSource).digest("hex")}`,
4400
+ configDigest: `sha256:${createHash7("sha256").update(configSource).digest("hex")}`,
4152
4401
  signal
4153
4402
  });
4154
4403
  secrets.push(registration.agentToken);
@@ -4267,10 +4516,10 @@ function createBuddyDevStarter(options = {}) {
4267
4516
  }
4268
4517
 
4269
4518
  // packages/cli-core/src/dev/token-store.ts
4270
- import { createHash as createHash7 } from "node:crypto";
4519
+ import { createHash as createHash8 } from "node:crypto";
4271
4520
 
4272
4521
  // packages/cli-core/src/cloud/credential-store.ts
4273
- import { spawn } from "node:child_process";
4522
+ import { spawn as spawn2 } from "node:child_process";
4274
4523
  var DEFAULT_SERVICE = "com.xiaohongshu.buddy.developer-cli";
4275
4524
  var DEFAULT_ACCOUNT = "developer-session";
4276
4525
  var MAX_CREDENTIAL_BYTES = 32 * 1024;
@@ -4313,7 +4562,7 @@ function parse(source) {
4313
4562
  }
4314
4563
  async function runCredentialCommand(input) {
4315
4564
  return await new Promise((resolve19, reject) => {
4316
- const child = spawn(input.command, [...input.args], {
4565
+ const child = spawn2(input.command, [...input.args], {
4317
4566
  shell: false,
4318
4567
  windowsHide: true,
4319
4568
  stdio: ["pipe", "pipe", "pipe"]
@@ -4411,7 +4660,7 @@ var SystemBuddyTokenStore = class {
4411
4660
  const scope = JSON.stringify([normalizePlatformUrl(options.platformUrl).origin, options.subject, options.buddyId]);
4412
4661
  this.#store = new SystemCredentialStore({
4413
4662
  service: "com.xiaohongshu.buddy.developer-cli.dev-token",
4414
- account: createHash7("sha256").update(scope).digest("hex"),
4663
+ account: createHash8("sha256").update(scope).digest("hex"),
4415
4664
  platform: options.platform,
4416
4665
  commandRunner: options.commandRunner
4417
4666
  });
@@ -5050,251 +5299,6 @@ var HttpDeveloperPlatformClient = class {
5050
5299
  }
5051
5300
  };
5052
5301
 
5053
- // packages/cli-core/src/cloud/browser-login.ts
5054
- import { createHash as createHash8, randomBytes } from "node:crypto";
5055
- import { spawn as spawn2 } from "node:child_process";
5056
- import { createServer } from "node:http";
5057
-
5058
- // packages/cli-core/src/cloud/login-result.ts
5059
- import { readFileSync as readFileSync2 } from "node:fs";
5060
- var brand = new URL("../../resources/brand/", import.meta.url);
5061
- var tokens = readFileSync2(new URL("tokens.css", brand), "utf8");
5062
- var styles = readFileSync2(new URL("login-result.css", brand), "utf8");
5063
- var logo = readFileSync2(new URL("logo.png", brand)).toString("base64");
5064
- function escapeHtml(value) {
5065
- return value.replace(/[&<>"']/gu, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;" })[character]);
5066
- }
5067
- function renderLoginResult(title, message, webUrl, tone) {
5068
- const label = { success: "\u5DF2\u6388\u6743", neutral: "\u5DF2\u53D6\u6D88", error: "\u672A\u5B8C\u6210" }[tone];
5069
- return `<!doctype html>
5070
- <html lang="zh-CN"><head>
5071
- <meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
5072
- <meta name="color-scheme" content="light dark"><title>${escapeHtml(title)} \xB7 \u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</title>
5073
- <style>${tokens}
5074
- ${styles}</style></head>
5075
- <body><div class="result-shell">
5076
- <header class="result-brand"><img src="data:image/png;base64,${logo}" alt="" width="44" height="44"><span>\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</span></header>
5077
- <main class="result-card" aria-labelledby="result-title">
5078
- <div class="result-heading"><p class="result-eyebrow">buddy-cli \u767B\u5F55</p><span class="result-status" data-tone="${tone}">${label}</span></div>
5079
- <h1 id="result-title">${escapeHtml(title)}</h1>
5080
- <p class="result-message">${escapeHtml(message)}</p>
5081
- <div class="result-next"><strong>${tone === "success" ? "\u56DE\u5230\u7EC8\u7AEF\uFF0C\u7EE7\u7EED\u521B\u4F5C" : "\u56DE\u5230\u7EC8\u7AEF\uFF0C\u91CD\u65B0\u5F00\u59CB"}</strong>
5082
- <p>${tone === "success" ? "\u67E5\u770B CLI \u7684\u767B\u5F55\u7ED3\u679C\uFF0C\u7EE7\u7EED\u521B\u5EFA\u3001\u5F00\u53D1\u548C\u9884\u89C8\u4F60\u7684\u642D\u5B50\u3002" : "\u9700\u8981\u7EE7\u7EED\u65F6\uFF0C\u5728\u7EC8\u7AEF\u91CD\u65B0\u8FD0\u884C\u767B\u5F55\u547D\u4EE4\u3002"}</p>
5083
- ${tone === "success" ? "" : "<code>buddy-cli login</code>"}</div>
5084
- <a class="result-link" href="${escapeHtml(webUrl.href)}">\u8FD4\u56DE\u5F00\u653E\u5E73\u53F0</a>
5085
- </main><footer>\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</footer></div></body></html>`;
5086
- }
5087
-
5088
- // packages/cli-core/src/cloud/browser-login.ts
5089
- var DEFAULT_LOGIN_TIMEOUT_MS = 5 * 6e4;
5090
- var DEFAULT_EXCHANGE_TIMEOUT_MS = 15e3;
5091
- var DEFAULT_DEVELOPER_WEB_URL = "https://47.116.168.81/developer/";
5092
- var REQUEST_ID = /^[A-Za-z0-9_-]{43}$/u;
5093
- var AUTH_CODE = /^[A-Za-z0-9_-]{43}$/u;
5094
- function positive(value, fallback, name) {
5095
- const result = value ?? fallback;
5096
- if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError(`${name} must be a positive safe integer`);
5097
- return result;
5098
- }
5099
- function webBase(value) {
5100
- const url = new URL(value);
5101
- const loopback = ["localhost", "127.0.0.1", "[::1]", "::1"].includes(url.hostname);
5102
- if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback) || url.username || url.password || url.search || url.hash) {
5103
- throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u5FC5\u987B\u4F7F\u7528 HTTPS\uFF1B\u672C\u673A\u5F00\u53D1\u53EF\u4EE5\u4F7F\u7528 loopback HTTP");
5104
- }
5105
- if (!url.pathname.endsWith("/")) url.pathname += "/";
5106
- return url;
5107
- }
5108
- function browserCommand(url) {
5109
- if (process.platform === "darwin") return { command: "open", args: [url.href] };
5110
- throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D CLI V1 \u672C\u5730\u767B\u5F55\u76EE\u524D\u53EA\u652F\u6301 macOS");
5111
- }
5112
- async function openExternalUrl(url) {
5113
- const launch = browserCommand(url);
5114
- await new Promise((resolve19, reject) => {
5115
- const child = spawn2(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
5116
- child.once("error", reject);
5117
- child.once("exit", (code2) => code2 === 0 ? resolve19() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
5118
- });
5119
- }
5120
- async function write(res, status, title, message, webUrl, tone = "error") {
5121
- res.writeHead(status, {
5122
- "cache-control": "no-store",
5123
- "connection": "close",
5124
- "content-security-policy": "default-src 'none'; img-src data:; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'",
5125
- "content-type": "text/html; charset=utf-8",
5126
- "referrer-policy": "no-referrer",
5127
- "x-content-type-options": "nosniff"
5128
- });
5129
- await new Promise((resolve19, reject) => {
5130
- res.once("error", reject);
5131
- res.once("close", resolve19);
5132
- res.end(renderLoginResult(title, message, webUrl, tone), resolve19);
5133
- });
5134
- }
5135
- async function readJson(response) {
5136
- const value = await response.json().catch(() => void 0);
5137
- if (!value || typeof value !== "object" || Array.isArray(value)) {
5138
- throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u6388\u6743\u7ED3\u679C");
5139
- }
5140
- return value;
5141
- }
5142
- var LoopbackBrowserLoginAdapter = class {
5143
- #platformUrl;
5144
- #developerWebUrl;
5145
- #openBrowser;
5146
- #onLoginUrl;
5147
- #timeoutMs;
5148
- #fetch;
5149
- constructor(options) {
5150
- this.#platformUrl = normalizePlatformUrl(options.platformUrl);
5151
- this.#developerWebUrl = webBase(options.developerWebUrl ?? DEFAULT_DEVELOPER_WEB_URL);
5152
- this.#openBrowser = options.openBrowser ?? openExternalUrl;
5153
- this.#onLoginUrl = options.onLoginUrl;
5154
- this.#timeoutMs = positive(options.timeoutMs, DEFAULT_LOGIN_TIMEOUT_MS, "timeoutMs");
5155
- this.#fetch = options.fetch ?? globalThis.fetch;
5156
- if (typeof this.#fetch !== "function") throw new TypeError("A Fetch API implementation is required");
5157
- }
5158
- async login(request) {
5159
- if (request.signal?.aborted) throw request.signal.reason;
5160
- const callbackSecret = randomBytes(24).toString("hex");
5161
- const codeVerifier = randomBytes(32).toString("base64url");
5162
- const codeChallenge = createHash8("sha256").update(codeVerifier).digest("base64url");
5163
- let requestId = "";
5164
- let settle;
5165
- let reject;
5166
- let settled = false;
5167
- const result = new Promise((resolve19, rejectResult) => {
5168
- settle = (value) => {
5169
- if (!settled) {
5170
- settled = true;
5171
- resolve19(value);
5172
- }
5173
- };
5174
- reject = (reason) => {
5175
- if (!settled) {
5176
- settled = true;
5177
- rejectResult(reason);
5178
- }
5179
- };
5180
- });
5181
- void result.catch(() => void 0);
5182
- const server = createServer((req, res) => {
5183
- void this.#receiveCallback(req, res, {
5184
- callbackSecret,
5185
- codeVerifier,
5186
- requestId,
5187
- state: request.state
5188
- }).then(settle, reject);
5189
- });
5190
- await new Promise((resolve19, rejectListen) => {
5191
- server.once("error", rejectListen);
5192
- server.listen(0, "127.0.0.1", () => resolve19());
5193
- });
5194
- const address = server.address();
5195
- if (!address || typeof address === "string") {
5196
- server.close();
5197
- throw new CloudLifecycleError("PLATFORM_UNAVAILABLE", "\u65E0\u6CD5\u542F\u52A8\u672C\u673A\u767B\u5F55\u56DE\u8C03");
5198
- }
5199
- const redirectUri = `http://127.0.0.1:${address.port}/callback/${callbackSecret}`;
5200
- const timer = setTimeout(() => reject(new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u6D4F\u89C8\u5668\u767B\u5F55\u7B49\u5F85\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5")), this.#timeoutMs);
5201
- timer.unref?.();
5202
- const abort = () => reject(request.signal?.reason ?? new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u767B\u5F55\u5DF2\u53D6\u6D88"));
5203
- request.signal?.addEventListener("abort", abort, { once: true });
5204
- try {
5205
- const created = await this.#post("api/cli/authorizations", {
5206
- state: request.state,
5207
- codeChallenge,
5208
- redirectUri,
5209
- platformOrigin: this.#platformUrl.origin
5210
- }, request.signal);
5211
- if (typeof created.requestId !== "string" || !REQUEST_ID.test(created.requestId)) {
5212
- throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u6388\u6743\u7F16\u53F7");
5213
- }
5214
- requestId = created.requestId;
5215
- const loginUrl = new URL(this.#developerWebUrl);
5216
- loginUrl.hash = `#/cli-authorize?requestId=${encodeURIComponent(requestId)}`;
5217
- this.#onLoginUrl?.(loginUrl);
5218
- await this.#openBrowser(loginUrl).catch(() => {
5219
- throw new CloudLifecycleError("PLATFORM_UNAVAILABLE", "\u65E0\u6CD5\u81EA\u52A8\u6253\u5F00\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\uFF0C\u8BF7\u68C0\u67E5\u7CFB\u7EDF\u9ED8\u8BA4\u6D4F\u89C8\u5668\u540E\u91CD\u8BD5");
5220
- });
5221
- return await result;
5222
- } finally {
5223
- clearTimeout(timer);
5224
- request.signal?.removeEventListener("abort", abort);
5225
- await new Promise((resolve19) => {
5226
- server.close(() => resolve19());
5227
- server.closeIdleConnections();
5228
- server.closeAllConnections();
5229
- });
5230
- }
5231
- }
5232
- async #receiveCallback(req, res, expected) {
5233
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
5234
- if (req.method !== "GET" || url.pathname !== `/callback/${expected.callbackSecret}`) {
5235
- await write(res, 404, "\u9875\u9762\u4E0D\u5B58\u5728", "\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u65B0\u53D1\u8D77\u767B\u5F55\u3002", this.#developerWebUrl);
5236
- throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u6D4F\u89C8\u5668\u56DE\u8C03\u65E0\u6548\uFF0C\u8BF7\u91CD\u8BD5");
5237
- }
5238
- if (url.searchParams.get("state") !== expected.state) {
5239
- await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
5240
- throw new CloudLifecycleError("AUTH_STATE_MISMATCH", "\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u7684 state \u4E0D\u5339\u914D\uFF1B\u51ED\u636E\u672A\u4FDD\u5B58");
5241
- }
5242
- if (url.searchParams.get("error") === "access_denied") {
5243
- await write(res, 200, "\u5DF2\u53D6\u6D88\u6388\u6743", "\u6CA1\u6709\u5411 buddy-cli \u63D0\u4F9B\u8D26\u53F7\u6743\u9650\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u3002", this.#developerWebUrl, "neutral");
5244
- throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u7528\u6237\u53D6\u6D88\u4E86 CLI \u6388\u6743");
5245
- }
5246
- const code2 = url.searchParams.get("code");
5247
- if (!code2 || !AUTH_CODE.test(code2) || !expected.requestId) {
5248
- await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u7ED3\u679C\u65E0\u6548\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
5249
- throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6388\u6743\u7801");
5250
- }
5251
- try {
5252
- const exchanged = await this.#post("api/cli/exchange", {
5253
- requestId: expected.requestId,
5254
- code: code2,
5255
- codeVerifier: expected.codeVerifier
5256
- });
5257
- if (exchanged.platformOrigin !== this.#platformUrl.origin || typeof exchanged.token !== "string" || typeof exchanged.userId !== "string" || !Number.isSafeInteger(exchanged.expiresAt)) {
5258
- throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u7684\u8D26\u53F7\u51ED\u636E\u4E0D\u5B8C\u6574");
5259
- }
5260
- await write(res, 200, "\u767B\u5F55\u6210\u529F", "buddy-cli \u5DF2\u83B7\u5F97\u6388\u6743\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u5E76\u8FD4\u56DE\u7EC8\u7AEF\u3002", this.#developerWebUrl, "success");
5261
- return {
5262
- state: expected.state,
5263
- token: {
5264
- accessToken: exchanged.token,
5265
- subject: exchanged.userId,
5266
- expiresAt: exchanged.expiresAt
5267
- }
5268
- };
5269
- } catch (error2) {
5270
- await write(res, 502, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u4EA4\u6362\u5931\u8D25\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
5271
- throw error2;
5272
- }
5273
- }
5274
- async #post(path, payload, signal) {
5275
- let response;
5276
- try {
5277
- const signals = [AbortSignal.timeout(DEFAULT_EXCHANGE_TIMEOUT_MS), ...signal ? [signal] : []];
5278
- response = await this.#fetch(new URL(path, this.#developerWebUrl), {
5279
- method: "POST",
5280
- headers: { Accept: "application/json", "Content-Type": "application/json" },
5281
- body: JSON.stringify(payload),
5282
- redirect: "error",
5283
- signal: AbortSignal.any(signals)
5284
- });
5285
- } catch {
5286
- if (signal?.aborted) throw signal.reason;
5287
- throw new CloudLifecycleError("PLATFORM_UNAVAILABLE", "\u65E0\u6CD5\u8FDE\u63A5\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u5B8C\u6210 CLI \u767B\u5F55");
5288
- }
5289
- const data = await readJson(response);
5290
- if (!response.ok) {
5291
- const message = typeof data.error === "string" ? data.error : "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u62D2\u7EDD\u4E86\u8FD9\u6B21 CLI \u6388\u6743";
5292
- throw new CloudLifecycleError(response.status === 400 || response.status === 409 || response.status === 410 ? "AUTH_LOGIN_CANCELLED" : "PLATFORM_UNAVAILABLE", message);
5293
- }
5294
- return data;
5295
- }
5296
- };
5297
-
5298
5302
  // packages/cli-core/src/cloud/mock-client.ts
5299
5303
  import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
5300
5304
  var MockDeveloperPlatformClient = class {
@@ -7865,6 +7869,7 @@ export {
7865
7869
  CloudAuthenticator,
7866
7870
  CloudCommandUsageError,
7867
7871
  CloudLifecycleError,
7872
+ DEFAULT_DEVELOPER_WEB_URL,
7868
7873
  DEFAULT_MLB_TEST_URL,
7869
7874
  DEFAULT_PUSH_MANIFEST_LIMITS,
7870
7875
  DEFAULT_SKILL_SCAN_LIMITS,