@useorgx/wizard 0.1.14 → 0.1.15

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/cli.js CHANGED
@@ -37,6 +37,8 @@ var ORGX_WIZARD_NPM_PACKAGE_NAME = "@useorgx/wizard";
37
37
  var NPM_REGISTRY_BASE_URL = "https://registry.npmjs.org";
38
38
  var DEFAULT_OPENCLAW_GATEWAY_PORT = 18789;
39
39
  var DEFAULT_ORGX_BASE_URL = process.env.ORGX_BASE_URL?.trim() || "https://useorgx.com";
40
+ var ORGX_WIZARD_OAUTH_CALLBACK_PATH = "/oauth/callback";
41
+ var ORGX_WIZARD_OAUTH_PREFERRED_PORT = 6274;
40
42
  var ORGX_WIZARD_OAUTH_SCOPES = [
41
43
  "decisions:read",
42
44
  "decisions:write",
@@ -368,12 +370,16 @@ function parseStoredWizardAuthRecord(raw) {
368
370
  return null;
369
371
  }
370
372
  const storage = parsed.storage === "keytar" ? "keytar" : "file";
373
+ const source = parsed.source === "pkce" ? "pkce" : "manual";
371
374
  return {
372
375
  baseUrl: parsed.baseUrl.trim(),
373
376
  ...isNonEmptyString(parsed.apiKey) ? { apiKey: parsed.apiKey.trim() } : {},
374
377
  keyPrefix: parsed.keyPrefix.trim(),
378
+ source,
375
379
  storage,
376
- verifiedAt: parsed.verifiedAt.trim()
380
+ verifiedAt: parsed.verifiedAt.trim(),
381
+ ...isNonEmptyString(parsed.refreshToken) ? { refreshToken: parsed.refreshToken.trim() } : {},
382
+ ...isNonEmptyString(parsed.oauthClientId) ? { oauthClientId: parsed.oauthClientId.trim() } : {}
377
383
  };
378
384
  }
379
385
  async function loadDefaultSecretStore() {
@@ -402,8 +408,11 @@ function buildStoredRecord(value) {
402
408
  ...value.storage === "file" ? { apiKey: value.apiKey } : {},
403
409
  baseUrl: value.baseUrl,
404
410
  keyPrefix: value.keyPrefix,
411
+ source: value.source,
405
412
  storage: value.storage,
406
- verifiedAt: value.verifiedAt
413
+ verifiedAt: value.verifiedAt,
414
+ ...value.refreshToken ? { refreshToken: value.refreshToken } : {},
415
+ ...value.oauthClientId ? { oauthClientId: value.oauthClientId } : {}
407
416
  };
408
417
  }
409
418
  function readWizardAuthMetadata(authPath = ORGX_WIZARD_AUTH_PATH) {
@@ -414,7 +423,7 @@ function readWizardAuthMetadata(authPath = ORGX_WIZARD_AUTH_PATH) {
414
423
  return {
415
424
  baseUrl: stored.baseUrl,
416
425
  keyPrefix: stored.keyPrefix,
417
- source: "manual",
426
+ source: stored.source ?? "manual",
418
427
  storage: stored.storage,
419
428
  verifiedAt: stored.verifiedAt
420
429
  };
@@ -424,15 +433,18 @@ async function readWizardAuth(authPath = ORGX_WIZARD_AUTH_PATH, options = {}) {
424
433
  if (!stored) {
425
434
  return null;
426
435
  }
436
+ const source = stored.source ?? "manual";
427
437
  const apiKeyFromFile = stored.storage === "file" && isNonEmptyString(stored.apiKey) ? stored.apiKey.trim() : null;
428
438
  if (apiKeyFromFile) {
429
439
  return {
430
440
  apiKey: apiKeyFromFile,
431
441
  baseUrl: stored.baseUrl,
432
442
  keyPrefix: stored.keyPrefix,
433
- source: "manual",
443
+ source,
434
444
  storage: "file",
435
- verifiedAt: stored.verifiedAt
445
+ verifiedAt: stored.verifiedAt,
446
+ ...stored.refreshToken ? { refreshToken: stored.refreshToken } : {},
447
+ ...stored.oauthClientId ? { oauthClientId: stored.oauthClientId } : {}
436
448
  };
437
449
  }
438
450
  if (stored.storage !== "keytar") {
@@ -453,9 +465,11 @@ async function readWizardAuth(authPath = ORGX_WIZARD_AUTH_PATH, options = {}) {
453
465
  apiKey: apiKey.trim(),
454
466
  baseUrl: stored.baseUrl,
455
467
  keyPrefix: stored.keyPrefix,
456
- source: "manual",
468
+ source,
457
469
  storage: "keytar",
458
- verifiedAt: stored.verifiedAt
470
+ verifiedAt: stored.verifiedAt,
471
+ ...stored.refreshToken ? { refreshToken: stored.refreshToken } : {},
472
+ ...stored.oauthClientId ? { oauthClientId: stored.oauthClientId } : {}
459
473
  };
460
474
  }
461
475
  async function writeWizardAuth(value, authPath = ORGX_WIZARD_AUTH_PATH, options = {}) {
@@ -464,9 +478,11 @@ async function writeWizardAuth(value, authPath = ORGX_WIZARD_AUTH_PATH, options
464
478
  apiKey: value.apiKey.trim(),
465
479
  keyPrefix: value.keyPrefix?.trim() || extractKeyPrefix(value.apiKey),
466
480
  baseUrl: value.baseUrl.trim(),
467
- source: "manual",
481
+ source: value.source ?? "manual",
468
482
  storage: secretStore ? "keytar" : "file",
469
- verifiedAt: value.verifiedAt.trim()
483
+ verifiedAt: value.verifiedAt.trim(),
484
+ ...value.refreshToken ? { refreshToken: value.refreshToken } : {},
485
+ ...value.oauthClientId ? { oauthClientId: value.oauthClientId } : {}
470
486
  };
471
487
  if (secretStore) {
472
488
  await secretStore.setPassword(
@@ -863,6 +879,241 @@ function openBrowser(url) {
863
879
  return { ok: true };
864
880
  }
865
881
 
882
+ // src/lib/local-auth-server.ts
883
+ import { createServer } from "http";
884
+ var DEFAULT_SUCCESS_HTML = `<!DOCTYPE html>
885
+ <html>
886
+ <head><title>OrgX connected</title>
887
+ <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f9fafb}
888
+ .card{text-align:center;padding:2rem;max-width:400px}
889
+ h1{font-size:1.5rem;color:#111}p{color:#555;margin-top:.5rem}</style>
890
+ </head>
891
+ <body><div class="card">
892
+ <h1>&#10003; Connected to OrgX</h1>
893
+ <p>You can close this tab and return to your terminal.</p>
894
+ </div></body></html>`;
895
+ var DEFAULT_ERROR_HTML = `<!DOCTYPE html>
896
+ <html>
897
+ <head><title>OrgX auth error</title>
898
+ <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#fef2f2}
899
+ .card{text-align:center;padding:2rem;max-width:400px}
900
+ h1{font-size:1.5rem;color:#b91c1c}p{color:#555;margin-top:.5rem}</style>
901
+ </head>
902
+ <body><div class="card">
903
+ <h1>&#10007; Authentication failed</h1>
904
+ <p>Return to your terminal and try again.</p>
905
+ </div></body></html>`;
906
+ function tryListen(port, hostname2) {
907
+ return new Promise((resolve, reject) => {
908
+ const server = createServer();
909
+ server.once("error", reject);
910
+ server.listen(port, hostname2, () => {
911
+ server.removeListener("error", reject);
912
+ resolve(server);
913
+ });
914
+ });
915
+ }
916
+ async function bindServer(preferredPort, hostname2) {
917
+ if (preferredPort === 0) {
918
+ const server = await tryListen(0, hostname2);
919
+ const port = server.address().port;
920
+ return { server, port };
921
+ }
922
+ for (let offset = 0; offset < 10; offset++) {
923
+ const port = preferredPort + offset;
924
+ try {
925
+ const server = await tryListen(port, hostname2);
926
+ return { server, port };
927
+ } catch {
928
+ }
929
+ }
930
+ throw new Error(
931
+ `Could not bind local auth server: ports ${preferredPort}\u2013${preferredPort + 9} are all in use.`
932
+ );
933
+ }
934
+ async function startLocalAuthServer(options) {
935
+ const hostname2 = "127.0.0.1";
936
+ const callbackPath = options.callbackPath ?? "/oauth/callback";
937
+ const timeoutMs = options.timeoutMs ?? 5 * 6e4;
938
+ const successHtml = options.successHtml ?? DEFAULT_SUCCESS_HTML;
939
+ const errorHtml = options.errorHtml ?? DEFAULT_ERROR_HTML;
940
+ const { server, port } = await bindServer(options.preferredPort, hostname2);
941
+ const result = new Promise((resolve, reject) => {
942
+ const timer = setTimeout(() => {
943
+ server.close();
944
+ reject(new Error("Timed out waiting for browser authorization."));
945
+ }, timeoutMs);
946
+ server.on("request", (req, res) => {
947
+ if (!req.url) {
948
+ res.writeHead(400).end(errorHtml);
949
+ return;
950
+ }
951
+ const url = new URL(req.url, `http://${hostname2}:${port}`);
952
+ if (url.pathname !== callbackPath) {
953
+ res.writeHead(404).end("Not found");
954
+ return;
955
+ }
956
+ const error = url.searchParams.get("error");
957
+ const errorDescription = url.searchParams.get("error_description");
958
+ if (error) {
959
+ res.writeHead(400, { "Content-Type": "text/html" }).end(errorHtml);
960
+ clearTimeout(timer);
961
+ server.close();
962
+ reject(new Error(errorDescription ?? error));
963
+ return;
964
+ }
965
+ const code = url.searchParams.get("code");
966
+ const state = url.searchParams.get("state");
967
+ if (!code || !state) {
968
+ res.writeHead(400, { "Content-Type": "text/html" }).end(errorHtml);
969
+ clearTimeout(timer);
970
+ server.close();
971
+ reject(new Error("Missing code or state in callback."));
972
+ return;
973
+ }
974
+ if (state !== options.expectedState) {
975
+ res.writeHead(400, { "Content-Type": "text/html" }).end(errorHtml);
976
+ clearTimeout(timer);
977
+ server.close();
978
+ reject(new Error("State mismatch \u2014 possible CSRF. Retry auth."));
979
+ return;
980
+ }
981
+ res.writeHead(200, { "Content-Type": "text/html" }).end(successHtml);
982
+ clearTimeout(timer);
983
+ server.close();
984
+ resolve({ code, state });
985
+ });
986
+ });
987
+ return { port, result };
988
+ }
989
+ function buildRedirectUri(port, callbackPath = "/oauth/callback") {
990
+ return `http://127.0.0.1:${port}${callbackPath}`;
991
+ }
992
+
993
+ // src/lib/pkce.ts
994
+ import { createHash, randomBytes } from "crypto";
995
+ function generatePkce() {
996
+ const codeVerifier = randomBytes(32).toString("base64url");
997
+ const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
998
+ return { codeVerifier, codeChallenge, codeChallengeMethod: "S256" };
999
+ }
1000
+ function generateState() {
1001
+ return randomBytes(16).toString("base64url");
1002
+ }
1003
+ function buildAuthorizeUrl(options) {
1004
+ const url = new URL(options.authorizeEndpoint);
1005
+ url.searchParams.set("response_type", "code");
1006
+ url.searchParams.set("client_id", options.clientId);
1007
+ url.searchParams.set("redirect_uri", options.redirectUri);
1008
+ url.searchParams.set("scope", options.scope);
1009
+ url.searchParams.set("code_challenge", options.codeChallenge);
1010
+ url.searchParams.set("code_challenge_method", options.codeChallengeMethod);
1011
+ url.searchParams.set("state", options.state);
1012
+ return url.toString();
1013
+ }
1014
+
1015
+ // src/lib/oauth-client.ts
1016
+ async function registerPublicClient(options) {
1017
+ const fetchImpl = options.fetchImpl ?? fetch;
1018
+ const response = await fetchImpl(ORGX_HOSTED_OAUTH_REGISTER_URL, {
1019
+ method: "POST",
1020
+ headers: { "Content-Type": "application/json" },
1021
+ body: JSON.stringify({
1022
+ client_name: "OrgX Wizard CLI",
1023
+ redirect_uris: [options.redirectUri],
1024
+ token_endpoint_auth_method: "none",
1025
+ grant_types: ["authorization_code", "refresh_token"],
1026
+ response_types: ["code"]
1027
+ }),
1028
+ signal: AbortSignal.timeout(1e4)
1029
+ });
1030
+ if (!response.ok) {
1031
+ const text2 = await response.text().catch(() => "");
1032
+ throw new Error(`OAuth client registration failed (HTTP ${response.status}): ${text2}`);
1033
+ }
1034
+ const data = await response.json();
1035
+ if (!isRecord(data) || typeof data.client_id !== "string") {
1036
+ throw new Error("OAuth registration response missing client_id.");
1037
+ }
1038
+ return { client_id: data.client_id };
1039
+ }
1040
+ async function exchangeCodeForTokens(options) {
1041
+ const fetchImpl = options.fetchImpl ?? fetch;
1042
+ const body = new URLSearchParams({
1043
+ grant_type: "authorization_code",
1044
+ code: options.code,
1045
+ redirect_uri: options.redirectUri,
1046
+ client_id: options.clientId,
1047
+ code_verifier: options.codeVerifier
1048
+ });
1049
+ const response = await fetchImpl(ORGX_HOSTED_OAUTH_TOKEN_URL, {
1050
+ method: "POST",
1051
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1052
+ body: body.toString(),
1053
+ signal: AbortSignal.timeout(1e4)
1054
+ });
1055
+ if (!response.ok) {
1056
+ const text2 = await response.text().catch(() => "");
1057
+ throw new Error(`Token exchange failed (HTTP ${response.status}): ${text2}`);
1058
+ }
1059
+ const data = await response.json();
1060
+ if (!isRecord(data) || typeof data.access_token !== "string") {
1061
+ throw new Error("Token response missing access_token.");
1062
+ }
1063
+ return {
1064
+ access_token: data.access_token,
1065
+ token_type: typeof data.token_type === "string" ? data.token_type : "Bearer",
1066
+ ...typeof data.expires_in === "number" ? { expires_in: data.expires_in } : {},
1067
+ ...typeof data.refresh_token === "string" ? { refresh_token: data.refresh_token } : {},
1068
+ ...typeof data.scope === "string" ? { scope: data.scope } : {}
1069
+ };
1070
+ }
1071
+ async function startPkceLogin(options = {}) {
1072
+ const port = options.preferredPort ?? ORGX_WIZARD_OAUTH_PREFERRED_PORT;
1073
+ const scope = options.scope ?? ORGX_WIZARD_OAUTH_SCOPE;
1074
+ const { codeVerifier, codeChallenge, codeChallengeMethod } = generatePkce();
1075
+ const state = generateState();
1076
+ const serverOpts = {
1077
+ preferredPort: port,
1078
+ expectedState: state,
1079
+ callbackPath: ORGX_WIZARD_OAUTH_CALLBACK_PATH,
1080
+ ...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {}
1081
+ };
1082
+ const { port: boundPort, result: callbackPromise } = await startLocalAuthServer(serverOpts);
1083
+ const redirectUri = buildRedirectUri(boundPort, ORGX_WIZARD_OAUTH_CALLBACK_PATH);
1084
+ const clientOpts = {
1085
+ redirectUri,
1086
+ ...options.fetchImpl !== void 0 ? { fetchImpl: options.fetchImpl } : {}
1087
+ };
1088
+ const client = await registerPublicClient(clientOpts);
1089
+ const authorizeUrl = buildAuthorizeUrl({
1090
+ authorizeEndpoint: ORGX_HOSTED_OAUTH_AUTHORIZE_URL,
1091
+ clientId: client.client_id,
1092
+ redirectUri,
1093
+ scope,
1094
+ codeChallenge,
1095
+ codeChallengeMethod,
1096
+ state
1097
+ });
1098
+ options.onAuthorizeUrl?.(authorizeUrl);
1099
+ const { code } = await callbackPromise;
1100
+ const exchangeOpts = {
1101
+ code,
1102
+ codeVerifier,
1103
+ redirectUri,
1104
+ clientId: client.client_id,
1105
+ ...options.fetchImpl !== void 0 ? { fetchImpl: options.fetchImpl } : {}
1106
+ };
1107
+ const tokens = await exchangeCodeForTokens(exchangeOpts);
1108
+ return {
1109
+ accessToken: tokens.access_token,
1110
+ refreshToken: tokens.refresh_token,
1111
+ redirectUri,
1112
+ clientId: client.client_id,
1113
+ expiresIn: tokens.expires_in
1114
+ };
1115
+ }
1116
+
866
1117
  // src/lib/wizard-state.ts
867
1118
  import { randomUUID } from "crypto";
868
1119
 
@@ -3133,7 +3384,7 @@ async function ensureOnboardingTask(workspace, options = {}) {
3133
3384
  }
3134
3385
 
3135
3386
  // src/lib/skills.ts
3136
- import { createHash } from "crypto";
3387
+ import { createHash as createHash2 } from "crypto";
3137
3388
  import { existsSync as existsSync3, readdirSync } from "fs";
3138
3389
  import { basename, join as join2 } from "path";
3139
3390
  var DEFAULT_ORGX_SKILL_PACKS = [
@@ -3206,7 +3457,7 @@ Install and use these packs alongside this base skill:
3206
3457
  5. Carry \`_context\` through any widget-producing flows so the UI can render and resume correctly.
3207
3458
  `;
3208
3459
  function sha256(value) {
3209
- return createHash("sha256").update(value).digest("hex");
3460
+ return createHash2("sha256").update(value).digest("hex");
3210
3461
  }
3211
3462
  function normalizeSkillId(value) {
3212
3463
  const normalized = value.trim().toLowerCase();
@@ -5787,7 +6038,7 @@ function printDoctorReport(report, assessment) {
5787
6038
  async function main() {
5788
6039
  const program = new Command();
5789
6040
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
5790
- const pkgVersion = true ? "0.1.14" : void 0;
6041
+ const pkgVersion = true ? "0.1.15" : void 0;
5791
6042
  program.version(pkgVersion ?? "unknown", "-V, --version");
5792
6043
  program.hook("preAction", () => {
5793
6044
  console.log(renderBanner(pkgVersion));
@@ -6017,6 +6268,70 @@ async function main() {
6017
6268
  }
6018
6269
  }
6019
6270
  });
6271
+ async function runPkceLogin(opts = {}) {
6272
+ const spinner = createOrgxSpinner("Starting OrgX OAuth login");
6273
+ spinner.start();
6274
+ try {
6275
+ const pkceResult = await startPkceLogin({
6276
+ timeoutMs: (opts.timeout ?? 600) * 1e3,
6277
+ onAuthorizeUrl: (url) => {
6278
+ spinner.succeed("Authorization ready");
6279
+ console.log(`
6280
+ ${pc3.dim("Open this URL to authorize:")}`);
6281
+ console.log(` ${pc3.cyan(url)}
6282
+ `);
6283
+ if (opts.open !== false) {
6284
+ const openResult = openBrowser(url);
6285
+ if (!openResult.ok && openResult.error) {
6286
+ console.log(pc3.yellow(`Browser open failed: ${openResult.error}`));
6287
+ }
6288
+ }
6289
+ spinner.start();
6290
+ spinner.text = "Waiting for browser authorization...";
6291
+ }
6292
+ });
6293
+ spinner.text = "Verifying token...";
6294
+ const baseUrl = normalizeOrgxBaseUrl(DEFAULT_ORGX_BASE_URL);
6295
+ const verification = await verifyOrgxAuth({
6296
+ apiKey: pkceResult.accessToken,
6297
+ keyPrefix: pkceResult.accessToken.slice(0, 12),
6298
+ baseUrl,
6299
+ source: "wizard-store"
6300
+ });
6301
+ if (!verification.ok) {
6302
+ spinner.fail("OAuth token verification failed");
6303
+ console.log(` ${pc3.red(verification.error ?? `HTTP ${verification.status ?? "error"}`)}`);
6304
+ console.log(` ${pc3.dim("If this persists, try: wizard auth set-key <oxk_...>")}`);
6305
+ return false;
6306
+ }
6307
+ const stored = await writeWizardAuth({
6308
+ apiKey: pkceResult.accessToken,
6309
+ baseUrl,
6310
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
6311
+ source: "pkce",
6312
+ ...pkceResult.refreshToken !== void 0 ? { refreshToken: pkceResult.refreshToken } : {},
6313
+ oauthClientId: pkceResult.clientId
6314
+ });
6315
+ await safeTrackWizardTelemetry("auth_completed", {
6316
+ auth_source: opts.telemetrySource ?? "pkce",
6317
+ has_base_url: false
6318
+ });
6319
+ const openclawResults = detectSurface("openclaw").detected ? await addSurface("openclaw") : [];
6320
+ spinner.succeed("OrgX account connected");
6321
+ await syncContinuityAfterAuth();
6322
+ console.log(` ${ICON.ok} ${pc3.green("verified ")} ${pc3.bold(stored.keyPrefix)} ${pc3.dim("OAuth / PKCE")}`);
6323
+ if (openclawResults.length > 0) {
6324
+ console.log("");
6325
+ printMutationResults(openclawResults);
6326
+ }
6327
+ return true;
6328
+ } catch (error) {
6329
+ const message = error instanceof Error ? error.message : String(error);
6330
+ spinner.fail("OAuth login failed");
6331
+ console.log(` ${pc3.red(message)}`);
6332
+ return false;
6333
+ }
6334
+ }
6020
6335
  async function runBrowserLogin(opts = {}) {
6021
6336
  const installationId = getOrCreateWizardInstallationId();
6022
6337
  const spinner = createOrgxSpinner("Starting OrgX browser pairing");
@@ -6096,7 +6411,7 @@ async function main() {
6096
6411
  console.log(pc3.dim(" account"));
6097
6412
  printAuthStatus(status);
6098
6413
  });
6099
- auth.command("login").description("Start browser pairing for OrgX auth, with direct API key fallback for CI and blocked browsers.").option("--api-key <key>", "Bypass browser pairing and verify this OrgX API key directly.").option("--base-url <url>", "OrgX base URL").option("--device-name <name>", "Device name shown during browser approval.").option("--no-open", "Do not automatically open the browser connect URL.").option("--timeout <seconds>", "How long to wait for browser pairing before giving up.", parseTimeoutSeconds, 600).action(async (options) => {
6414
+ auth.command("login").description("Authenticate with OrgX. Uses OAuth PKCE (no OpenClaw required) by default, with OpenClaw pairing and direct key fallbacks.").option("--api-key <key>", "Bypass browser auth and verify this OrgX API key directly.").option("--base-url <url>", "OrgX base URL (for OpenClaw pairing fallback).").option("--device-name <name>", "Device name shown during OpenClaw pairing.").option("--no-open", "Do not automatically open the browser auth URL.").option("--pairing", "Force OpenClaw pairing flow instead of OAuth PKCE.").option("--timeout <seconds>", "How long to wait for browser auth before giving up.", parseTimeoutSeconds, 600).action(async (options) => {
6100
6415
  if (options.apiKey) {
6101
6416
  const spinner = createOrgxSpinner("Verifying OrgX API key");
6102
6417
  spinner.start();
@@ -6118,13 +6433,31 @@ async function main() {
6118
6433
  }
6119
6434
  return;
6120
6435
  }
6121
- await runBrowserLogin({
6122
- ...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
6123
- ...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
6436
+ if (options.pairing) {
6437
+ await runBrowserLogin({
6438
+ ...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
6439
+ ...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
6440
+ ...options.open !== void 0 ? { open: options.open } : {},
6441
+ telemetrySource: "browser_pairing",
6442
+ timeout: options.timeout
6443
+ });
6444
+ return;
6445
+ }
6446
+ const pkceOk = await runPkceLogin({
6124
6447
  ...options.open !== void 0 ? { open: options.open } : {},
6125
- telemetrySource: "browser_pairing",
6448
+ telemetrySource: "pkce",
6126
6449
  timeout: options.timeout
6127
6450
  });
6451
+ if (!pkceOk) {
6452
+ console.log(pc3.dim("\n Falling back to OpenClaw pairing..."));
6453
+ await runBrowserLogin({
6454
+ ...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
6455
+ ...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
6456
+ ...options.open !== void 0 ? { open: options.open } : {},
6457
+ telemetrySource: "browser_pairing_fallback",
6458
+ timeout: options.timeout
6459
+ });
6460
+ }
6128
6461
  });
6129
6462
  auth.command("set-key").description("Verify and persist a per-user OrgX API key for the wizard.").argument("<apiKey>", "per-user OrgX API key (oxk_...)").option("--base-url <url>", "OrgX base URL").action(async (apiKey, options) => {
6130
6463
  const spinner = createOrgxSpinner("Verifying OrgX API key");