@useorgx/wizard 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/cli.js +2315 -272
- package/dist/cli.js.map +1 -1
- package/package.json +10 -11
package/dist/cli.js
CHANGED
|
@@ -28,10 +28,25 @@ var ORGX_HOSTED_MCP_KEY = "orgx";
|
|
|
28
28
|
var ORGX_LOCAL_MCP_KEY = "orgx-openclaw";
|
|
29
29
|
var ORGX_HOSTED_MCP_URL = "https://mcp.useorgx.com/mcp";
|
|
30
30
|
var ORGX_HOSTED_MCP_HEALTH_URL = "https://mcp.useorgx.com/health";
|
|
31
|
+
var ORGX_HOSTED_OAUTH_BASE_URL = "https://mcp.useorgx.com";
|
|
32
|
+
var ORGX_HOSTED_OAUTH_AUTHORIZE_URL = `${ORGX_HOSTED_OAUTH_BASE_URL}/authorize`;
|
|
33
|
+
var ORGX_HOSTED_OAUTH_TOKEN_URL = `${ORGX_HOSTED_OAUTH_BASE_URL}/token`;
|
|
34
|
+
var ORGX_HOSTED_OAUTH_REGISTER_URL = `${ORGX_HOSTED_OAUTH_BASE_URL}/register`;
|
|
31
35
|
var ORGX_WIZARD_NPM_PACKAGE_NAME = "@useorgx/wizard";
|
|
32
36
|
var NPM_REGISTRY_BASE_URL = "https://registry.npmjs.org";
|
|
33
37
|
var DEFAULT_OPENCLAW_GATEWAY_PORT = 18789;
|
|
34
|
-
var DEFAULT_ORGX_BASE_URL = process.env.ORGX_BASE_URL?.trim() || "https://
|
|
38
|
+
var DEFAULT_ORGX_BASE_URL = process.env.ORGX_BASE_URL?.trim() || "https://useorgx.com";
|
|
39
|
+
var ORGX_WIZARD_OAUTH_SCOPES = [
|
|
40
|
+
"decisions:read",
|
|
41
|
+
"decisions:write",
|
|
42
|
+
"agents:read",
|
|
43
|
+
"agents:write",
|
|
44
|
+
"initiatives:read",
|
|
45
|
+
"initiatives:write",
|
|
46
|
+
"memory:read",
|
|
47
|
+
"offline_access"
|
|
48
|
+
];
|
|
49
|
+
var ORGX_WIZARD_OAUTH_SCOPE = ORGX_WIZARD_OAUTH_SCOPES.join(" ");
|
|
35
50
|
var HOME = homedir();
|
|
36
51
|
var APPDATA = process.env.APPDATA?.trim();
|
|
37
52
|
var LOCAL_APPDATA = process.env.LOCALAPPDATA?.trim();
|
|
@@ -59,11 +74,31 @@ var CLAUDE_DIR = join(HOME, ".claude");
|
|
|
59
74
|
var CURSOR_DIR = join(HOME, ".cursor");
|
|
60
75
|
var CODEX_DIR = join(HOME, ".codex");
|
|
61
76
|
var OPENCLAW_DIR = join(HOME, ".openclaw");
|
|
77
|
+
var AGENTS_DIR = join(HOME, ".agents");
|
|
62
78
|
var CLAUDE_SKILLS_DIR = join(CLAUDE_DIR, "skills");
|
|
63
79
|
var CLAUDE_ORGX_SKILL_DIR = join(CLAUDE_SKILLS_DIR, "orgx");
|
|
64
80
|
var CLAUDE_ORGX_SKILL_PATH = join(CLAUDE_ORGX_SKILL_DIR, "SKILL.md");
|
|
65
81
|
var CURSOR_RULES_DIR = join(CURSOR_DIR, "rules");
|
|
66
82
|
var CURSOR_ORGX_RULE_PATH = join(CURSOR_RULES_DIR, "orgx.md");
|
|
83
|
+
var CODEX_PLUGINS_DIR = join(CODEX_DIR, "plugins");
|
|
84
|
+
var CODEX_ORGX_PLUGIN_DIR = join(CODEX_PLUGINS_DIR, "orgx-codex-plugin");
|
|
85
|
+
var CODEX_MARKETPLACE_DIR = join(AGENTS_DIR, "plugins");
|
|
86
|
+
var CODEX_MARKETPLACE_PATH = join(CODEX_MARKETPLACE_DIR, "marketplace.json");
|
|
87
|
+
var CLAUDE_MANAGED_MARKETPLACE_DIR = join(
|
|
88
|
+
ORGX_WIZARD_CONFIG_HOME,
|
|
89
|
+
"plugins",
|
|
90
|
+
"claude-marketplace"
|
|
91
|
+
);
|
|
92
|
+
var CLAUDE_MANAGED_MARKETPLACE_MANIFEST_PATH = join(
|
|
93
|
+
CLAUDE_MANAGED_MARKETPLACE_DIR,
|
|
94
|
+
".claude-plugin",
|
|
95
|
+
"marketplace.json"
|
|
96
|
+
);
|
|
97
|
+
var CLAUDE_MANAGED_PLUGIN_DIR = join(
|
|
98
|
+
CLAUDE_MANAGED_MARKETPLACE_DIR,
|
|
99
|
+
"plugins",
|
|
100
|
+
"orgx-claude-code-plugin"
|
|
101
|
+
);
|
|
67
102
|
var CLAUDE_MCP_PATHS = uniquePaths([join(CLAUDE_DIR, "mcp.json")]);
|
|
68
103
|
var CURSOR_MCP_PATHS = uniquePaths([join(CURSOR_DIR, "mcp.json")]);
|
|
69
104
|
var CODEX_CONFIG_PATHS = uniquePaths([join(CODEX_DIR, "config.toml")]);
|
|
@@ -367,6 +402,19 @@ function buildStoredRecord(value) {
|
|
|
367
402
|
verifiedAt: value.verifiedAt
|
|
368
403
|
};
|
|
369
404
|
}
|
|
405
|
+
function readWizardAuthMetadata(authPath = ORGX_WIZARD_AUTH_PATH) {
|
|
406
|
+
const stored = parseStoredWizardAuthRecord(readTextIfExists(authPath));
|
|
407
|
+
if (!stored) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
baseUrl: stored.baseUrl,
|
|
412
|
+
keyPrefix: stored.keyPrefix,
|
|
413
|
+
source: "manual",
|
|
414
|
+
storage: stored.storage,
|
|
415
|
+
verifiedAt: stored.verifiedAt
|
|
416
|
+
};
|
|
417
|
+
}
|
|
370
418
|
async function readWizardAuth(authPath = ORGX_WIZARD_AUTH_PATH, options = {}) {
|
|
371
419
|
const stored = parseStoredWizardAuthRecord(readTextIfExists(authPath));
|
|
372
420
|
if (!stored) {
|
|
@@ -466,7 +514,11 @@ function normalizeOrgxBaseUrl(raw) {
|
|
|
466
514
|
}
|
|
467
515
|
}
|
|
468
516
|
function buildOrgxApiUrl(path, baseUrl) {
|
|
469
|
-
const
|
|
517
|
+
const parsedBase = new URL(normalizeOrgxBaseUrl(baseUrl));
|
|
518
|
+
if (parsedBase.protocol === "https:" && normalizeHost(parsedBase.hostname) === "useorgx.com") {
|
|
519
|
+
parsedBase.hostname = "www.useorgx.com";
|
|
520
|
+
}
|
|
521
|
+
const normalizedBase = parsedBase.toString().replace(/\/+$/, "");
|
|
470
522
|
const apiBase = normalizedBase.endsWith("/api") ? normalizedBase : `${normalizedBase}/api`;
|
|
471
523
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
472
524
|
return `${apiBase}${normalizedPath}`;
|
|
@@ -484,6 +536,52 @@ function buildResolvedAuth(apiKey, baseUrl, source, extras = {}) {
|
|
|
484
536
|
...extras.verifiedAt ? { verifiedAt: extras.verifiedAt } : {}
|
|
485
537
|
};
|
|
486
538
|
}
|
|
539
|
+
function buildAuthHint(keyPrefix, baseUrl, source, extras = {}) {
|
|
540
|
+
return {
|
|
541
|
+
keyPrefix: keyPrefix.trim(),
|
|
542
|
+
baseUrl,
|
|
543
|
+
source,
|
|
544
|
+
...extras.path ? { path: extras.path } : {},
|
|
545
|
+
...extras.verifiedAt ? { verifiedAt: extras.verifiedAt } : {}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function peekResolvedOrgxAuth(options = {}) {
|
|
549
|
+
const envApiKey = process.env.ORGX_API_KEY?.trim();
|
|
550
|
+
const envBaseUrl = process.env.ORGX_BASE_URL?.trim();
|
|
551
|
+
if (envApiKey) {
|
|
552
|
+
return buildAuthHint(
|
|
553
|
+
extractKeyPrefix(envApiKey),
|
|
554
|
+
withResolvedBaseUrl(DEFAULT_ORGX_BASE_URL, envBaseUrl),
|
|
555
|
+
"environment"
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
const authPath = options.authPath ?? ORGX_WIZARD_AUTH_PATH;
|
|
559
|
+
const stored = readWizardAuthMetadata(authPath);
|
|
560
|
+
if (stored) {
|
|
561
|
+
return buildAuthHint(
|
|
562
|
+
stored.keyPrefix,
|
|
563
|
+
withResolvedBaseUrl(stored.baseUrl, envBaseUrl),
|
|
564
|
+
"wizard-store",
|
|
565
|
+
{
|
|
566
|
+
path: authPath,
|
|
567
|
+
verifiedAt: stored.verifiedAt
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
const openclawConfigPath = options.openclawConfigPath ?? OPENCLAW_CONFIG_PATH;
|
|
572
|
+
const openclawAuth = readOpenClawAuth(
|
|
573
|
+
openclawConfigPath ? readTextIfExists(openclawConfigPath) : null
|
|
574
|
+
);
|
|
575
|
+
if (openclawAuth.apiKey && openclawConfigPath) {
|
|
576
|
+
return buildAuthHint(
|
|
577
|
+
extractKeyPrefix(openclawAuth.apiKey),
|
|
578
|
+
withResolvedBaseUrl(openclawAuth.baseUrl || DEFAULT_ORGX_BASE_URL, envBaseUrl),
|
|
579
|
+
"openclaw-config-file",
|
|
580
|
+
{ path: openclawConfigPath }
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
487
585
|
async function resolveOrgxAuth(options = {}) {
|
|
488
586
|
const envApiKey = process.env.ORGX_API_KEY?.trim();
|
|
489
587
|
const envBaseUrl = process.env.ORGX_BASE_URL?.trim();
|
|
@@ -745,6 +843,295 @@ function openBrowser(url) {
|
|
|
745
843
|
return { ok: true };
|
|
746
844
|
}
|
|
747
845
|
|
|
846
|
+
// src/lib/wizard-state.ts
|
|
847
|
+
import { randomUUID } from "crypto";
|
|
848
|
+
|
|
849
|
+
// src/surfaces/names.ts
|
|
850
|
+
var SURFACE_NAMES = [
|
|
851
|
+
"claude",
|
|
852
|
+
"cursor",
|
|
853
|
+
"codex",
|
|
854
|
+
"openclaw",
|
|
855
|
+
"vscode",
|
|
856
|
+
"windsurf",
|
|
857
|
+
"zed",
|
|
858
|
+
"chatgpt"
|
|
859
|
+
];
|
|
860
|
+
var AUTOMATED_SURFACE_NAMES = [
|
|
861
|
+
"claude",
|
|
862
|
+
"cursor",
|
|
863
|
+
"codex",
|
|
864
|
+
"openclaw",
|
|
865
|
+
"vscode",
|
|
866
|
+
"windsurf",
|
|
867
|
+
"zed"
|
|
868
|
+
];
|
|
869
|
+
var MCP_SURFACE_NAMES = [
|
|
870
|
+
"claude",
|
|
871
|
+
"cursor",
|
|
872
|
+
"codex",
|
|
873
|
+
"vscode",
|
|
874
|
+
"windsurf",
|
|
875
|
+
"zed"
|
|
876
|
+
];
|
|
877
|
+
|
|
878
|
+
// src/lib/wizard-state.ts
|
|
879
|
+
function isNonEmptyString2(value) {
|
|
880
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
881
|
+
}
|
|
882
|
+
function isSurfaceName(value) {
|
|
883
|
+
return typeof value === "string" && SURFACE_NAMES.includes(value);
|
|
884
|
+
}
|
|
885
|
+
function parseContinuityDefaults(value) {
|
|
886
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
887
|
+
return void 0;
|
|
888
|
+
}
|
|
889
|
+
const record = value;
|
|
890
|
+
const parsed = {};
|
|
891
|
+
if (record.executionMode === "cloud" || record.executionMode === "local") {
|
|
892
|
+
parsed.executionMode = record.executionMode;
|
|
893
|
+
}
|
|
894
|
+
if (isSurfaceName(record.reviewSurface)) {
|
|
895
|
+
parsed.reviewSurface = record.reviewSurface;
|
|
896
|
+
}
|
|
897
|
+
if (isNonEmptyString2(record.workspaceId)) {
|
|
898
|
+
parsed.workspaceId = record.workspaceId.trim();
|
|
899
|
+
}
|
|
900
|
+
if (isNonEmptyString2(record.workspaceName)) {
|
|
901
|
+
parsed.workspaceName = record.workspaceName.trim();
|
|
902
|
+
}
|
|
903
|
+
return Object.keys(parsed).length > 0 ? parsed : void 0;
|
|
904
|
+
}
|
|
905
|
+
function parseDemoInitiative(value) {
|
|
906
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
907
|
+
return void 0;
|
|
908
|
+
}
|
|
909
|
+
const record = value;
|
|
910
|
+
if (!isNonEmptyString2(record.id) || !isNonEmptyString2(record.title) || !isNonEmptyString2(record.liveUrl) || !isNonEmptyString2(record.createdAt)) {
|
|
911
|
+
return void 0;
|
|
912
|
+
}
|
|
913
|
+
return {
|
|
914
|
+
createdAt: record.createdAt.trim(),
|
|
915
|
+
...isNonEmptyString2(record.artifactId) ? { artifactId: record.artifactId.trim() } : {},
|
|
916
|
+
...isNonEmptyString2(record.artifactName) ? { artifactName: record.artifactName.trim() } : {},
|
|
917
|
+
...isNonEmptyString2(record.artifactType) ? { artifactType: record.artifactType.trim() } : {},
|
|
918
|
+
...isNonEmptyString2(record.artifactUrl) ? { artifactUrl: record.artifactUrl.trim() } : {},
|
|
919
|
+
...isNonEmptyString2(record.decisionId) ? { decisionId: record.decisionId.trim() } : {},
|
|
920
|
+
...isNonEmptyString2(record.decisionStatus) ? { decisionStatus: record.decisionStatus.trim() } : {},
|
|
921
|
+
...isNonEmptyString2(record.decisionTitle) ? { decisionTitle: record.decisionTitle.trim() } : {},
|
|
922
|
+
id: record.id.trim(),
|
|
923
|
+
liveUrl: record.liveUrl.trim(),
|
|
924
|
+
title: record.title.trim(),
|
|
925
|
+
...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
|
|
926
|
+
...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
function parseOnboardingTask(value) {
|
|
930
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
931
|
+
return void 0;
|
|
932
|
+
}
|
|
933
|
+
const record = value;
|
|
934
|
+
if (!isNonEmptyString2(record.id) || !isNonEmptyString2(record.title) || !isNonEmptyString2(record.createdAt)) {
|
|
935
|
+
return void 0;
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
createdAt: record.createdAt.trim(),
|
|
939
|
+
id: record.id.trim(),
|
|
940
|
+
...isNonEmptyString2(record.initiativeId) ? { initiativeId: record.initiativeId.trim() } : {},
|
|
941
|
+
...isNonEmptyString2(record.status) ? { status: record.status.trim() } : {},
|
|
942
|
+
title: record.title.trim(),
|
|
943
|
+
...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
|
|
944
|
+
...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function parseAgentRosterEntry(value) {
|
|
948
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
949
|
+
return void 0;
|
|
950
|
+
}
|
|
951
|
+
const record = value;
|
|
952
|
+
if (!isNonEmptyString2(record.agentType) || !isNonEmptyString2(record.domain) || !isNonEmptyString2(record.name)) {
|
|
953
|
+
return void 0;
|
|
954
|
+
}
|
|
955
|
+
return {
|
|
956
|
+
agentType: record.agentType.trim(),
|
|
957
|
+
domain: record.domain.trim(),
|
|
958
|
+
name: record.name.trim()
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function parseAgentRoster(value) {
|
|
962
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
963
|
+
return void 0;
|
|
964
|
+
}
|
|
965
|
+
const record = value;
|
|
966
|
+
if (!isNonEmptyString2(record.configuredAt) || !Array.isArray(record.agents)) {
|
|
967
|
+
return void 0;
|
|
968
|
+
}
|
|
969
|
+
const agents = record.agents.map((entry) => parseAgentRosterEntry(entry)).filter((entry) => Boolean(entry));
|
|
970
|
+
if (agents.length === 0) {
|
|
971
|
+
return void 0;
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
agents,
|
|
975
|
+
configuredAt: record.configuredAt.trim(),
|
|
976
|
+
...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
|
|
977
|
+
...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function createWizardState(now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
981
|
+
return {
|
|
982
|
+
installationId: `wizard-${randomUUID()}`,
|
|
983
|
+
createdAt: now,
|
|
984
|
+
updatedAt: now
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
function sanitizeWizardStateRecord(record) {
|
|
988
|
+
const continuity = parseContinuityDefaults(record.continuity);
|
|
989
|
+
const agentRoster = parseAgentRoster(record.agentRoster);
|
|
990
|
+
const demoInitiative = parseDemoInitiative(record.demoInitiative);
|
|
991
|
+
const onboardingTask = parseOnboardingTask(record.onboardingTask);
|
|
992
|
+
return {
|
|
993
|
+
installationId: record.installationId.trim(),
|
|
994
|
+
createdAt: record.createdAt.trim(),
|
|
995
|
+
updatedAt: record.updatedAt.trim(),
|
|
996
|
+
...continuity ? { continuity } : {},
|
|
997
|
+
...agentRoster ? { agentRoster } : {},
|
|
998
|
+
...demoInitiative ? { demoInitiative } : {},
|
|
999
|
+
...onboardingTask ? { onboardingTask } : {}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1003
|
+
const parsed = parseJsonObject(readTextIfExists(statePath));
|
|
1004
|
+
if (!isNonEmptyString2(parsed.installationId) || !isNonEmptyString2(parsed.createdAt) || !isNonEmptyString2(parsed.updatedAt)) {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
const state = {
|
|
1008
|
+
installationId: parsed.installationId.trim(),
|
|
1009
|
+
createdAt: parsed.createdAt.trim(),
|
|
1010
|
+
updatedAt: parsed.updatedAt.trim()
|
|
1011
|
+
};
|
|
1012
|
+
const continuity = parseContinuityDefaults(parsed.continuity);
|
|
1013
|
+
if (continuity !== void 0) state.continuity = continuity;
|
|
1014
|
+
const agentRoster = parseAgentRoster(parsed.agentRoster);
|
|
1015
|
+
if (agentRoster !== void 0) state.agentRoster = agentRoster;
|
|
1016
|
+
const demoInitiative = parseDemoInitiative(parsed.demoInitiative);
|
|
1017
|
+
if (demoInitiative !== void 0) state.demoInitiative = demoInitiative;
|
|
1018
|
+
const onboardingTask = parseOnboardingTask(parsed.onboardingTask);
|
|
1019
|
+
if (onboardingTask !== void 0) state.onboardingTask = onboardingTask;
|
|
1020
|
+
return state;
|
|
1021
|
+
}
|
|
1022
|
+
function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1023
|
+
const record = sanitizeWizardStateRecord(value);
|
|
1024
|
+
writeJsonFile(statePath, record, { mode: 384 });
|
|
1025
|
+
return record;
|
|
1026
|
+
}
|
|
1027
|
+
function updateWizardState(updater, statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1028
|
+
const current = readWizardState(statePath) ?? createWizardState();
|
|
1029
|
+
const next = updater(current);
|
|
1030
|
+
const sanitized = sanitizeWizardStateRecord({
|
|
1031
|
+
...next,
|
|
1032
|
+
installationId: next.installationId || current.installationId,
|
|
1033
|
+
createdAt: next.createdAt || current.createdAt,
|
|
1034
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1035
|
+
});
|
|
1036
|
+
writeJsonFile(statePath, sanitized, { mode: 384 });
|
|
1037
|
+
return sanitized;
|
|
1038
|
+
}
|
|
1039
|
+
function getOrCreateWizardInstallationId(statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1040
|
+
const existing = readWizardState(statePath);
|
|
1041
|
+
if (existing) {
|
|
1042
|
+
return existing.installationId;
|
|
1043
|
+
}
|
|
1044
|
+
const record = createWizardState();
|
|
1045
|
+
writeWizardState(record, statePath);
|
|
1046
|
+
return record.installationId;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/lib/agent-roster.ts
|
|
1050
|
+
var AGENT_ROSTER_PROFILES = [
|
|
1051
|
+
{ agentType: "orchestrator-agent", defaultDomain: "orchestrator", name: "Xandy" },
|
|
1052
|
+
{ agentType: "product-agent", defaultDomain: "product", name: "Pace" },
|
|
1053
|
+
{ agentType: "engineering-agent", defaultDomain: "engineering", name: "Eli" },
|
|
1054
|
+
{ agentType: "marketing-agent", defaultDomain: "marketing", name: "Mark" },
|
|
1055
|
+
{ agentType: "sales-agent", defaultDomain: "sales", name: "Sage" },
|
|
1056
|
+
{ agentType: "operations-agent", defaultDomain: "operations", name: "Orion" },
|
|
1057
|
+
{ agentType: "design-agent", defaultDomain: "design", name: "Dana" }
|
|
1058
|
+
];
|
|
1059
|
+
var AGENT_ROSTER_DOMAINS = AGENT_ROSTER_PROFILES.map((profile) => profile.defaultDomain);
|
|
1060
|
+
var DEFAULT_AGENT_ROSTER = AGENT_ROSTER_PROFILES.map(
|
|
1061
|
+
({ agentType, defaultDomain, name }) => ({
|
|
1062
|
+
agentType,
|
|
1063
|
+
domain: defaultDomain,
|
|
1064
|
+
name
|
|
1065
|
+
})
|
|
1066
|
+
);
|
|
1067
|
+
function findAgentRosterProfile(name) {
|
|
1068
|
+
return AGENT_ROSTER_PROFILES.find((profile) => profile.name === name);
|
|
1069
|
+
}
|
|
1070
|
+
function listAgentRosterProfiles() {
|
|
1071
|
+
return AGENT_ROSTER_PROFILES.map((profile) => ({ ...profile }));
|
|
1072
|
+
}
|
|
1073
|
+
function buildAgentRosterEntries(assignments) {
|
|
1074
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1075
|
+
return assignments.map((assignment) => {
|
|
1076
|
+
const name = assignment.name.trim();
|
|
1077
|
+
const domain = assignment.domain.trim();
|
|
1078
|
+
if (domain.length === 0) {
|
|
1079
|
+
throw new Error("OrgX agent roster domains must be non-empty.");
|
|
1080
|
+
}
|
|
1081
|
+
const profile = findAgentRosterProfile(name);
|
|
1082
|
+
if (!profile) {
|
|
1083
|
+
throw new Error(`Unknown OrgX agent '${assignment.name}'.`);
|
|
1084
|
+
}
|
|
1085
|
+
if (seenNames.has(profile.name)) {
|
|
1086
|
+
throw new Error(`OrgX agent '${profile.name}' can only be assigned once.`);
|
|
1087
|
+
}
|
|
1088
|
+
seenNames.add(profile.name);
|
|
1089
|
+
return {
|
|
1090
|
+
agentType: profile.agentType,
|
|
1091
|
+
domain,
|
|
1092
|
+
name: profile.name
|
|
1093
|
+
};
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
function toAgentRosterRecord(workspace, agents) {
|
|
1097
|
+
return {
|
|
1098
|
+
agents: agents.map((agent) => ({ ...agent })),
|
|
1099
|
+
configuredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1100
|
+
workspaceId: workspace.id,
|
|
1101
|
+
workspaceName: workspace.name
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
function configureAgentRoster(workspace, assignments, options = {}) {
|
|
1105
|
+
const agents = buildAgentRosterEntries(assignments);
|
|
1106
|
+
if (agents.length === 0) {
|
|
1107
|
+
throw new Error("Select at least one OrgX agent before saving the roster.");
|
|
1108
|
+
}
|
|
1109
|
+
const roster = toAgentRosterRecord(workspace, agents);
|
|
1110
|
+
updateWizardState(
|
|
1111
|
+
(current) => ({
|
|
1112
|
+
...current,
|
|
1113
|
+
agentRoster: roster
|
|
1114
|
+
}),
|
|
1115
|
+
options.statePath
|
|
1116
|
+
);
|
|
1117
|
+
return roster;
|
|
1118
|
+
}
|
|
1119
|
+
function ensureDefaultAgentRoster(workspace, options = {}) {
|
|
1120
|
+
const existing = readWizardState(options.statePath)?.agentRoster;
|
|
1121
|
+
if (existing?.workspaceId === workspace.id && existing.agents.length > 0) {
|
|
1122
|
+
return existing;
|
|
1123
|
+
}
|
|
1124
|
+
const roster = toAgentRosterRecord(workspace, DEFAULT_AGENT_ROSTER);
|
|
1125
|
+
updateWizardState(
|
|
1126
|
+
(current) => ({
|
|
1127
|
+
...current,
|
|
1128
|
+
agentRoster: roster
|
|
1129
|
+
}),
|
|
1130
|
+
options.statePath
|
|
1131
|
+
);
|
|
1132
|
+
return roster;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
748
1135
|
// src/lib/openclaw-health.ts
|
|
749
1136
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
750
1137
|
function trimOutput(value) {
|
|
@@ -1714,35 +2101,6 @@ function inspectCodexConfigToml(input) {
|
|
|
1714
2101
|
};
|
|
1715
2102
|
}
|
|
1716
2103
|
|
|
1717
|
-
// src/surfaces/names.ts
|
|
1718
|
-
var SURFACE_NAMES = [
|
|
1719
|
-
"claude",
|
|
1720
|
-
"cursor",
|
|
1721
|
-
"codex",
|
|
1722
|
-
"openclaw",
|
|
1723
|
-
"vscode",
|
|
1724
|
-
"windsurf",
|
|
1725
|
-
"zed",
|
|
1726
|
-
"chatgpt"
|
|
1727
|
-
];
|
|
1728
|
-
var AUTOMATED_SURFACE_NAMES = [
|
|
1729
|
-
"claude",
|
|
1730
|
-
"cursor",
|
|
1731
|
-
"codex",
|
|
1732
|
-
"openclaw",
|
|
1733
|
-
"vscode",
|
|
1734
|
-
"windsurf",
|
|
1735
|
-
"zed"
|
|
1736
|
-
];
|
|
1737
|
-
var MCP_SURFACE_NAMES = [
|
|
1738
|
-
"claude",
|
|
1739
|
-
"cursor",
|
|
1740
|
-
"codex",
|
|
1741
|
-
"vscode",
|
|
1742
|
-
"windsurf",
|
|
1743
|
-
"zed"
|
|
1744
|
-
];
|
|
1745
|
-
|
|
1746
2104
|
// src/surfaces/detection.ts
|
|
1747
2105
|
import { existsSync as existsSync2 } from "fs";
|
|
1748
2106
|
import { dirname as dirname2 } from "path";
|
|
@@ -1823,6 +2181,9 @@ function detectSurface(name, exists = existsSync2) {
|
|
|
1823
2181
|
}
|
|
1824
2182
|
|
|
1825
2183
|
// src/surfaces/registry.ts
|
|
2184
|
+
var AUTH_SETUP_HINT = "orgx-wizard auth login";
|
|
2185
|
+
var AUTH_SET_KEY_HINT = "orgx-wizard auth set-key";
|
|
2186
|
+
var DOCTOR_HINT = "orgx-wizard doctor";
|
|
1826
2187
|
function getSurfacePath(name) {
|
|
1827
2188
|
const detection = detectSurface(name);
|
|
1828
2189
|
const locator = getSurfaceLocator(name);
|
|
@@ -1951,27 +2312,92 @@ function automatedSurfaceStatus(name) {
|
|
|
1951
2312
|
summary: inspection.configured ? `OpenClaw plugin entry ${inspection.pluginKey ?? "missing"} is configured.` : inspection.installed ? "OpenClaw is installed but OrgX defaults have not been written." : detection.detected ? "OpenClaw support files exist but no OrgX plugin entry was found." : "OpenClaw is not installed or no support files were found."
|
|
1952
2313
|
};
|
|
1953
2314
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
2315
|
+
function formatAuthHintSource(authHint) {
|
|
2316
|
+
switch (authHint.source) {
|
|
2317
|
+
case "environment":
|
|
2318
|
+
return "env ORGX_API_KEY";
|
|
2319
|
+
case "wizard-store":
|
|
2320
|
+
return "wizard auth store";
|
|
2321
|
+
case "openclaw-config-file":
|
|
2322
|
+
return "openclaw config";
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
function buildChatgptManualStatus(detection, authHint) {
|
|
1956
2326
|
const path = detection.existingPath ?? detection.preferredPath;
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2327
|
+
const details = [...detection.evidence];
|
|
2328
|
+
if (authHint) {
|
|
2329
|
+
details.push(
|
|
2330
|
+
`OrgX auth ready via ${formatAuthHintSource(authHint)} (${authHint.keyPrefix})`
|
|
2331
|
+
);
|
|
2332
|
+
details.push(`Hosted connector URL: ${ORGX_HOSTED_MCP_URL}`);
|
|
2333
|
+
details.push(
|
|
2334
|
+
"Manual step: in ChatGPT desktop developer mode, add a custom connector pointing at the hosted OrgX MCP URL."
|
|
2335
|
+
);
|
|
2336
|
+
details.push(
|
|
2337
|
+
"After connecting ChatGPT, run `orgx-wizard doctor` to verify hosted MCP tool access."
|
|
2338
|
+
);
|
|
2339
|
+
} else {
|
|
2340
|
+
details.push(
|
|
2341
|
+
"OrgX auth is not configured yet. Run `orgx-wizard auth login` or `orgx-wizard auth set-key` first."
|
|
2342
|
+
);
|
|
2343
|
+
details.push(
|
|
2344
|
+
"After auth is configured, add a custom ChatGPT developer connector pointing at the hosted OrgX MCP URL."
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
let summary;
|
|
2348
|
+
if (!authHint) {
|
|
2349
|
+
summary = detection.detected ? "ChatGPT is installed, but OrgX auth is not configured yet." : "ChatGPT support files were not found; configure OrgX auth first, then add the hosted connector manually.";
|
|
2350
|
+
} else {
|
|
2351
|
+
summary = detection.detected ? "ChatGPT is installed; finish the manual developer connector setup." : "OrgX auth is ready, but ChatGPT desktop support files were not found on disk.";
|
|
2352
|
+
}
|
|
2353
|
+
return {
|
|
2354
|
+
name: "chatgpt",
|
|
1959
2355
|
mode: "manual",
|
|
1960
2356
|
detected: detection.detected,
|
|
1961
2357
|
configured: false,
|
|
1962
|
-
...path ? { path } : {}
|
|
2358
|
+
...path ? { path } : {},
|
|
2359
|
+
details,
|
|
2360
|
+
summary
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
function buildChatgptAddResult(path, authHint, toolCheck) {
|
|
2364
|
+
if (!authHint) {
|
|
2365
|
+
return {
|
|
2366
|
+
name: "chatgpt",
|
|
2367
|
+
changed: false,
|
|
2368
|
+
...path ? { path } : {},
|
|
2369
|
+
message: `Run \`${AUTH_SETUP_HINT}\` or \`${AUTH_SET_KEY_HINT}\` first, then add ${ORGX_HOSTED_MCP_URL} as a custom connector in ChatGPT developer mode.`
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
if (!toolCheck || !toolCheck.ok) {
|
|
2373
|
+
const failure = toolCheck?.error ?? "hosted MCP verification did not complete.";
|
|
2374
|
+
return {
|
|
2375
|
+
name: "chatgpt",
|
|
2376
|
+
changed: false,
|
|
2377
|
+
...path ? { path } : {},
|
|
2378
|
+
message: `OrgX auth is configured via ${formatAuthHintSource(authHint)}, but hosted MCP verification failed (${failure}). Fix that first, then add ${ORGX_HOSTED_MCP_URL} in ChatGPT developer mode.`
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
return {
|
|
2382
|
+
name: "chatgpt",
|
|
2383
|
+
changed: false,
|
|
2384
|
+
...path ? { path } : {},
|
|
2385
|
+
message: `OrgX auth and hosted MCP access are verified. Add ${ORGX_HOSTED_MCP_URL} as a custom connector in ChatGPT developer mode, then run \`${DOCTOR_HINT}\` to confirm \`${toolCheck.toolName}\`.`
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
function buildChatgptRemoveResult(path) {
|
|
2389
|
+
return {
|
|
2390
|
+
name: "chatgpt",
|
|
2391
|
+
changed: false,
|
|
2392
|
+
...path ? { path } : {},
|
|
2393
|
+
message: "Remove the custom OrgX connector from ChatGPT developer mode; the wizard does not manage ChatGPT config files directly."
|
|
1963
2394
|
};
|
|
2395
|
+
}
|
|
2396
|
+
function manualSurfaceStatus(name) {
|
|
2397
|
+
const detection = detectSurface(name);
|
|
1964
2398
|
switch (name) {
|
|
1965
2399
|
case "chatgpt":
|
|
1966
|
-
return
|
|
1967
|
-
...shared,
|
|
1968
|
-
details: [
|
|
1969
|
-
...detection.evidence,
|
|
1970
|
-
"ChatGPT desktop support is not automated yet.",
|
|
1971
|
-
"Manual follow-up: wire the OrgX surface once the app-side config contract is finalized."
|
|
1972
|
-
],
|
|
1973
|
-
summary: detection.detected ? "ChatGPT support files detected; manual OrgX wiring still required." : "ChatGPT desktop support files were not found on disk."
|
|
1974
|
-
};
|
|
2400
|
+
return buildChatgptManualStatus(detection, peekResolvedOrgxAuth());
|
|
1975
2401
|
}
|
|
1976
2402
|
}
|
|
1977
2403
|
function getSurfaceStatus(name) {
|
|
@@ -2089,11 +2515,19 @@ async function addAutomatedSurface(name) {
|
|
|
2089
2515
|
writeTextFile(path, next);
|
|
2090
2516
|
return { name, changed: true, path, message: "OrgX cloud MCP is connected in Zed." };
|
|
2091
2517
|
}
|
|
2518
|
+
case "chatgpt": {
|
|
2519
|
+
const authHint = peekResolvedOrgxAuth();
|
|
2520
|
+
if (!authHint) {
|
|
2521
|
+
return buildChatgptAddResult(path, null);
|
|
2522
|
+
}
|
|
2523
|
+
const toolCheck = await checkHostedMcpToolAccess();
|
|
2524
|
+
return buildChatgptAddResult(path, authHint, toolCheck);
|
|
2525
|
+
}
|
|
2092
2526
|
default:
|
|
2093
2527
|
return {
|
|
2094
2528
|
name,
|
|
2095
2529
|
changed: false,
|
|
2096
|
-
message: `${name} is not
|
|
2530
|
+
message: `${name} is not supported by the wizard.`
|
|
2097
2531
|
};
|
|
2098
2532
|
}
|
|
2099
2533
|
}
|
|
@@ -2184,11 +2618,13 @@ function removeAutomatedSurface(name) {
|
|
|
2184
2618
|
message: "OrgX connection was removed from Zed."
|
|
2185
2619
|
};
|
|
2186
2620
|
}
|
|
2621
|
+
case "chatgpt":
|
|
2622
|
+
return buildChatgptRemoveResult(path);
|
|
2187
2623
|
default:
|
|
2188
2624
|
return {
|
|
2189
2625
|
name,
|
|
2190
2626
|
changed: false,
|
|
2191
|
-
message: `${name} is not
|
|
2627
|
+
message: `${name} is not supported by the wizard.`
|
|
2192
2628
|
};
|
|
2193
2629
|
}
|
|
2194
2630
|
}
|
|
@@ -2305,112 +2741,6 @@ function assessDoctorReport(report) {
|
|
|
2305
2741
|
};
|
|
2306
2742
|
}
|
|
2307
2743
|
|
|
2308
|
-
// src/lib/wizard-state.ts
|
|
2309
|
-
import { randomUUID } from "crypto";
|
|
2310
|
-
function isNonEmptyString2(value) {
|
|
2311
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
2312
|
-
}
|
|
2313
|
-
function isSurfaceName(value) {
|
|
2314
|
-
return typeof value === "string" && SURFACE_NAMES.includes(value);
|
|
2315
|
-
}
|
|
2316
|
-
function parseContinuityDefaults(value) {
|
|
2317
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2318
|
-
return void 0;
|
|
2319
|
-
}
|
|
2320
|
-
const record = value;
|
|
2321
|
-
const parsed = {};
|
|
2322
|
-
if (record.executionMode === "cloud" || record.executionMode === "local") {
|
|
2323
|
-
parsed.executionMode = record.executionMode;
|
|
2324
|
-
}
|
|
2325
|
-
if (isSurfaceName(record.reviewSurface)) {
|
|
2326
|
-
parsed.reviewSurface = record.reviewSurface;
|
|
2327
|
-
}
|
|
2328
|
-
if (isNonEmptyString2(record.workspaceId)) {
|
|
2329
|
-
parsed.workspaceId = record.workspaceId.trim();
|
|
2330
|
-
}
|
|
2331
|
-
if (isNonEmptyString2(record.workspaceName)) {
|
|
2332
|
-
parsed.workspaceName = record.workspaceName.trim();
|
|
2333
|
-
}
|
|
2334
|
-
return Object.keys(parsed).length > 0 ? parsed : void 0;
|
|
2335
|
-
}
|
|
2336
|
-
function parseDemoInitiative(value) {
|
|
2337
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2338
|
-
return void 0;
|
|
2339
|
-
}
|
|
2340
|
-
const record = value;
|
|
2341
|
-
if (!isNonEmptyString2(record.id) || !isNonEmptyString2(record.title) || !isNonEmptyString2(record.liveUrl) || !isNonEmptyString2(record.createdAt)) {
|
|
2342
|
-
return void 0;
|
|
2343
|
-
}
|
|
2344
|
-
return {
|
|
2345
|
-
createdAt: record.createdAt.trim(),
|
|
2346
|
-
id: record.id.trim(),
|
|
2347
|
-
liveUrl: record.liveUrl.trim(),
|
|
2348
|
-
title: record.title.trim(),
|
|
2349
|
-
...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
|
|
2350
|
-
...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
|
-
function createWizardState(now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2354
|
-
return {
|
|
2355
|
-
installationId: `wizard-${randomUUID()}`,
|
|
2356
|
-
createdAt: now,
|
|
2357
|
-
updatedAt: now
|
|
2358
|
-
};
|
|
2359
|
-
}
|
|
2360
|
-
function sanitizeWizardStateRecord(record) {
|
|
2361
|
-
const continuity = parseContinuityDefaults(record.continuity);
|
|
2362
|
-
const demoInitiative = parseDemoInitiative(record.demoInitiative);
|
|
2363
|
-
return {
|
|
2364
|
-
installationId: record.installationId.trim(),
|
|
2365
|
-
createdAt: record.createdAt.trim(),
|
|
2366
|
-
updatedAt: record.updatedAt.trim(),
|
|
2367
|
-
...continuity ? { continuity } : {},
|
|
2368
|
-
...demoInitiative ? { demoInitiative } : {}
|
|
2369
|
-
};
|
|
2370
|
-
}
|
|
2371
|
-
function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
|
|
2372
|
-
const parsed = parseJsonObject(readTextIfExists(statePath));
|
|
2373
|
-
if (!isNonEmptyString2(parsed.installationId) || !isNonEmptyString2(parsed.createdAt) || !isNonEmptyString2(parsed.updatedAt)) {
|
|
2374
|
-
return null;
|
|
2375
|
-
}
|
|
2376
|
-
const state = {
|
|
2377
|
-
installationId: parsed.installationId.trim(),
|
|
2378
|
-
createdAt: parsed.createdAt.trim(),
|
|
2379
|
-
updatedAt: parsed.updatedAt.trim()
|
|
2380
|
-
};
|
|
2381
|
-
const continuity = parseContinuityDefaults(parsed.continuity);
|
|
2382
|
-
if (continuity !== void 0) state.continuity = continuity;
|
|
2383
|
-
const demoInitiative = parseDemoInitiative(parsed.demoInitiative);
|
|
2384
|
-
if (demoInitiative !== void 0) state.demoInitiative = demoInitiative;
|
|
2385
|
-
return state;
|
|
2386
|
-
}
|
|
2387
|
-
function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
|
|
2388
|
-
const record = sanitizeWizardStateRecord(value);
|
|
2389
|
-
writeJsonFile(statePath, record, { mode: 384 });
|
|
2390
|
-
return record;
|
|
2391
|
-
}
|
|
2392
|
-
function updateWizardState(updater, statePath = ORGX_WIZARD_STATE_PATH) {
|
|
2393
|
-
const current = readWizardState(statePath) ?? createWizardState();
|
|
2394
|
-
const next = updater(current);
|
|
2395
|
-
const sanitized = sanitizeWizardStateRecord({
|
|
2396
|
-
...next,
|
|
2397
|
-
installationId: next.installationId || current.installationId,
|
|
2398
|
-
createdAt: next.createdAt || current.createdAt,
|
|
2399
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2400
|
-
});
|
|
2401
|
-
writeJsonFile(statePath, sanitized, { mode: 384 });
|
|
2402
|
-
return sanitized;
|
|
2403
|
-
}
|
|
2404
|
-
function getOrCreateWizardInstallationId(statePath = ORGX_WIZARD_STATE_PATH) {
|
|
2405
|
-
const existing = readWizardState(statePath);
|
|
2406
|
-
if (existing) {
|
|
2407
|
-
return existing.installationId;
|
|
2408
|
-
}
|
|
2409
|
-
const record = createWizardState();
|
|
2410
|
-
writeWizardState(record, statePath);
|
|
2411
|
-
return record.installationId;
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
2744
|
// src/lib/continuity.ts
|
|
2415
2745
|
var REVIEW_SURFACE_ORDER = [
|
|
2416
2746
|
"claude",
|
|
@@ -2489,6 +2819,14 @@ function persistContinuityDefaults(seed = {}, statePath) {
|
|
|
2489
2819
|
// src/lib/initiatives.ts
|
|
2490
2820
|
var FOUNDER_DEMO_INITIATIVE_TITLE = "Founder Demo Initiative";
|
|
2491
2821
|
var FOUNDER_DEMO_INITIATIVE_SUMMARY = "Starter initiative created by @useorgx/wizard to validate workspace routing, live views, and the default OrgX skill pack.";
|
|
2822
|
+
var FOUNDER_DEMO_DECISION_TITLE = "Approve founder demo workspace";
|
|
2823
|
+
var FOUNDER_DEMO_DECISION_SUMMARY = "Initial decision created by @useorgx/wizard so the founder preset leaves the workspace with a resolved approval trail.";
|
|
2824
|
+
var FOUNDER_DEMO_DECISION_RESOLUTION = "Approved automatically by @useorgx/wizard after the founder preset finished creating the live demo workspace.";
|
|
2825
|
+
var FOUNDER_DEMO_ARTIFACT_NAME = "Founder Demo Live View";
|
|
2826
|
+
var FOUNDER_DEMO_ARTIFACT_TYPE = "document";
|
|
2827
|
+
var FOUNDER_DEMO_ARTIFACT_DESCRIPTION = "Live founder demo generated by @useorgx/wizard after workspace bootstrap completed.";
|
|
2828
|
+
var ONBOARDING_TASK_TITLE = "Complete OrgX onboarding";
|
|
2829
|
+
var ONBOARDING_TASK_SUMMARY = "Starter onboarding task created by @useorgx/wizard so the first workspace has a clear follow-up after setup.";
|
|
2492
2830
|
function parseResponseBody3(text2) {
|
|
2493
2831
|
if (!text2) {
|
|
2494
2832
|
return null;
|
|
@@ -2508,11 +2846,15 @@ function formatHttpError2(status, body) {
|
|
|
2508
2846
|
}
|
|
2509
2847
|
return `HTTP ${status}`;
|
|
2510
2848
|
}
|
|
2511
|
-
function
|
|
2849
|
+
function extractEntity(payload) {
|
|
2512
2850
|
const entity = isRecord(payload) && isRecord(payload.data) ? payload.data : payload;
|
|
2513
2851
|
if (!isRecord(entity)) {
|
|
2514
|
-
throw new Error("OrgX returned an unexpected
|
|
2852
|
+
throw new Error("OrgX returned an unexpected entity payload.");
|
|
2515
2853
|
}
|
|
2854
|
+
return entity;
|
|
2855
|
+
}
|
|
2856
|
+
function parseInitiative(payload) {
|
|
2857
|
+
const entity = extractEntity(payload);
|
|
2516
2858
|
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2517
2859
|
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2518
2860
|
if (!id || !title) {
|
|
@@ -2524,9 +2866,60 @@ function parseInitiative(payload) {
|
|
|
2524
2866
|
...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
|
|
2525
2867
|
};
|
|
2526
2868
|
}
|
|
2527
|
-
function
|
|
2869
|
+
function parseDecision(payload) {
|
|
2870
|
+
const entity = extractEntity(payload);
|
|
2871
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2872
|
+
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2873
|
+
if (!id || !title) {
|
|
2874
|
+
throw new Error("OrgX returned an incomplete decision payload.");
|
|
2875
|
+
}
|
|
2876
|
+
return {
|
|
2877
|
+
id,
|
|
2878
|
+
title,
|
|
2879
|
+
...typeof entity.status === "string" && entity.status.trim().length > 0 ? { status: entity.status.trim() } : {},
|
|
2880
|
+
...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
function parseArtifact(payload) {
|
|
2884
|
+
const entity = extractEntity(payload);
|
|
2885
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2886
|
+
const name = typeof entity.name === "string" ? entity.name.trim() : typeof entity.title === "string" ? entity.title.trim() : "";
|
|
2887
|
+
const type = typeof entity.artifact_type === "string" ? entity.artifact_type.trim() : typeof entity.type === "string" ? entity.type.trim() : "";
|
|
2888
|
+
const url = typeof entity.external_url === "string" ? entity.external_url.trim() : typeof entity.url === "string" ? entity.url.trim() : "";
|
|
2889
|
+
if (!id || !name) {
|
|
2890
|
+
throw new Error("OrgX returned an incomplete artifact payload.");
|
|
2891
|
+
}
|
|
2892
|
+
return {
|
|
2893
|
+
id,
|
|
2894
|
+
name,
|
|
2895
|
+
...type ? { type } : {},
|
|
2896
|
+
...url ? { url } : {}
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
function parseTask(payload) {
|
|
2900
|
+
const entity = extractEntity(payload);
|
|
2901
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2902
|
+
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2903
|
+
if (!id || !title) {
|
|
2904
|
+
throw new Error("OrgX returned an incomplete task payload.");
|
|
2905
|
+
}
|
|
2906
|
+
return {
|
|
2907
|
+
id,
|
|
2908
|
+
title,
|
|
2909
|
+
...typeof entity.status === "string" && entity.status.trim().length > 0 ? { status: entity.status.trim() } : {},
|
|
2910
|
+
...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
function toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace) {
|
|
2528
2914
|
return {
|
|
2529
2915
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2916
|
+
artifactId: artifact.id,
|
|
2917
|
+
artifactName: artifact.name,
|
|
2918
|
+
...artifact.type ? { artifactType: artifact.type } : {},
|
|
2919
|
+
...artifact.url ? { artifactUrl: artifact.url } : {},
|
|
2920
|
+
decisionId: decision.id,
|
|
2921
|
+
...decision.status ? { decisionStatus: decision.status } : {},
|
|
2922
|
+
decisionTitle: decision.title,
|
|
2530
2923
|
id: initiative.id,
|
|
2531
2924
|
liveUrl,
|
|
2532
2925
|
title: initiative.title,
|
|
@@ -2534,6 +2927,17 @@ function toDemoInitiativeRecord(initiative, liveUrl, workspace) {
|
|
|
2534
2927
|
workspaceName: workspace.name
|
|
2535
2928
|
};
|
|
2536
2929
|
}
|
|
2930
|
+
function toOnboardingTaskRecord(task, workspace, initiativeId) {
|
|
2931
|
+
return {
|
|
2932
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2933
|
+
id: task.id,
|
|
2934
|
+
...initiativeId ? { initiativeId } : {},
|
|
2935
|
+
...task.status ? { status: task.status } : {},
|
|
2936
|
+
title: task.title,
|
|
2937
|
+
workspaceId: workspace.id,
|
|
2938
|
+
workspaceName: workspace.name
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2537
2941
|
async function requireOrgxAuth2(options = {}) {
|
|
2538
2942
|
const auth = await resolveOrgxAuth(options);
|
|
2539
2943
|
if (!auth) {
|
|
@@ -2543,11 +2947,7 @@ async function requireOrgxAuth2(options = {}) {
|
|
|
2543
2947
|
}
|
|
2544
2948
|
return auth;
|
|
2545
2949
|
}
|
|
2546
|
-
async function
|
|
2547
|
-
const title = input.title.trim();
|
|
2548
|
-
if (!title) {
|
|
2549
|
-
throw new Error("Initiative title is required.");
|
|
2550
|
-
}
|
|
2950
|
+
async function createEntity(type, body, parse2, options = {}) {
|
|
2551
2951
|
const auth = await requireOrgxAuth2(options);
|
|
2552
2952
|
const response = await fetch(buildOrgxApiUrl("/entities", auth.baseUrl), {
|
|
2553
2953
|
method: "POST",
|
|
@@ -2556,56 +2956,183 @@ async function createInitiative(input, options = {}) {
|
|
|
2556
2956
|
"Content-Type": "application/json"
|
|
2557
2957
|
},
|
|
2558
2958
|
body: JSON.stringify({
|
|
2559
|
-
type
|
|
2560
|
-
|
|
2561
|
-
status: "active",
|
|
2562
|
-
...input.summary?.trim() ? { summary: input.summary.trim() } : {},
|
|
2563
|
-
...input.workspaceId?.trim() ? { workspace_id: input.workspaceId.trim() } : {}
|
|
2959
|
+
type,
|
|
2960
|
+
...body
|
|
2564
2961
|
}),
|
|
2565
2962
|
signal: AbortSignal.timeout(7e3)
|
|
2566
2963
|
});
|
|
2567
|
-
const
|
|
2964
|
+
const responseBody = parseResponseBody3(await response.text());
|
|
2568
2965
|
if (!response.ok) {
|
|
2569
|
-
throw new Error(`Failed to create
|
|
2966
|
+
throw new Error(`Failed to create ${type}. ${formatHttpError2(response.status, responseBody)}`);
|
|
2570
2967
|
}
|
|
2571
|
-
return
|
|
2968
|
+
return parse2(responseBody);
|
|
2572
2969
|
}
|
|
2573
|
-
async function
|
|
2574
|
-
const existingRecord = readWizardState(options.statePath)?.demoInitiative;
|
|
2575
|
-
if (existingRecord?.workspaceId === workspace.id) {
|
|
2576
|
-
return {
|
|
2577
|
-
created: false,
|
|
2578
|
-
initiative: {
|
|
2579
|
-
id: existingRecord.id,
|
|
2580
|
-
title: existingRecord.title
|
|
2581
|
-
},
|
|
2582
|
-
liveUrl: existingRecord.liveUrl
|
|
2583
|
-
};
|
|
2584
|
-
}
|
|
2970
|
+
async function updateEntity(type, id, body, parse2, options = {}) {
|
|
2585
2971
|
const auth = await requireOrgxAuth2(options);
|
|
2586
|
-
const
|
|
2972
|
+
const response = await fetch(buildOrgxApiUrl("/entities", auth.baseUrl), {
|
|
2973
|
+
method: "PATCH",
|
|
2974
|
+
headers: {
|
|
2975
|
+
Authorization: `Bearer ${auth.apiKey}`,
|
|
2976
|
+
"Content-Type": "application/json"
|
|
2977
|
+
},
|
|
2978
|
+
body: JSON.stringify({
|
|
2979
|
+
type,
|
|
2980
|
+
id,
|
|
2981
|
+
...body
|
|
2982
|
+
}),
|
|
2983
|
+
signal: AbortSignal.timeout(7e3)
|
|
2984
|
+
});
|
|
2985
|
+
const responseBody = parseResponseBody3(await response.text());
|
|
2986
|
+
if (!response.ok) {
|
|
2987
|
+
throw new Error(`Failed to update ${type}. ${formatHttpError2(response.status, responseBody)}`);
|
|
2988
|
+
}
|
|
2989
|
+
return parse2(responseBody);
|
|
2990
|
+
}
|
|
2991
|
+
function buildLiveUrl(baseUrl, initiativeId) {
|
|
2992
|
+
const parsed = new URL(normalizeOrgxBaseUrl(baseUrl));
|
|
2993
|
+
if (parsed.protocol === "https:" && parsed.hostname === "useorgx.com") {
|
|
2994
|
+
parsed.hostname = "www.useorgx.com";
|
|
2995
|
+
}
|
|
2996
|
+
parsed.pathname = `/live/${initiativeId}`;
|
|
2997
|
+
parsed.search = "";
|
|
2998
|
+
parsed.hash = "";
|
|
2999
|
+
return parsed.toString();
|
|
3000
|
+
}
|
|
3001
|
+
async function createFounderDemoDecision(initiative, workspaceId, options = {}) {
|
|
3002
|
+
return createEntity(
|
|
3003
|
+
"decision",
|
|
2587
3004
|
{
|
|
2588
|
-
|
|
2589
|
-
summary:
|
|
2590
|
-
|
|
3005
|
+
initiative_id: initiative.id,
|
|
3006
|
+
summary: FOUNDER_DEMO_DECISION_SUMMARY,
|
|
3007
|
+
title: FOUNDER_DEMO_DECISION_TITLE,
|
|
3008
|
+
workspace_id: workspaceId
|
|
2591
3009
|
},
|
|
3010
|
+
parseDecision,
|
|
2592
3011
|
options
|
|
2593
3012
|
);
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
3013
|
+
}
|
|
3014
|
+
async function approveFounderDemoDecision(decisionId, options = {}) {
|
|
3015
|
+
return updateEntity(
|
|
3016
|
+
"decision",
|
|
3017
|
+
decisionId,
|
|
3018
|
+
{
|
|
3019
|
+
resolution_summary: FOUNDER_DEMO_DECISION_RESOLUTION,
|
|
3020
|
+
status: "approved"
|
|
3021
|
+
},
|
|
3022
|
+
parseDecision,
|
|
3023
|
+
options
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
async function createFounderDemoArtifact(initiative, liveUrl, workspaceId, options = {}) {
|
|
3027
|
+
return createEntity(
|
|
3028
|
+
"artifact",
|
|
3029
|
+
{
|
|
3030
|
+
artifact_type: FOUNDER_DEMO_ARTIFACT_TYPE,
|
|
3031
|
+
description: FOUNDER_DEMO_ARTIFACT_DESCRIPTION,
|
|
3032
|
+
entity_id: initiative.id,
|
|
3033
|
+
entity_type: "initiative",
|
|
3034
|
+
external_url: liveUrl,
|
|
3035
|
+
initiative_id: initiative.id,
|
|
3036
|
+
name: FOUNDER_DEMO_ARTIFACT_NAME,
|
|
3037
|
+
workspace_id: workspaceId
|
|
3038
|
+
},
|
|
3039
|
+
parseArtifact,
|
|
3040
|
+
options
|
|
3041
|
+
);
|
|
3042
|
+
}
|
|
3043
|
+
async function createInitiative(input, options = {}) {
|
|
3044
|
+
const title = input.title.trim();
|
|
3045
|
+
if (!title) {
|
|
3046
|
+
throw new Error("Initiative title is required.");
|
|
3047
|
+
}
|
|
3048
|
+
return createEntity(
|
|
3049
|
+
"initiative",
|
|
3050
|
+
{
|
|
3051
|
+
title,
|
|
3052
|
+
status: "active",
|
|
3053
|
+
...input.summary?.trim() ? { summary: input.summary.trim() } : {},
|
|
3054
|
+
...input.workspaceId?.trim() ? { workspace_id: input.workspaceId.trim() } : {}
|
|
3055
|
+
},
|
|
3056
|
+
parseInitiative,
|
|
3057
|
+
options
|
|
3058
|
+
);
|
|
3059
|
+
}
|
|
3060
|
+
async function ensureFounderDemoInitiative(workspace, options = {}) {
|
|
3061
|
+
const existingRecord = readWizardState(options.statePath)?.demoInitiative;
|
|
3062
|
+
const matchingRecord = existingRecord?.workspaceId === workspace.id ? existingRecord : void 0;
|
|
3063
|
+
const auth = await requireOrgxAuth2(options);
|
|
3064
|
+
const initiative = matchingRecord ? {
|
|
3065
|
+
id: matchingRecord.id,
|
|
3066
|
+
title: matchingRecord.title
|
|
3067
|
+
} : await createInitiative(
|
|
3068
|
+
{
|
|
3069
|
+
title: FOUNDER_DEMO_INITIATIVE_TITLE,
|
|
3070
|
+
summary: FOUNDER_DEMO_INITIATIVE_SUMMARY,
|
|
3071
|
+
workspaceId: workspace.id
|
|
3072
|
+
},
|
|
3073
|
+
options
|
|
3074
|
+
);
|
|
3075
|
+
const liveUrl = matchingRecord?.liveUrl || buildLiveUrl(auth.baseUrl, initiative.id);
|
|
3076
|
+
const decision = matchingRecord?.decisionId ? matchingRecord.decisionStatus === "approved" && matchingRecord.decisionTitle ? {
|
|
3077
|
+
id: matchingRecord.decisionId,
|
|
3078
|
+
status: matchingRecord.decisionStatus,
|
|
3079
|
+
title: matchingRecord.decisionTitle
|
|
3080
|
+
} : await approveFounderDemoDecision(matchingRecord.decisionId, options) : await approveFounderDemoDecision(
|
|
3081
|
+
(await createFounderDemoDecision(initiative, workspace.id, options)).id,
|
|
3082
|
+
options
|
|
3083
|
+
);
|
|
3084
|
+
const artifact = matchingRecord?.artifactId && matchingRecord.artifactName ? {
|
|
3085
|
+
id: matchingRecord.artifactId,
|
|
3086
|
+
name: matchingRecord.artifactName,
|
|
3087
|
+
...matchingRecord.artifactType ? { type: matchingRecord.artifactType } : {},
|
|
3088
|
+
...matchingRecord.artifactUrl ? { url: matchingRecord.artifactUrl } : {}
|
|
3089
|
+
} : await createFounderDemoArtifact(initiative, liveUrl, workspace.id, options);
|
|
3090
|
+
const record = toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace);
|
|
3091
|
+
updateWizardState(
|
|
3092
|
+
(current) => ({
|
|
3093
|
+
...current,
|
|
3094
|
+
demoInitiative: record
|
|
3095
|
+
}),
|
|
3096
|
+
options.statePath
|
|
2602
3097
|
);
|
|
2603
3098
|
return {
|
|
2604
|
-
|
|
3099
|
+
artifact,
|
|
3100
|
+
created: !matchingRecord,
|
|
3101
|
+
decision,
|
|
2605
3102
|
initiative,
|
|
2606
3103
|
liveUrl
|
|
2607
3104
|
};
|
|
2608
3105
|
}
|
|
3106
|
+
async function ensureOnboardingTask(workspace, options = {}) {
|
|
3107
|
+
const existingRecord = readWizardState(options.statePath)?.onboardingTask;
|
|
3108
|
+
if (existingRecord?.workspaceId === workspace.id) {
|
|
3109
|
+
return {
|
|
3110
|
+
id: existingRecord.id,
|
|
3111
|
+
title: existingRecord.title,
|
|
3112
|
+
...existingRecord.status ? { status: existingRecord.status } : {}
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
const task = await createEntity(
|
|
3116
|
+
"task",
|
|
3117
|
+
{
|
|
3118
|
+
status: "todo",
|
|
3119
|
+
summary: ONBOARDING_TASK_SUMMARY,
|
|
3120
|
+
title: ONBOARDING_TASK_TITLE,
|
|
3121
|
+
workspace_id: workspace.id,
|
|
3122
|
+
...options.initiativeId ? { initiative_id: options.initiativeId } : {}
|
|
3123
|
+
},
|
|
3124
|
+
parseTask,
|
|
3125
|
+
options
|
|
3126
|
+
);
|
|
3127
|
+
updateWizardState(
|
|
3128
|
+
(current) => ({
|
|
3129
|
+
...current,
|
|
3130
|
+
onboardingTask: toOnboardingTaskRecord(task, workspace, options.initiativeId)
|
|
3131
|
+
}),
|
|
3132
|
+
options.statePath
|
|
3133
|
+
);
|
|
3134
|
+
return task;
|
|
3135
|
+
}
|
|
2609
3136
|
|
|
2610
3137
|
// src/lib/skills.ts
|
|
2611
3138
|
import { join as join2 } from "path";
|
|
@@ -2615,6 +3142,7 @@ var DEFAULT_ORGX_SKILL_PACKS = [
|
|
|
2615
3142
|
"bulk-create",
|
|
2616
3143
|
"nightly-recap"
|
|
2617
3144
|
];
|
|
3145
|
+
var PLUGIN_MANAGED_SKILL_TARGETS = ["claude", "codex"];
|
|
2618
3146
|
var EXCLUDED_PACK_DIRS = /* @__PURE__ */ new Set([".github", "scripts"]);
|
|
2619
3147
|
var ORGX_SKILLS_OWNER = "useorgx";
|
|
2620
3148
|
var ORGX_SKILLS_REPO = "skills";
|
|
@@ -2627,6 +3155,8 @@ var CURSOR_RULES_CONTENT = `# OrgX Rules
|
|
|
2627
3155
|
- Preserve \`_context\` when widget or app-rendering flows depend on client, conversation, session, or user metadata. Do not strip or rename it.
|
|
2628
3156
|
- Prefer \`list_entities\` for pending approvals and state inspection before reaching for legacy aliases.
|
|
2629
3157
|
- Keep hosted OrgX MCP calls distinct from local OpenClaw/plugin surfaces before mutating config or entity state.
|
|
3158
|
+
- Artifact proof must use durable sources: GitHub PR/commit/blob permalinks, published/public URLs, or absolute file paths / \`file://...\`.
|
|
3159
|
+
- Never use OrgX wrapper pages like \`/live/...\`, \`/artifacts/...\`, or \`/console/...\` as proof links. \`preview_markdown\` is supplemental, not proof.
|
|
2630
3160
|
`;
|
|
2631
3161
|
var CLAUDE_ORGX_SKILL_CONTENT = `---
|
|
2632
3162
|
name: orgx
|
|
@@ -2652,6 +3182,8 @@ This skill is for the hosted OrgX tool surface, not the local OpenClaw plugin. K
|
|
|
2652
3182
|
- Treat \`scaffold_initiative continue_on_error\` as best-effort scaffolding. Use it only when partial creation is acceptable and inspect failures afterward.
|
|
2653
3183
|
- Preserve \`_context\` when widget or app rendering depends on client, conversation, session, or user metadata.
|
|
2654
3184
|
- Prefer \`list_entities\` over older aliases when you need pending decisions, blockers, or entity state.
|
|
3185
|
+
- Artifact proof must use durable sources: GitHub PR/commit/blob permalinks, published/public URLs, or absolute file paths / \`file://...\`.
|
|
3186
|
+
- Never use OrgX wrapper pages like \`/live/...\`, \`/artifacts/...\`, or \`/console/...\` as proof links. \`preview_markdown\` is supporting context only.
|
|
2655
3187
|
|
|
2656
3188
|
## Suggested Skill Packs
|
|
2657
3189
|
|
|
@@ -2742,85 +3274,972 @@ function writeManagedFile(path, content, label, sourceUrl) {
|
|
|
2742
3274
|
writeTextFile(path, content);
|
|
2743
3275
|
}
|
|
2744
3276
|
return {
|
|
2745
|
-
label,
|
|
2746
|
-
path,
|
|
2747
|
-
changed,
|
|
2748
|
-
...sourceUrl ? { sourceUrl } : {}
|
|
3277
|
+
label,
|
|
3278
|
+
path,
|
|
3279
|
+
changed,
|
|
3280
|
+
...sourceUrl ? { sourceUrl } : {}
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
function resolveSkillPackNames(requested = []) {
|
|
3284
|
+
if (requested.length === 0 || requested.includes("all")) {
|
|
3285
|
+
return [...DEFAULT_ORGX_SKILL_PACKS];
|
|
3286
|
+
}
|
|
3287
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3288
|
+
const normalized = [];
|
|
3289
|
+
for (const name of requested) {
|
|
3290
|
+
const next = name.trim();
|
|
3291
|
+
if (!next || seen.has(next)) {
|
|
3292
|
+
continue;
|
|
3293
|
+
}
|
|
3294
|
+
seen.add(next);
|
|
3295
|
+
normalized.push(next);
|
|
3296
|
+
}
|
|
3297
|
+
return normalized;
|
|
3298
|
+
}
|
|
3299
|
+
function resolvePluginManagedSkillTargets(requested = []) {
|
|
3300
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3301
|
+
const targets = [];
|
|
3302
|
+
for (const rawTarget of requested) {
|
|
3303
|
+
const target = rawTarget.trim().toLowerCase();
|
|
3304
|
+
if (!PLUGIN_MANAGED_SKILL_TARGETS.includes(target)) {
|
|
3305
|
+
continue;
|
|
3306
|
+
}
|
|
3307
|
+
if (seen.has(target)) {
|
|
3308
|
+
continue;
|
|
3309
|
+
}
|
|
3310
|
+
seen.add(target);
|
|
3311
|
+
targets.push(target);
|
|
3312
|
+
}
|
|
3313
|
+
return targets;
|
|
3314
|
+
}
|
|
3315
|
+
function planOrgxSkillsInstall(pluginTargets = []) {
|
|
3316
|
+
const pluginManagedTargets = resolvePluginManagedSkillTargets(pluginTargets);
|
|
3317
|
+
const managesClaudeSkills = pluginManagedTargets.includes("claude");
|
|
3318
|
+
const notes = [];
|
|
3319
|
+
if (managesClaudeSkills) {
|
|
3320
|
+
notes.push(
|
|
3321
|
+
"Claude Code plugin is installed, so the wizard skipped overlapping standalone Claude OrgX skill files and Claude skill-pack sync."
|
|
3322
|
+
);
|
|
3323
|
+
}
|
|
3324
|
+
if (pluginManagedTargets.includes("codex")) {
|
|
3325
|
+
notes.push(
|
|
3326
|
+
"Codex companion plugin bundles its own OrgX skills, so `wizard skills add` only manages standalone editor assets outside Codex."
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
return {
|
|
3330
|
+
installClaudeSkillBootstrap: !managesClaudeSkills,
|
|
3331
|
+
installClaudeSkillPacks: !managesClaudeSkills,
|
|
3332
|
+
notes,
|
|
3333
|
+
pluginManagedTargets
|
|
3334
|
+
};
|
|
3335
|
+
}
|
|
3336
|
+
async function fetchAvailablePackNames(fetchImpl, ref) {
|
|
3337
|
+
const entries = await fetchDirectoryEntries("", fetchImpl, ref);
|
|
3338
|
+
return entries.filter((e) => e.type === "dir" && !EXCLUDED_PACK_DIRS.has(e.name)).map((e) => e.name);
|
|
3339
|
+
}
|
|
3340
|
+
async function installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref) {
|
|
3341
|
+
const rootPath = skillName;
|
|
3342
|
+
const files = await listRemoteSkillFiles(rootPath, fetchImpl, ref);
|
|
3343
|
+
const writes = [];
|
|
3344
|
+
for (const file of files) {
|
|
3345
|
+
const content = await fetchRemoteText(file.sourceUrl, fetchImpl);
|
|
3346
|
+
const relativePath = file.path.slice(`${rootPath}/`.length);
|
|
3347
|
+
writes.push(
|
|
3348
|
+
writeManagedFile(
|
|
3349
|
+
join2(claudeSkillsDir, skillName, relativePath),
|
|
3350
|
+
content,
|
|
3351
|
+
`${skillName}/${relativePath}`,
|
|
3352
|
+
file.sourceUrl
|
|
3353
|
+
)
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
return {
|
|
3357
|
+
name: skillName,
|
|
3358
|
+
files: writes
|
|
3359
|
+
};
|
|
3360
|
+
}
|
|
3361
|
+
async function installOrgxSkills(options = {}) {
|
|
3362
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
3363
|
+
if (!fetchImpl) {
|
|
3364
|
+
throw new Error("Global fetch is unavailable in this runtime.");
|
|
3365
|
+
}
|
|
3366
|
+
const ref = options.ref ?? ORGX_SKILLS_REF;
|
|
3367
|
+
const claudeSkillsDir = options.claudeSkillsDir ?? CLAUDE_SKILLS_DIR;
|
|
3368
|
+
const claudeOrgxSkillPath = options.claudeOrgxSkillPath ?? CLAUDE_ORGX_SKILL_PATH;
|
|
3369
|
+
const cursorRulePath = options.cursorRulePath ?? CURSOR_ORGX_RULE_PATH;
|
|
3370
|
+
const plan = planOrgxSkillsInstall(options.pluginTargets ?? []);
|
|
3371
|
+
const requestedNames = options.skillNames ?? [];
|
|
3372
|
+
let skillNames;
|
|
3373
|
+
if (requestedNames.length === 0 || requestedNames.includes("all")) {
|
|
3374
|
+
try {
|
|
3375
|
+
skillNames = await fetchAvailablePackNames(fetchImpl, ref);
|
|
3376
|
+
} catch {
|
|
3377
|
+
skillNames = [...DEFAULT_ORGX_SKILL_PACKS];
|
|
3378
|
+
}
|
|
3379
|
+
} else {
|
|
3380
|
+
skillNames = resolveSkillPackNames(requestedNames);
|
|
3381
|
+
}
|
|
3382
|
+
const writes = [
|
|
3383
|
+
writeManagedFile(cursorRulePath, CURSOR_RULES_CONTENT, "cursor-rules")
|
|
3384
|
+
];
|
|
3385
|
+
if (plan.installClaudeSkillBootstrap) {
|
|
3386
|
+
writes.push(
|
|
3387
|
+
writeManagedFile(claudeOrgxSkillPath, CLAUDE_ORGX_SKILL_CONTENT, "claude-orgx-skill")
|
|
3388
|
+
);
|
|
3389
|
+
}
|
|
3390
|
+
const packs = [];
|
|
3391
|
+
if (plan.installClaudeSkillPacks) {
|
|
3392
|
+
for (const skillName of skillNames) {
|
|
3393
|
+
packs.push(await installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref));
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
return {
|
|
3397
|
+
notes: plan.notes,
|
|
3398
|
+
writes,
|
|
3399
|
+
packs
|
|
3400
|
+
};
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
// src/lib/plugins.ts
|
|
3404
|
+
import { spawn } from "child_process";
|
|
3405
|
+
import {
|
|
3406
|
+
existsSync as existsSync3,
|
|
3407
|
+
mkdirSync as mkdirSync2,
|
|
3408
|
+
mkdtempSync,
|
|
3409
|
+
readFileSync as readFileSync2,
|
|
3410
|
+
readdirSync,
|
|
3411
|
+
rmSync,
|
|
3412
|
+
statSync as statSync2,
|
|
3413
|
+
writeFileSync as writeFileSync2
|
|
3414
|
+
} from "fs";
|
|
3415
|
+
import { tmpdir } from "os";
|
|
3416
|
+
import { dirname as dirname3, join as join3, relative } from "path";
|
|
3417
|
+
var DEFAULT_ORGX_PLUGIN_TARGETS = ["claude", "codex", "openclaw"];
|
|
3418
|
+
var ORGX_PLUGIN_GITHUB_OWNER = "useorgx";
|
|
3419
|
+
var ORGX_PLUGIN_GITHUB_REF = "main";
|
|
3420
|
+
var ORGX_CLAUDE_PLUGIN_NAME = "orgx-claude-code-plugin";
|
|
3421
|
+
var ORGX_CLAUDE_MARKETPLACE_NAME = "orgx-local";
|
|
3422
|
+
var ORGX_CODEX_PLUGIN_NAME = "orgx-codex-plugin";
|
|
3423
|
+
var ORGX_OPENCLAW_PLUGIN_ID = "orgx";
|
|
3424
|
+
var ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME = "@useorgx/openclaw-plugin";
|
|
3425
|
+
var CLAUDE_PLUGIN_SYNC_SPEC = {
|
|
3426
|
+
owner: ORGX_PLUGIN_GITHUB_OWNER,
|
|
3427
|
+
repo: ORGX_CLAUDE_PLUGIN_NAME,
|
|
3428
|
+
ref: ORGX_PLUGIN_GITHUB_REF,
|
|
3429
|
+
include: [
|
|
3430
|
+
{ localPath: ".claude-plugin", remotePath: ".claude-plugin" },
|
|
3431
|
+
{ localPath: "agents", remotePath: "agents" },
|
|
3432
|
+
{ localPath: "commands", remotePath: "commands" },
|
|
3433
|
+
{ localPath: "hooks", remotePath: "hooks" },
|
|
3434
|
+
{ localPath: "lib", remotePath: "lib" },
|
|
3435
|
+
{ localPath: "scripts", remotePath: "scripts" },
|
|
3436
|
+
{ localPath: "skills", remotePath: "skills" }
|
|
3437
|
+
]
|
|
3438
|
+
};
|
|
3439
|
+
var CODEX_PLUGIN_SYNC_SPEC = {
|
|
3440
|
+
owner: ORGX_PLUGIN_GITHUB_OWNER,
|
|
3441
|
+
repo: ORGX_CODEX_PLUGIN_NAME,
|
|
3442
|
+
ref: ORGX_PLUGIN_GITHUB_REF,
|
|
3443
|
+
include: [
|
|
3444
|
+
{ localPath: ".codex-plugin", remotePath: ".codex-plugin" },
|
|
3445
|
+
{ localPath: ".mcp.json", remotePath: ".mcp.json" },
|
|
3446
|
+
{ localPath: "assets", remotePath: "assets" },
|
|
3447
|
+
{ localPath: "skills", remotePath: "skills" }
|
|
3448
|
+
]
|
|
3449
|
+
};
|
|
3450
|
+
function defaultPluginPaths() {
|
|
3451
|
+
return {
|
|
3452
|
+
claudeMarketplaceDir: CLAUDE_MANAGED_MARKETPLACE_DIR,
|
|
3453
|
+
claudeMarketplaceManifestPath: CLAUDE_MANAGED_MARKETPLACE_MANIFEST_PATH,
|
|
3454
|
+
claudePluginDir: CLAUDE_MANAGED_PLUGIN_DIR,
|
|
3455
|
+
codexMarketplacePath: CODEX_MARKETPLACE_PATH,
|
|
3456
|
+
codexPluginDir: CODEX_ORGX_PLUGIN_DIR
|
|
3457
|
+
};
|
|
3458
|
+
}
|
|
3459
|
+
function resolvePluginPaths(paths = {}) {
|
|
3460
|
+
return {
|
|
3461
|
+
...defaultPluginPaths(),
|
|
3462
|
+
...paths
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
function isRepoDirectoryEntry(value) {
|
|
3466
|
+
if (!value || typeof value !== "object") return false;
|
|
3467
|
+
const entry = value;
|
|
3468
|
+
const hasType = entry.type === "file" || entry.type === "dir";
|
|
3469
|
+
return typeof entry.name === "string" && typeof entry.path === "string" && hasType;
|
|
3470
|
+
}
|
|
3471
|
+
function encodeRepoPath2(value) {
|
|
3472
|
+
return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
|
|
3473
|
+
}
|
|
3474
|
+
function isLikelyRepoFilePath(path) {
|
|
3475
|
+
const basename = path.split("/").pop() ?? path;
|
|
3476
|
+
return basename.includes(".") && !/^\.[^./]+$/.test(basename);
|
|
3477
|
+
}
|
|
3478
|
+
function buildContentsUrl2(spec, path) {
|
|
3479
|
+
const encodedPath = encodeRepoPath2(path);
|
|
3480
|
+
return `https://api.github.com/repos/${spec.owner}/${spec.repo}/contents/${encodedPath}?ref=${encodeURIComponent(spec.ref)}`;
|
|
3481
|
+
}
|
|
3482
|
+
function resolveGitHubError2(spec, status, path) {
|
|
3483
|
+
const repoName = `${spec.owner}/${spec.repo}`;
|
|
3484
|
+
if (status === 404) {
|
|
3485
|
+
return `Could not find '${path}' in ${repoName}.`;
|
|
3486
|
+
}
|
|
3487
|
+
return `GitHub returned HTTP ${status} while loading '${path}' from ${repoName}.`;
|
|
3488
|
+
}
|
|
3489
|
+
async function fetchDirectoryEntries2(spec, path, fetchImpl) {
|
|
3490
|
+
const response = await fetchImpl(buildContentsUrl2(spec, path), {
|
|
3491
|
+
headers: {
|
|
3492
|
+
Accept: "application/vnd.github+json"
|
|
3493
|
+
},
|
|
3494
|
+
signal: AbortSignal.timeout(1e4)
|
|
3495
|
+
});
|
|
3496
|
+
if (!response.ok) {
|
|
3497
|
+
throw new Error(resolveGitHubError2(spec, response.status, path));
|
|
3498
|
+
}
|
|
3499
|
+
const data = await response.json();
|
|
3500
|
+
if (!Array.isArray(data)) {
|
|
3501
|
+
throw new Error(`Expected '${path}' to be a directory in ${spec.owner}/${spec.repo}.`);
|
|
3502
|
+
}
|
|
3503
|
+
return data.filter(isRepoDirectoryEntry).sort((left, right) => left.path.localeCompare(right.path));
|
|
3504
|
+
}
|
|
3505
|
+
async function fetchRepoFile(spec, path, fetchImpl) {
|
|
3506
|
+
const response = await fetchImpl(buildContentsUrl2(spec, path), {
|
|
3507
|
+
headers: {
|
|
3508
|
+
Accept: "application/vnd.github+json"
|
|
3509
|
+
},
|
|
3510
|
+
signal: AbortSignal.timeout(1e4)
|
|
3511
|
+
});
|
|
3512
|
+
if (!response.ok) {
|
|
3513
|
+
throw new Error(resolveGitHubError2(spec, response.status, path));
|
|
3514
|
+
}
|
|
3515
|
+
const data = await response.json();
|
|
3516
|
+
if (!isRepoDirectoryEntry(data) || data.type !== "file" || typeof data.download_url !== "string") {
|
|
3517
|
+
throw new Error(`Expected '${path}' to be a file in ${spec.owner}/${spec.repo}.`);
|
|
3518
|
+
}
|
|
3519
|
+
return data;
|
|
3520
|
+
}
|
|
3521
|
+
async function listRemoteRepoFiles(spec, path, localPath, fetchImpl) {
|
|
3522
|
+
const files = [];
|
|
3523
|
+
if (isLikelyRepoFilePath(path) && !path.endsWith("/")) {
|
|
3524
|
+
const file = await fetchRepoFile(spec, path, fetchImpl);
|
|
3525
|
+
files.push({
|
|
3526
|
+
localPath,
|
|
3527
|
+
path: file.path,
|
|
3528
|
+
sourceUrl: file.download_url ?? ""
|
|
3529
|
+
});
|
|
3530
|
+
return files;
|
|
3531
|
+
}
|
|
3532
|
+
const entries = await fetchDirectoryEntries2(spec, path, fetchImpl);
|
|
3533
|
+
for (const entry of entries) {
|
|
3534
|
+
if (entry.type === "dir") {
|
|
3535
|
+
files.push(
|
|
3536
|
+
...await listRemoteRepoFiles(
|
|
3537
|
+
spec,
|
|
3538
|
+
entry.path,
|
|
3539
|
+
join3(localPath, entry.name),
|
|
3540
|
+
fetchImpl
|
|
3541
|
+
)
|
|
3542
|
+
);
|
|
3543
|
+
continue;
|
|
3544
|
+
}
|
|
3545
|
+
if (!entry.download_url) {
|
|
3546
|
+
throw new Error(`GitHub did not provide a download URL for '${entry.path}'.`);
|
|
3547
|
+
}
|
|
3548
|
+
files.push({
|
|
3549
|
+
localPath: join3(localPath, entry.name),
|
|
3550
|
+
path: entry.path,
|
|
3551
|
+
sourceUrl: entry.download_url
|
|
3552
|
+
});
|
|
3553
|
+
}
|
|
3554
|
+
return files;
|
|
3555
|
+
}
|
|
3556
|
+
async function collectRemoteRepoFiles(spec, fetchImpl) {
|
|
3557
|
+
const files = [];
|
|
3558
|
+
for (const entry of spec.include) {
|
|
3559
|
+
const isFile = isLikelyRepoFilePath(entry.remotePath) && !entry.remotePath.endsWith("/");
|
|
3560
|
+
if (isFile) {
|
|
3561
|
+
const file = await fetchRepoFile(spec, entry.remotePath, fetchImpl);
|
|
3562
|
+
files.push({
|
|
3563
|
+
localPath: entry.localPath,
|
|
3564
|
+
path: file.path,
|
|
3565
|
+
sourceUrl: file.download_url ?? ""
|
|
3566
|
+
});
|
|
3567
|
+
continue;
|
|
3568
|
+
}
|
|
3569
|
+
files.push(...await listRemoteRepoFiles(spec, entry.remotePath, entry.localPath, fetchImpl));
|
|
3570
|
+
}
|
|
3571
|
+
return files.sort((left, right) => left.localPath.localeCompare(right.localPath));
|
|
3572
|
+
}
|
|
3573
|
+
async function fetchRemoteBytes(sourceUrl, fetchImpl) {
|
|
3574
|
+
const response = await fetchImpl(sourceUrl, {
|
|
3575
|
+
headers: {
|
|
3576
|
+
Accept: "application/octet-stream"
|
|
3577
|
+
},
|
|
3578
|
+
signal: AbortSignal.timeout(1e4)
|
|
3579
|
+
});
|
|
3580
|
+
if (!response.ok) {
|
|
3581
|
+
throw new Error(`GitHub returned HTTP ${response.status} while downloading ${sourceUrl}.`);
|
|
3582
|
+
}
|
|
3583
|
+
return Buffer.from(await response.arrayBuffer());
|
|
3584
|
+
}
|
|
3585
|
+
function readBytesIfExists(path) {
|
|
3586
|
+
if (!existsSync3(path)) return null;
|
|
3587
|
+
try {
|
|
3588
|
+
if (!statSync2(path).isFile()) {
|
|
3589
|
+
return null;
|
|
3590
|
+
}
|
|
3591
|
+
return readFileSync2(path);
|
|
3592
|
+
} catch (error) {
|
|
3593
|
+
const code = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
|
|
3594
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "EISDIR") {
|
|
3595
|
+
return null;
|
|
3596
|
+
}
|
|
3597
|
+
throw error;
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
function writeBytesIfChanged(path, bytes) {
|
|
3601
|
+
const existing = readBytesIfExists(path);
|
|
3602
|
+
if (existing && Buffer.compare(existing, bytes) === 0) {
|
|
3603
|
+
return false;
|
|
3604
|
+
}
|
|
3605
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
3606
|
+
writeFileSync2(path, bytes);
|
|
3607
|
+
return true;
|
|
3608
|
+
}
|
|
3609
|
+
function removePathIfExists(path) {
|
|
3610
|
+
if (!existsSync3(path)) return false;
|
|
3611
|
+
rmSync(path, { force: true, recursive: true });
|
|
3612
|
+
return true;
|
|
3613
|
+
}
|
|
3614
|
+
function listRelativeFiles(root, base = root) {
|
|
3615
|
+
if (!existsSync3(root)) return [];
|
|
3616
|
+
if (!statSync2(root).isDirectory()) {
|
|
3617
|
+
return [];
|
|
3618
|
+
}
|
|
3619
|
+
const files = [];
|
|
3620
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
3621
|
+
const nextPath = join3(root, entry.name);
|
|
3622
|
+
if (entry.isDirectory()) {
|
|
3623
|
+
files.push(...listRelativeFiles(nextPath, base));
|
|
3624
|
+
continue;
|
|
3625
|
+
}
|
|
3626
|
+
if (entry.isFile()) {
|
|
3627
|
+
files.push(relative(base, nextPath));
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
return files.sort();
|
|
3631
|
+
}
|
|
3632
|
+
function pruneEmptyDirectories(root, current = root) {
|
|
3633
|
+
if (!existsSync3(current) || !statSync2(current).isDirectory()) {
|
|
3634
|
+
return false;
|
|
3635
|
+
}
|
|
3636
|
+
let changed = false;
|
|
3637
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
3638
|
+
if (!entry.isDirectory()) continue;
|
|
3639
|
+
changed = pruneEmptyDirectories(root, join3(current, entry.name)) || changed;
|
|
3640
|
+
}
|
|
3641
|
+
if (current !== root && readdirSync(current).length === 0) {
|
|
3642
|
+
rmSync(current, { force: true, recursive: true });
|
|
3643
|
+
return true;
|
|
3644
|
+
}
|
|
3645
|
+
return changed;
|
|
3646
|
+
}
|
|
3647
|
+
async function syncManagedRepoTree(spec, destinationRoot, fetchImpl) {
|
|
3648
|
+
const remoteFiles = await collectRemoteRepoFiles(spec, fetchImpl);
|
|
3649
|
+
let changed = false;
|
|
3650
|
+
const expected = new Set(remoteFiles.map((file) => file.localPath));
|
|
3651
|
+
if (existsSync3(destinationRoot) && !statSync2(destinationRoot).isDirectory()) {
|
|
3652
|
+
rmSync(destinationRoot, { force: true, recursive: true });
|
|
3653
|
+
changed = true;
|
|
3654
|
+
}
|
|
3655
|
+
for (const file of listRelativeFiles(destinationRoot)) {
|
|
3656
|
+
if (expected.has(file)) continue;
|
|
3657
|
+
rmSync(join3(destinationRoot, file), { force: true });
|
|
3658
|
+
changed = true;
|
|
3659
|
+
}
|
|
3660
|
+
changed = pruneEmptyDirectories(destinationRoot) || changed;
|
|
3661
|
+
for (const file of remoteFiles) {
|
|
3662
|
+
const bytes = await fetchRemoteBytes(file.sourceUrl, fetchImpl);
|
|
3663
|
+
if (writeBytesIfChanged(join3(destinationRoot, file.localPath), bytes)) {
|
|
3664
|
+
changed = true;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
return { changed, fileCount: remoteFiles.length };
|
|
3668
|
+
}
|
|
3669
|
+
function serializeJson(value) {
|
|
3670
|
+
return `${JSON.stringify(value, null, 2)}
|
|
3671
|
+
`;
|
|
3672
|
+
}
|
|
3673
|
+
function writeJsonIfChanged(path, value) {
|
|
3674
|
+
const next = serializeJson(value);
|
|
3675
|
+
const existing = readTextIfExists(path);
|
|
3676
|
+
if (existing === next) {
|
|
3677
|
+
return false;
|
|
3678
|
+
}
|
|
3679
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
3680
|
+
writeFileSync2(path, next, "utf8");
|
|
3681
|
+
return true;
|
|
3682
|
+
}
|
|
3683
|
+
function buildClaudeMarketplaceManifest() {
|
|
3684
|
+
return {
|
|
3685
|
+
$schema: "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
3686
|
+
name: ORGX_CLAUDE_MARKETPLACE_NAME,
|
|
3687
|
+
description: "Local OrgX plugins",
|
|
3688
|
+
owner: {
|
|
3689
|
+
name: "OrgX Team"
|
|
3690
|
+
},
|
|
3691
|
+
plugins: [
|
|
3692
|
+
{
|
|
3693
|
+
name: ORGX_CLAUDE_PLUGIN_NAME,
|
|
3694
|
+
description: "OrgX MCP tools and runtime telemetry hooks for Claude Code.",
|
|
3695
|
+
source: `./plugins/${ORGX_CLAUDE_PLUGIN_NAME}`
|
|
3696
|
+
}
|
|
3697
|
+
]
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
function buildCodexMarketplaceEntry() {
|
|
3701
|
+
return {
|
|
3702
|
+
name: ORGX_CODEX_PLUGIN_NAME,
|
|
3703
|
+
source: {
|
|
3704
|
+
source: "local",
|
|
3705
|
+
path: `./.codex/plugins/${ORGX_CODEX_PLUGIN_NAME}`
|
|
3706
|
+
},
|
|
3707
|
+
policy: {
|
|
3708
|
+
installation: "AVAILABLE",
|
|
3709
|
+
authentication: "ON_INSTALL"
|
|
3710
|
+
},
|
|
3711
|
+
category: "Productivity"
|
|
3712
|
+
};
|
|
3713
|
+
}
|
|
3714
|
+
function readMarketplacePlugins(path) {
|
|
3715
|
+
const document = parseJsonObject(readTextIfExists(path));
|
|
3716
|
+
const plugins = Array.isArray(document.plugins) ? document.plugins.filter((value) => Boolean(value) && typeof value === "object" && !Array.isArray(value)) : [];
|
|
3717
|
+
return { document, plugins };
|
|
3718
|
+
}
|
|
3719
|
+
function upsertCodexMarketplaceEntry(path) {
|
|
3720
|
+
const { document, plugins } = readMarketplacePlugins(path);
|
|
3721
|
+
const nextEntry = buildCodexMarketplaceEntry();
|
|
3722
|
+
let replaced = false;
|
|
3723
|
+
const nextPlugins = plugins.map((plugin) => {
|
|
3724
|
+
if (plugin.name !== ORGX_CODEX_PLUGIN_NAME) {
|
|
3725
|
+
return plugin;
|
|
3726
|
+
}
|
|
3727
|
+
replaced = true;
|
|
3728
|
+
return nextEntry;
|
|
3729
|
+
});
|
|
3730
|
+
if (!replaced) {
|
|
3731
|
+
nextPlugins.push(nextEntry);
|
|
3732
|
+
}
|
|
3733
|
+
const nextDocument = {
|
|
3734
|
+
...document,
|
|
3735
|
+
...document.name ? {} : { name: ORGX_CLAUDE_MARKETPLACE_NAME },
|
|
3736
|
+
...readObject(document.interface).displayName ? {} : { interface: { displayName: "OrgX Local" } },
|
|
3737
|
+
plugins: nextPlugins
|
|
3738
|
+
};
|
|
3739
|
+
return writeJsonIfChanged(path, nextDocument);
|
|
3740
|
+
}
|
|
3741
|
+
function removeCodexMarketplaceEntry(path) {
|
|
3742
|
+
if (!existsSync3(path)) {
|
|
3743
|
+
return false;
|
|
3744
|
+
}
|
|
3745
|
+
const { document, plugins } = readMarketplacePlugins(path);
|
|
3746
|
+
const nextPlugins = plugins.filter((plugin) => plugin.name !== ORGX_CODEX_PLUGIN_NAME);
|
|
3747
|
+
if (nextPlugins.length === plugins.length) {
|
|
3748
|
+
return false;
|
|
3749
|
+
}
|
|
3750
|
+
if (nextPlugins.length === 0) {
|
|
3751
|
+
rmSync(path, { force: true });
|
|
3752
|
+
return true;
|
|
3753
|
+
}
|
|
3754
|
+
return writeJsonIfChanged(path, {
|
|
3755
|
+
...document,
|
|
3756
|
+
plugins: nextPlugins
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3759
|
+
function codexMarketplaceHasOrgxEntry(path) {
|
|
3760
|
+
const { plugins } = readMarketplacePlugins(path);
|
|
3761
|
+
return plugins.some((plugin) => plugin.name === ORGX_CODEX_PLUGIN_NAME);
|
|
3762
|
+
}
|
|
3763
|
+
function extractClaudePluginNames(payload) {
|
|
3764
|
+
try {
|
|
3765
|
+
const parsed = JSON.parse(payload);
|
|
3766
|
+
if (!Array.isArray(parsed)) return [];
|
|
3767
|
+
return parsed.flatMap((entry) => {
|
|
3768
|
+
if (typeof entry === "string") return [entry];
|
|
3769
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3770
|
+
if (typeof entry.name === "string") return [entry.name];
|
|
3771
|
+
if (typeof entry.id === "string") return [entry.id];
|
|
3772
|
+
return [];
|
|
3773
|
+
});
|
|
3774
|
+
} catch {
|
|
3775
|
+
return [];
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
function extractOpenclawPluginIds(payload) {
|
|
3779
|
+
try {
|
|
3780
|
+
const parsed = JSON.parse(payload);
|
|
3781
|
+
if (Array.isArray(parsed)) {
|
|
3782
|
+
return parsed.flatMap((entry) => {
|
|
3783
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3784
|
+
if (typeof entry.id === "string") return [entry.id];
|
|
3785
|
+
if (typeof entry.name === "string") return [entry.name];
|
|
3786
|
+
return [];
|
|
3787
|
+
});
|
|
3788
|
+
}
|
|
3789
|
+
const root = readObject(parsed);
|
|
3790
|
+
const plugins = Array.isArray(root.plugins) ? root.plugins : [];
|
|
3791
|
+
return plugins.flatMap((entry) => {
|
|
3792
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3793
|
+
const item = entry;
|
|
3794
|
+
if (typeof item.id === "string") return [item.id];
|
|
3795
|
+
if (typeof item.name === "string") return [item.name];
|
|
3796
|
+
return [];
|
|
3797
|
+
});
|
|
3798
|
+
} catch {
|
|
3799
|
+
return [];
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
function formatCommandFailure(command, args, result) {
|
|
3803
|
+
const detail = [result.stderr.trim(), result.stdout.trim()].find((value) => value.length > 0);
|
|
3804
|
+
if (result.errorCode === "ENOENT") {
|
|
3805
|
+
return `Command '${command}' is not available on PATH.`;
|
|
3806
|
+
}
|
|
3807
|
+
return `${command} ${args.join(" ")} failed${result.exitCode >= 0 ? ` with exit code ${result.exitCode}` : ""}${detail ? `: ${detail}` : "."}`;
|
|
3808
|
+
}
|
|
3809
|
+
async function defaultCommandRunner(command, args) {
|
|
3810
|
+
return await new Promise((resolve) => {
|
|
3811
|
+
const child = spawn(command, [...args], {
|
|
3812
|
+
env: process.env,
|
|
3813
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3814
|
+
});
|
|
3815
|
+
let stdout = "";
|
|
3816
|
+
let stderr = "";
|
|
3817
|
+
child.stdout.on("data", (chunk) => {
|
|
3818
|
+
stdout += chunk.toString();
|
|
3819
|
+
});
|
|
3820
|
+
child.stderr.on("data", (chunk) => {
|
|
3821
|
+
stderr += chunk.toString();
|
|
3822
|
+
});
|
|
3823
|
+
child.on("error", (error) => {
|
|
3824
|
+
const errorCode = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
|
|
3825
|
+
resolve({
|
|
3826
|
+
exitCode: -1,
|
|
3827
|
+
stdout,
|
|
3828
|
+
stderr,
|
|
3829
|
+
...errorCode ? { errorCode } : {}
|
|
3830
|
+
});
|
|
3831
|
+
});
|
|
3832
|
+
child.on("close", (code) => {
|
|
3833
|
+
resolve({
|
|
3834
|
+
exitCode: code ?? -1,
|
|
3835
|
+
stdout,
|
|
3836
|
+
stderr
|
|
3837
|
+
});
|
|
3838
|
+
});
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3841
|
+
function resolveCommandRunner(runner) {
|
|
3842
|
+
return runner ?? defaultCommandRunner;
|
|
3843
|
+
}
|
|
3844
|
+
async function commandExists(command, runner) {
|
|
3845
|
+
const result = await runner(command, ["--help"]);
|
|
3846
|
+
return result.errorCode !== "ENOENT" && result.exitCode !== 127;
|
|
3847
|
+
}
|
|
3848
|
+
async function getClaudeInstallState(runner) {
|
|
3849
|
+
const available = detectSurface("claude").detected || await commandExists("claude", runner);
|
|
3850
|
+
if (!available) {
|
|
3851
|
+
return { available: false, installed: false };
|
|
3852
|
+
}
|
|
3853
|
+
const result = await runner("claude", ["plugin", "list", "--json"]);
|
|
3854
|
+
if (result.exitCode !== 0) {
|
|
3855
|
+
return { available: true, installed: false };
|
|
3856
|
+
}
|
|
3857
|
+
return {
|
|
3858
|
+
available: true,
|
|
3859
|
+
installed: extractClaudePluginNames(result.stdout).includes(ORGX_CLAUDE_PLUGIN_NAME)
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
async function getOpenclawInstallState(runner) {
|
|
3863
|
+
const available = detectSurface("openclaw").detected || await commandExists("openclaw", runner);
|
|
3864
|
+
if (!available) {
|
|
3865
|
+
return { available: false, installed: false };
|
|
3866
|
+
}
|
|
3867
|
+
const result = await runner("openclaw", ["plugins", "list", "--json"]);
|
|
3868
|
+
if (result.exitCode !== 0) {
|
|
3869
|
+
return { available: true, installed: false };
|
|
3870
|
+
}
|
|
3871
|
+
return {
|
|
3872
|
+
available: true,
|
|
3873
|
+
installed: extractOpenclawPluginIds(result.stdout).includes(ORGX_OPENCLAW_PLUGIN_ID)
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
async function buildClaudeStatus(paths, runner) {
|
|
3877
|
+
const state = await getClaudeInstallState(runner);
|
|
3878
|
+
if (state.installed) {
|
|
3879
|
+
return {
|
|
3880
|
+
target: "claude",
|
|
3881
|
+
available: true,
|
|
3882
|
+
installed: true,
|
|
3883
|
+
message: "Claude Code plugin is installed."
|
|
3884
|
+
};
|
|
3885
|
+
}
|
|
3886
|
+
if (!state.available) {
|
|
3887
|
+
return {
|
|
3888
|
+
target: "claude",
|
|
3889
|
+
available: false,
|
|
3890
|
+
installed: false,
|
|
3891
|
+
message: "Claude Code was not detected."
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
const marketplaceExists = existsSync3(paths.claudeMarketplaceManifestPath);
|
|
3895
|
+
return {
|
|
3896
|
+
target: "claude",
|
|
3897
|
+
available: true,
|
|
3898
|
+
installed: false,
|
|
3899
|
+
message: marketplaceExists ? "Claude marketplace wrapper exists, but the plugin is not installed." : "Claude Code is available and ready for plugin install."
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
function buildCodexStatus(paths, runner) {
|
|
3903
|
+
return (async () => {
|
|
3904
|
+
const available = detectSurface("codex").detected || await commandExists("codex", runner);
|
|
3905
|
+
const pluginExists = existsSync3(paths.codexPluginDir);
|
|
3906
|
+
const marketplaceExists = codexMarketplaceHasOrgxEntry(paths.codexMarketplacePath);
|
|
3907
|
+
const installed = pluginExists && marketplaceExists;
|
|
3908
|
+
if (installed) {
|
|
3909
|
+
return {
|
|
3910
|
+
target: "codex",
|
|
3911
|
+
available: true,
|
|
3912
|
+
installed: true,
|
|
3913
|
+
message: "Codex plugin files and marketplace entry are present."
|
|
3914
|
+
};
|
|
3915
|
+
}
|
|
3916
|
+
if (!available) {
|
|
3917
|
+
return {
|
|
3918
|
+
target: "codex",
|
|
3919
|
+
available: false,
|
|
3920
|
+
installed: false,
|
|
3921
|
+
message: "Codex was not detected."
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
if (pluginExists && !marketplaceExists) {
|
|
3925
|
+
return {
|
|
3926
|
+
target: "codex",
|
|
3927
|
+
available: true,
|
|
3928
|
+
installed: false,
|
|
3929
|
+
message: "Codex plugin files exist, but the marketplace entry is missing."
|
|
3930
|
+
};
|
|
3931
|
+
}
|
|
3932
|
+
if (!pluginExists && marketplaceExists) {
|
|
3933
|
+
return {
|
|
3934
|
+
target: "codex",
|
|
3935
|
+
available: true,
|
|
3936
|
+
installed: false,
|
|
3937
|
+
message: "Codex marketplace entry exists, but plugin files are missing."
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
return {
|
|
3941
|
+
target: "codex",
|
|
3942
|
+
available: true,
|
|
3943
|
+
installed: false,
|
|
3944
|
+
message: "Codex is available and ready for plugin install."
|
|
3945
|
+
};
|
|
3946
|
+
})();
|
|
3947
|
+
}
|
|
3948
|
+
async function buildOpenclawStatus(runner) {
|
|
3949
|
+
const state = await getOpenclawInstallState(runner);
|
|
3950
|
+
if (state.installed) {
|
|
3951
|
+
return {
|
|
3952
|
+
target: "openclaw",
|
|
3953
|
+
available: true,
|
|
3954
|
+
installed: true,
|
|
3955
|
+
message: "OpenClaw plugin is installed."
|
|
3956
|
+
};
|
|
3957
|
+
}
|
|
3958
|
+
return {
|
|
3959
|
+
target: "openclaw",
|
|
3960
|
+
available: state.available,
|
|
3961
|
+
installed: false,
|
|
3962
|
+
message: state.available ? "OpenClaw is available and ready for plugin install." : "OpenClaw was not detected."
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3965
|
+
async function resolveOpenclawTarball(fetchImpl) {
|
|
3966
|
+
const response = await fetchImpl(
|
|
3967
|
+
`${NPM_REGISTRY_BASE_URL}/${encodeURIComponent(ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME)}`,
|
|
3968
|
+
{
|
|
3969
|
+
headers: {
|
|
3970
|
+
Accept: "application/json"
|
|
3971
|
+
},
|
|
3972
|
+
signal: AbortSignal.timeout(1e4)
|
|
3973
|
+
}
|
|
3974
|
+
);
|
|
3975
|
+
if (!response.ok) {
|
|
3976
|
+
throw new Error(
|
|
3977
|
+
`npm registry returned HTTP ${response.status} while resolving ${ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME}.`
|
|
3978
|
+
);
|
|
3979
|
+
}
|
|
3980
|
+
const data = await response.json();
|
|
3981
|
+
const latestVersion = data["dist-tags"]?.latest;
|
|
3982
|
+
const tarballUrl = latestVersion ? data.versions?.[latestVersion]?.dist?.tarball : void 0;
|
|
3983
|
+
if (!latestVersion || !tarballUrl) {
|
|
3984
|
+
throw new Error(`Could not resolve a tarball URL for ${ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME}.`);
|
|
3985
|
+
}
|
|
3986
|
+
return {
|
|
3987
|
+
tarballUrl,
|
|
3988
|
+
version: latestVersion
|
|
3989
|
+
};
|
|
3990
|
+
}
|
|
3991
|
+
async function installClaudePlugin(paths, fetchImpl, runner) {
|
|
3992
|
+
const state = await getClaudeInstallState(runner);
|
|
3993
|
+
if (!state.available) {
|
|
3994
|
+
return {
|
|
3995
|
+
target: "claude",
|
|
3996
|
+
changed: false,
|
|
3997
|
+
message: "Claude Code is not available on this machine."
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
const syncResult = await syncManagedRepoTree(CLAUDE_PLUGIN_SYNC_SPEC, paths.claudePluginDir, fetchImpl);
|
|
4001
|
+
const manifestChanged = writeJsonIfChanged(
|
|
4002
|
+
paths.claudeMarketplaceManifestPath,
|
|
4003
|
+
buildClaudeMarketplaceManifest()
|
|
4004
|
+
);
|
|
4005
|
+
const marketplaceAdd = await runner("claude", ["plugin", "marketplace", "add", paths.claudeMarketplaceDir]);
|
|
4006
|
+
if (marketplaceAdd.exitCode !== 0) {
|
|
4007
|
+
throw new Error(formatCommandFailure("claude", ["plugin", "marketplace", "add", paths.claudeMarketplaceDir], marketplaceAdd));
|
|
4008
|
+
}
|
|
4009
|
+
let installedChanged = false;
|
|
4010
|
+
if (!state.installed) {
|
|
4011
|
+
const install = await runner("claude", [
|
|
4012
|
+
"plugin",
|
|
4013
|
+
"install",
|
|
4014
|
+
`${ORGX_CLAUDE_PLUGIN_NAME}@${ORGX_CLAUDE_MARKETPLACE_NAME}`,
|
|
4015
|
+
"--scope",
|
|
4016
|
+
"user"
|
|
4017
|
+
]);
|
|
4018
|
+
if (install.exitCode !== 0) {
|
|
4019
|
+
throw new Error(
|
|
4020
|
+
formatCommandFailure(
|
|
4021
|
+
"claude",
|
|
4022
|
+
[
|
|
4023
|
+
"plugin",
|
|
4024
|
+
"install",
|
|
4025
|
+
`${ORGX_CLAUDE_PLUGIN_NAME}@${ORGX_CLAUDE_MARKETPLACE_NAME}`,
|
|
4026
|
+
"--scope",
|
|
4027
|
+
"user"
|
|
4028
|
+
],
|
|
4029
|
+
install
|
|
4030
|
+
)
|
|
4031
|
+
);
|
|
4032
|
+
}
|
|
4033
|
+
installedChanged = true;
|
|
4034
|
+
}
|
|
4035
|
+
const changed = syncResult.changed || manifestChanged || installedChanged;
|
|
4036
|
+
return {
|
|
4037
|
+
target: "claude",
|
|
4038
|
+
changed,
|
|
4039
|
+
message: changed ? `Synced ${syncResult.fileCount} Claude plugin files and ensured the plugin is installed.` : "Claude Code plugin is already installed and up to date."
|
|
4040
|
+
};
|
|
4041
|
+
}
|
|
4042
|
+
async function installCodexPlugin(paths, fetchImpl, runner) {
|
|
4043
|
+
const available = detectSurface("codex").detected || await commandExists("codex", runner);
|
|
4044
|
+
if (!available) {
|
|
4045
|
+
return {
|
|
4046
|
+
target: "codex",
|
|
4047
|
+
changed: false,
|
|
4048
|
+
message: "Codex is not available on this machine."
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
const syncResult = await syncManagedRepoTree(CODEX_PLUGIN_SYNC_SPEC, paths.codexPluginDir, fetchImpl);
|
|
4052
|
+
const marketplaceChanged = upsertCodexMarketplaceEntry(paths.codexMarketplacePath);
|
|
4053
|
+
const changed = syncResult.changed || marketplaceChanged;
|
|
4054
|
+
return {
|
|
4055
|
+
target: "codex",
|
|
4056
|
+
changed,
|
|
4057
|
+
message: changed ? `Synced ${syncResult.fileCount} Codex plugin files and updated the marketplace entry.` : "Codex plugin files and marketplace entry already match the managed OrgX plugin."
|
|
4058
|
+
};
|
|
4059
|
+
}
|
|
4060
|
+
async function installOpenclawPlugin(fetchImpl, runner) {
|
|
4061
|
+
const state = await getOpenclawInstallState(runner);
|
|
4062
|
+
if (!state.available) {
|
|
4063
|
+
return {
|
|
4064
|
+
target: "openclaw",
|
|
4065
|
+
changed: false,
|
|
4066
|
+
message: "OpenClaw is not available on this machine."
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
4069
|
+
if (state.installed) {
|
|
4070
|
+
return {
|
|
4071
|
+
target: "openclaw",
|
|
4072
|
+
changed: false,
|
|
4073
|
+
message: "OpenClaw plugin is already installed."
|
|
4074
|
+
};
|
|
4075
|
+
}
|
|
4076
|
+
const { tarballUrl, version } = await resolveOpenclawTarball(fetchImpl);
|
|
4077
|
+
const tarballBytes = await fetchRemoteBytes(tarballUrl, fetchImpl);
|
|
4078
|
+
const tempRoot = mkdtempSync(join3(tmpdir(), "orgx-wizard-openclaw-"));
|
|
4079
|
+
const archivePath = join3(tempRoot, `orgx-openclaw-plugin-${version}.tgz`);
|
|
4080
|
+
try {
|
|
4081
|
+
writeFileSync2(archivePath, tarballBytes);
|
|
4082
|
+
const install = await runner("openclaw", ["plugins", "install", archivePath]);
|
|
4083
|
+
if (install.exitCode !== 0) {
|
|
4084
|
+
throw new Error(
|
|
4085
|
+
formatCommandFailure("openclaw", ["plugins", "install", archivePath], install)
|
|
4086
|
+
);
|
|
4087
|
+
}
|
|
4088
|
+
} finally {
|
|
4089
|
+
rmSync(tempRoot, { force: true, recursive: true });
|
|
4090
|
+
}
|
|
4091
|
+
return {
|
|
4092
|
+
target: "openclaw",
|
|
4093
|
+
changed: true,
|
|
4094
|
+
message: `Installed OpenClaw plugin ${ORGX_OPENCLAW_PLUGIN_ID} from ${ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME}@${version}.`
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
async function uninstallClaudePlugin(paths, runner) {
|
|
4098
|
+
const state = await getClaudeInstallState(runner);
|
|
4099
|
+
let changed = false;
|
|
4100
|
+
if (state.available && state.installed) {
|
|
4101
|
+
const uninstall = await runner("claude", [
|
|
4102
|
+
"plugin",
|
|
4103
|
+
"uninstall",
|
|
4104
|
+
ORGX_CLAUDE_PLUGIN_NAME,
|
|
4105
|
+
"--scope",
|
|
4106
|
+
"user"
|
|
4107
|
+
]);
|
|
4108
|
+
if (uninstall.exitCode !== 0) {
|
|
4109
|
+
throw new Error(
|
|
4110
|
+
formatCommandFailure(
|
|
4111
|
+
"claude",
|
|
4112
|
+
["plugin", "uninstall", ORGX_CLAUDE_PLUGIN_NAME, "--scope", "user"],
|
|
4113
|
+
uninstall
|
|
4114
|
+
)
|
|
4115
|
+
);
|
|
4116
|
+
}
|
|
4117
|
+
changed = true;
|
|
4118
|
+
}
|
|
4119
|
+
if (state.available) {
|
|
4120
|
+
const removeMarketplace = await runner("claude", ["plugin", "marketplace", "remove", ORGX_CLAUDE_MARKETPLACE_NAME]);
|
|
4121
|
+
if (removeMarketplace.exitCode === 0) {
|
|
4122
|
+
changed = true;
|
|
4123
|
+
}
|
|
4124
|
+
}
|
|
4125
|
+
changed = removePathIfExists(paths.claudeMarketplaceDir) || changed;
|
|
4126
|
+
return {
|
|
4127
|
+
target: "claude",
|
|
4128
|
+
changed,
|
|
4129
|
+
message: changed ? "Removed the managed Claude Code plugin and marketplace wrapper." : "Claude Code plugin was not installed."
|
|
4130
|
+
};
|
|
4131
|
+
}
|
|
4132
|
+
async function uninstallCodexPlugin(paths) {
|
|
4133
|
+
const removedPluginDir = removePathIfExists(paths.codexPluginDir);
|
|
4134
|
+
const removedMarketplaceEntry = removeCodexMarketplaceEntry(paths.codexMarketplacePath);
|
|
4135
|
+
const changed = removedPluginDir || removedMarketplaceEntry;
|
|
4136
|
+
return {
|
|
4137
|
+
target: "codex",
|
|
4138
|
+
changed,
|
|
4139
|
+
message: changed ? "Removed the managed Codex plugin files and marketplace entry." : "Codex plugin was not installed."
|
|
4140
|
+
};
|
|
4141
|
+
}
|
|
4142
|
+
async function uninstallOpenclawPlugin(runner) {
|
|
4143
|
+
const state = await getOpenclawInstallState(runner);
|
|
4144
|
+
if (!state.available || !state.installed) {
|
|
4145
|
+
return {
|
|
4146
|
+
target: "openclaw",
|
|
4147
|
+
changed: false,
|
|
4148
|
+
message: "OpenClaw plugin was not installed."
|
|
4149
|
+
};
|
|
4150
|
+
}
|
|
4151
|
+
const uninstall = await runner("openclaw", ["plugins", "uninstall", ORGX_OPENCLAW_PLUGIN_ID, "--force"]);
|
|
4152
|
+
if (uninstall.exitCode !== 0) {
|
|
4153
|
+
throw new Error(
|
|
4154
|
+
formatCommandFailure(
|
|
4155
|
+
"openclaw",
|
|
4156
|
+
["plugins", "uninstall", ORGX_OPENCLAW_PLUGIN_ID, "--force"],
|
|
4157
|
+
uninstall
|
|
4158
|
+
)
|
|
4159
|
+
);
|
|
4160
|
+
}
|
|
4161
|
+
return {
|
|
4162
|
+
target: "openclaw",
|
|
4163
|
+
changed: true,
|
|
4164
|
+
message: "Removed the managed OpenClaw plugin."
|
|
2749
4165
|
};
|
|
2750
4166
|
}
|
|
2751
|
-
function
|
|
4167
|
+
function resolvePluginTargets(requested = []) {
|
|
2752
4168
|
if (requested.length === 0 || requested.includes("all")) {
|
|
2753
|
-
return [...
|
|
4169
|
+
return [...DEFAULT_ORGX_PLUGIN_TARGETS];
|
|
2754
4170
|
}
|
|
2755
4171
|
const seen = /* @__PURE__ */ new Set();
|
|
2756
4172
|
const normalized = [];
|
|
2757
|
-
for (const
|
|
2758
|
-
const
|
|
2759
|
-
if (
|
|
2760
|
-
|
|
4173
|
+
for (const rawTarget of requested) {
|
|
4174
|
+
const target = rawTarget.trim().toLowerCase();
|
|
4175
|
+
if (target === "" || target === "all") continue;
|
|
4176
|
+
if (!DEFAULT_ORGX_PLUGIN_TARGETS.includes(target)) {
|
|
4177
|
+
throw new Error(
|
|
4178
|
+
`Unknown plugin target '${rawTarget}'. Supported targets: ${DEFAULT_ORGX_PLUGIN_TARGETS.join(", ")}.`
|
|
4179
|
+
);
|
|
2761
4180
|
}
|
|
2762
|
-
|
|
2763
|
-
|
|
4181
|
+
const nextTarget = target;
|
|
4182
|
+
if (seen.has(nextTarget)) continue;
|
|
4183
|
+
seen.add(nextTarget);
|
|
4184
|
+
normalized.push(nextTarget);
|
|
2764
4185
|
}
|
|
2765
4186
|
return normalized;
|
|
2766
4187
|
}
|
|
2767
|
-
async function
|
|
2768
|
-
const
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
writes.push(
|
|
2779
|
-
writeManagedFile(
|
|
2780
|
-
join2(claudeSkillsDir, skillName, relativePath),
|
|
2781
|
-
content,
|
|
2782
|
-
`${skillName}/${relativePath}`,
|
|
2783
|
-
file.sourceUrl
|
|
2784
|
-
)
|
|
2785
|
-
);
|
|
2786
|
-
}
|
|
2787
|
-
return {
|
|
2788
|
-
name: skillName,
|
|
2789
|
-
files: writes
|
|
2790
|
-
};
|
|
2791
|
-
}
|
|
2792
|
-
async function installOrgxSkills(options = {}) {
|
|
4188
|
+
async function listOrgxPluginStatuses(options = {}) {
|
|
4189
|
+
const paths = resolvePluginPaths(options.paths);
|
|
4190
|
+
const runner = resolveCommandRunner(options.commandRunner);
|
|
4191
|
+
return await Promise.all([
|
|
4192
|
+
buildClaudeStatus(paths, runner),
|
|
4193
|
+
buildCodexStatus(paths, runner),
|
|
4194
|
+
buildOpenclawStatus(runner)
|
|
4195
|
+
]);
|
|
4196
|
+
}
|
|
4197
|
+
async function installOrgxPlugins(options = {}) {
|
|
4198
|
+
const targets = resolvePluginTargets(options.targets ?? []);
|
|
2793
4199
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
2794
4200
|
if (!fetchImpl) {
|
|
2795
4201
|
throw new Error("Global fetch is unavailable in this runtime.");
|
|
2796
4202
|
}
|
|
2797
|
-
const
|
|
2798
|
-
const
|
|
2799
|
-
const
|
|
2800
|
-
const
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
4203
|
+
const paths = resolvePluginPaths(options.paths);
|
|
4204
|
+
const runner = resolveCommandRunner(options.commandRunner);
|
|
4205
|
+
const results = [];
|
|
4206
|
+
for (const target of targets) {
|
|
4207
|
+
switch (target) {
|
|
4208
|
+
case "claude":
|
|
4209
|
+
results.push(await installClaudePlugin(paths, fetchImpl, runner));
|
|
4210
|
+
break;
|
|
4211
|
+
case "codex":
|
|
4212
|
+
results.push(await installCodexPlugin(paths, fetchImpl, runner));
|
|
4213
|
+
break;
|
|
4214
|
+
case "openclaw":
|
|
4215
|
+
results.push(await installOpenclawPlugin(fetchImpl, runner));
|
|
4216
|
+
break;
|
|
2808
4217
|
}
|
|
2809
|
-
} else {
|
|
2810
|
-
skillNames = resolveSkillPackNames(requestedNames);
|
|
2811
4218
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
];
|
|
2816
|
-
const
|
|
2817
|
-
|
|
2818
|
-
|
|
4219
|
+
return { results };
|
|
4220
|
+
}
|
|
4221
|
+
async function uninstallOrgxPlugins(options = {}) {
|
|
4222
|
+
const targets = resolvePluginTargets(options.targets ?? []);
|
|
4223
|
+
const paths = resolvePluginPaths(options.paths);
|
|
4224
|
+
const runner = resolveCommandRunner(options.commandRunner);
|
|
4225
|
+
const results = [];
|
|
4226
|
+
for (const target of targets) {
|
|
4227
|
+
switch (target) {
|
|
4228
|
+
case "claude":
|
|
4229
|
+
results.push(await uninstallClaudePlugin(paths, runner));
|
|
4230
|
+
break;
|
|
4231
|
+
case "codex":
|
|
4232
|
+
results.push(await uninstallCodexPlugin(paths));
|
|
4233
|
+
break;
|
|
4234
|
+
case "openclaw":
|
|
4235
|
+
results.push(await uninstallOpenclawPlugin(runner));
|
|
4236
|
+
break;
|
|
4237
|
+
}
|
|
2819
4238
|
}
|
|
2820
|
-
return {
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
4239
|
+
return { results };
|
|
4240
|
+
}
|
|
4241
|
+
function countPluginReportChanges(report) {
|
|
4242
|
+
return report.results.filter((result) => result.changed).length;
|
|
2824
4243
|
}
|
|
2825
4244
|
|
|
2826
4245
|
// src/lib/setup-workspace.ts
|
|
@@ -3010,7 +4429,9 @@ async function runWorkspaceSetup(client, prompts, options) {
|
|
|
3010
4429
|
// src/lib/founder-preset.ts
|
|
3011
4430
|
async function runFounderPreset(prompts, options) {
|
|
3012
4431
|
const surfaceResults = await setupDetectedSurfaces();
|
|
4432
|
+
const pluginTargets = options.pluginTargets ?? (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
|
|
3013
4433
|
const skillReport = await installOrgxSkills({
|
|
4434
|
+
pluginTargets,
|
|
3014
4435
|
skillNames: [...DEFAULT_ORGX_SKILL_PACKS]
|
|
3015
4436
|
});
|
|
3016
4437
|
let workspaceSetup;
|
|
@@ -3058,6 +4479,179 @@ function summarizeMutationResults(results) {
|
|
|
3058
4479
|
}));
|
|
3059
4480
|
}
|
|
3060
4481
|
|
|
4482
|
+
// src/lib/setup-verification.ts
|
|
4483
|
+
var HOSTED_MCP_OUTAGE_TITLE = "Hosted OrgX MCP is unreachable.";
|
|
4484
|
+
function getSetupVerificationHeadline(summary) {
|
|
4485
|
+
switch (summary.status) {
|
|
4486
|
+
case "degraded":
|
|
4487
|
+
return "Hosted OrgX MCP is down; local setup can still continue.";
|
|
4488
|
+
case "error":
|
|
4489
|
+
return "Issues detected";
|
|
4490
|
+
case "warning":
|
|
4491
|
+
return "Warnings detected";
|
|
4492
|
+
case "ok":
|
|
4493
|
+
return "All systems ready.";
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
function summarizeSetupVerification(assessment) {
|
|
4497
|
+
const errors = assessment.issues.filter((issue) => issue.level === "error");
|
|
4498
|
+
if (errors.length === 0) {
|
|
4499
|
+
return {
|
|
4500
|
+
hostedMcpDegraded: false,
|
|
4501
|
+
status: assessment.issues.length > 0 ? "warning" : "ok"
|
|
4502
|
+
};
|
|
4503
|
+
}
|
|
4504
|
+
const hostedMcpDegraded = errors.every((issue) => issue.title === HOSTED_MCP_OUTAGE_TITLE);
|
|
4505
|
+
return {
|
|
4506
|
+
hostedMcpDegraded,
|
|
4507
|
+
status: hostedMcpDegraded ? "degraded" : "error"
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
// src/lib/telemetry-events.ts
|
|
4512
|
+
function withBaseProperties(properties, base) {
|
|
4513
|
+
return {
|
|
4514
|
+
...base,
|
|
4515
|
+
...properties
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
function buildWorkspaceSetupTelemetryProperties(result, base = {}) {
|
|
4519
|
+
return withBaseProperties(
|
|
4520
|
+
{
|
|
4521
|
+
created: result.created === true,
|
|
4522
|
+
default_changed: result.defaultChanged === true,
|
|
4523
|
+
has_workspace: Boolean(result.workspace),
|
|
4524
|
+
status: result.status
|
|
4525
|
+
},
|
|
4526
|
+
base
|
|
4527
|
+
);
|
|
4528
|
+
}
|
|
4529
|
+
function buildOnboardingTaskTelemetryProperties(input, base = {}) {
|
|
4530
|
+
return withBaseProperties(
|
|
4531
|
+
{
|
|
4532
|
+
created: input.created,
|
|
4533
|
+
has_initiative: input.hasInitiative,
|
|
4534
|
+
task_status: input.task.status ?? "unknown"
|
|
4535
|
+
},
|
|
4536
|
+
base
|
|
4537
|
+
);
|
|
4538
|
+
}
|
|
4539
|
+
function buildAgentRosterTelemetryProperties(input, base = {}) {
|
|
4540
|
+
return withBaseProperties(
|
|
4541
|
+
{
|
|
4542
|
+
agent_count: input.roster.agents.length,
|
|
4543
|
+
domain_count: new Set(input.roster.agents.map((agent) => agent.domain)).size,
|
|
4544
|
+
strategy: input.strategy
|
|
4545
|
+
},
|
|
4546
|
+
base
|
|
4547
|
+
);
|
|
4548
|
+
}
|
|
4549
|
+
function buildFounderDemoTelemetryProperties(result, base = {}) {
|
|
4550
|
+
return withBaseProperties(
|
|
4551
|
+
{
|
|
4552
|
+
created: result.created,
|
|
4553
|
+
decision_status: result.decision.status ?? "unknown",
|
|
4554
|
+
has_artifact_url: Boolean(result.artifact.url)
|
|
4555
|
+
},
|
|
4556
|
+
base
|
|
4557
|
+
);
|
|
4558
|
+
}
|
|
4559
|
+
function buildDoctorTelemetryProperties(report, assessment, verification, base = {}) {
|
|
4560
|
+
const errorCount = assessment.issues.filter((issue) => issue.level === "error").length;
|
|
4561
|
+
const warningCount = assessment.issues.filter((issue) => issue.level === "warning").length;
|
|
4562
|
+
return withBaseProperties(
|
|
4563
|
+
{
|
|
4564
|
+
auth_configured: report.auth.configured,
|
|
4565
|
+
auth_ok: report.auth.ok,
|
|
4566
|
+
configured_surface_count: report.surfaces.filter((surface) => surface.configured).length,
|
|
4567
|
+
detected_surface_count: report.surfaces.filter((surface) => surface.detected).length,
|
|
4568
|
+
error_count: errorCount,
|
|
4569
|
+
hosted_mcp_degraded: verification.hostedMcpDegraded,
|
|
4570
|
+
hosted_mcp_ok: report.hostedMcp.ok,
|
|
4571
|
+
hosted_mcp_tool_ok: report.hostedMcpTool.ok,
|
|
4572
|
+
issue_count: assessment.issues.length,
|
|
4573
|
+
openclaw_available: !report.openclaw.skipped,
|
|
4574
|
+
openclaw_ok: report.openclaw.ok,
|
|
4575
|
+
status: verification.status,
|
|
4576
|
+
warning_count: warningCount,
|
|
4577
|
+
workspace_configured: report.workspace.configured,
|
|
4578
|
+
workspace_ok: report.workspace.ok
|
|
4579
|
+
},
|
|
4580
|
+
base
|
|
4581
|
+
);
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4584
|
+
// src/lib/telemetry.ts
|
|
4585
|
+
var POSTHOG_DEFAULT_HOST = "https://us.i.posthog.com";
|
|
4586
|
+
var WIZARD_LIB = "@useorgx/wizard";
|
|
4587
|
+
function isTruthyEnv(value) {
|
|
4588
|
+
if (!value) return false;
|
|
4589
|
+
switch (value.trim().toLowerCase()) {
|
|
4590
|
+
case "1":
|
|
4591
|
+
case "true":
|
|
4592
|
+
case "yes":
|
|
4593
|
+
case "y":
|
|
4594
|
+
case "on":
|
|
4595
|
+
return true;
|
|
4596
|
+
default:
|
|
4597
|
+
return false;
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
function isWizardTelemetryDisabled() {
|
|
4601
|
+
const explicitEnable = isTruthyEnv(process.env.ORGX_TELEMETRY_ENABLED);
|
|
4602
|
+
if (!explicitEnable) return true;
|
|
4603
|
+
return isTruthyEnv(process.env.ORGX_TELEMETRY_DISABLED) || isTruthyEnv(process.env.OPENCLAW_TELEMETRY_DISABLED) || isTruthyEnv(process.env.POSTHOG_DISABLED);
|
|
4604
|
+
}
|
|
4605
|
+
function resolvePosthogApiKey() {
|
|
4606
|
+
const value = process.env.ORGX_POSTHOG_API_KEY ?? process.env.POSTHOG_API_KEY ?? process.env.ORGX_POSTHOG_KEY ?? process.env.POSTHOG_KEY ?? "";
|
|
4607
|
+
const trimmed = value.trim();
|
|
4608
|
+
return trimmed || null;
|
|
4609
|
+
}
|
|
4610
|
+
function resolvePosthogHost() {
|
|
4611
|
+
const value = process.env.ORGX_POSTHOG_HOST ?? process.env.POSTHOG_HOST ?? process.env.ORGX_POSTHOG_API_HOST ?? process.env.POSTHOG_API_HOST ?? "";
|
|
4612
|
+
const trimmed = value.trim();
|
|
4613
|
+
return trimmed || POSTHOG_DEFAULT_HOST;
|
|
4614
|
+
}
|
|
4615
|
+
function toPosthogBatchUrl(host) {
|
|
4616
|
+
try {
|
|
4617
|
+
return new URL("/batch/", host).toString();
|
|
4618
|
+
} catch {
|
|
4619
|
+
return `${POSTHOG_DEFAULT_HOST}/batch/`;
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
async function trackWizardTelemetry(event, properties = {}, options = {}) {
|
|
4623
|
+
if (isWizardTelemetryDisabled()) return false;
|
|
4624
|
+
const apiKey = resolvePosthogApiKey();
|
|
4625
|
+
if (!apiKey) return false;
|
|
4626
|
+
const installationId = getOrCreateWizardInstallationId(options.statePath);
|
|
4627
|
+
const timestamp = options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4628
|
+
const response = await fetch(toPosthogBatchUrl(resolvePosthogHost()), {
|
|
4629
|
+
method: "POST",
|
|
4630
|
+
headers: {
|
|
4631
|
+
"Content-Type": "application/json"
|
|
4632
|
+
},
|
|
4633
|
+
body: JSON.stringify({
|
|
4634
|
+
api_key: apiKey,
|
|
4635
|
+
batch: [
|
|
4636
|
+
{
|
|
4637
|
+
type: "capture",
|
|
4638
|
+
event,
|
|
4639
|
+
distinct_id: installationId,
|
|
4640
|
+
properties: {
|
|
4641
|
+
$lib: WIZARD_LIB,
|
|
4642
|
+
source: WIZARD_LIB,
|
|
4643
|
+
wizard_installation_id: installationId,
|
|
4644
|
+
...properties
|
|
4645
|
+
},
|
|
4646
|
+
timestamp
|
|
4647
|
+
}
|
|
4648
|
+
],
|
|
4649
|
+
sent_at: timestamp
|
|
4650
|
+
})
|
|
4651
|
+
}).catch(() => null);
|
|
4652
|
+
return response?.ok === true;
|
|
4653
|
+
}
|
|
4654
|
+
|
|
3061
4655
|
// src/spinner.ts
|
|
3062
4656
|
import ora from "ora";
|
|
3063
4657
|
import pc2 from "picocolors";
|
|
@@ -3095,12 +4689,34 @@ function formatAuthSource(source) {
|
|
|
3095
4689
|
return "not configured";
|
|
3096
4690
|
}
|
|
3097
4691
|
}
|
|
4692
|
+
function formatPluginTargetLabel(target) {
|
|
4693
|
+
switch (target) {
|
|
4694
|
+
case "claude":
|
|
4695
|
+
return "Claude Code";
|
|
4696
|
+
case "codex":
|
|
4697
|
+
return "Codex";
|
|
4698
|
+
case "openclaw":
|
|
4699
|
+
return "OpenClaw";
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
3098
4702
|
function printSurfaceTable(statuses) {
|
|
3099
4703
|
for (const status of statuses) {
|
|
3100
4704
|
const icon = status.configured ? ICON.ok : status.detected ? ICON.warn : ICON.skip;
|
|
3101
4705
|
const state = status.configured ? pc3.green("configured") : status.detected ? pc3.yellow("detected") : pc3.dim("not found");
|
|
3102
4706
|
const mode = pc3.dim(status.mode === "automated" ? "auto " : "manual");
|
|
3103
4707
|
console.log(` ${icon} ${pc3.bold(status.name.padEnd(10))} ${mode} ${state}`);
|
|
4708
|
+
if (status.mode === "manual") {
|
|
4709
|
+
console.log(` ${pc3.dim(status.summary)}`);
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
function printPluginStatusReport(statuses) {
|
|
4714
|
+
for (const status of statuses) {
|
|
4715
|
+
const icon = status.installed ? ICON.ok : status.available ? ICON.warn : ICON.skip;
|
|
4716
|
+
const state = status.installed ? pc3.green("installed") : status.available ? pc3.yellow("available") : pc3.dim("not found");
|
|
4717
|
+
console.log(
|
|
4718
|
+
` ${icon} ${pc3.bold(formatPluginTargetLabel(status.target).padEnd(12))} ${state} ${pc3.dim(status.message)}`
|
|
4719
|
+
);
|
|
3104
4720
|
}
|
|
3105
4721
|
}
|
|
3106
4722
|
function printMutationResults(results) {
|
|
@@ -3110,6 +4726,15 @@ function printMutationResults(results) {
|
|
|
3110
4726
|
console.log(` ${icon} ${pc3.bold(result.name.padEnd(10))} ${state} ${pc3.dim(result.message)}`);
|
|
3111
4727
|
}
|
|
3112
4728
|
}
|
|
4729
|
+
function printPluginMutationReport(report) {
|
|
4730
|
+
for (const result of report.results) {
|
|
4731
|
+
const icon = result.changed ? ICON.ok : ICON.skip;
|
|
4732
|
+
const state = result.changed ? pc3.green("updated ") : pc3.dim("unchanged");
|
|
4733
|
+
console.log(
|
|
4734
|
+
` ${icon} ${pc3.bold(formatPluginTargetLabel(result.target).padEnd(12))} ${state} ${pc3.dim(result.message)}`
|
|
4735
|
+
);
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
3113
4738
|
function printSkillInstallReport(report) {
|
|
3114
4739
|
for (const write of report.writes) {
|
|
3115
4740
|
const icon = write.changed ? ICON.ok : ICON.skip;
|
|
@@ -3122,6 +4747,23 @@ function printSkillInstallReport(report) {
|
|
|
3122
4747
|
const state = changedCount > 0 ? pc3.green(`${changedCount} updated `) : pc3.dim("unchanged");
|
|
3123
4748
|
console.log(` ${icon} ${pc3.bold(pack.name.padEnd(10))} ${state} ${pc3.dim(`${pack.files.length} files`)}`);
|
|
3124
4749
|
}
|
|
4750
|
+
for (const note of report.notes) {
|
|
4751
|
+
console.log(` ${ICON.skip} ${pc3.dim(note)}`);
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
function countSkillReportChanges(report) {
|
|
4755
|
+
const writeChanges = report.writes.filter((write) => write.changed).length;
|
|
4756
|
+
const packChanges = report.packs.reduce(
|
|
4757
|
+
(count, pack) => count + pack.files.filter((file) => file.changed).length,
|
|
4758
|
+
0
|
|
4759
|
+
);
|
|
4760
|
+
return writeChanges + packChanges;
|
|
4761
|
+
}
|
|
4762
|
+
async function safeTrackWizardTelemetry(event, properties = {}) {
|
|
4763
|
+
try {
|
|
4764
|
+
await trackWizardTelemetry(event, properties);
|
|
4765
|
+
} catch {
|
|
4766
|
+
}
|
|
3125
4767
|
}
|
|
3126
4768
|
function printWorkspace(workspace) {
|
|
3127
4769
|
console.log(
|
|
@@ -3170,6 +4812,12 @@ function printFounderPresetResult(result) {
|
|
|
3170
4812
|
` ${result.demoInitiative.created ? pc3.green("created") : pc3.yellow("unchanged")} ${result.demoInitiative.initiative.title}`
|
|
3171
4813
|
);
|
|
3172
4814
|
console.log(` live: ${result.demoInitiative.liveUrl}`);
|
|
4815
|
+
console.log(
|
|
4816
|
+
` decision: ${result.demoInitiative.decision.title} ${pc3.dim(`(${result.demoInitiative.decision.status ?? "pending"})`)}`
|
|
4817
|
+
);
|
|
4818
|
+
console.log(
|
|
4819
|
+
` artifact: ${result.demoInitiative.artifact.name}${result.demoInitiative.artifact.url ? ` ${pc3.dim(result.demoInitiative.artifact.url)}` : ""}`
|
|
4820
|
+
);
|
|
3173
4821
|
}
|
|
3174
4822
|
}
|
|
3175
4823
|
function normalizePromptResult(value) {
|
|
@@ -3187,6 +4835,64 @@ async function textPrompt(input) {
|
|
|
3187
4835
|
};
|
|
3188
4836
|
return normalizePromptResult(await clack.text(promptInput));
|
|
3189
4837
|
}
|
|
4838
|
+
async function multiselectPrompt(input) {
|
|
4839
|
+
const promptInput = {
|
|
4840
|
+
message: input.message,
|
|
4841
|
+
options: input.options,
|
|
4842
|
+
...input.required !== void 0 ? { required: input.required } : {}
|
|
4843
|
+
};
|
|
4844
|
+
return normalizePromptResult(await clack.multiselect(promptInput));
|
|
4845
|
+
}
|
|
4846
|
+
function printAgentRoster(roster) {
|
|
4847
|
+
console.log(` ${ICON.ok} ${pc3.green("agent roster")} ${pc3.bold(`${roster.agents.length} agents`)}`);
|
|
4848
|
+
for (const agent of roster.agents) {
|
|
4849
|
+
console.log(
|
|
4850
|
+
` ${pc3.dim(agent.domain.padEnd(12))} ${pc3.bold(agent.name)} ${pc3.dim(agent.agentType)}`
|
|
4851
|
+
);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
async function promptForGuidedAgentRoster(workspace) {
|
|
4855
|
+
const remainingAgents = listAgentRosterProfiles();
|
|
4856
|
+
const assignments = [];
|
|
4857
|
+
for (const domain of AGENT_ROSTER_DOMAINS) {
|
|
4858
|
+
if (remainingAgents.length === 0) {
|
|
4859
|
+
break;
|
|
4860
|
+
}
|
|
4861
|
+
const defaultAgent = remainingAgents.find((agent) => agent.defaultDomain === domain);
|
|
4862
|
+
const choice = await selectPrompt({
|
|
4863
|
+
initialValue: defaultAgent?.name ?? "skip",
|
|
4864
|
+
message: `Assign an OrgX agent to the ${domain} domain`,
|
|
4865
|
+
options: [
|
|
4866
|
+
...remainingAgents.map((agent) => ({
|
|
4867
|
+
value: agent.name,
|
|
4868
|
+
label: agent.name,
|
|
4869
|
+
hint: `${agent.agentType} \xB7 default ${agent.defaultDomain}`
|
|
4870
|
+
})),
|
|
4871
|
+
{ value: "skip", label: `Leave ${domain} unassigned`, hint: "optional" }
|
|
4872
|
+
]
|
|
4873
|
+
});
|
|
4874
|
+
if (clack.isCancel(choice)) {
|
|
4875
|
+
clack.cancel("Setup cancelled.");
|
|
4876
|
+
return "cancelled";
|
|
4877
|
+
}
|
|
4878
|
+
if (choice === "skip") {
|
|
4879
|
+
continue;
|
|
4880
|
+
}
|
|
4881
|
+
const index = remainingAgents.findIndex((agent) => agent.name === choice);
|
|
4882
|
+
if (index === -1) {
|
|
4883
|
+
continue;
|
|
4884
|
+
}
|
|
4885
|
+
const [selectedAgent] = remainingAgents.splice(index, 1);
|
|
4886
|
+
if (!selectedAgent) {
|
|
4887
|
+
continue;
|
|
4888
|
+
}
|
|
4889
|
+
assignments.push({ domain, name: selectedAgent.name });
|
|
4890
|
+
}
|
|
4891
|
+
if (assignments.length === 0) {
|
|
4892
|
+
return null;
|
|
4893
|
+
}
|
|
4894
|
+
return configureAgentRoster(workspace, assignments);
|
|
4895
|
+
}
|
|
3190
4896
|
async function verifyAndPersistAuth(apiKey, options) {
|
|
3191
4897
|
const trimmedKey = apiKey.trim();
|
|
3192
4898
|
if (!trimmedKey.toLowerCase().startsWith("oxk_")) {
|
|
@@ -3207,6 +4913,10 @@ async function verifyAndPersistAuth(apiKey, options) {
|
|
|
3207
4913
|
baseUrl,
|
|
3208
4914
|
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3209
4915
|
});
|
|
4916
|
+
await safeTrackWizardTelemetry("auth_completed", {
|
|
4917
|
+
auth_source: options.telemetrySource ?? "manual_api_key",
|
|
4918
|
+
has_base_url: Boolean(baseUrl)
|
|
4919
|
+
});
|
|
3210
4920
|
const openclawResults = detectSurface("openclaw").detected ? await addSurface("openclaw") : [];
|
|
3211
4921
|
return { openclawResults, stored, verification };
|
|
3212
4922
|
}
|
|
@@ -3228,6 +4938,168 @@ function parseTimeoutSeconds(value) {
|
|
|
3228
4938
|
}
|
|
3229
4939
|
return parsed;
|
|
3230
4940
|
}
|
|
4941
|
+
async function maybeConfigureOptionalWorkspaceAddOns(input) {
|
|
4942
|
+
if (!input.interactive || !input.workspace) {
|
|
4943
|
+
return "skipped";
|
|
4944
|
+
}
|
|
4945
|
+
const existingState = readWizardState();
|
|
4946
|
+
const hasOnboardingTask = existingState?.onboardingTask?.workspaceId === input.workspace.id;
|
|
4947
|
+
if (!hasOnboardingTask) {
|
|
4948
|
+
const onboardingChoice = await selectPrompt({
|
|
4949
|
+
message: `Create a starter onboarding task for ${input.workspace.name}?`,
|
|
4950
|
+
options: [
|
|
4951
|
+
{ value: "yes", label: "Create onboarding task", hint: "recommended" },
|
|
4952
|
+
{ value: "no", label: "Skip task creation" }
|
|
4953
|
+
]
|
|
4954
|
+
});
|
|
4955
|
+
if (clack.isCancel(onboardingChoice)) {
|
|
4956
|
+
clack.cancel("Setup cancelled.");
|
|
4957
|
+
return "cancelled";
|
|
4958
|
+
}
|
|
4959
|
+
if (onboardingChoice === "yes") {
|
|
4960
|
+
const task = await ensureOnboardingTask(input.workspace, {
|
|
4961
|
+
...input.initiativeId ? { initiativeId: input.initiativeId } : {}
|
|
4962
|
+
});
|
|
4963
|
+
console.log(` ${ICON.ok} ${pc3.green("onboarding")} ${pc3.bold(task.title)}`);
|
|
4964
|
+
await safeTrackWizardTelemetry(
|
|
4965
|
+
"onboarding_task_created",
|
|
4966
|
+
buildOnboardingTaskTelemetryProperties(
|
|
4967
|
+
{
|
|
4968
|
+
created: true,
|
|
4969
|
+
hasInitiative: Boolean(input.initiativeId),
|
|
4970
|
+
task
|
|
4971
|
+
},
|
|
4972
|
+
{
|
|
4973
|
+
command: input.telemetry?.command ?? "setup",
|
|
4974
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
|
|
4975
|
+
}
|
|
4976
|
+
)
|
|
4977
|
+
);
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
const refreshedState = readWizardState();
|
|
4981
|
+
const hasAgentRoster = refreshedState?.agentRoster?.workspaceId === input.workspace.id;
|
|
4982
|
+
if (!hasAgentRoster) {
|
|
4983
|
+
const rosterChoice = await selectPrompt({
|
|
4984
|
+
message: `Set up an OrgX agent roster for ${input.workspace.name}?`,
|
|
4985
|
+
options: [
|
|
4986
|
+
{ value: "guided", label: "Choose agents and domains", hint: "recommended" },
|
|
4987
|
+
{ value: "default", label: "Install the full default roster" },
|
|
4988
|
+
{ value: "no", label: "Skip roster setup" }
|
|
4989
|
+
]
|
|
4990
|
+
});
|
|
4991
|
+
if (clack.isCancel(rosterChoice)) {
|
|
4992
|
+
clack.cancel("Setup cancelled.");
|
|
4993
|
+
return "cancelled";
|
|
4994
|
+
}
|
|
4995
|
+
if (rosterChoice === "guided") {
|
|
4996
|
+
const roster = await promptForGuidedAgentRoster(input.workspace);
|
|
4997
|
+
if (roster === "cancelled") {
|
|
4998
|
+
return "cancelled";
|
|
4999
|
+
}
|
|
5000
|
+
if (roster) {
|
|
5001
|
+
printAgentRoster(roster);
|
|
5002
|
+
await safeTrackWizardTelemetry(
|
|
5003
|
+
"agent_roster_configured",
|
|
5004
|
+
buildAgentRosterTelemetryProperties(
|
|
5005
|
+
{
|
|
5006
|
+
roster,
|
|
5007
|
+
strategy: "guided"
|
|
5008
|
+
},
|
|
5009
|
+
{
|
|
5010
|
+
command: input.telemetry?.command ?? "setup",
|
|
5011
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
|
|
5012
|
+
}
|
|
5013
|
+
)
|
|
5014
|
+
);
|
|
5015
|
+
} else {
|
|
5016
|
+
console.log(` ${ICON.skip} ${pc3.dim("agent roster skipped")} ${pc3.dim("(no agents selected)")}`);
|
|
5017
|
+
}
|
|
5018
|
+
} else if (rosterChoice === "default") {
|
|
5019
|
+
const roster = ensureDefaultAgentRoster(input.workspace);
|
|
5020
|
+
printAgentRoster(roster);
|
|
5021
|
+
await safeTrackWizardTelemetry(
|
|
5022
|
+
"agent_roster_configured",
|
|
5023
|
+
buildAgentRosterTelemetryProperties(
|
|
5024
|
+
{
|
|
5025
|
+
roster,
|
|
5026
|
+
strategy: "default"
|
|
5027
|
+
},
|
|
5028
|
+
{
|
|
5029
|
+
command: input.telemetry?.command ?? "setup",
|
|
5030
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
|
|
5031
|
+
}
|
|
5032
|
+
)
|
|
5033
|
+
);
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
return "configured";
|
|
5037
|
+
}
|
|
5038
|
+
async function promptOptionalCompanionPluginTargets(input) {
|
|
5039
|
+
if (!input.interactive) {
|
|
5040
|
+
return [];
|
|
5041
|
+
}
|
|
5042
|
+
const statuses = await listOrgxPluginStatuses();
|
|
5043
|
+
const installable = statuses.filter((status) => status.available && !status.installed);
|
|
5044
|
+
if (installable.length === 0) {
|
|
5045
|
+
return [];
|
|
5046
|
+
}
|
|
5047
|
+
const selection = await multiselectPrompt({
|
|
5048
|
+
message: "Install companion OrgX plugins into detected tools?",
|
|
5049
|
+
options: installable.map((status) => ({
|
|
5050
|
+
value: status.target,
|
|
5051
|
+
label: `Install ${formatPluginTargetLabel(status.target)}`,
|
|
5052
|
+
hint: status.message
|
|
5053
|
+
})),
|
|
5054
|
+
required: false
|
|
5055
|
+
});
|
|
5056
|
+
if (clack.isCancel(selection)) {
|
|
5057
|
+
clack.cancel("Setup cancelled.");
|
|
5058
|
+
return "cancelled";
|
|
5059
|
+
}
|
|
5060
|
+
return selection;
|
|
5061
|
+
}
|
|
5062
|
+
function printPluginSkillOwnershipNote(targets) {
|
|
5063
|
+
if (!targets.some((target) => target === "claude" || target === "codex")) {
|
|
5064
|
+
return;
|
|
5065
|
+
}
|
|
5066
|
+
console.log(
|
|
5067
|
+
` ${ICON.skip} ${pc3.dim(
|
|
5068
|
+
"Claude Code and Codex companion plugins carry their own OrgX skills. Use 'wizard skills add' for Cursor rules or standalone Claude setup when those plugins are not in play."
|
|
5069
|
+
)}`
|
|
5070
|
+
);
|
|
5071
|
+
}
|
|
5072
|
+
async function installSelectedCompanionPlugins(input) {
|
|
5073
|
+
if (input.targets.length === 0) {
|
|
5074
|
+
return "skipped";
|
|
5075
|
+
}
|
|
5076
|
+
const spinner = createOrgxSpinner("Installing OrgX companion plugins");
|
|
5077
|
+
spinner.start();
|
|
5078
|
+
const report = await installOrgxPlugins({ targets: input.targets });
|
|
5079
|
+
spinner.succeed("OrgX companion plugins processed");
|
|
5080
|
+
printPluginMutationReport(report);
|
|
5081
|
+
printPluginSkillOwnershipNote(input.targets);
|
|
5082
|
+
await safeTrackWizardTelemetry("plugins_installed", {
|
|
5083
|
+
changed_count: countPluginReportChanges(report),
|
|
5084
|
+
command: input.telemetry?.command ?? "setup",
|
|
5085
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {},
|
|
5086
|
+
requested_target_count: input.targets.length,
|
|
5087
|
+
target_count: report.results.length
|
|
5088
|
+
});
|
|
5089
|
+
return countPluginReportChanges(report) > 0 ? "configured" : "skipped";
|
|
5090
|
+
}
|
|
5091
|
+
async function maybeInstallOptionalCompanionPlugins(input) {
|
|
5092
|
+
const selection = await promptOptionalCompanionPluginTargets({
|
|
5093
|
+
interactive: input.interactive
|
|
5094
|
+
});
|
|
5095
|
+
if (selection === "cancelled") {
|
|
5096
|
+
return "cancelled";
|
|
5097
|
+
}
|
|
5098
|
+
return await installSelectedCompanionPlugins({
|
|
5099
|
+
targets: selection,
|
|
5100
|
+
...input.telemetry ? { telemetry: input.telemetry } : {}
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
3231
5103
|
function printAuthStatus(status) {
|
|
3232
5104
|
if (!status.configured) {
|
|
3233
5105
|
console.log(` ${ICON.warn} ${pc3.yellow("no account")} run ${pc3.cyan(`${getCmd()} auth login`)} to connect`);
|
|
@@ -3242,6 +5114,7 @@ function printAuthStatus(status) {
|
|
|
3242
5114
|
}
|
|
3243
5115
|
}
|
|
3244
5116
|
function printDoctorReport(report, assessment) {
|
|
5117
|
+
const verification = summarizeSetupVerification(assessment);
|
|
3245
5118
|
console.log(pc3.dim(" surfaces"));
|
|
3246
5119
|
printSurfaceTable(report.surfaces);
|
|
3247
5120
|
console.log("");
|
|
@@ -3290,11 +5163,12 @@ function printDoctorReport(report, assessment) {
|
|
|
3290
5163
|
console.log(` ${pc3.dim("\u2192")} ${pc3.dim(`OrgX is active across ${configuredCount} editor${configuredCount !== 1 ? "s" : ""}`)}`);
|
|
3291
5164
|
}
|
|
3292
5165
|
} else {
|
|
3293
|
-
const
|
|
3294
|
-
const headline =
|
|
5166
|
+
const headlineText = getSetupVerificationHeadline(verification);
|
|
5167
|
+
const headline = verification.status === "error" ? `${ICON.err} ${pc3.red(headlineText)}` : `${ICON.warn} ${pc3.yellow(headlineText)}`;
|
|
3295
5168
|
console.log(` ${headline}`);
|
|
3296
5169
|
for (const issue of assessment.issues) {
|
|
3297
|
-
const
|
|
5170
|
+
const degradedHostedMcpIssue = verification.hostedMcpDegraded && issue.title === HOSTED_MCP_OUTAGE_TITLE;
|
|
5171
|
+
const label = degradedHostedMcpIssue ? pc3.yellow("degr ") : issue.level === "error" ? pc3.red("error") : pc3.yellow("warn ");
|
|
3298
5172
|
console.log(`
|
|
3299
5173
|
${label} ${issue.title}`);
|
|
3300
5174
|
console.log(` ${pc3.dim("\u2192")} ${issue.suggestion}`);
|
|
@@ -3304,16 +5178,28 @@ function printDoctorReport(report, assessment) {
|
|
|
3304
5178
|
async function main() {
|
|
3305
5179
|
const program = new Command();
|
|
3306
5180
|
program.name("orgx-wizard").description("One-line CLI onboarding for OrgX surfaces.").showHelpAfterError();
|
|
3307
|
-
const pkgVersion = true ? "0.1.
|
|
5181
|
+
const pkgVersion = true ? "0.1.8" : void 0;
|
|
3308
5182
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
3309
5183
|
program.hook("preAction", () => {
|
|
3310
5184
|
console.log(renderBanner(pkgVersion));
|
|
3311
5185
|
});
|
|
3312
5186
|
program.command("setup").description("Patch all detected automated OrgX surfaces.").option("--preset <name>", "run a setup bundle (currently: founder)").action(async (options) => {
|
|
5187
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
5188
|
+
await safeTrackWizardTelemetry("wizard_started", {
|
|
5189
|
+
command: "setup",
|
|
5190
|
+
interactive,
|
|
5191
|
+
preset: options.preset ?? "standard"
|
|
5192
|
+
});
|
|
3313
5193
|
if (options.preset) {
|
|
3314
5194
|
if (options.preset !== "founder") {
|
|
3315
5195
|
throw new Error(`Unknown setup preset '${options.preset}'. Supported presets: founder.`);
|
|
3316
5196
|
}
|
|
5197
|
+
const founderPluginTargets = await promptOptionalCompanionPluginTargets({
|
|
5198
|
+
interactive
|
|
5199
|
+
});
|
|
5200
|
+
if (founderPluginTargets === "cancelled") {
|
|
5201
|
+
return;
|
|
5202
|
+
}
|
|
3317
5203
|
const spinner2 = createOrgxSpinner("Running founder setup preset");
|
|
3318
5204
|
spinner2.start();
|
|
3319
5205
|
const presetResult = await runFounderPreset(
|
|
@@ -3324,7 +5210,8 @@ async function main() {
|
|
|
3324
5210
|
text: textPrompt
|
|
3325
5211
|
},
|
|
3326
5212
|
{
|
|
3327
|
-
interactive
|
|
5213
|
+
interactive,
|
|
5214
|
+
pluginTargets: founderPluginTargets
|
|
3328
5215
|
}
|
|
3329
5216
|
);
|
|
3330
5217
|
if ("status" in presetResult) {
|
|
@@ -3337,11 +5224,62 @@ async function main() {
|
|
|
3337
5224
|
}
|
|
3338
5225
|
spinner2.succeed("Founder setup preset complete");
|
|
3339
5226
|
printFounderPresetResult(presetResult);
|
|
5227
|
+
await safeTrackWizardTelemetry("mcp_injected", {
|
|
5228
|
+
changed_count: presetResult.surfaceResults.filter((result) => result.changed).length,
|
|
5229
|
+
preset: "founder",
|
|
5230
|
+
surface_count: presetResult.surfaceResults.length
|
|
5231
|
+
});
|
|
5232
|
+
await safeTrackWizardTelemetry("skills_installed", {
|
|
5233
|
+
changed_count: countSkillReportChanges(presetResult.skillReport),
|
|
5234
|
+
pack_count: presetResult.skillReport.packs.length,
|
|
5235
|
+
preset: "founder",
|
|
5236
|
+
write_count: presetResult.skillReport.writes.length
|
|
5237
|
+
});
|
|
5238
|
+
if (presetResult.workspaceSetup) {
|
|
5239
|
+
await safeTrackWizardTelemetry(
|
|
5240
|
+
"workspace_bootstrapped",
|
|
5241
|
+
buildWorkspaceSetupTelemetryProperties(presetResult.workspaceSetup, {
|
|
5242
|
+
command: "setup",
|
|
5243
|
+
interactive,
|
|
5244
|
+
preset: "founder"
|
|
5245
|
+
})
|
|
5246
|
+
);
|
|
5247
|
+
}
|
|
5248
|
+
if (presetResult.demoInitiative) {
|
|
5249
|
+
await safeTrackWizardTelemetry(
|
|
5250
|
+
"founder_demo_ready",
|
|
5251
|
+
buildFounderDemoTelemetryProperties(presetResult.demoInitiative, {
|
|
5252
|
+
command: "setup",
|
|
5253
|
+
preset: "founder"
|
|
5254
|
+
})
|
|
5255
|
+
);
|
|
5256
|
+
}
|
|
5257
|
+
const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
|
|
5258
|
+
interactive,
|
|
5259
|
+
telemetry: { command: "setup", preset: "founder" },
|
|
5260
|
+
workspace: presetResult.workspace,
|
|
5261
|
+
...presetResult.demoInitiative ? { initiativeId: presetResult.demoInitiative.initiative.id } : {}
|
|
5262
|
+
});
|
|
5263
|
+
if (addOnResult === "cancelled") {
|
|
5264
|
+
return;
|
|
5265
|
+
}
|
|
5266
|
+
await installSelectedCompanionPlugins({
|
|
5267
|
+
telemetry: { command: "setup", preset: "founder" },
|
|
5268
|
+
targets: founderPluginTargets
|
|
5269
|
+
});
|
|
3340
5270
|
console.log("");
|
|
3341
5271
|
const doctor2 = await runDoctor();
|
|
3342
5272
|
const assessment2 = assessDoctorReport(doctor2);
|
|
5273
|
+
const verification2 = summarizeSetupVerification(assessment2);
|
|
5274
|
+
await safeTrackWizardTelemetry(
|
|
5275
|
+
"setup_verified",
|
|
5276
|
+
buildDoctorTelemetryProperties(doctor2, assessment2, verification2, {
|
|
5277
|
+
command: "setup",
|
|
5278
|
+
preset: "founder"
|
|
5279
|
+
})
|
|
5280
|
+
);
|
|
3343
5281
|
printDoctorReport(doctor2, assessment2);
|
|
3344
|
-
if (
|
|
5282
|
+
if (verification2.status === "error") {
|
|
3345
5283
|
process.exitCode = 1;
|
|
3346
5284
|
}
|
|
3347
5285
|
return;
|
|
@@ -3351,10 +5289,14 @@ async function main() {
|
|
|
3351
5289
|
const results = await setupDetectedSurfaces();
|
|
3352
5290
|
spinner.succeed("Detected surfaces configured");
|
|
3353
5291
|
printMutationResults(results);
|
|
5292
|
+
await safeTrackWizardTelemetry("mcp_injected", {
|
|
5293
|
+
changed_count: results.filter((result) => result.changed).length,
|
|
5294
|
+
preset: "standard",
|
|
5295
|
+
surface_count: results.length
|
|
5296
|
+
});
|
|
3354
5297
|
let resolvedAuth = await resolveOrgxAuth();
|
|
3355
5298
|
if (!resolvedAuth) {
|
|
3356
5299
|
console.log("");
|
|
3357
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
3358
5300
|
if (interactive) {
|
|
3359
5301
|
const choice = await selectPrompt({
|
|
3360
5302
|
message: "Connect your OrgX account to enable workspace and AI tool access",
|
|
@@ -3368,7 +5310,7 @@ async function main() {
|
|
|
3368
5310
|
return;
|
|
3369
5311
|
}
|
|
3370
5312
|
if (choice === "login") {
|
|
3371
|
-
const loginOk = await runBrowserLogin();
|
|
5313
|
+
const loginOk = await runBrowserLogin({ telemetrySource: "browser_pairing" });
|
|
3372
5314
|
if (!loginOk) {
|
|
3373
5315
|
console.log(`
|
|
3374
5316
|
${pc3.dim("\u2192")} ${pc3.cyan(`${getCmd()} auth login`)} ${pc3.dim("to try again")}`);
|
|
@@ -3404,25 +5346,60 @@ async function main() {
|
|
|
3404
5346
|
text: textPrompt
|
|
3405
5347
|
},
|
|
3406
5348
|
{
|
|
3407
|
-
interactive
|
|
5349
|
+
interactive
|
|
3408
5350
|
}
|
|
3409
5351
|
);
|
|
3410
5352
|
if (workspaceSetup.status === "cancelled") {
|
|
3411
5353
|
return;
|
|
3412
5354
|
}
|
|
5355
|
+
await safeTrackWizardTelemetry(
|
|
5356
|
+
"workspace_bootstrapped",
|
|
5357
|
+
buildWorkspaceSetupTelemetryProperties(workspaceSetup, {
|
|
5358
|
+
command: "setup",
|
|
5359
|
+
interactive,
|
|
5360
|
+
preset: "standard"
|
|
5361
|
+
})
|
|
5362
|
+
);
|
|
5363
|
+
const resolvedWorkspace = workspaceSetup.workspace ?? await getCurrentWorkspace().catch(() => null);
|
|
5364
|
+
if (resolvedWorkspace) {
|
|
5365
|
+
persistContinuityDefaults({ workspace: resolvedWorkspace });
|
|
5366
|
+
}
|
|
3413
5367
|
if (workspaceSetup.workspace) {
|
|
3414
5368
|
console.log(` ${ICON.ok} ${pc3.green("workspace ")} ${pc3.bold(workspaceSetup.workspace.name)}`);
|
|
3415
5369
|
}
|
|
5370
|
+
const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
|
|
5371
|
+
interactive,
|
|
5372
|
+
telemetry: { command: "setup", preset: "standard" },
|
|
5373
|
+
workspace: resolvedWorkspace
|
|
5374
|
+
});
|
|
5375
|
+
if (addOnResult === "cancelled") {
|
|
5376
|
+
return;
|
|
5377
|
+
}
|
|
5378
|
+
const pluginInstallResult = await maybeInstallOptionalCompanionPlugins({
|
|
5379
|
+
interactive,
|
|
5380
|
+
telemetry: { command: "setup", preset: "standard" }
|
|
5381
|
+
});
|
|
5382
|
+
if (pluginInstallResult === "cancelled") {
|
|
5383
|
+
return;
|
|
5384
|
+
}
|
|
3416
5385
|
}
|
|
3417
5386
|
const doctor = await runDoctor();
|
|
3418
5387
|
const assessment = assessDoctorReport(doctor);
|
|
5388
|
+
const verification = summarizeSetupVerification(assessment);
|
|
5389
|
+
await safeTrackWizardTelemetry(
|
|
5390
|
+
"setup_verified",
|
|
5391
|
+
buildDoctorTelemetryProperties(doctor, assessment, verification, {
|
|
5392
|
+
command: "setup",
|
|
5393
|
+
preset: "standard"
|
|
5394
|
+
})
|
|
5395
|
+
);
|
|
3419
5396
|
const configuredCount = doctor.surfaces.filter((s) => s.configured).length;
|
|
3420
5397
|
console.log("");
|
|
3421
5398
|
if (assessment.issues.length === 0) {
|
|
3422
5399
|
console.log(` ${ICON.ok} ${pc3.green("You're all set.")} ${pc3.dim(`OrgX is active across ${configuredCount} editor${configuredCount !== 1 ? "s" : ""}`)}`);
|
|
3423
5400
|
} else {
|
|
3424
5401
|
printDoctorReport(doctor, assessment);
|
|
3425
|
-
if (
|
|
5402
|
+
if (verification.status === "error") {
|
|
3426
5403
|
process.exitCode = 1;
|
|
3427
5404
|
}
|
|
3428
5405
|
}
|
|
@@ -3460,7 +5437,8 @@ async function main() {
|
|
|
3460
5437
|
});
|
|
3461
5438
|
spinner.text = "Verifying API key...";
|
|
3462
5439
|
const result = await verifyAndPersistAuth(ready.key, {
|
|
3463
|
-
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {}
|
|
5440
|
+
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
|
|
5441
|
+
...opts.telemetrySource ? { telemetrySource: opts.telemetrySource } : {}
|
|
3464
5442
|
});
|
|
3465
5443
|
if (!("stored" in result)) {
|
|
3466
5444
|
spinner.fail("Pairing completed but key was rejected");
|
|
@@ -3509,7 +5487,10 @@ async function main() {
|
|
|
3509
5487
|
if (options.apiKey) {
|
|
3510
5488
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|
|
3511
5489
|
spinner.start();
|
|
3512
|
-
const result = await verifyAndPersistAuth(options.apiKey,
|
|
5490
|
+
const result = await verifyAndPersistAuth(options.apiKey, {
|
|
5491
|
+
...options,
|
|
5492
|
+
telemetrySource: "manual_api_key"
|
|
5493
|
+
});
|
|
3513
5494
|
if (!("stored" in result)) {
|
|
3514
5495
|
spinner.fail("OrgX API key verification failed");
|
|
3515
5496
|
printAuthStatus(result.verification);
|
|
@@ -3528,13 +5509,17 @@ async function main() {
|
|
|
3528
5509
|
...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
|
|
3529
5510
|
...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
|
|
3530
5511
|
...options.open !== void 0 ? { open: options.open } : {},
|
|
5512
|
+
telemetrySource: "browser_pairing",
|
|
3531
5513
|
timeout: options.timeout
|
|
3532
5514
|
});
|
|
3533
5515
|
});
|
|
3534
5516
|
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) => {
|
|
3535
5517
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|
|
3536
5518
|
spinner.start();
|
|
3537
|
-
const result = await verifyAndPersistAuth(apiKey,
|
|
5519
|
+
const result = await verifyAndPersistAuth(apiKey, {
|
|
5520
|
+
...options,
|
|
5521
|
+
telemetrySource: "manual_api_key"
|
|
5522
|
+
});
|
|
3538
5523
|
if (!("stored" in result)) {
|
|
3539
5524
|
spinner.fail("OrgX API key verification failed");
|
|
3540
5525
|
printAuthStatus(result.verification);
|
|
@@ -3583,6 +5568,41 @@ async function main() {
|
|
|
3583
5568
|
const results = removeMcpSurface(names);
|
|
3584
5569
|
printMutationResults(results);
|
|
3585
5570
|
});
|
|
5571
|
+
const plugins = program.command("plugins").description("Install or remove companion OrgX plugins for Claude Code, Codex, and OpenClaw.");
|
|
5572
|
+
plugins.command("list").description("Show companion plugin availability and install status.").action(async () => {
|
|
5573
|
+
const spinner = createOrgxSpinner("Checking companion plugin status");
|
|
5574
|
+
spinner.start();
|
|
5575
|
+
const statuses = await listOrgxPluginStatuses();
|
|
5576
|
+
spinner.stop();
|
|
5577
|
+
printPluginStatusReport(statuses);
|
|
5578
|
+
});
|
|
5579
|
+
plugins.command("add").description("Install OrgX companion plugins into Claude Code, Codex, or OpenClaw.").argument("[targets...]", "claude, codex, openclaw, or all", ["all"]).action(async (targets) => {
|
|
5580
|
+
const spinner = createOrgxSpinner("Installing OrgX companion plugins");
|
|
5581
|
+
spinner.start();
|
|
5582
|
+
const report = await installOrgxPlugins({ targets });
|
|
5583
|
+
spinner.succeed("OrgX companion plugins processed");
|
|
5584
|
+
printPluginMutationReport(report);
|
|
5585
|
+
printPluginSkillOwnershipNote(report.results.map((result) => result.target));
|
|
5586
|
+
await safeTrackWizardTelemetry("plugins_installed", {
|
|
5587
|
+
changed_count: countPluginReportChanges(report),
|
|
5588
|
+
command: "plugins:add",
|
|
5589
|
+
requested_target_count: targets.length,
|
|
5590
|
+
target_count: report.results.length
|
|
5591
|
+
});
|
|
5592
|
+
});
|
|
5593
|
+
plugins.command("remove").description("Uninstall managed OrgX companion plugins from Claude Code, Codex, or OpenClaw.").argument("[targets...]", "claude, codex, openclaw, or all", ["all"]).action(async (targets) => {
|
|
5594
|
+
const spinner = createOrgxSpinner("Removing OrgX companion plugins");
|
|
5595
|
+
spinner.start();
|
|
5596
|
+
const report = await uninstallOrgxPlugins({ targets });
|
|
5597
|
+
spinner.succeed("OrgX companion plugins removed");
|
|
5598
|
+
printPluginMutationReport(report);
|
|
5599
|
+
await safeTrackWizardTelemetry("plugins_removed", {
|
|
5600
|
+
changed_count: countPluginReportChanges(report),
|
|
5601
|
+
command: "plugins:remove",
|
|
5602
|
+
requested_target_count: targets.length,
|
|
5603
|
+
target_count: report.results.length
|
|
5604
|
+
});
|
|
5605
|
+
});
|
|
3586
5606
|
const workspace = program.command("workspace").description("Inspect and create OrgX workspaces.");
|
|
3587
5607
|
workspace.command("current").description("Show the current OrgX workspace.").action(async () => {
|
|
3588
5608
|
const spinner = createOrgxSpinner("Loading current OrgX workspace");
|
|
@@ -3612,6 +5632,9 @@ async function main() {
|
|
|
3612
5632
|
}
|
|
3613
5633
|
);
|
|
3614
5634
|
spinner.succeed("OrgX workspace created");
|
|
5635
|
+
if (created.isDefault) {
|
|
5636
|
+
persistContinuityDefaults({ workspace: created });
|
|
5637
|
+
}
|
|
3615
5638
|
printWorkspace(created);
|
|
3616
5639
|
if (!created.isDefault) {
|
|
3617
5640
|
console.log("");
|
|
@@ -3625,6 +5648,7 @@ async function main() {
|
|
|
3625
5648
|
spinner.succeed(
|
|
3626
5649
|
result.changed ? "Default OrgX workspace updated" : "OrgX workspace already set as default"
|
|
3627
5650
|
);
|
|
5651
|
+
persistContinuityDefaults({ workspace: result.workspace });
|
|
3628
5652
|
printWorkspace(result.workspace);
|
|
3629
5653
|
});
|
|
3630
5654
|
program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
|
|
@@ -3633,18 +5657,37 @@ async function main() {
|
|
|
3633
5657
|
const report = await runDoctor();
|
|
3634
5658
|
spinner.stop();
|
|
3635
5659
|
const assessment = assessDoctorReport(report);
|
|
5660
|
+
const verification = summarizeSetupVerification(assessment);
|
|
5661
|
+
await safeTrackWizardTelemetry(
|
|
5662
|
+
"doctor_ran",
|
|
5663
|
+
buildDoctorTelemetryProperties(report, assessment, verification, {
|
|
5664
|
+
command: "doctor"
|
|
5665
|
+
})
|
|
5666
|
+
);
|
|
3636
5667
|
printDoctorReport(report, assessment);
|
|
3637
|
-
if (
|
|
5668
|
+
if (verification.status === "error") {
|
|
3638
5669
|
process.exitCode = 1;
|
|
3639
5670
|
}
|
|
3640
5671
|
});
|
|
3641
5672
|
const skills = program.command("skills").description("Install OrgX rules and Claude skill packs.");
|
|
3642
|
-
skills.command("add").description("Write
|
|
5673
|
+
skills.command("add").description("Write standalone OrgX editor rules and Claude skill packs, skipping surfaces already owned by companion plugins.").argument("[packs...]", "skill pack names or 'all'", ["all"]).action(async (packs) => {
|
|
5674
|
+
const pluginTargets = (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
|
|
3643
5675
|
const spinner = createOrgxSpinner("Installing OrgX rules and skills");
|
|
3644
5676
|
spinner.start();
|
|
3645
|
-
const report = await installOrgxSkills({
|
|
5677
|
+
const report = await installOrgxSkills({
|
|
5678
|
+
pluginTargets,
|
|
5679
|
+
skillNames: packs
|
|
5680
|
+
});
|
|
3646
5681
|
spinner.succeed("OrgX rules and skills installed");
|
|
3647
5682
|
printSkillInstallReport(report);
|
|
5683
|
+
await safeTrackWizardTelemetry("skills_installed", {
|
|
5684
|
+
changed_count: countSkillReportChanges(report),
|
|
5685
|
+
command: "skills:add",
|
|
5686
|
+
pack_count: report.packs.length,
|
|
5687
|
+
plugin_managed_target_count: pluginTargets.length,
|
|
5688
|
+
requested_pack_count: packs.length,
|
|
5689
|
+
write_count: report.writes.length
|
|
5690
|
+
});
|
|
3648
5691
|
});
|
|
3649
5692
|
await program.parseAsync(process.argv);
|
|
3650
5693
|
}
|