@useorgx/wizard 0.1.13 → 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 +370 -18
- 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(
|
|
@@ -723,6 +739,21 @@ async function fetchJson({
|
|
|
723
739
|
}
|
|
724
740
|
return payload;
|
|
725
741
|
}
|
|
742
|
+
var UNROUTABLE_HOSTNAMES = /* @__PURE__ */ new Set(["0.0.0.0", "::", "[::]"]);
|
|
743
|
+
function sanitizeConnectUrl(connectUrl, baseUrl) {
|
|
744
|
+
try {
|
|
745
|
+
const parsed = new URL(connectUrl);
|
|
746
|
+
if (UNROUTABLE_HOSTNAMES.has(parsed.hostname)) {
|
|
747
|
+
const base = new URL(normalizeOrgxBaseUrl(baseUrl));
|
|
748
|
+
parsed.protocol = base.protocol;
|
|
749
|
+
parsed.hostname = base.hostname;
|
|
750
|
+
parsed.port = base.port;
|
|
751
|
+
}
|
|
752
|
+
return parsed.toString();
|
|
753
|
+
} catch {
|
|
754
|
+
return connectUrl;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
726
757
|
function parsePairingStartResult(value) {
|
|
727
758
|
if (!isRecord(value)) {
|
|
728
759
|
throw new Error("OrgX pairing start returned an invalid response.");
|
|
@@ -774,7 +805,11 @@ async function startBrowserPairing(options, fetchImpl) {
|
|
|
774
805
|
}),
|
|
775
806
|
fetchImpl
|
|
776
807
|
});
|
|
777
|
-
|
|
808
|
+
const result = parsePairingStartResult(data);
|
|
809
|
+
return {
|
|
810
|
+
...result,
|
|
811
|
+
connectUrl: sanitizeConnectUrl(result.connectUrl, options.baseUrl)
|
|
812
|
+
};
|
|
778
813
|
}
|
|
779
814
|
async function pollBrowserPairing(options, fetchImpl) {
|
|
780
815
|
const baseUrl = normalizeOrgxBaseUrl(options.baseUrl);
|
|
@@ -844,6 +879,241 @@ function openBrowser(url) {
|
|
|
844
879
|
return { ok: true };
|
|
845
880
|
}
|
|
846
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
|
+
|
|
847
1117
|
// src/lib/wizard-state.ts
|
|
848
1118
|
import { randomUUID } from "crypto";
|
|
849
1119
|
|
|
@@ -3114,7 +3384,7 @@ async function ensureOnboardingTask(workspace, options = {}) {
|
|
|
3114
3384
|
}
|
|
3115
3385
|
|
|
3116
3386
|
// src/lib/skills.ts
|
|
3117
|
-
import { createHash } from "crypto";
|
|
3387
|
+
import { createHash as createHash2 } from "crypto";
|
|
3118
3388
|
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
3119
3389
|
import { basename, join as join2 } from "path";
|
|
3120
3390
|
var DEFAULT_ORGX_SKILL_PACKS = [
|
|
@@ -3187,7 +3457,7 @@ Install and use these packs alongside this base skill:
|
|
|
3187
3457
|
5. Carry \`_context\` through any widget-producing flows so the UI can render and resume correctly.
|
|
3188
3458
|
`;
|
|
3189
3459
|
function sha256(value) {
|
|
3190
|
-
return
|
|
3460
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
3191
3461
|
}
|
|
3192
3462
|
function normalizeSkillId(value) {
|
|
3193
3463
|
const normalized = value.trim().toLowerCase();
|
|
@@ -5768,7 +6038,7 @@ function printDoctorReport(report, assessment) {
|
|
|
5768
6038
|
async function main() {
|
|
5769
6039
|
const program = new Command();
|
|
5770
6040
|
program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
|
|
5771
|
-
const pkgVersion = true ? "0.1.
|
|
6041
|
+
const pkgVersion = true ? "0.1.15" : void 0;
|
|
5772
6042
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
5773
6043
|
program.hook("preAction", () => {
|
|
5774
6044
|
console.log(renderBanner(pkgVersion));
|
|
@@ -5998,6 +6268,70 @@ async function main() {
|
|
|
5998
6268
|
}
|
|
5999
6269
|
}
|
|
6000
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
|
+
}
|
|
6001
6335
|
async function runBrowserLogin(opts = {}) {
|
|
6002
6336
|
const installationId = getOrCreateWizardInstallationId();
|
|
6003
6337
|
const spinner = createOrgxSpinner("Starting OrgX browser pairing");
|
|
@@ -6077,7 +6411,7 @@ async function main() {
|
|
|
6077
6411
|
console.log(pc3.dim(" account"));
|
|
6078
6412
|
printAuthStatus(status);
|
|
6079
6413
|
});
|
|
6080
|
-
auth.command("login").description("
|
|
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) => {
|
|
6081
6415
|
if (options.apiKey) {
|
|
6082
6416
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|
|
6083
6417
|
spinner.start();
|
|
@@ -6099,13 +6433,31 @@ async function main() {
|
|
|
6099
6433
|
}
|
|
6100
6434
|
return;
|
|
6101
6435
|
}
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
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({
|
|
6105
6447
|
...options.open !== void 0 ? { open: options.open } : {},
|
|
6106
|
-
telemetrySource: "
|
|
6448
|
+
telemetrySource: "pkce",
|
|
6107
6449
|
timeout: options.timeout
|
|
6108
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
|
+
}
|
|
6109
6461
|
});
|
|
6110
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) => {
|
|
6111
6463
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|