@useorgx/wizard 0.1.14 → 0.1.16
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 +594 -23
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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>✓ 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>✗ 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
|
|
|
@@ -1516,7 +1767,7 @@ function formatHttpError(status, body) {
|
|
|
1516
1767
|
}
|
|
1517
1768
|
async function listWorkspaces(options = {}) {
|
|
1518
1769
|
const auth = await requireOrgxAuth(options);
|
|
1519
|
-
const url = buildOrgxApiUrl("/entities?type=
|
|
1770
|
+
const url = buildOrgxApiUrl("/entities?type=workspace&limit=100", auth.baseUrl);
|
|
1520
1771
|
const response = await fetch(url, {
|
|
1521
1772
|
method: "GET",
|
|
1522
1773
|
headers: {
|
|
@@ -1562,7 +1813,7 @@ async function createWorkspace(input, options = {}) {
|
|
|
1562
1813
|
const auth = await requireOrgxAuth(options);
|
|
1563
1814
|
const url = buildOrgxApiUrl("/entities", auth.baseUrl);
|
|
1564
1815
|
const payload = {
|
|
1565
|
-
type: "
|
|
1816
|
+
type: "workspace",
|
|
1566
1817
|
name,
|
|
1567
1818
|
...input.description?.trim() ? { description: input.description.trim() } : {}
|
|
1568
1819
|
};
|
|
@@ -1589,7 +1840,7 @@ async function updateWorkspace(input, patch, options = {}) {
|
|
|
1589
1840
|
method: "PATCH",
|
|
1590
1841
|
headers: buildRequestHeaders(auth.apiKey),
|
|
1591
1842
|
body: JSON.stringify({
|
|
1592
|
-
type: "
|
|
1843
|
+
type: "workspace",
|
|
1593
1844
|
id: input.id,
|
|
1594
1845
|
...patch
|
|
1595
1846
|
}),
|
|
@@ -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
|
|
3460
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
3210
3461
|
}
|
|
3211
3462
|
function normalizeSkillId(value) {
|
|
3212
3463
|
const normalized = value.trim().toLowerCase();
|
|
@@ -5081,6 +5332,199 @@ async function trackWizardTelemetry(event, properties = {}, options = {}) {
|
|
|
5081
5332
|
return response?.ok === true;
|
|
5082
5333
|
}
|
|
5083
5334
|
|
|
5335
|
+
// src/lib/daily-brief-onboarding.ts
|
|
5336
|
+
var BASELINE_PROMPTS = [
|
|
5337
|
+
{ task_type: "code_review", label: "Code review", placeholder: "20" },
|
|
5338
|
+
{ task_type: "prd_draft", label: "PRD draft", placeholder: "45" },
|
|
5339
|
+
{ task_type: "test_writeup", label: "Test writeup", placeholder: "25" },
|
|
5340
|
+
{ task_type: "pr_description", label: "PR description", placeholder: "10" },
|
|
5341
|
+
{ task_type: "launch_post", label: "Launch post", placeholder: "60" },
|
|
5342
|
+
{ task_type: "architecture_doc", label: "Architecture doc", placeholder: "90" }
|
|
5343
|
+
];
|
|
5344
|
+
async function runDailyBriefOnboarding(options) {
|
|
5345
|
+
if (!options.interactive) {
|
|
5346
|
+
return {
|
|
5347
|
+
status: "skipped_non_interactive",
|
|
5348
|
+
message: "Daily Brief onboarding skipped \u2014 not attached to a TTY."
|
|
5349
|
+
};
|
|
5350
|
+
}
|
|
5351
|
+
if (!options.workspace) {
|
|
5352
|
+
return {
|
|
5353
|
+
status: "skipped_no_workspace",
|
|
5354
|
+
message: "Daily Brief onboarding skipped \u2014 no workspace resolved."
|
|
5355
|
+
};
|
|
5356
|
+
}
|
|
5357
|
+
const auth = await resolveOrgxAuth();
|
|
5358
|
+
if (!auth) {
|
|
5359
|
+
return {
|
|
5360
|
+
status: "failed",
|
|
5361
|
+
message: "Daily Brief onboarding needs OrgX auth.",
|
|
5362
|
+
error: "no_auth"
|
|
5363
|
+
};
|
|
5364
|
+
}
|
|
5365
|
+
const state = await fetchOnboardingState(auth).catch(() => null);
|
|
5366
|
+
if (state) {
|
|
5367
|
+
const row = state.workspaces.find((w) => w.id === options.workspace.id);
|
|
5368
|
+
if (row?.onboardingCompletedAt) {
|
|
5369
|
+
return {
|
|
5370
|
+
status: "skipped_already_completed",
|
|
5371
|
+
message: "Daily Brief onboarding already completed for this workspace."
|
|
5372
|
+
};
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5375
|
+
const { prompts } = options;
|
|
5376
|
+
const proceed = await prompts.confirm({
|
|
5377
|
+
message: "Set up your Daily Brief? (captures baselines so tomorrow's brief can show real time-saved numbers)",
|
|
5378
|
+
initialValue: true
|
|
5379
|
+
});
|
|
5380
|
+
if (prompts.isCancel(proceed)) {
|
|
5381
|
+
return { status: "cancelled", message: "Daily Brief onboarding cancelled." };
|
|
5382
|
+
}
|
|
5383
|
+
if (!proceed) {
|
|
5384
|
+
return {
|
|
5385
|
+
status: "cancelled",
|
|
5386
|
+
message: "Skipped \u2014 run `wizard brief setup` later to configure."
|
|
5387
|
+
};
|
|
5388
|
+
}
|
|
5389
|
+
const baselines = [];
|
|
5390
|
+
for (const bp of BASELINE_PROMPTS) {
|
|
5391
|
+
const answer = await prompts.text({
|
|
5392
|
+
message: `How long does ${bp.label.toLowerCase()} usually take? (minutes, blank to skip)`,
|
|
5393
|
+
placeholder: bp.placeholder,
|
|
5394
|
+
validate(value) {
|
|
5395
|
+
if (!value || value.trim() === "") return void 0;
|
|
5396
|
+
const n = Number.parseInt(value, 10);
|
|
5397
|
+
if (!Number.isFinite(n) || n <= 0 || n > 24 * 60) {
|
|
5398
|
+
return "Enter 1 \u2013 1440 or leave blank.";
|
|
5399
|
+
}
|
|
5400
|
+
return void 0;
|
|
5401
|
+
}
|
|
5402
|
+
});
|
|
5403
|
+
if (prompts.isCancel(answer)) {
|
|
5404
|
+
return { status: "cancelled", message: "Daily Brief onboarding cancelled." };
|
|
5405
|
+
}
|
|
5406
|
+
if (typeof answer === "string" && answer.trim() !== "") {
|
|
5407
|
+
const minutes = Number.parseInt(answer, 10);
|
|
5408
|
+
if (Number.isFinite(minutes) && minutes > 0) {
|
|
5409
|
+
baselines.push({ task_type: bp.task_type, minutes, confidence: 0.5 });
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
const goals = [];
|
|
5414
|
+
const goalAnswer = await prompts.text({
|
|
5415
|
+
message: "Weekly time saved target (minutes, blank to skip)",
|
|
5416
|
+
placeholder: "180",
|
|
5417
|
+
validate(value) {
|
|
5418
|
+
if (!value || value.trim() === "") return void 0;
|
|
5419
|
+
const n = Number.parseInt(value, 10);
|
|
5420
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
5421
|
+
return "Enter a positive number or leave blank.";
|
|
5422
|
+
}
|
|
5423
|
+
return void 0;
|
|
5424
|
+
}
|
|
5425
|
+
});
|
|
5426
|
+
if (prompts.isCancel(goalAnswer)) {
|
|
5427
|
+
return { status: "cancelled", message: "Daily Brief onboarding cancelled." };
|
|
5428
|
+
}
|
|
5429
|
+
if (typeof goalAnswer === "string" && goalAnswer.trim() !== "") {
|
|
5430
|
+
const target = Number.parseInt(goalAnswer, 10);
|
|
5431
|
+
if (Number.isFinite(target) && target > 0) {
|
|
5432
|
+
goals.push({
|
|
5433
|
+
goal_type: "time_saved_weekly",
|
|
5434
|
+
target_value: target,
|
|
5435
|
+
unit: "minutes",
|
|
5436
|
+
period: "weekly"
|
|
5437
|
+
});
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
5440
|
+
const sendTimeAnswer = await prompts.text({
|
|
5441
|
+
message: "When should your daily brief arrive? (HH:MM local, default 07:00)",
|
|
5442
|
+
placeholder: "07:00",
|
|
5443
|
+
validate(value) {
|
|
5444
|
+
if (!value || value.trim() === "") return void 0;
|
|
5445
|
+
if (!/^\d{1,2}:\d{2}$/.test(value.trim())) {
|
|
5446
|
+
return "Use HH:MM (24h).";
|
|
5447
|
+
}
|
|
5448
|
+
return void 0;
|
|
5449
|
+
}
|
|
5450
|
+
});
|
|
5451
|
+
if (prompts.isCancel(sendTimeAnswer)) {
|
|
5452
|
+
return { status: "cancelled", message: "Daily Brief onboarding cancelled." };
|
|
5453
|
+
}
|
|
5454
|
+
const sendTimeLocal = typeof sendTimeAnswer === "string" && sendTimeAnswer.trim() ? sendTimeAnswer.trim().length === 4 ? `0${sendTimeAnswer.trim()}` : sendTimeAnswer.trim() : "07:00";
|
|
5455
|
+
const onlyOnAttentionAnswer = await prompts.confirm({
|
|
5456
|
+
message: "Only email when something needs your attention?",
|
|
5457
|
+
initialValue: false
|
|
5458
|
+
});
|
|
5459
|
+
if (prompts.isCancel(onlyOnAttentionAnswer)) {
|
|
5460
|
+
return { status: "cancelled", message: "Daily Brief onboarding cancelled." };
|
|
5461
|
+
}
|
|
5462
|
+
const onlyOnAttention = Boolean(onlyOnAttentionAnswer);
|
|
5463
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
5464
|
+
const commitPayload = {
|
|
5465
|
+
workspace_id: options.workspace.id,
|
|
5466
|
+
agent_roster: [
|
|
5467
|
+
{ id: "engineering-agent", display_name: "Eli", color: "6, 182, 212", enabled: true },
|
|
5468
|
+
{ id: "product-agent", display_name: "Pace", color: "22, 163, 74", enabled: true },
|
|
5469
|
+
{ id: "marketing-agent", display_name: "Mark", color: "249, 115, 22", enabled: true },
|
|
5470
|
+
{ id: "orchestrator-agent", display_name: "Xandy", color: "20, 184, 166", enabled: true }
|
|
5471
|
+
],
|
|
5472
|
+
baselines,
|
|
5473
|
+
goals,
|
|
5474
|
+
starter_skill_loadouts: [],
|
|
5475
|
+
notification_preference: {
|
|
5476
|
+
channel: "email",
|
|
5477
|
+
digest_kind: "daily_brief",
|
|
5478
|
+
enabled: true,
|
|
5479
|
+
send_time_local: `${sendTimeLocal}:00`,
|
|
5480
|
+
timezone,
|
|
5481
|
+
only_on_attention: onlyOnAttention
|
|
5482
|
+
}
|
|
5483
|
+
};
|
|
5484
|
+
const commitResponse = await fetch(buildOrgxApiUrl("/v1/onboarding/daily-brief", auth.baseUrl), {
|
|
5485
|
+
method: "POST",
|
|
5486
|
+
headers: {
|
|
5487
|
+
Authorization: `Bearer ${auth.apiKey}`,
|
|
5488
|
+
"Content-Type": "application/json"
|
|
5489
|
+
},
|
|
5490
|
+
body: JSON.stringify(commitPayload),
|
|
5491
|
+
signal: AbortSignal.timeout(1e4)
|
|
5492
|
+
});
|
|
5493
|
+
if (!commitResponse.ok) {
|
|
5494
|
+
const text2 = await commitResponse.text().catch(() => "");
|
|
5495
|
+
return {
|
|
5496
|
+
status: "failed",
|
|
5497
|
+
message: "Could not commit onboarding capture.",
|
|
5498
|
+
error: `HTTP ${commitResponse.status}: ${text2.slice(0, 200)}`
|
|
5499
|
+
};
|
|
5500
|
+
}
|
|
5501
|
+
const commitBody = await commitResponse.json().catch(() => ({}));
|
|
5502
|
+
const previewUrl = typeof commitBody.daily_brief_preview_url === "string" ? commitBody.daily_brief_preview_url : "/today?mode=preview";
|
|
5503
|
+
return {
|
|
5504
|
+
status: "completed",
|
|
5505
|
+
message: "Your Daily Brief is configured. First brief lands tomorrow.",
|
|
5506
|
+
previewUrl
|
|
5507
|
+
};
|
|
5508
|
+
}
|
|
5509
|
+
async function fetchOnboardingState(auth) {
|
|
5510
|
+
try {
|
|
5511
|
+
const res = await fetch(
|
|
5512
|
+
buildOrgxApiUrl("/v1/onboarding/daily-brief", auth.baseUrl),
|
|
5513
|
+
{
|
|
5514
|
+
method: "GET",
|
|
5515
|
+
headers: { Authorization: `Bearer ${auth.apiKey}` },
|
|
5516
|
+
signal: AbortSignal.timeout(5e3)
|
|
5517
|
+
}
|
|
5518
|
+
);
|
|
5519
|
+
if (!res.ok) return null;
|
|
5520
|
+
const body = await res.json();
|
|
5521
|
+
if (!body || !Array.isArray(body.workspaces)) return null;
|
|
5522
|
+
return body;
|
|
5523
|
+
} catch {
|
|
5524
|
+
return null;
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
|
|
5084
5528
|
// src/spinner.ts
|
|
5085
5529
|
import ora from "ora";
|
|
5086
5530
|
import pc2 from "picocolors";
|
|
@@ -5157,6 +5601,21 @@ function printMutationResults(results) {
|
|
|
5157
5601
|
console.log(` ${icon} ${pc3.bold(result.name.padEnd(10))} ${state} ${pc3.dim(result.message)}`);
|
|
5158
5602
|
}
|
|
5159
5603
|
}
|
|
5604
|
+
function printSurfaceSummary(results) {
|
|
5605
|
+
const summarized = summarizeMutationResults(results);
|
|
5606
|
+
const updated = summarized.filter((r) => r.state === "updated");
|
|
5607
|
+
if (updated.length === 0) {
|
|
5608
|
+
console.log(
|
|
5609
|
+
` ${ICON.skip} ${pc3.dim(`${summarized.length} surface${summarized.length === 1 ? "" : "s"} already configured`)}`
|
|
5610
|
+
);
|
|
5611
|
+
return;
|
|
5612
|
+
}
|
|
5613
|
+
for (const result of summarized) {
|
|
5614
|
+
const icon = result.state === "updated" ? ICON.ok : ICON.skip;
|
|
5615
|
+
const state = result.state === "updated" ? pc3.green("updated ") : pc3.dim("unchanged");
|
|
5616
|
+
console.log(` ${icon} ${pc3.bold(result.name.padEnd(10))} ${state} ${pc3.dim(result.message)}`);
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5160
5619
|
function printPluginMutationReport(report) {
|
|
5161
5620
|
for (const result of report.results) {
|
|
5162
5621
|
const icon = result.changed ? ICON.ok : ICON.skip;
|
|
@@ -5166,10 +5625,16 @@ function printPluginMutationReport(report) {
|
|
|
5166
5625
|
);
|
|
5167
5626
|
}
|
|
5168
5627
|
}
|
|
5169
|
-
async function
|
|
5628
|
+
async function checkPluginStatusesCompact() {
|
|
5170
5629
|
const spinner = createOrgxSpinner("Checking OrgX companion plugin status");
|
|
5171
5630
|
spinner.start();
|
|
5172
5631
|
const statuses = await listOrgxPluginStatuses();
|
|
5632
|
+
const installable = statuses.filter((s) => s.available && !s.installed);
|
|
5633
|
+
const installed = statuses.filter((s) => s.installed).length;
|
|
5634
|
+
if (installable.length === 0) {
|
|
5635
|
+
spinner.succeed(`Plugins ready (${installed} installed)`);
|
|
5636
|
+
return statuses;
|
|
5637
|
+
}
|
|
5173
5638
|
spinner.succeed("OrgX companion plugin status checked");
|
|
5174
5639
|
console.log("");
|
|
5175
5640
|
console.log(pc3.dim(" plugins"));
|
|
@@ -5787,7 +6252,7 @@ function printDoctorReport(report, assessment) {
|
|
|
5787
6252
|
async function main() {
|
|
5788
6253
|
const program = new Command();
|
|
5789
6254
|
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.
|
|
6255
|
+
const pkgVersion = true ? "0.1.16" : void 0;
|
|
5791
6256
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
5792
6257
|
program.hook("preAction", () => {
|
|
5793
6258
|
console.log(renderBanner(pkgVersion));
|
|
@@ -5899,13 +6364,14 @@ async function main() {
|
|
|
5899
6364
|
spinner.start();
|
|
5900
6365
|
const results = await setupDetectedSurfaces();
|
|
5901
6366
|
spinner.succeed("Detected surfaces configured");
|
|
5902
|
-
|
|
5903
|
-
const pluginStatuses = await
|
|
6367
|
+
printSurfaceSummary(results);
|
|
6368
|
+
const pluginStatuses = await checkPluginStatusesCompact();
|
|
5904
6369
|
await safeTrackWizardTelemetry("mcp_injected", {
|
|
5905
6370
|
changed_count: results.filter((result) => result.changed).length,
|
|
5906
6371
|
preset: "standard",
|
|
5907
6372
|
surface_count: results.length
|
|
5908
6373
|
});
|
|
6374
|
+
const wasAlreadyPaired = await resolveOrgxAuth() !== null;
|
|
5909
6375
|
let resolvedAuth = await resolveOrgxAuth();
|
|
5910
6376
|
if (!resolvedAuth) {
|
|
5911
6377
|
console.log("");
|
|
@@ -5979,6 +6445,24 @@ async function main() {
|
|
|
5979
6445
|
if (workspaceSetup.workspace) {
|
|
5980
6446
|
console.log(` ${ICON.ok} ${pc3.green("workspace ")} ${pc3.bold(workspaceSetup.workspace.name)}`);
|
|
5981
6447
|
}
|
|
6448
|
+
const briefResult = await runDailyBriefOnboarding({
|
|
6449
|
+
interactive,
|
|
6450
|
+
workspace: resolvedWorkspace,
|
|
6451
|
+
prompts: {
|
|
6452
|
+
cancel: clack.cancel,
|
|
6453
|
+
isCancel: clack.isCancel,
|
|
6454
|
+
text: textPrompt,
|
|
6455
|
+
select: selectPrompt,
|
|
6456
|
+
confirm: clack.confirm
|
|
6457
|
+
}
|
|
6458
|
+
});
|
|
6459
|
+
if (briefResult.status === "cancelled") {
|
|
6460
|
+
console.log(` ${ICON.skip} ${pc3.dim(briefResult.message)}`);
|
|
6461
|
+
} else if (briefResult.status === "completed") {
|
|
6462
|
+
console.log(` ${ICON.ok} ${pc3.green("daily brief")} ${pc3.dim(briefResult.message)}`);
|
|
6463
|
+
} else if (briefResult.status === "failed") {
|
|
6464
|
+
console.log(` ${ICON.warn} ${pc3.yellow("daily brief")} ${pc3.dim(briefResult.message)}`);
|
|
6465
|
+
}
|
|
5982
6466
|
const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
|
|
5983
6467
|
interactive,
|
|
5984
6468
|
telemetry: { command: "setup", preset: "standard" },
|
|
@@ -6010,6 +6494,11 @@ async function main() {
|
|
|
6010
6494
|
console.log("");
|
|
6011
6495
|
if (assessment.issues.length === 0) {
|
|
6012
6496
|
console.log(` ${ICON.ok} ${pc3.green("You're all set.")} ${pc3.dim(`OrgX is active across ${configuredCount} editor${configuredCount !== 1 ? "s" : ""}`)}`);
|
|
6497
|
+
if (wasAlreadyPaired) {
|
|
6498
|
+
console.log(
|
|
6499
|
+
` ${pc3.dim("\u2192")} ${pc3.dim("If you opened the browser onboarding, click")} ${pc3.cyan("I've already paired")} ${pc3.dim("to continue.")}`
|
|
6500
|
+
);
|
|
6501
|
+
}
|
|
6013
6502
|
} else {
|
|
6014
6503
|
printDoctorReport(doctor, assessment);
|
|
6015
6504
|
if (verification.status === "error") {
|
|
@@ -6017,6 +6506,70 @@ async function main() {
|
|
|
6017
6506
|
}
|
|
6018
6507
|
}
|
|
6019
6508
|
});
|
|
6509
|
+
async function runPkceLogin(opts = {}) {
|
|
6510
|
+
const spinner = createOrgxSpinner("Starting OrgX OAuth login");
|
|
6511
|
+
spinner.start();
|
|
6512
|
+
try {
|
|
6513
|
+
const pkceResult = await startPkceLogin({
|
|
6514
|
+
timeoutMs: (opts.timeout ?? 600) * 1e3,
|
|
6515
|
+
onAuthorizeUrl: (url) => {
|
|
6516
|
+
spinner.succeed("Authorization ready");
|
|
6517
|
+
console.log(`
|
|
6518
|
+
${pc3.dim("Open this URL to authorize:")}`);
|
|
6519
|
+
console.log(` ${pc3.cyan(url)}
|
|
6520
|
+
`);
|
|
6521
|
+
if (opts.open !== false) {
|
|
6522
|
+
const openResult = openBrowser(url);
|
|
6523
|
+
if (!openResult.ok && openResult.error) {
|
|
6524
|
+
console.log(pc3.yellow(`Browser open failed: ${openResult.error}`));
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
spinner.start();
|
|
6528
|
+
spinner.text = "Waiting for browser authorization...";
|
|
6529
|
+
}
|
|
6530
|
+
});
|
|
6531
|
+
spinner.text = "Verifying token...";
|
|
6532
|
+
const baseUrl = normalizeOrgxBaseUrl(DEFAULT_ORGX_BASE_URL);
|
|
6533
|
+
const verification = await verifyOrgxAuth({
|
|
6534
|
+
apiKey: pkceResult.accessToken,
|
|
6535
|
+
keyPrefix: pkceResult.accessToken.slice(0, 12),
|
|
6536
|
+
baseUrl,
|
|
6537
|
+
source: "wizard-store"
|
|
6538
|
+
});
|
|
6539
|
+
if (!verification.ok) {
|
|
6540
|
+
spinner.fail("OAuth token verification failed");
|
|
6541
|
+
console.log(` ${pc3.red(verification.error ?? `HTTP ${verification.status ?? "error"}`)}`);
|
|
6542
|
+
console.log(` ${pc3.dim("If this persists, try: wizard auth set-key <oxk_...>")}`);
|
|
6543
|
+
return false;
|
|
6544
|
+
}
|
|
6545
|
+
const stored = await writeWizardAuth({
|
|
6546
|
+
apiKey: pkceResult.accessToken,
|
|
6547
|
+
baseUrl,
|
|
6548
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6549
|
+
source: "pkce",
|
|
6550
|
+
...pkceResult.refreshToken !== void 0 ? { refreshToken: pkceResult.refreshToken } : {},
|
|
6551
|
+
oauthClientId: pkceResult.clientId
|
|
6552
|
+
});
|
|
6553
|
+
await safeTrackWizardTelemetry("auth_completed", {
|
|
6554
|
+
auth_source: opts.telemetrySource ?? "pkce",
|
|
6555
|
+
has_base_url: false
|
|
6556
|
+
});
|
|
6557
|
+
const openclawResults = detectSurface("openclaw").detected ? await addSurface("openclaw") : [];
|
|
6558
|
+
spinner.succeed("OrgX account connected");
|
|
6559
|
+
await syncContinuityAfterAuth();
|
|
6560
|
+
console.log(` ${ICON.ok} ${pc3.green("verified ")} ${pc3.bold(stored.keyPrefix)} ${pc3.dim("OAuth / PKCE")}`);
|
|
6561
|
+
if (openclawResults.length > 0) {
|
|
6562
|
+
console.log("");
|
|
6563
|
+
printMutationResults(openclawResults);
|
|
6564
|
+
}
|
|
6565
|
+
return true;
|
|
6566
|
+
} catch (error) {
|
|
6567
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6568
|
+
spinner.fail("OAuth login failed");
|
|
6569
|
+
console.log(` ${pc3.red(message)}`);
|
|
6570
|
+
return false;
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6020
6573
|
async function runBrowserLogin(opts = {}) {
|
|
6021
6574
|
const installationId = getOrCreateWizardInstallationId();
|
|
6022
6575
|
const spinner = createOrgxSpinner("Starting OrgX browser pairing");
|
|
@@ -6096,7 +6649,7 @@ async function main() {
|
|
|
6096
6649
|
console.log(pc3.dim(" account"));
|
|
6097
6650
|
printAuthStatus(status);
|
|
6098
6651
|
});
|
|
6099
|
-
auth.command("login").description("
|
|
6652
|
+
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
6653
|
if (options.apiKey) {
|
|
6101
6654
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|
|
6102
6655
|
spinner.start();
|
|
@@ -6118,13 +6671,31 @@ async function main() {
|
|
|
6118
6671
|
}
|
|
6119
6672
|
return;
|
|
6120
6673
|
}
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6674
|
+
if (options.pairing) {
|
|
6675
|
+
await runBrowserLogin({
|
|
6676
|
+
...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
|
|
6677
|
+
...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
|
|
6678
|
+
...options.open !== void 0 ? { open: options.open } : {},
|
|
6679
|
+
telemetrySource: "browser_pairing",
|
|
6680
|
+
timeout: options.timeout
|
|
6681
|
+
});
|
|
6682
|
+
return;
|
|
6683
|
+
}
|
|
6684
|
+
const pkceOk = await runPkceLogin({
|
|
6124
6685
|
...options.open !== void 0 ? { open: options.open } : {},
|
|
6125
|
-
telemetrySource: "
|
|
6686
|
+
telemetrySource: "pkce",
|
|
6126
6687
|
timeout: options.timeout
|
|
6127
6688
|
});
|
|
6689
|
+
if (!pkceOk) {
|
|
6690
|
+
console.log(pc3.dim("\n Falling back to OpenClaw pairing..."));
|
|
6691
|
+
await runBrowserLogin({
|
|
6692
|
+
...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
|
|
6693
|
+
...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
|
|
6694
|
+
...options.open !== void 0 ? { open: options.open } : {},
|
|
6695
|
+
telemetrySource: "browser_pairing_fallback",
|
|
6696
|
+
timeout: options.timeout
|
|
6697
|
+
});
|
|
6698
|
+
}
|
|
6128
6699
|
});
|
|
6129
6700
|
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
6701
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|