@useorgx/wizard 0.1.7 → 0.1.9
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 +10 -4
- package/dist/cli.js +2414 -498
- 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,296 @@ 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.workstreamId) ? { workstreamId: record.workstreamId.trim() } : {},
|
|
944
|
+
...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
|
|
945
|
+
...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function parseAgentRosterEntry(value) {
|
|
949
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
950
|
+
return void 0;
|
|
951
|
+
}
|
|
952
|
+
const record = value;
|
|
953
|
+
if (!isNonEmptyString2(record.agentType) || !isNonEmptyString2(record.domain) || !isNonEmptyString2(record.name)) {
|
|
954
|
+
return void 0;
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
agentType: record.agentType.trim(),
|
|
958
|
+
domain: record.domain.trim(),
|
|
959
|
+
name: record.name.trim()
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function parseAgentRoster(value) {
|
|
963
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
964
|
+
return void 0;
|
|
965
|
+
}
|
|
966
|
+
const record = value;
|
|
967
|
+
if (!isNonEmptyString2(record.configuredAt) || !Array.isArray(record.agents)) {
|
|
968
|
+
return void 0;
|
|
969
|
+
}
|
|
970
|
+
const agents = record.agents.map((entry) => parseAgentRosterEntry(entry)).filter((entry) => Boolean(entry));
|
|
971
|
+
if (agents.length === 0) {
|
|
972
|
+
return void 0;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
agents,
|
|
976
|
+
configuredAt: record.configuredAt.trim(),
|
|
977
|
+
...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
|
|
978
|
+
...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function createWizardState(now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
982
|
+
return {
|
|
983
|
+
installationId: `wizard-${randomUUID()}`,
|
|
984
|
+
createdAt: now,
|
|
985
|
+
updatedAt: now
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
function sanitizeWizardStateRecord(record) {
|
|
989
|
+
const continuity = parseContinuityDefaults(record.continuity);
|
|
990
|
+
const agentRoster = parseAgentRoster(record.agentRoster);
|
|
991
|
+
const demoInitiative = parseDemoInitiative(record.demoInitiative);
|
|
992
|
+
const onboardingTask = parseOnboardingTask(record.onboardingTask);
|
|
993
|
+
return {
|
|
994
|
+
installationId: record.installationId.trim(),
|
|
995
|
+
createdAt: record.createdAt.trim(),
|
|
996
|
+
updatedAt: record.updatedAt.trim(),
|
|
997
|
+
...continuity ? { continuity } : {},
|
|
998
|
+
...agentRoster ? { agentRoster } : {},
|
|
999
|
+
...demoInitiative ? { demoInitiative } : {},
|
|
1000
|
+
...onboardingTask ? { onboardingTask } : {}
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1004
|
+
const parsed = parseJsonObject(readTextIfExists(statePath));
|
|
1005
|
+
if (!isNonEmptyString2(parsed.installationId) || !isNonEmptyString2(parsed.createdAt) || !isNonEmptyString2(parsed.updatedAt)) {
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
const state = {
|
|
1009
|
+
installationId: parsed.installationId.trim(),
|
|
1010
|
+
createdAt: parsed.createdAt.trim(),
|
|
1011
|
+
updatedAt: parsed.updatedAt.trim()
|
|
1012
|
+
};
|
|
1013
|
+
const continuity = parseContinuityDefaults(parsed.continuity);
|
|
1014
|
+
if (continuity !== void 0) state.continuity = continuity;
|
|
1015
|
+
const agentRoster = parseAgentRoster(parsed.agentRoster);
|
|
1016
|
+
if (agentRoster !== void 0) state.agentRoster = agentRoster;
|
|
1017
|
+
const demoInitiative = parseDemoInitiative(parsed.demoInitiative);
|
|
1018
|
+
if (demoInitiative !== void 0) state.demoInitiative = demoInitiative;
|
|
1019
|
+
const onboardingTask = parseOnboardingTask(parsed.onboardingTask);
|
|
1020
|
+
if (onboardingTask !== void 0) state.onboardingTask = onboardingTask;
|
|
1021
|
+
return state;
|
|
1022
|
+
}
|
|
1023
|
+
function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1024
|
+
const record = sanitizeWizardStateRecord(value);
|
|
1025
|
+
writeJsonFile(statePath, record, { mode: 384 });
|
|
1026
|
+
return record;
|
|
1027
|
+
}
|
|
1028
|
+
function updateWizardState(updater, statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1029
|
+
const current = readWizardState(statePath) ?? createWizardState();
|
|
1030
|
+
const next = updater(current);
|
|
1031
|
+
const sanitized = sanitizeWizardStateRecord({
|
|
1032
|
+
...next,
|
|
1033
|
+
installationId: next.installationId || current.installationId,
|
|
1034
|
+
createdAt: next.createdAt || current.createdAt,
|
|
1035
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1036
|
+
});
|
|
1037
|
+
writeJsonFile(statePath, sanitized, { mode: 384 });
|
|
1038
|
+
return sanitized;
|
|
1039
|
+
}
|
|
1040
|
+
function getOrCreateWizardInstallationId(statePath = ORGX_WIZARD_STATE_PATH) {
|
|
1041
|
+
const existing = readWizardState(statePath);
|
|
1042
|
+
if (existing) {
|
|
1043
|
+
return existing.installationId;
|
|
1044
|
+
}
|
|
1045
|
+
const record = createWizardState();
|
|
1046
|
+
writeWizardState(record, statePath);
|
|
1047
|
+
return record.installationId;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// src/lib/agent-roster.ts
|
|
1051
|
+
var AGENT_ROSTER_PROFILES = [
|
|
1052
|
+
{ agentType: "orchestrator-agent", defaultDomain: "orchestrator", name: "Xandy" },
|
|
1053
|
+
{ agentType: "product-agent", defaultDomain: "product", name: "Pace" },
|
|
1054
|
+
{ agentType: "engineering-agent", defaultDomain: "engineering", name: "Eli" },
|
|
1055
|
+
{ agentType: "marketing-agent", defaultDomain: "marketing", name: "Mark" },
|
|
1056
|
+
{ agentType: "sales-agent", defaultDomain: "sales", name: "Sage" },
|
|
1057
|
+
{ agentType: "operations-agent", defaultDomain: "operations", name: "Orion" },
|
|
1058
|
+
{ agentType: "design-agent", defaultDomain: "design", name: "Dana" }
|
|
1059
|
+
];
|
|
1060
|
+
var AGENT_ROSTER_DOMAINS = AGENT_ROSTER_PROFILES.map((profile) => profile.defaultDomain);
|
|
1061
|
+
var DEFAULT_AGENT_ROSTER = AGENT_ROSTER_PROFILES.map(
|
|
1062
|
+
({ agentType, defaultDomain, name }) => ({
|
|
1063
|
+
agentType,
|
|
1064
|
+
domain: defaultDomain,
|
|
1065
|
+
name
|
|
1066
|
+
})
|
|
1067
|
+
);
|
|
1068
|
+
function findAgentRosterProfile(name) {
|
|
1069
|
+
return AGENT_ROSTER_PROFILES.find((profile) => profile.name === name);
|
|
1070
|
+
}
|
|
1071
|
+
function listAgentRosterProfiles() {
|
|
1072
|
+
return AGENT_ROSTER_PROFILES.map((profile) => ({ ...profile }));
|
|
1073
|
+
}
|
|
1074
|
+
function buildAgentRosterEntries(assignments) {
|
|
1075
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1076
|
+
return assignments.map((assignment) => {
|
|
1077
|
+
const name = assignment.name.trim();
|
|
1078
|
+
const domain = assignment.domain.trim();
|
|
1079
|
+
if (domain.length === 0) {
|
|
1080
|
+
throw new Error("OrgX agent roster domains must be non-empty.");
|
|
1081
|
+
}
|
|
1082
|
+
const profile = findAgentRosterProfile(name);
|
|
1083
|
+
if (!profile) {
|
|
1084
|
+
throw new Error(`Unknown OrgX agent '${assignment.name}'.`);
|
|
1085
|
+
}
|
|
1086
|
+
if (seenNames.has(profile.name)) {
|
|
1087
|
+
throw new Error(`OrgX agent '${profile.name}' can only be assigned once.`);
|
|
1088
|
+
}
|
|
1089
|
+
seenNames.add(profile.name);
|
|
1090
|
+
return {
|
|
1091
|
+
agentType: profile.agentType,
|
|
1092
|
+
domain,
|
|
1093
|
+
name: profile.name
|
|
1094
|
+
};
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
function toAgentRosterRecord(workspace, agents) {
|
|
1098
|
+
return {
|
|
1099
|
+
agents: agents.map((agent) => ({ ...agent })),
|
|
1100
|
+
configuredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1101
|
+
workspaceId: workspace.id,
|
|
1102
|
+
workspaceName: workspace.name
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function configureAgentRoster(workspace, assignments, options = {}) {
|
|
1106
|
+
const agents = buildAgentRosterEntries(assignments);
|
|
1107
|
+
if (agents.length === 0) {
|
|
1108
|
+
throw new Error("Select at least one OrgX agent before saving the roster.");
|
|
1109
|
+
}
|
|
1110
|
+
const roster = toAgentRosterRecord(workspace, agents);
|
|
1111
|
+
updateWizardState(
|
|
1112
|
+
(current) => ({
|
|
1113
|
+
...current,
|
|
1114
|
+
agentRoster: roster
|
|
1115
|
+
}),
|
|
1116
|
+
options.statePath
|
|
1117
|
+
);
|
|
1118
|
+
return roster;
|
|
1119
|
+
}
|
|
1120
|
+
function ensureDefaultAgentRoster(workspace, options = {}) {
|
|
1121
|
+
const existing = readWizardState(options.statePath)?.agentRoster;
|
|
1122
|
+
if (existing?.workspaceId === workspace.id && existing.agents.length > 0) {
|
|
1123
|
+
return existing;
|
|
1124
|
+
}
|
|
1125
|
+
const roster = toAgentRosterRecord(workspace, DEFAULT_AGENT_ROSTER);
|
|
1126
|
+
updateWizardState(
|
|
1127
|
+
(current) => ({
|
|
1128
|
+
...current,
|
|
1129
|
+
agentRoster: roster
|
|
1130
|
+
}),
|
|
1131
|
+
options.statePath
|
|
1132
|
+
);
|
|
1133
|
+
return roster;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
748
1136
|
// src/lib/openclaw-health.ts
|
|
749
1137
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
750
1138
|
function trimOutput(value) {
|
|
@@ -852,39 +1240,6 @@ async function checkHostedMcpHealth() {
|
|
|
852
1240
|
|
|
853
1241
|
// src/lib/hosted-mcp-tool-check.ts
|
|
854
1242
|
var DEFAULT_TOOL_NAME = "get_setup_status";
|
|
855
|
-
function buildHeaders(apiKey, sessionId) {
|
|
856
|
-
const headers = {
|
|
857
|
-
Authorization: `Bearer ${apiKey}`,
|
|
858
|
-
"Content-Type": "application/json",
|
|
859
|
-
Accept: "application/json, text/event-stream"
|
|
860
|
-
};
|
|
861
|
-
if (sessionId) {
|
|
862
|
-
headers["Mcp-Session-Id"] = sessionId;
|
|
863
|
-
}
|
|
864
|
-
return headers;
|
|
865
|
-
}
|
|
866
|
-
async function readJsonSafe(response) {
|
|
867
|
-
const text2 = await response.text();
|
|
868
|
-
if (!text2) return null;
|
|
869
|
-
try {
|
|
870
|
-
return JSON.parse(text2);
|
|
871
|
-
} catch {
|
|
872
|
-
return text2;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
function readJsonRpcError(payload) {
|
|
876
|
-
if (!payload || typeof payload !== "object") {
|
|
877
|
-
return void 0;
|
|
878
|
-
}
|
|
879
|
-
const maybeError = payload.error;
|
|
880
|
-
if (!maybeError || typeof maybeError !== "object") {
|
|
881
|
-
return void 0;
|
|
882
|
-
}
|
|
883
|
-
return typeof maybeError.message === "string" ? maybeError.message : "Unknown MCP JSON-RPC error.";
|
|
884
|
-
}
|
|
885
|
-
function readSessionId(response) {
|
|
886
|
-
return response.headers.get("mcp-session-id") ?? response.headers.get("Mcp-Session-Id") ?? void 0;
|
|
887
|
-
}
|
|
888
1243
|
async function checkHostedMcpToolAccess(options = {}, toolName = DEFAULT_TOOL_NAME) {
|
|
889
1244
|
const auth = await resolveOrgxAuth(options);
|
|
890
1245
|
if (!auth) {
|
|
@@ -899,157 +1254,20 @@ async function checkHostedMcpToolAccess(options = {}, toolName = DEFAULT_TOOL_NA
|
|
|
899
1254
|
details: []
|
|
900
1255
|
};
|
|
901
1256
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
capabilities: {}
|
|
917
|
-
}
|
|
918
|
-
}),
|
|
919
|
-
signal: AbortSignal.timeout(1e4)
|
|
920
|
-
});
|
|
921
|
-
const initializePayload = await readJsonSafe(initializeResponse);
|
|
922
|
-
const initializeError = readJsonRpcError(initializePayload);
|
|
923
|
-
const sessionId = readSessionId(initializeResponse);
|
|
924
|
-
if (!initializeResponse.ok) {
|
|
925
|
-
return {
|
|
926
|
-
configured: true,
|
|
927
|
-
ok: false,
|
|
928
|
-
skipped: false,
|
|
929
|
-
source: auth.source,
|
|
930
|
-
toolName,
|
|
931
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
932
|
-
baseUrl: auth.baseUrl,
|
|
933
|
-
initializeStatus: initializeResponse.status,
|
|
934
|
-
error: `MCP initialize failed with HTTP ${initializeResponse.status}.`,
|
|
935
|
-
details: [`auth source: ${auth.source}`],
|
|
936
|
-
response: initializePayload
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
if (initializeError) {
|
|
940
|
-
return {
|
|
941
|
-
configured: true,
|
|
942
|
-
ok: false,
|
|
943
|
-
skipped: false,
|
|
944
|
-
source: auth.source,
|
|
945
|
-
toolName,
|
|
946
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
947
|
-
baseUrl: auth.baseUrl,
|
|
948
|
-
initializeStatus: initializeResponse.status,
|
|
949
|
-
error: `MCP initialize returned JSON-RPC error: ${initializeError}`,
|
|
950
|
-
details: [`auth source: ${auth.source}`],
|
|
951
|
-
response: initializePayload
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
if (!sessionId) {
|
|
955
|
-
return {
|
|
956
|
-
configured: true,
|
|
957
|
-
ok: false,
|
|
958
|
-
skipped: false,
|
|
959
|
-
source: auth.source,
|
|
960
|
-
toolName,
|
|
961
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
962
|
-
baseUrl: auth.baseUrl,
|
|
963
|
-
initializeStatus: initializeResponse.status,
|
|
964
|
-
error: "MCP initialize succeeded but no Mcp-Session-Id header was returned.",
|
|
965
|
-
details: [`auth source: ${auth.source}`],
|
|
966
|
-
response: initializePayload
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
const callResponse = await fetch(ORGX_HOSTED_MCP_URL, {
|
|
970
|
-
method: "POST",
|
|
971
|
-
headers: buildHeaders(auth.apiKey, sessionId),
|
|
972
|
-
body: JSON.stringify({
|
|
973
|
-
jsonrpc: "2.0",
|
|
974
|
-
id: 2,
|
|
975
|
-
method: "tools/call",
|
|
976
|
-
params: {
|
|
977
|
-
name: toolName,
|
|
978
|
-
arguments: {}
|
|
979
|
-
}
|
|
980
|
-
}),
|
|
981
|
-
signal: AbortSignal.timeout(1e4)
|
|
982
|
-
});
|
|
983
|
-
const callPayload = await readJsonSafe(callResponse);
|
|
984
|
-
const callError = readJsonRpcError(callPayload);
|
|
985
|
-
if (!callResponse.ok) {
|
|
986
|
-
return {
|
|
987
|
-
configured: true,
|
|
988
|
-
ok: false,
|
|
989
|
-
skipped: false,
|
|
990
|
-
source: auth.source,
|
|
991
|
-
toolName,
|
|
992
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
993
|
-
baseUrl: auth.baseUrl,
|
|
994
|
-
initializeStatus: initializeResponse.status,
|
|
995
|
-
callStatus: callResponse.status,
|
|
996
|
-
error: `MCP tool call failed with HTTP ${callResponse.status}.`,
|
|
997
|
-
details: [
|
|
998
|
-
`auth source: ${auth.source}`,
|
|
999
|
-
`session established: ${sessionId}`
|
|
1000
|
-
],
|
|
1001
|
-
response: callPayload
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
if (callError) {
|
|
1005
|
-
return {
|
|
1006
|
-
configured: true,
|
|
1007
|
-
ok: false,
|
|
1008
|
-
skipped: false,
|
|
1009
|
-
source: auth.source,
|
|
1010
|
-
toolName,
|
|
1011
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
1012
|
-
baseUrl: auth.baseUrl,
|
|
1013
|
-
initializeStatus: initializeResponse.status,
|
|
1014
|
-
callStatus: callResponse.status,
|
|
1015
|
-
error: `MCP tool call returned JSON-RPC error: ${callError}`,
|
|
1016
|
-
details: [
|
|
1017
|
-
`auth source: ${auth.source}`,
|
|
1018
|
-
`session established: ${sessionId}`
|
|
1019
|
-
],
|
|
1020
|
-
response: callPayload
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
return {
|
|
1024
|
-
configured: true,
|
|
1025
|
-
ok: true,
|
|
1026
|
-
skipped: false,
|
|
1027
|
-
source: auth.source,
|
|
1028
|
-
toolName,
|
|
1029
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
1030
|
-
baseUrl: auth.baseUrl,
|
|
1031
|
-
initializeStatus: initializeResponse.status,
|
|
1032
|
-
callStatus: callResponse.status,
|
|
1033
|
-
details: [
|
|
1034
|
-
`auth source: ${auth.source}`,
|
|
1035
|
-
`session established: ${sessionId}`,
|
|
1036
|
-
`tool call: ${toolName}`
|
|
1037
|
-
],
|
|
1038
|
-
response: callPayload
|
|
1039
|
-
};
|
|
1040
|
-
} catch (error) {
|
|
1041
|
-
return {
|
|
1042
|
-
configured: true,
|
|
1043
|
-
ok: false,
|
|
1044
|
-
skipped: false,
|
|
1045
|
-
source: auth.source,
|
|
1046
|
-
toolName,
|
|
1047
|
-
url: ORGX_HOSTED_MCP_URL,
|
|
1048
|
-
baseUrl: auth.baseUrl,
|
|
1049
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1050
|
-
details: [`auth source: ${auth.source}`]
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1257
|
+
return {
|
|
1258
|
+
configured: true,
|
|
1259
|
+
ok: false,
|
|
1260
|
+
skipped: true,
|
|
1261
|
+
source: auth.source,
|
|
1262
|
+
toolName,
|
|
1263
|
+
url: ORGX_HOSTED_MCP_URL,
|
|
1264
|
+
baseUrl: auth.baseUrl,
|
|
1265
|
+
error: "Hosted OrgX MCP uses OAuth; API-key tool verification is skipped.",
|
|
1266
|
+
details: [
|
|
1267
|
+
`auth source: ${auth.source}`,
|
|
1268
|
+
"Hosted OrgX MCP connectors complete OAuth during client setup, so the wizard cannot preflight tools with an oxk_ API key."
|
|
1269
|
+
]
|
|
1270
|
+
};
|
|
1053
1271
|
}
|
|
1054
1272
|
|
|
1055
1273
|
// src/lib/npm-registry-health.ts
|
|
@@ -1246,7 +1464,7 @@ async function getCurrentWorkspace(options = {}) {
|
|
|
1246
1464
|
}
|
|
1247
1465
|
return workspace;
|
|
1248
1466
|
}
|
|
1249
|
-
if (response.status === 404) {
|
|
1467
|
+
if (response.status === 401 || response.status === 404) {
|
|
1250
1468
|
const workspaces = await listWorkspaces(options);
|
|
1251
1469
|
return workspaces[0] ?? null;
|
|
1252
1470
|
}
|
|
@@ -1714,35 +1932,6 @@ function inspectCodexConfigToml(input) {
|
|
|
1714
1932
|
};
|
|
1715
1933
|
}
|
|
1716
1934
|
|
|
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
1935
|
// src/surfaces/detection.ts
|
|
1747
1936
|
import { existsSync as existsSync2 } from "fs";
|
|
1748
1937
|
import { dirname as dirname2 } from "path";
|
|
@@ -1823,6 +2012,9 @@ function detectSurface(name, exists = existsSync2) {
|
|
|
1823
2012
|
}
|
|
1824
2013
|
|
|
1825
2014
|
// src/surfaces/registry.ts
|
|
2015
|
+
var AUTH_SETUP_HINT = "orgx-wizard auth login";
|
|
2016
|
+
var AUTH_SET_KEY_HINT = "orgx-wizard auth set-key";
|
|
2017
|
+
var DOCTOR_HINT = "orgx-wizard doctor";
|
|
1826
2018
|
function getSurfacePath(name) {
|
|
1827
2019
|
const detection = detectSurface(name);
|
|
1828
2020
|
const locator = getSurfaceLocator(name);
|
|
@@ -1951,27 +2143,95 @@ function automatedSurfaceStatus(name) {
|
|
|
1951
2143
|
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
2144
|
};
|
|
1953
2145
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
2146
|
+
function formatAuthHintSource(authHint) {
|
|
2147
|
+
switch (authHint.source) {
|
|
2148
|
+
case "environment":
|
|
2149
|
+
return "env ORGX_API_KEY";
|
|
2150
|
+
case "wizard-store":
|
|
2151
|
+
return "wizard auth store";
|
|
2152
|
+
case "openclaw-config-file":
|
|
2153
|
+
return "openclaw config";
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
function buildChatgptManualStatus(detection, authHint) {
|
|
1956
2157
|
const path = detection.existingPath ?? detection.preferredPath;
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2158
|
+
const details = [...detection.evidence];
|
|
2159
|
+
if (authHint) {
|
|
2160
|
+
details.push(
|
|
2161
|
+
`OrgX auth ready via ${formatAuthHintSource(authHint)} (${authHint.keyPrefix})`
|
|
2162
|
+
);
|
|
2163
|
+
details.push(`Hosted connector URL: ${ORGX_HOSTED_MCP_URL}`);
|
|
2164
|
+
details.push(
|
|
2165
|
+
"Manual step: in ChatGPT desktop developer mode, add a custom connector pointing at the hosted OrgX MCP URL."
|
|
2166
|
+
);
|
|
2167
|
+
details.push(
|
|
2168
|
+
"ChatGPT completes OAuth during connector setup; the wizard cannot preflight hosted MCP tools with an oxk_ API key."
|
|
2169
|
+
);
|
|
2170
|
+
details.push(
|
|
2171
|
+
"After connecting ChatGPT, run `orgx-wizard doctor` to verify hosted MCP health from this machine."
|
|
2172
|
+
);
|
|
2173
|
+
} else {
|
|
2174
|
+
details.push(
|
|
2175
|
+
"OrgX auth is not configured yet. Run `orgx-wizard auth login` or `orgx-wizard auth set-key` first."
|
|
2176
|
+
);
|
|
2177
|
+
details.push(
|
|
2178
|
+
"After auth is configured, add a custom ChatGPT developer connector pointing at the hosted OrgX MCP URL."
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
let summary;
|
|
2182
|
+
if (!authHint) {
|
|
2183
|
+
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.";
|
|
2184
|
+
} else {
|
|
2185
|
+
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.";
|
|
2186
|
+
}
|
|
2187
|
+
return {
|
|
2188
|
+
name: "chatgpt",
|
|
1959
2189
|
mode: "manual",
|
|
1960
2190
|
detected: detection.detected,
|
|
1961
2191
|
configured: false,
|
|
1962
|
-
...path ? { path } : {}
|
|
2192
|
+
...path ? { path } : {},
|
|
2193
|
+
details,
|
|
2194
|
+
summary
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
function buildChatgptAddResult(path, authHint, toolCheck) {
|
|
2198
|
+
if (!authHint) {
|
|
2199
|
+
return {
|
|
2200
|
+
name: "chatgpt",
|
|
2201
|
+
changed: false,
|
|
2202
|
+
...path ? { path } : {},
|
|
2203
|
+
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.`
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
if (toolCheck && !toolCheck.ok && !toolCheck.skipped) {
|
|
2207
|
+
const failure = toolCheck?.error ?? "hosted MCP verification did not complete.";
|
|
2208
|
+
return {
|
|
2209
|
+
name: "chatgpt",
|
|
2210
|
+
changed: false,
|
|
2211
|
+
...path ? { path } : {},
|
|
2212
|
+
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.`
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
return {
|
|
2216
|
+
name: "chatgpt",
|
|
2217
|
+
changed: false,
|
|
2218
|
+
...path ? { path } : {},
|
|
2219
|
+
message: `OrgX auth is ready via ${formatAuthHintSource(authHint)}. Add ${ORGX_HOSTED_MCP_URL} as a custom connector in ChatGPT developer mode; ChatGPT will complete the OAuth flow there. Then run \`${DOCTOR_HINT}\` to verify hosted MCP health from this machine.`
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
function buildChatgptRemoveResult(path) {
|
|
2223
|
+
return {
|
|
2224
|
+
name: "chatgpt",
|
|
2225
|
+
changed: false,
|
|
2226
|
+
...path ? { path } : {},
|
|
2227
|
+
message: "Remove the custom OrgX connector from ChatGPT developer mode; the wizard does not manage ChatGPT config files directly."
|
|
1963
2228
|
};
|
|
2229
|
+
}
|
|
2230
|
+
function manualSurfaceStatus(name) {
|
|
2231
|
+
const detection = detectSurface(name);
|
|
1964
2232
|
switch (name) {
|
|
1965
2233
|
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
|
-
};
|
|
2234
|
+
return buildChatgptManualStatus(detection, peekResolvedOrgxAuth());
|
|
1975
2235
|
}
|
|
1976
2236
|
}
|
|
1977
2237
|
function getSurfaceStatus(name) {
|
|
@@ -2089,11 +2349,18 @@ async function addAutomatedSurface(name) {
|
|
|
2089
2349
|
writeTextFile(path, next);
|
|
2090
2350
|
return { name, changed: true, path, message: "OrgX cloud MCP is connected in Zed." };
|
|
2091
2351
|
}
|
|
2352
|
+
case "chatgpt": {
|
|
2353
|
+
const authHint = peekResolvedOrgxAuth();
|
|
2354
|
+
if (!authHint) {
|
|
2355
|
+
return buildChatgptAddResult(path, null);
|
|
2356
|
+
}
|
|
2357
|
+
return buildChatgptAddResult(path, authHint);
|
|
2358
|
+
}
|
|
2092
2359
|
default:
|
|
2093
2360
|
return {
|
|
2094
2361
|
name,
|
|
2095
2362
|
changed: false,
|
|
2096
|
-
message: `${name} is not
|
|
2363
|
+
message: `${name} is not supported by the wizard.`
|
|
2097
2364
|
};
|
|
2098
2365
|
}
|
|
2099
2366
|
}
|
|
@@ -2184,11 +2451,13 @@ function removeAutomatedSurface(name) {
|
|
|
2184
2451
|
message: "OrgX connection was removed from Zed."
|
|
2185
2452
|
};
|
|
2186
2453
|
}
|
|
2454
|
+
case "chatgpt":
|
|
2455
|
+
return buildChatgptRemoveResult(path);
|
|
2187
2456
|
default:
|
|
2188
2457
|
return {
|
|
2189
2458
|
name,
|
|
2190
2459
|
changed: false,
|
|
2191
|
-
message: `${name} is not
|
|
2460
|
+
message: `${name} is not supported by the wizard.`
|
|
2192
2461
|
};
|
|
2193
2462
|
}
|
|
2194
2463
|
}
|
|
@@ -2305,171 +2574,65 @@ function assessDoctorReport(report) {
|
|
|
2305
2574
|
};
|
|
2306
2575
|
}
|
|
2307
2576
|
|
|
2308
|
-
// src/lib/
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2577
|
+
// src/lib/continuity.ts
|
|
2578
|
+
var REVIEW_SURFACE_ORDER = [
|
|
2579
|
+
"claude",
|
|
2580
|
+
"cursor",
|
|
2581
|
+
"codex",
|
|
2582
|
+
"vscode",
|
|
2583
|
+
"windsurf",
|
|
2584
|
+
"zed",
|
|
2585
|
+
"chatgpt"
|
|
2586
|
+
];
|
|
2587
|
+
function selectReviewSurface(statuses) {
|
|
2588
|
+
for (const name of REVIEW_SURFACE_ORDER) {
|
|
2589
|
+
const configured = statuses.find((status) => status.name === name && status.configured);
|
|
2590
|
+
if (configured) {
|
|
2591
|
+
return configured.name;
|
|
2592
|
+
}
|
|
2324
2593
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2594
|
+
for (const name of REVIEW_SURFACE_ORDER) {
|
|
2595
|
+
const detected = statuses.find((status) => status.name === name && status.detected);
|
|
2596
|
+
if (detected) {
|
|
2597
|
+
return detected.name;
|
|
2598
|
+
}
|
|
2327
2599
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2600
|
+
return void 0;
|
|
2601
|
+
}
|
|
2602
|
+
function inferExecutionMode(explicitMode, statuses) {
|
|
2603
|
+
if (explicitMode) {
|
|
2604
|
+
return explicitMode;
|
|
2330
2605
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2606
|
+
const openclaw = statuses.find((status) => status.name === "openclaw");
|
|
2607
|
+
if (openclaw?.configured || openclaw?.detected) {
|
|
2608
|
+
return "local";
|
|
2333
2609
|
}
|
|
2334
|
-
|
|
2610
|
+
const hostedSurface = statuses.find(
|
|
2611
|
+
(status) => status.name !== "openclaw" && (status.configured || status.detected)
|
|
2612
|
+
);
|
|
2613
|
+
return hostedSurface ? "cloud" : void 0;
|
|
2335
2614
|
}
|
|
2336
|
-
function
|
|
2337
|
-
|
|
2338
|
-
|
|
2615
|
+
function inferContinuityDefaults(seed = {}) {
|
|
2616
|
+
const statuses = seed.statuses ?? listSurfaceStatuses();
|
|
2617
|
+
const continuity = {};
|
|
2618
|
+
const reviewSurface = selectReviewSurface(statuses);
|
|
2619
|
+
if (reviewSurface) {
|
|
2620
|
+
continuity.reviewSurface = reviewSurface;
|
|
2339
2621
|
}
|
|
2340
|
-
const
|
|
2341
|
-
if (
|
|
2342
|
-
|
|
2622
|
+
const executionMode = inferExecutionMode(seed.executionMode, statuses);
|
|
2623
|
+
if (executionMode) {
|
|
2624
|
+
continuity.executionMode = executionMode;
|
|
2625
|
+
}
|
|
2626
|
+
if (seed.workspace?.id) {
|
|
2627
|
+
continuity.workspaceId = seed.workspace.id;
|
|
2343
2628
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
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
|
-
// src/lib/continuity.ts
|
|
2415
|
-
var REVIEW_SURFACE_ORDER = [
|
|
2416
|
-
"claude",
|
|
2417
|
-
"cursor",
|
|
2418
|
-
"codex",
|
|
2419
|
-
"vscode",
|
|
2420
|
-
"windsurf",
|
|
2421
|
-
"zed",
|
|
2422
|
-
"chatgpt"
|
|
2423
|
-
];
|
|
2424
|
-
function selectReviewSurface(statuses) {
|
|
2425
|
-
for (const name of REVIEW_SURFACE_ORDER) {
|
|
2426
|
-
const configured = statuses.find((status) => status.name === name && status.configured);
|
|
2427
|
-
if (configured) {
|
|
2428
|
-
return configured.name;
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
for (const name of REVIEW_SURFACE_ORDER) {
|
|
2432
|
-
const detected = statuses.find((status) => status.name === name && status.detected);
|
|
2433
|
-
if (detected) {
|
|
2434
|
-
return detected.name;
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
return void 0;
|
|
2438
|
-
}
|
|
2439
|
-
function inferExecutionMode(explicitMode, statuses) {
|
|
2440
|
-
if (explicitMode) {
|
|
2441
|
-
return explicitMode;
|
|
2442
|
-
}
|
|
2443
|
-
const openclaw = statuses.find((status) => status.name === "openclaw");
|
|
2444
|
-
if (openclaw?.configured || openclaw?.detected) {
|
|
2445
|
-
return "local";
|
|
2446
|
-
}
|
|
2447
|
-
const hostedSurface = statuses.find(
|
|
2448
|
-
(status) => status.name !== "openclaw" && (status.configured || status.detected)
|
|
2449
|
-
);
|
|
2450
|
-
return hostedSurface ? "cloud" : void 0;
|
|
2451
|
-
}
|
|
2452
|
-
function inferContinuityDefaults(seed = {}) {
|
|
2453
|
-
const statuses = seed.statuses ?? listSurfaceStatuses();
|
|
2454
|
-
const continuity = {};
|
|
2455
|
-
const reviewSurface = selectReviewSurface(statuses);
|
|
2456
|
-
if (reviewSurface) {
|
|
2457
|
-
continuity.reviewSurface = reviewSurface;
|
|
2458
|
-
}
|
|
2459
|
-
const executionMode = inferExecutionMode(seed.executionMode, statuses);
|
|
2460
|
-
if (executionMode) {
|
|
2461
|
-
continuity.executionMode = executionMode;
|
|
2462
|
-
}
|
|
2463
|
-
if (seed.workspace?.id) {
|
|
2464
|
-
continuity.workspaceId = seed.workspace.id;
|
|
2465
|
-
}
|
|
2466
|
-
const workspaceName = seed.workspace?.name ?? seed.workspaceName ?? void 0;
|
|
2467
|
-
if (workspaceName?.trim()) {
|
|
2468
|
-
continuity.workspaceName = workspaceName.trim();
|
|
2469
|
-
}
|
|
2470
|
-
return continuity;
|
|
2471
|
-
}
|
|
2472
|
-
function mergeContinuityDefaults(current, next) {
|
|
2629
|
+
const workspaceName = seed.workspace?.name ?? seed.workspaceName ?? void 0;
|
|
2630
|
+
if (workspaceName?.trim()) {
|
|
2631
|
+
continuity.workspaceName = workspaceName.trim();
|
|
2632
|
+
}
|
|
2633
|
+
return continuity;
|
|
2634
|
+
}
|
|
2635
|
+
function mergeContinuityDefaults(current, next) {
|
|
2473
2636
|
return {
|
|
2474
2637
|
...current ?? {},
|
|
2475
2638
|
...next
|
|
@@ -2489,6 +2652,16 @@ function persistContinuityDefaults(seed = {}, statePath) {
|
|
|
2489
2652
|
// src/lib/initiatives.ts
|
|
2490
2653
|
var FOUNDER_DEMO_INITIATIVE_TITLE = "Founder Demo Initiative";
|
|
2491
2654
|
var FOUNDER_DEMO_INITIATIVE_SUMMARY = "Starter initiative created by @useorgx/wizard to validate workspace routing, live views, and the default OrgX skill pack.";
|
|
2655
|
+
var FOUNDER_DEMO_DECISION_TITLE = "Approve founder demo workspace";
|
|
2656
|
+
var FOUNDER_DEMO_DECISION_SUMMARY = "Initial decision created by @useorgx/wizard so the founder preset leaves the workspace with a resolved approval trail.";
|
|
2657
|
+
var FOUNDER_DEMO_DECISION_RESOLUTION = "Approved automatically by @useorgx/wizard after the founder preset finished creating the live demo workspace.";
|
|
2658
|
+
var FOUNDER_DEMO_ARTIFACT_NAME = "Founder Demo Live View";
|
|
2659
|
+
var FOUNDER_DEMO_ARTIFACT_TYPE = "document";
|
|
2660
|
+
var FOUNDER_DEMO_ARTIFACT_DESCRIPTION = "Live founder demo generated by @useorgx/wizard after workspace bootstrap completed.";
|
|
2661
|
+
var ONBOARDING_TASK_TITLE = "Complete OrgX onboarding";
|
|
2662
|
+
var ONBOARDING_TASK_SUMMARY = "Starter onboarding task created by @useorgx/wizard so the first workspace has a clear follow-up after setup.";
|
|
2663
|
+
var ONBOARDING_WORKSTREAM_TITLE = "OrgX onboarding";
|
|
2664
|
+
var ONBOARDING_WORKSTREAM_SUMMARY = "Starter onboarding workstream created by @useorgx/wizard so the first workspace has a home for setup follow-up tasks.";
|
|
2492
2665
|
function parseResponseBody3(text2) {
|
|
2493
2666
|
if (!text2) {
|
|
2494
2667
|
return null;
|
|
@@ -2508,11 +2681,15 @@ function formatHttpError2(status, body) {
|
|
|
2508
2681
|
}
|
|
2509
2682
|
return `HTTP ${status}`;
|
|
2510
2683
|
}
|
|
2511
|
-
function
|
|
2684
|
+
function extractEntity(payload) {
|
|
2512
2685
|
const entity = isRecord(payload) && isRecord(payload.data) ? payload.data : payload;
|
|
2513
2686
|
if (!isRecord(entity)) {
|
|
2514
|
-
throw new Error("OrgX returned an unexpected
|
|
2687
|
+
throw new Error("OrgX returned an unexpected entity payload.");
|
|
2515
2688
|
}
|
|
2689
|
+
return entity;
|
|
2690
|
+
}
|
|
2691
|
+
function parseInitiative(payload) {
|
|
2692
|
+
const entity = extractEntity(payload);
|
|
2516
2693
|
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2517
2694
|
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2518
2695
|
if (!id || !title) {
|
|
@@ -2524,9 +2701,69 @@ function parseInitiative(payload) {
|
|
|
2524
2701
|
...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
|
|
2525
2702
|
};
|
|
2526
2703
|
}
|
|
2527
|
-
function
|
|
2704
|
+
function parseDecision(payload) {
|
|
2705
|
+
const entity = extractEntity(payload);
|
|
2706
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2707
|
+
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2708
|
+
if (!id || !title) {
|
|
2709
|
+
throw new Error("OrgX returned an incomplete decision payload.");
|
|
2710
|
+
}
|
|
2711
|
+
return {
|
|
2712
|
+
id,
|
|
2713
|
+
title,
|
|
2714
|
+
...typeof entity.status === "string" && entity.status.trim().length > 0 ? { status: entity.status.trim() } : {},
|
|
2715
|
+
...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
function parseArtifact(payload) {
|
|
2719
|
+
const entity = extractEntity(payload);
|
|
2720
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2721
|
+
const name = typeof entity.name === "string" ? entity.name.trim() : typeof entity.title === "string" ? entity.title.trim() : "";
|
|
2722
|
+
const type = typeof entity.artifact_type === "string" ? entity.artifact_type.trim() : typeof entity.type === "string" ? entity.type.trim() : "";
|
|
2723
|
+
const url = typeof entity.external_url === "string" ? entity.external_url.trim() : typeof entity.url === "string" ? entity.url.trim() : "";
|
|
2724
|
+
if (!id || !name) {
|
|
2725
|
+
throw new Error("OrgX returned an incomplete artifact payload.");
|
|
2726
|
+
}
|
|
2727
|
+
return {
|
|
2728
|
+
id,
|
|
2729
|
+
name,
|
|
2730
|
+
...type ? { type } : {},
|
|
2731
|
+
...url ? { url } : {}
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
function parseTask(payload) {
|
|
2735
|
+
const entity = extractEntity(payload);
|
|
2736
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2737
|
+
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2738
|
+
if (!id || !title) {
|
|
2739
|
+
throw new Error("OrgX returned an incomplete task payload.");
|
|
2740
|
+
}
|
|
2741
|
+
return {
|
|
2742
|
+
id,
|
|
2743
|
+
title,
|
|
2744
|
+
...typeof entity.status === "string" && entity.status.trim().length > 0 ? { status: entity.status.trim() } : {},
|
|
2745
|
+
...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
function parseWorkstream(payload) {
|
|
2749
|
+
const entity = extractEntity(payload);
|
|
2750
|
+
const id = typeof entity.id === "string" ? entity.id.trim() : "";
|
|
2751
|
+
const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
|
|
2752
|
+
if (!id || !title) {
|
|
2753
|
+
throw new Error("OrgX returned an incomplete workstream payload.");
|
|
2754
|
+
}
|
|
2755
|
+
return { id, title };
|
|
2756
|
+
}
|
|
2757
|
+
function toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace) {
|
|
2528
2758
|
return {
|
|
2529
2759
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2760
|
+
artifactId: artifact.id,
|
|
2761
|
+
artifactName: artifact.name,
|
|
2762
|
+
...artifact.type ? { artifactType: artifact.type } : {},
|
|
2763
|
+
...artifact.url ? { artifactUrl: artifact.url } : {},
|
|
2764
|
+
decisionId: decision.id,
|
|
2765
|
+
...decision.status ? { decisionStatus: decision.status } : {},
|
|
2766
|
+
decisionTitle: decision.title,
|
|
2530
2767
|
id: initiative.id,
|
|
2531
2768
|
liveUrl,
|
|
2532
2769
|
title: initiative.title,
|
|
@@ -2534,6 +2771,18 @@ function toDemoInitiativeRecord(initiative, liveUrl, workspace) {
|
|
|
2534
2771
|
workspaceName: workspace.name
|
|
2535
2772
|
};
|
|
2536
2773
|
}
|
|
2774
|
+
function toOnboardingTaskRecord(task, workspace, options = {}) {
|
|
2775
|
+
return {
|
|
2776
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2777
|
+
id: task.id,
|
|
2778
|
+
...options.initiativeId ? { initiativeId: options.initiativeId } : {},
|
|
2779
|
+
...task.status ? { status: task.status } : {},
|
|
2780
|
+
title: task.title,
|
|
2781
|
+
...options.workstreamId ? { workstreamId: options.workstreamId } : {},
|
|
2782
|
+
workspaceId: workspace.id,
|
|
2783
|
+
workspaceName: workspace.name
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2537
2786
|
async function requireOrgxAuth2(options = {}) {
|
|
2538
2787
|
const auth = await resolveOrgxAuth(options);
|
|
2539
2788
|
if (!auth) {
|
|
@@ -2543,11 +2792,7 @@ async function requireOrgxAuth2(options = {}) {
|
|
|
2543
2792
|
}
|
|
2544
2793
|
return auth;
|
|
2545
2794
|
}
|
|
2546
|
-
async function
|
|
2547
|
-
const title = input.title.trim();
|
|
2548
|
-
if (!title) {
|
|
2549
|
-
throw new Error("Initiative title is required.");
|
|
2550
|
-
}
|
|
2795
|
+
async function createEntity(type, body, parse2, options = {}) {
|
|
2551
2796
|
const auth = await requireOrgxAuth2(options);
|
|
2552
2797
|
const response = await fetch(buildOrgxApiUrl("/entities", auth.baseUrl), {
|
|
2553
2798
|
method: "POST",
|
|
@@ -2556,34 +2801,115 @@ async function createInitiative(input, options = {}) {
|
|
|
2556
2801
|
"Content-Type": "application/json"
|
|
2557
2802
|
},
|
|
2558
2803
|
body: JSON.stringify({
|
|
2559
|
-
type
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2804
|
+
type,
|
|
2805
|
+
...body
|
|
2806
|
+
}),
|
|
2807
|
+
signal: AbortSignal.timeout(7e3)
|
|
2808
|
+
});
|
|
2809
|
+
const responseBody = parseResponseBody3(await response.text());
|
|
2810
|
+
if (!response.ok) {
|
|
2811
|
+
throw new Error(`Failed to create ${type}. ${formatHttpError2(response.status, responseBody)}`);
|
|
2812
|
+
}
|
|
2813
|
+
return parse2(responseBody);
|
|
2814
|
+
}
|
|
2815
|
+
async function updateEntity(type, id, body, parse2, options = {}) {
|
|
2816
|
+
const auth = await requireOrgxAuth2(options);
|
|
2817
|
+
const response = await fetch(buildOrgxApiUrl("/entities", auth.baseUrl), {
|
|
2818
|
+
method: "PATCH",
|
|
2819
|
+
headers: {
|
|
2820
|
+
Authorization: `Bearer ${auth.apiKey}`,
|
|
2821
|
+
"Content-Type": "application/json"
|
|
2822
|
+
},
|
|
2823
|
+
body: JSON.stringify({
|
|
2824
|
+
type,
|
|
2825
|
+
id,
|
|
2826
|
+
...body
|
|
2564
2827
|
}),
|
|
2565
2828
|
signal: AbortSignal.timeout(7e3)
|
|
2566
2829
|
});
|
|
2567
|
-
const
|
|
2830
|
+
const responseBody = parseResponseBody3(await response.text());
|
|
2568
2831
|
if (!response.ok) {
|
|
2569
|
-
throw new Error(`Failed to
|
|
2832
|
+
throw new Error(`Failed to update ${type}. ${formatHttpError2(response.status, responseBody)}`);
|
|
2833
|
+
}
|
|
2834
|
+
return parse2(responseBody);
|
|
2835
|
+
}
|
|
2836
|
+
function buildLiveUrl(baseUrl, initiativeId) {
|
|
2837
|
+
const parsed = new URL(normalizeOrgxBaseUrl(baseUrl));
|
|
2838
|
+
if (parsed.protocol === "https:" && parsed.hostname === "useorgx.com") {
|
|
2839
|
+
parsed.hostname = "www.useorgx.com";
|
|
2840
|
+
}
|
|
2841
|
+
parsed.pathname = `/live/${initiativeId}`;
|
|
2842
|
+
parsed.search = "";
|
|
2843
|
+
parsed.hash = "";
|
|
2844
|
+
return parsed.toString();
|
|
2845
|
+
}
|
|
2846
|
+
async function createFounderDemoDecision(initiative, workspaceId, options = {}) {
|
|
2847
|
+
return createEntity(
|
|
2848
|
+
"decision",
|
|
2849
|
+
{
|
|
2850
|
+
initiative_id: initiative.id,
|
|
2851
|
+
summary: FOUNDER_DEMO_DECISION_SUMMARY,
|
|
2852
|
+
title: FOUNDER_DEMO_DECISION_TITLE,
|
|
2853
|
+
workspace_id: workspaceId
|
|
2854
|
+
},
|
|
2855
|
+
parseDecision,
|
|
2856
|
+
options
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
async function approveFounderDemoDecision(decisionId, options = {}) {
|
|
2860
|
+
return updateEntity(
|
|
2861
|
+
"decision",
|
|
2862
|
+
decisionId,
|
|
2863
|
+
{
|
|
2864
|
+
resolution_summary: FOUNDER_DEMO_DECISION_RESOLUTION,
|
|
2865
|
+
status: "approved"
|
|
2866
|
+
},
|
|
2867
|
+
parseDecision,
|
|
2868
|
+
options
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
async function createFounderDemoArtifact(initiative, liveUrl, workspaceId, options = {}) {
|
|
2872
|
+
return createEntity(
|
|
2873
|
+
"artifact",
|
|
2874
|
+
{
|
|
2875
|
+
artifact_type: FOUNDER_DEMO_ARTIFACT_TYPE,
|
|
2876
|
+
description: FOUNDER_DEMO_ARTIFACT_DESCRIPTION,
|
|
2877
|
+
entity_id: initiative.id,
|
|
2878
|
+
entity_type: "initiative",
|
|
2879
|
+
external_url: liveUrl,
|
|
2880
|
+
initiative_id: initiative.id,
|
|
2881
|
+
name: FOUNDER_DEMO_ARTIFACT_NAME,
|
|
2882
|
+
workspace_id: workspaceId
|
|
2883
|
+
},
|
|
2884
|
+
parseArtifact,
|
|
2885
|
+
options
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
async function createInitiative(input, options = {}) {
|
|
2889
|
+
const title = input.title.trim();
|
|
2890
|
+
if (!title) {
|
|
2891
|
+
throw new Error("Initiative title is required.");
|
|
2570
2892
|
}
|
|
2571
|
-
return
|
|
2893
|
+
return createEntity(
|
|
2894
|
+
"initiative",
|
|
2895
|
+
{
|
|
2896
|
+
title,
|
|
2897
|
+
status: "active",
|
|
2898
|
+
...input.summary?.trim() ? { summary: input.summary.trim() } : {},
|
|
2899
|
+
...input.workspaceId?.trim() ? { workspace_id: input.workspaceId.trim() } : {}
|
|
2900
|
+
},
|
|
2901
|
+
parseInitiative,
|
|
2902
|
+
options
|
|
2903
|
+
);
|
|
2572
2904
|
}
|
|
2573
2905
|
async function ensureFounderDemoInitiative(workspace, options = {}) {
|
|
2574
2906
|
const existingRecord = readWizardState(options.statePath)?.demoInitiative;
|
|
2575
|
-
|
|
2576
|
-
return {
|
|
2577
|
-
created: false,
|
|
2578
|
-
initiative: {
|
|
2579
|
-
id: existingRecord.id,
|
|
2580
|
-
title: existingRecord.title
|
|
2581
|
-
},
|
|
2582
|
-
liveUrl: existingRecord.liveUrl
|
|
2583
|
-
};
|
|
2584
|
-
}
|
|
2907
|
+
const matchingRecord = existingRecord?.workspaceId === workspace.id ? existingRecord : void 0;
|
|
2585
2908
|
const auth = await requireOrgxAuth2(options);
|
|
2586
|
-
const initiative =
|
|
2909
|
+
const initiative = matchingRecord ? {
|
|
2910
|
+
id: matchingRecord.id,
|
|
2911
|
+
title: matchingRecord.title
|
|
2912
|
+
} : await createInitiative(
|
|
2587
2913
|
{
|
|
2588
2914
|
title: FOUNDER_DEMO_INITIATIVE_TITLE,
|
|
2589
2915
|
summary: FOUNDER_DEMO_INITIATIVE_SUMMARY,
|
|
@@ -2591,8 +2917,22 @@ async function ensureFounderDemoInitiative(workspace, options = {}) {
|
|
|
2591
2917
|
},
|
|
2592
2918
|
options
|
|
2593
2919
|
);
|
|
2594
|
-
const liveUrl =
|
|
2595
|
-
const
|
|
2920
|
+
const liveUrl = matchingRecord?.liveUrl || buildLiveUrl(auth.baseUrl, initiative.id);
|
|
2921
|
+
const decision = matchingRecord?.decisionId ? matchingRecord.decisionStatus === "approved" && matchingRecord.decisionTitle ? {
|
|
2922
|
+
id: matchingRecord.decisionId,
|
|
2923
|
+
status: matchingRecord.decisionStatus,
|
|
2924
|
+
title: matchingRecord.decisionTitle
|
|
2925
|
+
} : await approveFounderDemoDecision(matchingRecord.decisionId, options) : await approveFounderDemoDecision(
|
|
2926
|
+
(await createFounderDemoDecision(initiative, workspace.id, options)).id,
|
|
2927
|
+
options
|
|
2928
|
+
);
|
|
2929
|
+
const artifact = matchingRecord?.artifactId && matchingRecord.artifactName ? {
|
|
2930
|
+
id: matchingRecord.artifactId,
|
|
2931
|
+
name: matchingRecord.artifactName,
|
|
2932
|
+
...matchingRecord.artifactType ? { type: matchingRecord.artifactType } : {},
|
|
2933
|
+
...matchingRecord.artifactUrl ? { url: matchingRecord.artifactUrl } : {}
|
|
2934
|
+
} : await createFounderDemoArtifact(initiative, liveUrl, workspace.id, options);
|
|
2935
|
+
const record = toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace);
|
|
2596
2936
|
updateWizardState(
|
|
2597
2937
|
(current) => ({
|
|
2598
2938
|
...current,
|
|
@@ -2601,11 +2941,65 @@ async function ensureFounderDemoInitiative(workspace, options = {}) {
|
|
|
2601
2941
|
options.statePath
|
|
2602
2942
|
);
|
|
2603
2943
|
return {
|
|
2604
|
-
|
|
2944
|
+
artifact,
|
|
2945
|
+
created: !matchingRecord,
|
|
2946
|
+
decision,
|
|
2605
2947
|
initiative,
|
|
2606
2948
|
liveUrl
|
|
2607
2949
|
};
|
|
2608
2950
|
}
|
|
2951
|
+
async function ensureOnboardingTask(workspace, options = {}) {
|
|
2952
|
+
const existingRecord = readWizardState(options.statePath)?.onboardingTask;
|
|
2953
|
+
if (existingRecord?.workspaceId === workspace.id) {
|
|
2954
|
+
return {
|
|
2955
|
+
id: existingRecord.id,
|
|
2956
|
+
title: existingRecord.title,
|
|
2957
|
+
...existingRecord.status ? { status: existingRecord.status } : {}
|
|
2958
|
+
};
|
|
2959
|
+
}
|
|
2960
|
+
const initiativeId = options.initiativeId?.trim();
|
|
2961
|
+
if (!initiativeId) {
|
|
2962
|
+
throw new Error(
|
|
2963
|
+
"Starter onboarding task requires an initiative context. Re-run setup with the founder preset or create an initiative before requesting onboarding tasks."
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
const workstream = await createEntity(
|
|
2967
|
+
"workstream",
|
|
2968
|
+
{
|
|
2969
|
+
initiative_id: initiativeId,
|
|
2970
|
+
status: "active",
|
|
2971
|
+
summary: ONBOARDING_WORKSTREAM_SUMMARY,
|
|
2972
|
+
title: ONBOARDING_WORKSTREAM_TITLE,
|
|
2973
|
+
workspace_id: workspace.id
|
|
2974
|
+
},
|
|
2975
|
+
parseWorkstream,
|
|
2976
|
+
options
|
|
2977
|
+
);
|
|
2978
|
+
const task = await createEntity(
|
|
2979
|
+
"task",
|
|
2980
|
+
{
|
|
2981
|
+
initiative_id: initiativeId,
|
|
2982
|
+
status: "todo",
|
|
2983
|
+
summary: ONBOARDING_TASK_SUMMARY,
|
|
2984
|
+
title: ONBOARDING_TASK_TITLE,
|
|
2985
|
+
workstream_id: workstream.id,
|
|
2986
|
+
workspace_id: workspace.id
|
|
2987
|
+
},
|
|
2988
|
+
parseTask,
|
|
2989
|
+
options
|
|
2990
|
+
);
|
|
2991
|
+
updateWizardState(
|
|
2992
|
+
(current) => ({
|
|
2993
|
+
...current,
|
|
2994
|
+
onboardingTask: toOnboardingTaskRecord(task, workspace, {
|
|
2995
|
+
initiativeId,
|
|
2996
|
+
workstreamId: workstream.id
|
|
2997
|
+
})
|
|
2998
|
+
}),
|
|
2999
|
+
options.statePath
|
|
3000
|
+
);
|
|
3001
|
+
return task;
|
|
3002
|
+
}
|
|
2609
3003
|
|
|
2610
3004
|
// src/lib/skills.ts
|
|
2611
3005
|
import { join as join2 } from "path";
|
|
@@ -2615,6 +3009,7 @@ var DEFAULT_ORGX_SKILL_PACKS = [
|
|
|
2615
3009
|
"bulk-create",
|
|
2616
3010
|
"nightly-recap"
|
|
2617
3011
|
];
|
|
3012
|
+
var PLUGIN_MANAGED_SKILL_TARGETS = ["claude", "codex"];
|
|
2618
3013
|
var EXCLUDED_PACK_DIRS = /* @__PURE__ */ new Set([".github", "scripts"]);
|
|
2619
3014
|
var ORGX_SKILLS_OWNER = "useorgx";
|
|
2620
3015
|
var ORGX_SKILLS_REPO = "skills";
|
|
@@ -2627,6 +3022,8 @@ var CURSOR_RULES_CONTENT = `# OrgX Rules
|
|
|
2627
3022
|
- Preserve \`_context\` when widget or app-rendering flows depend on client, conversation, session, or user metadata. Do not strip or rename it.
|
|
2628
3023
|
- Prefer \`list_entities\` for pending approvals and state inspection before reaching for legacy aliases.
|
|
2629
3024
|
- Keep hosted OrgX MCP calls distinct from local OpenClaw/plugin surfaces before mutating config or entity state.
|
|
3025
|
+
- Artifact proof must use durable sources: GitHub PR/commit/blob permalinks, published/public URLs, or absolute file paths / \`file://...\`.
|
|
3026
|
+
- Never use OrgX wrapper pages like \`/live/...\`, \`/artifacts/...\`, or \`/console/...\` as proof links. \`preview_markdown\` is supplemental, not proof.
|
|
2630
3027
|
`;
|
|
2631
3028
|
var CLAUDE_ORGX_SKILL_CONTENT = `---
|
|
2632
3029
|
name: orgx
|
|
@@ -2652,6 +3049,8 @@ This skill is for the hosted OrgX tool surface, not the local OpenClaw plugin. K
|
|
|
2652
3049
|
- Treat \`scaffold_initiative continue_on_error\` as best-effort scaffolding. Use it only when partial creation is acceptable and inspect failures afterward.
|
|
2653
3050
|
- Preserve \`_context\` when widget or app rendering depends on client, conversation, session, or user metadata.
|
|
2654
3051
|
- Prefer \`list_entities\` over older aliases when you need pending decisions, blockers, or entity state.
|
|
3052
|
+
- Artifact proof must use durable sources: GitHub PR/commit/blob permalinks, published/public URLs, or absolute file paths / \`file://...\`.
|
|
3053
|
+
- Never use OrgX wrapper pages like \`/live/...\`, \`/artifacts/...\`, or \`/console/...\` as proof links. \`preview_markdown\` is supporting context only.
|
|
2655
3054
|
|
|
2656
3055
|
## Suggested Skill Packs
|
|
2657
3056
|
|
|
@@ -2742,85 +3141,972 @@ function writeManagedFile(path, content, label, sourceUrl) {
|
|
|
2742
3141
|
writeTextFile(path, content);
|
|
2743
3142
|
}
|
|
2744
3143
|
return {
|
|
2745
|
-
label,
|
|
2746
|
-
path,
|
|
2747
|
-
changed,
|
|
2748
|
-
...sourceUrl ? { sourceUrl } : {}
|
|
3144
|
+
label,
|
|
3145
|
+
path,
|
|
3146
|
+
changed,
|
|
3147
|
+
...sourceUrl ? { sourceUrl } : {}
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
function resolveSkillPackNames(requested = []) {
|
|
3151
|
+
if (requested.length === 0 || requested.includes("all")) {
|
|
3152
|
+
return [...DEFAULT_ORGX_SKILL_PACKS];
|
|
3153
|
+
}
|
|
3154
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3155
|
+
const normalized = [];
|
|
3156
|
+
for (const name of requested) {
|
|
3157
|
+
const next = name.trim();
|
|
3158
|
+
if (!next || seen.has(next)) {
|
|
3159
|
+
continue;
|
|
3160
|
+
}
|
|
3161
|
+
seen.add(next);
|
|
3162
|
+
normalized.push(next);
|
|
3163
|
+
}
|
|
3164
|
+
return normalized;
|
|
3165
|
+
}
|
|
3166
|
+
function resolvePluginManagedSkillTargets(requested = []) {
|
|
3167
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3168
|
+
const targets = [];
|
|
3169
|
+
for (const rawTarget of requested) {
|
|
3170
|
+
const target = rawTarget.trim().toLowerCase();
|
|
3171
|
+
if (!PLUGIN_MANAGED_SKILL_TARGETS.includes(target)) {
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
3174
|
+
if (seen.has(target)) {
|
|
3175
|
+
continue;
|
|
3176
|
+
}
|
|
3177
|
+
seen.add(target);
|
|
3178
|
+
targets.push(target);
|
|
3179
|
+
}
|
|
3180
|
+
return targets;
|
|
3181
|
+
}
|
|
3182
|
+
function planOrgxSkillsInstall(pluginTargets = []) {
|
|
3183
|
+
const pluginManagedTargets = resolvePluginManagedSkillTargets(pluginTargets);
|
|
3184
|
+
const managesClaudeSkills = pluginManagedTargets.includes("claude");
|
|
3185
|
+
const notes = [];
|
|
3186
|
+
if (managesClaudeSkills) {
|
|
3187
|
+
notes.push(
|
|
3188
|
+
"Claude Code plugin is installed, so the wizard skipped overlapping standalone Claude OrgX skill files and Claude skill-pack sync."
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
if (pluginManagedTargets.includes("codex")) {
|
|
3192
|
+
notes.push(
|
|
3193
|
+
"Codex companion plugin bundles its own OrgX skills, so `wizard skills add` only manages standalone editor assets outside Codex."
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
return {
|
|
3197
|
+
installClaudeSkillBootstrap: !managesClaudeSkills,
|
|
3198
|
+
installClaudeSkillPacks: !managesClaudeSkills,
|
|
3199
|
+
notes,
|
|
3200
|
+
pluginManagedTargets
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
async function fetchAvailablePackNames(fetchImpl, ref) {
|
|
3204
|
+
const entries = await fetchDirectoryEntries("", fetchImpl, ref);
|
|
3205
|
+
return entries.filter((e) => e.type === "dir" && !EXCLUDED_PACK_DIRS.has(e.name)).map((e) => e.name);
|
|
3206
|
+
}
|
|
3207
|
+
async function installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref) {
|
|
3208
|
+
const rootPath = skillName;
|
|
3209
|
+
const files = await listRemoteSkillFiles(rootPath, fetchImpl, ref);
|
|
3210
|
+
const writes = [];
|
|
3211
|
+
for (const file of files) {
|
|
3212
|
+
const content = await fetchRemoteText(file.sourceUrl, fetchImpl);
|
|
3213
|
+
const relativePath = file.path.slice(`${rootPath}/`.length);
|
|
3214
|
+
writes.push(
|
|
3215
|
+
writeManagedFile(
|
|
3216
|
+
join2(claudeSkillsDir, skillName, relativePath),
|
|
3217
|
+
content,
|
|
3218
|
+
`${skillName}/${relativePath}`,
|
|
3219
|
+
file.sourceUrl
|
|
3220
|
+
)
|
|
3221
|
+
);
|
|
3222
|
+
}
|
|
3223
|
+
return {
|
|
3224
|
+
name: skillName,
|
|
3225
|
+
files: writes
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
async function installOrgxSkills(options = {}) {
|
|
3229
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
3230
|
+
if (!fetchImpl) {
|
|
3231
|
+
throw new Error("Global fetch is unavailable in this runtime.");
|
|
3232
|
+
}
|
|
3233
|
+
const ref = options.ref ?? ORGX_SKILLS_REF;
|
|
3234
|
+
const claudeSkillsDir = options.claudeSkillsDir ?? CLAUDE_SKILLS_DIR;
|
|
3235
|
+
const claudeOrgxSkillPath = options.claudeOrgxSkillPath ?? CLAUDE_ORGX_SKILL_PATH;
|
|
3236
|
+
const cursorRulePath = options.cursorRulePath ?? CURSOR_ORGX_RULE_PATH;
|
|
3237
|
+
const plan = planOrgxSkillsInstall(options.pluginTargets ?? []);
|
|
3238
|
+
const requestedNames = options.skillNames ?? [];
|
|
3239
|
+
let skillNames;
|
|
3240
|
+
if (requestedNames.length === 0 || requestedNames.includes("all")) {
|
|
3241
|
+
try {
|
|
3242
|
+
skillNames = await fetchAvailablePackNames(fetchImpl, ref);
|
|
3243
|
+
} catch {
|
|
3244
|
+
skillNames = [...DEFAULT_ORGX_SKILL_PACKS];
|
|
3245
|
+
}
|
|
3246
|
+
} else {
|
|
3247
|
+
skillNames = resolveSkillPackNames(requestedNames);
|
|
3248
|
+
}
|
|
3249
|
+
const writes = [
|
|
3250
|
+
writeManagedFile(cursorRulePath, CURSOR_RULES_CONTENT, "cursor-rules")
|
|
3251
|
+
];
|
|
3252
|
+
if (plan.installClaudeSkillBootstrap) {
|
|
3253
|
+
writes.push(
|
|
3254
|
+
writeManagedFile(claudeOrgxSkillPath, CLAUDE_ORGX_SKILL_CONTENT, "claude-orgx-skill")
|
|
3255
|
+
);
|
|
3256
|
+
}
|
|
3257
|
+
const packs = [];
|
|
3258
|
+
if (plan.installClaudeSkillPacks) {
|
|
3259
|
+
for (const skillName of skillNames) {
|
|
3260
|
+
packs.push(await installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref));
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
return {
|
|
3264
|
+
notes: plan.notes,
|
|
3265
|
+
writes,
|
|
3266
|
+
packs
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
// src/lib/plugins.ts
|
|
3271
|
+
import { spawn } from "child_process";
|
|
3272
|
+
import {
|
|
3273
|
+
existsSync as existsSync3,
|
|
3274
|
+
mkdirSync as mkdirSync2,
|
|
3275
|
+
mkdtempSync,
|
|
3276
|
+
readFileSync as readFileSync2,
|
|
3277
|
+
readdirSync,
|
|
3278
|
+
rmSync,
|
|
3279
|
+
statSync as statSync2,
|
|
3280
|
+
writeFileSync as writeFileSync2
|
|
3281
|
+
} from "fs";
|
|
3282
|
+
import { tmpdir } from "os";
|
|
3283
|
+
import { dirname as dirname3, join as join3, relative } from "path";
|
|
3284
|
+
var DEFAULT_ORGX_PLUGIN_TARGETS = ["claude", "codex", "openclaw"];
|
|
3285
|
+
var ORGX_PLUGIN_GITHUB_OWNER = "useorgx";
|
|
3286
|
+
var ORGX_PLUGIN_GITHUB_REF = "main";
|
|
3287
|
+
var ORGX_CLAUDE_PLUGIN_NAME = "orgx-claude-code-plugin";
|
|
3288
|
+
var ORGX_CLAUDE_MARKETPLACE_NAME = "orgx-local";
|
|
3289
|
+
var ORGX_CODEX_PLUGIN_NAME = "orgx-codex-plugin";
|
|
3290
|
+
var ORGX_OPENCLAW_PLUGIN_ID = "orgx";
|
|
3291
|
+
var ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME = "@useorgx/openclaw-plugin";
|
|
3292
|
+
var CLAUDE_PLUGIN_SYNC_SPEC = {
|
|
3293
|
+
owner: ORGX_PLUGIN_GITHUB_OWNER,
|
|
3294
|
+
repo: ORGX_CLAUDE_PLUGIN_NAME,
|
|
3295
|
+
ref: ORGX_PLUGIN_GITHUB_REF,
|
|
3296
|
+
include: [
|
|
3297
|
+
{ localPath: ".claude-plugin", remotePath: ".claude-plugin" },
|
|
3298
|
+
{ localPath: "agents", remotePath: "agents" },
|
|
3299
|
+
{ localPath: "commands", remotePath: "commands" },
|
|
3300
|
+
{ localPath: "hooks", remotePath: "hooks" },
|
|
3301
|
+
{ localPath: "lib", remotePath: "lib" },
|
|
3302
|
+
{ localPath: "scripts", remotePath: "scripts" },
|
|
3303
|
+
{ localPath: "skills", remotePath: "skills" }
|
|
3304
|
+
]
|
|
3305
|
+
};
|
|
3306
|
+
var CODEX_PLUGIN_SYNC_SPEC = {
|
|
3307
|
+
owner: ORGX_PLUGIN_GITHUB_OWNER,
|
|
3308
|
+
repo: ORGX_CODEX_PLUGIN_NAME,
|
|
3309
|
+
ref: ORGX_PLUGIN_GITHUB_REF,
|
|
3310
|
+
include: [
|
|
3311
|
+
{ localPath: ".codex-plugin", remotePath: ".codex-plugin" },
|
|
3312
|
+
{ localPath: ".mcp.json", remotePath: ".mcp.json" },
|
|
3313
|
+
{ localPath: "assets", remotePath: "assets" },
|
|
3314
|
+
{ localPath: "skills", remotePath: "skills" }
|
|
3315
|
+
]
|
|
3316
|
+
};
|
|
3317
|
+
function defaultPluginPaths() {
|
|
3318
|
+
return {
|
|
3319
|
+
claudeMarketplaceDir: CLAUDE_MANAGED_MARKETPLACE_DIR,
|
|
3320
|
+
claudeMarketplaceManifestPath: CLAUDE_MANAGED_MARKETPLACE_MANIFEST_PATH,
|
|
3321
|
+
claudePluginDir: CLAUDE_MANAGED_PLUGIN_DIR,
|
|
3322
|
+
codexMarketplacePath: CODEX_MARKETPLACE_PATH,
|
|
3323
|
+
codexPluginDir: CODEX_ORGX_PLUGIN_DIR
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
function resolvePluginPaths(paths = {}) {
|
|
3327
|
+
return {
|
|
3328
|
+
...defaultPluginPaths(),
|
|
3329
|
+
...paths
|
|
3330
|
+
};
|
|
3331
|
+
}
|
|
3332
|
+
function isRepoDirectoryEntry(value) {
|
|
3333
|
+
if (!value || typeof value !== "object") return false;
|
|
3334
|
+
const entry = value;
|
|
3335
|
+
const hasType = entry.type === "file" || entry.type === "dir";
|
|
3336
|
+
return typeof entry.name === "string" && typeof entry.path === "string" && hasType;
|
|
3337
|
+
}
|
|
3338
|
+
function encodeRepoPath2(value) {
|
|
3339
|
+
return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
|
|
3340
|
+
}
|
|
3341
|
+
function isLikelyRepoFilePath(path) {
|
|
3342
|
+
const basename = path.split("/").pop() ?? path;
|
|
3343
|
+
return basename.includes(".") && !/^\.[^./]+$/.test(basename);
|
|
3344
|
+
}
|
|
3345
|
+
function buildContentsUrl2(spec, path) {
|
|
3346
|
+
const encodedPath = encodeRepoPath2(path);
|
|
3347
|
+
return `https://api.github.com/repos/${spec.owner}/${spec.repo}/contents/${encodedPath}?ref=${encodeURIComponent(spec.ref)}`;
|
|
3348
|
+
}
|
|
3349
|
+
function resolveGitHubError2(spec, status, path) {
|
|
3350
|
+
const repoName = `${spec.owner}/${spec.repo}`;
|
|
3351
|
+
if (status === 404) {
|
|
3352
|
+
return `Could not find '${path}' in ${repoName}.`;
|
|
3353
|
+
}
|
|
3354
|
+
return `GitHub returned HTTP ${status} while loading '${path}' from ${repoName}.`;
|
|
3355
|
+
}
|
|
3356
|
+
async function fetchDirectoryEntries2(spec, path, fetchImpl) {
|
|
3357
|
+
const response = await fetchImpl(buildContentsUrl2(spec, path), {
|
|
3358
|
+
headers: {
|
|
3359
|
+
Accept: "application/vnd.github+json"
|
|
3360
|
+
},
|
|
3361
|
+
signal: AbortSignal.timeout(1e4)
|
|
3362
|
+
});
|
|
3363
|
+
if (!response.ok) {
|
|
3364
|
+
throw new Error(resolveGitHubError2(spec, response.status, path));
|
|
3365
|
+
}
|
|
3366
|
+
const data = await response.json();
|
|
3367
|
+
if (!Array.isArray(data)) {
|
|
3368
|
+
throw new Error(`Expected '${path}' to be a directory in ${spec.owner}/${spec.repo}.`);
|
|
3369
|
+
}
|
|
3370
|
+
return data.filter(isRepoDirectoryEntry).sort((left, right) => left.path.localeCompare(right.path));
|
|
3371
|
+
}
|
|
3372
|
+
async function fetchRepoFile(spec, path, fetchImpl) {
|
|
3373
|
+
const response = await fetchImpl(buildContentsUrl2(spec, path), {
|
|
3374
|
+
headers: {
|
|
3375
|
+
Accept: "application/vnd.github+json"
|
|
3376
|
+
},
|
|
3377
|
+
signal: AbortSignal.timeout(1e4)
|
|
3378
|
+
});
|
|
3379
|
+
if (!response.ok) {
|
|
3380
|
+
throw new Error(resolveGitHubError2(spec, response.status, path));
|
|
3381
|
+
}
|
|
3382
|
+
const data = await response.json();
|
|
3383
|
+
if (!isRepoDirectoryEntry(data) || data.type !== "file" || typeof data.download_url !== "string") {
|
|
3384
|
+
throw new Error(`Expected '${path}' to be a file in ${spec.owner}/${spec.repo}.`);
|
|
3385
|
+
}
|
|
3386
|
+
return data;
|
|
3387
|
+
}
|
|
3388
|
+
async function listRemoteRepoFiles(spec, path, localPath, fetchImpl) {
|
|
3389
|
+
const files = [];
|
|
3390
|
+
if (isLikelyRepoFilePath(path) && !path.endsWith("/")) {
|
|
3391
|
+
const file = await fetchRepoFile(spec, path, fetchImpl);
|
|
3392
|
+
files.push({
|
|
3393
|
+
localPath,
|
|
3394
|
+
path: file.path,
|
|
3395
|
+
sourceUrl: file.download_url ?? ""
|
|
3396
|
+
});
|
|
3397
|
+
return files;
|
|
3398
|
+
}
|
|
3399
|
+
const entries = await fetchDirectoryEntries2(spec, path, fetchImpl);
|
|
3400
|
+
for (const entry of entries) {
|
|
3401
|
+
if (entry.type === "dir") {
|
|
3402
|
+
files.push(
|
|
3403
|
+
...await listRemoteRepoFiles(
|
|
3404
|
+
spec,
|
|
3405
|
+
entry.path,
|
|
3406
|
+
join3(localPath, entry.name),
|
|
3407
|
+
fetchImpl
|
|
3408
|
+
)
|
|
3409
|
+
);
|
|
3410
|
+
continue;
|
|
3411
|
+
}
|
|
3412
|
+
if (!entry.download_url) {
|
|
3413
|
+
throw new Error(`GitHub did not provide a download URL for '${entry.path}'.`);
|
|
3414
|
+
}
|
|
3415
|
+
files.push({
|
|
3416
|
+
localPath: join3(localPath, entry.name),
|
|
3417
|
+
path: entry.path,
|
|
3418
|
+
sourceUrl: entry.download_url
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3421
|
+
return files;
|
|
3422
|
+
}
|
|
3423
|
+
async function collectRemoteRepoFiles(spec, fetchImpl) {
|
|
3424
|
+
const files = [];
|
|
3425
|
+
for (const entry of spec.include) {
|
|
3426
|
+
const isFile = isLikelyRepoFilePath(entry.remotePath) && !entry.remotePath.endsWith("/");
|
|
3427
|
+
if (isFile) {
|
|
3428
|
+
const file = await fetchRepoFile(spec, entry.remotePath, fetchImpl);
|
|
3429
|
+
files.push({
|
|
3430
|
+
localPath: entry.localPath,
|
|
3431
|
+
path: file.path,
|
|
3432
|
+
sourceUrl: file.download_url ?? ""
|
|
3433
|
+
});
|
|
3434
|
+
continue;
|
|
3435
|
+
}
|
|
3436
|
+
files.push(...await listRemoteRepoFiles(spec, entry.remotePath, entry.localPath, fetchImpl));
|
|
3437
|
+
}
|
|
3438
|
+
return files.sort((left, right) => left.localPath.localeCompare(right.localPath));
|
|
3439
|
+
}
|
|
3440
|
+
async function fetchRemoteBytes(sourceUrl, fetchImpl) {
|
|
3441
|
+
const response = await fetchImpl(sourceUrl, {
|
|
3442
|
+
headers: {
|
|
3443
|
+
Accept: "application/octet-stream"
|
|
3444
|
+
},
|
|
3445
|
+
signal: AbortSignal.timeout(1e4)
|
|
3446
|
+
});
|
|
3447
|
+
if (!response.ok) {
|
|
3448
|
+
throw new Error(`GitHub returned HTTP ${response.status} while downloading ${sourceUrl}.`);
|
|
3449
|
+
}
|
|
3450
|
+
return Buffer.from(await response.arrayBuffer());
|
|
3451
|
+
}
|
|
3452
|
+
function readBytesIfExists(path) {
|
|
3453
|
+
if (!existsSync3(path)) return null;
|
|
3454
|
+
try {
|
|
3455
|
+
if (!statSync2(path).isFile()) {
|
|
3456
|
+
return null;
|
|
3457
|
+
}
|
|
3458
|
+
return readFileSync2(path);
|
|
3459
|
+
} catch (error) {
|
|
3460
|
+
const code = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
|
|
3461
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "EISDIR") {
|
|
3462
|
+
return null;
|
|
3463
|
+
}
|
|
3464
|
+
throw error;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
function writeBytesIfChanged(path, bytes) {
|
|
3468
|
+
const existing = readBytesIfExists(path);
|
|
3469
|
+
if (existing && Buffer.compare(existing, bytes) === 0) {
|
|
3470
|
+
return false;
|
|
3471
|
+
}
|
|
3472
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
3473
|
+
writeFileSync2(path, bytes);
|
|
3474
|
+
return true;
|
|
3475
|
+
}
|
|
3476
|
+
function removePathIfExists(path) {
|
|
3477
|
+
if (!existsSync3(path)) return false;
|
|
3478
|
+
rmSync(path, { force: true, recursive: true });
|
|
3479
|
+
return true;
|
|
3480
|
+
}
|
|
3481
|
+
function listRelativeFiles(root, base = root) {
|
|
3482
|
+
if (!existsSync3(root)) return [];
|
|
3483
|
+
if (!statSync2(root).isDirectory()) {
|
|
3484
|
+
return [];
|
|
3485
|
+
}
|
|
3486
|
+
const files = [];
|
|
3487
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
3488
|
+
const nextPath = join3(root, entry.name);
|
|
3489
|
+
if (entry.isDirectory()) {
|
|
3490
|
+
files.push(...listRelativeFiles(nextPath, base));
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
if (entry.isFile()) {
|
|
3494
|
+
files.push(relative(base, nextPath));
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
return files.sort();
|
|
3498
|
+
}
|
|
3499
|
+
function pruneEmptyDirectories(root, current = root) {
|
|
3500
|
+
if (!existsSync3(current) || !statSync2(current).isDirectory()) {
|
|
3501
|
+
return false;
|
|
3502
|
+
}
|
|
3503
|
+
let changed = false;
|
|
3504
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
3505
|
+
if (!entry.isDirectory()) continue;
|
|
3506
|
+
changed = pruneEmptyDirectories(root, join3(current, entry.name)) || changed;
|
|
3507
|
+
}
|
|
3508
|
+
if (current !== root && readdirSync(current).length === 0) {
|
|
3509
|
+
rmSync(current, { force: true, recursive: true });
|
|
3510
|
+
return true;
|
|
3511
|
+
}
|
|
3512
|
+
return changed;
|
|
3513
|
+
}
|
|
3514
|
+
async function syncManagedRepoTree(spec, destinationRoot, fetchImpl) {
|
|
3515
|
+
const remoteFiles = await collectRemoteRepoFiles(spec, fetchImpl);
|
|
3516
|
+
let changed = false;
|
|
3517
|
+
const expected = new Set(remoteFiles.map((file) => file.localPath));
|
|
3518
|
+
if (existsSync3(destinationRoot) && !statSync2(destinationRoot).isDirectory()) {
|
|
3519
|
+
rmSync(destinationRoot, { force: true, recursive: true });
|
|
3520
|
+
changed = true;
|
|
3521
|
+
}
|
|
3522
|
+
for (const file of listRelativeFiles(destinationRoot)) {
|
|
3523
|
+
if (expected.has(file)) continue;
|
|
3524
|
+
rmSync(join3(destinationRoot, file), { force: true });
|
|
3525
|
+
changed = true;
|
|
3526
|
+
}
|
|
3527
|
+
changed = pruneEmptyDirectories(destinationRoot) || changed;
|
|
3528
|
+
for (const file of remoteFiles) {
|
|
3529
|
+
const bytes = await fetchRemoteBytes(file.sourceUrl, fetchImpl);
|
|
3530
|
+
if (writeBytesIfChanged(join3(destinationRoot, file.localPath), bytes)) {
|
|
3531
|
+
changed = true;
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
return { changed, fileCount: remoteFiles.length };
|
|
3535
|
+
}
|
|
3536
|
+
function serializeJson(value) {
|
|
3537
|
+
return `${JSON.stringify(value, null, 2)}
|
|
3538
|
+
`;
|
|
3539
|
+
}
|
|
3540
|
+
function writeJsonIfChanged(path, value) {
|
|
3541
|
+
const next = serializeJson(value);
|
|
3542
|
+
const existing = readTextIfExists(path);
|
|
3543
|
+
if (existing === next) {
|
|
3544
|
+
return false;
|
|
3545
|
+
}
|
|
3546
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
3547
|
+
writeFileSync2(path, next, "utf8");
|
|
3548
|
+
return true;
|
|
3549
|
+
}
|
|
3550
|
+
function buildClaudeMarketplaceManifest() {
|
|
3551
|
+
return {
|
|
3552
|
+
$schema: "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
3553
|
+
name: ORGX_CLAUDE_MARKETPLACE_NAME,
|
|
3554
|
+
description: "Local OrgX plugins",
|
|
3555
|
+
owner: {
|
|
3556
|
+
name: "OrgX Team"
|
|
3557
|
+
},
|
|
3558
|
+
plugins: [
|
|
3559
|
+
{
|
|
3560
|
+
name: ORGX_CLAUDE_PLUGIN_NAME,
|
|
3561
|
+
description: "OrgX MCP tools and runtime telemetry hooks for Claude Code.",
|
|
3562
|
+
source: `./plugins/${ORGX_CLAUDE_PLUGIN_NAME}`
|
|
3563
|
+
}
|
|
3564
|
+
]
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
function buildCodexMarketplaceEntry() {
|
|
3568
|
+
return {
|
|
3569
|
+
name: ORGX_CODEX_PLUGIN_NAME,
|
|
3570
|
+
source: {
|
|
3571
|
+
source: "local",
|
|
3572
|
+
path: `./.codex/plugins/${ORGX_CODEX_PLUGIN_NAME}`
|
|
3573
|
+
},
|
|
3574
|
+
policy: {
|
|
3575
|
+
installation: "AVAILABLE",
|
|
3576
|
+
authentication: "ON_INSTALL"
|
|
3577
|
+
},
|
|
3578
|
+
category: "Productivity"
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
function readMarketplacePlugins(path) {
|
|
3582
|
+
const document = parseJsonObject(readTextIfExists(path));
|
|
3583
|
+
const plugins = Array.isArray(document.plugins) ? document.plugins.filter((value) => Boolean(value) && typeof value === "object" && !Array.isArray(value)) : [];
|
|
3584
|
+
return { document, plugins };
|
|
3585
|
+
}
|
|
3586
|
+
function upsertCodexMarketplaceEntry(path) {
|
|
3587
|
+
const { document, plugins } = readMarketplacePlugins(path);
|
|
3588
|
+
const nextEntry = buildCodexMarketplaceEntry();
|
|
3589
|
+
let replaced = false;
|
|
3590
|
+
const nextPlugins = plugins.map((plugin) => {
|
|
3591
|
+
if (plugin.name !== ORGX_CODEX_PLUGIN_NAME) {
|
|
3592
|
+
return plugin;
|
|
3593
|
+
}
|
|
3594
|
+
replaced = true;
|
|
3595
|
+
return nextEntry;
|
|
3596
|
+
});
|
|
3597
|
+
if (!replaced) {
|
|
3598
|
+
nextPlugins.push(nextEntry);
|
|
3599
|
+
}
|
|
3600
|
+
const nextDocument = {
|
|
3601
|
+
...document,
|
|
3602
|
+
...document.name ? {} : { name: ORGX_CLAUDE_MARKETPLACE_NAME },
|
|
3603
|
+
...readObject(document.interface).displayName ? {} : { interface: { displayName: "OrgX Local" } },
|
|
3604
|
+
plugins: nextPlugins
|
|
3605
|
+
};
|
|
3606
|
+
return writeJsonIfChanged(path, nextDocument);
|
|
3607
|
+
}
|
|
3608
|
+
function removeCodexMarketplaceEntry(path) {
|
|
3609
|
+
if (!existsSync3(path)) {
|
|
3610
|
+
return false;
|
|
3611
|
+
}
|
|
3612
|
+
const { document, plugins } = readMarketplacePlugins(path);
|
|
3613
|
+
const nextPlugins = plugins.filter((plugin) => plugin.name !== ORGX_CODEX_PLUGIN_NAME);
|
|
3614
|
+
if (nextPlugins.length === plugins.length) {
|
|
3615
|
+
return false;
|
|
3616
|
+
}
|
|
3617
|
+
if (nextPlugins.length === 0) {
|
|
3618
|
+
rmSync(path, { force: true });
|
|
3619
|
+
return true;
|
|
3620
|
+
}
|
|
3621
|
+
return writeJsonIfChanged(path, {
|
|
3622
|
+
...document,
|
|
3623
|
+
plugins: nextPlugins
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
function codexMarketplaceHasOrgxEntry(path) {
|
|
3627
|
+
const { plugins } = readMarketplacePlugins(path);
|
|
3628
|
+
return plugins.some((plugin) => plugin.name === ORGX_CODEX_PLUGIN_NAME);
|
|
3629
|
+
}
|
|
3630
|
+
function extractClaudePluginNames(payload) {
|
|
3631
|
+
try {
|
|
3632
|
+
const parsed = JSON.parse(payload);
|
|
3633
|
+
if (!Array.isArray(parsed)) return [];
|
|
3634
|
+
return parsed.flatMap((entry) => {
|
|
3635
|
+
if (typeof entry === "string") return [entry];
|
|
3636
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3637
|
+
if (typeof entry.name === "string") return [entry.name];
|
|
3638
|
+
if (typeof entry.id === "string") return [entry.id];
|
|
3639
|
+
return [];
|
|
3640
|
+
});
|
|
3641
|
+
} catch {
|
|
3642
|
+
return [];
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
function extractOpenclawPluginIds(payload) {
|
|
3646
|
+
try {
|
|
3647
|
+
const parsed = JSON.parse(payload);
|
|
3648
|
+
if (Array.isArray(parsed)) {
|
|
3649
|
+
return parsed.flatMap((entry) => {
|
|
3650
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3651
|
+
if (typeof entry.id === "string") return [entry.id];
|
|
3652
|
+
if (typeof entry.name === "string") return [entry.name];
|
|
3653
|
+
return [];
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
const root = readObject(parsed);
|
|
3657
|
+
const plugins = Array.isArray(root.plugins) ? root.plugins : [];
|
|
3658
|
+
return plugins.flatMap((entry) => {
|
|
3659
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3660
|
+
const item = entry;
|
|
3661
|
+
if (typeof item.id === "string") return [item.id];
|
|
3662
|
+
if (typeof item.name === "string") return [item.name];
|
|
3663
|
+
return [];
|
|
3664
|
+
});
|
|
3665
|
+
} catch {
|
|
3666
|
+
return [];
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
function formatCommandFailure(command, args, result) {
|
|
3670
|
+
const detail = [result.stderr.trim(), result.stdout.trim()].find((value) => value.length > 0);
|
|
3671
|
+
if (result.errorCode === "ENOENT") {
|
|
3672
|
+
return `Command '${command}' is not available on PATH.`;
|
|
3673
|
+
}
|
|
3674
|
+
return `${command} ${args.join(" ")} failed${result.exitCode >= 0 ? ` with exit code ${result.exitCode}` : ""}${detail ? `: ${detail}` : "."}`;
|
|
3675
|
+
}
|
|
3676
|
+
async function defaultCommandRunner(command, args) {
|
|
3677
|
+
return await new Promise((resolve) => {
|
|
3678
|
+
const child = spawn(command, [...args], {
|
|
3679
|
+
env: process.env,
|
|
3680
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3681
|
+
});
|
|
3682
|
+
let stdout = "";
|
|
3683
|
+
let stderr = "";
|
|
3684
|
+
child.stdout.on("data", (chunk) => {
|
|
3685
|
+
stdout += chunk.toString();
|
|
3686
|
+
});
|
|
3687
|
+
child.stderr.on("data", (chunk) => {
|
|
3688
|
+
stderr += chunk.toString();
|
|
3689
|
+
});
|
|
3690
|
+
child.on("error", (error) => {
|
|
3691
|
+
const errorCode = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
|
|
3692
|
+
resolve({
|
|
3693
|
+
exitCode: -1,
|
|
3694
|
+
stdout,
|
|
3695
|
+
stderr,
|
|
3696
|
+
...errorCode ? { errorCode } : {}
|
|
3697
|
+
});
|
|
3698
|
+
});
|
|
3699
|
+
child.on("close", (code) => {
|
|
3700
|
+
resolve({
|
|
3701
|
+
exitCode: code ?? -1,
|
|
3702
|
+
stdout,
|
|
3703
|
+
stderr
|
|
3704
|
+
});
|
|
3705
|
+
});
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
function resolveCommandRunner(runner) {
|
|
3709
|
+
return runner ?? defaultCommandRunner;
|
|
3710
|
+
}
|
|
3711
|
+
async function commandExists(command, runner) {
|
|
3712
|
+
const result = await runner(command, ["--help"]);
|
|
3713
|
+
return result.errorCode !== "ENOENT" && result.exitCode !== 127;
|
|
3714
|
+
}
|
|
3715
|
+
async function getClaudeInstallState(runner) {
|
|
3716
|
+
const available = detectSurface("claude").detected || await commandExists("claude", runner);
|
|
3717
|
+
if (!available) {
|
|
3718
|
+
return { available: false, installed: false };
|
|
3719
|
+
}
|
|
3720
|
+
const result = await runner("claude", ["plugin", "list", "--json"]);
|
|
3721
|
+
if (result.exitCode !== 0) {
|
|
3722
|
+
return { available: true, installed: false };
|
|
3723
|
+
}
|
|
3724
|
+
return {
|
|
3725
|
+
available: true,
|
|
3726
|
+
installed: extractClaudePluginNames(result.stdout).includes(ORGX_CLAUDE_PLUGIN_NAME)
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
async function getOpenclawInstallState(runner) {
|
|
3730
|
+
const available = detectSurface("openclaw").detected || await commandExists("openclaw", runner);
|
|
3731
|
+
if (!available) {
|
|
3732
|
+
return { available: false, installed: false };
|
|
3733
|
+
}
|
|
3734
|
+
const result = await runner("openclaw", ["plugins", "list", "--json"]);
|
|
3735
|
+
if (result.exitCode !== 0) {
|
|
3736
|
+
return { available: true, installed: false };
|
|
3737
|
+
}
|
|
3738
|
+
return {
|
|
3739
|
+
available: true,
|
|
3740
|
+
installed: extractOpenclawPluginIds(result.stdout).includes(ORGX_OPENCLAW_PLUGIN_ID)
|
|
3741
|
+
};
|
|
3742
|
+
}
|
|
3743
|
+
async function buildClaudeStatus(paths, runner) {
|
|
3744
|
+
const state = await getClaudeInstallState(runner);
|
|
3745
|
+
if (state.installed) {
|
|
3746
|
+
return {
|
|
3747
|
+
target: "claude",
|
|
3748
|
+
available: true,
|
|
3749
|
+
installed: true,
|
|
3750
|
+
message: "Claude Code plugin is installed."
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3753
|
+
if (!state.available) {
|
|
3754
|
+
return {
|
|
3755
|
+
target: "claude",
|
|
3756
|
+
available: false,
|
|
3757
|
+
installed: false,
|
|
3758
|
+
message: "Claude Code was not detected."
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
const marketplaceExists = existsSync3(paths.claudeMarketplaceManifestPath);
|
|
3762
|
+
return {
|
|
3763
|
+
target: "claude",
|
|
3764
|
+
available: true,
|
|
3765
|
+
installed: false,
|
|
3766
|
+
message: marketplaceExists ? "Claude marketplace wrapper exists, but the plugin is not installed." : "Claude Code is available and ready for plugin install."
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
function buildCodexStatus(paths, runner) {
|
|
3770
|
+
return (async () => {
|
|
3771
|
+
const available = detectSurface("codex").detected || await commandExists("codex", runner);
|
|
3772
|
+
const pluginExists = existsSync3(paths.codexPluginDir);
|
|
3773
|
+
const marketplaceExists = codexMarketplaceHasOrgxEntry(paths.codexMarketplacePath);
|
|
3774
|
+
const installed = pluginExists && marketplaceExists;
|
|
3775
|
+
if (installed) {
|
|
3776
|
+
return {
|
|
3777
|
+
target: "codex",
|
|
3778
|
+
available: true,
|
|
3779
|
+
installed: true,
|
|
3780
|
+
message: "Codex plugin files and marketplace entry are present."
|
|
3781
|
+
};
|
|
3782
|
+
}
|
|
3783
|
+
if (!available) {
|
|
3784
|
+
return {
|
|
3785
|
+
target: "codex",
|
|
3786
|
+
available: false,
|
|
3787
|
+
installed: false,
|
|
3788
|
+
message: "Codex was not detected."
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
if (pluginExists && !marketplaceExists) {
|
|
3792
|
+
return {
|
|
3793
|
+
target: "codex",
|
|
3794
|
+
available: true,
|
|
3795
|
+
installed: false,
|
|
3796
|
+
message: "Codex plugin files exist, but the marketplace entry is missing."
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
if (!pluginExists && marketplaceExists) {
|
|
3800
|
+
return {
|
|
3801
|
+
target: "codex",
|
|
3802
|
+
available: true,
|
|
3803
|
+
installed: false,
|
|
3804
|
+
message: "Codex marketplace entry exists, but plugin files are missing."
|
|
3805
|
+
};
|
|
3806
|
+
}
|
|
3807
|
+
return {
|
|
3808
|
+
target: "codex",
|
|
3809
|
+
available: true,
|
|
3810
|
+
installed: false,
|
|
3811
|
+
message: "Codex is available and ready for plugin install."
|
|
3812
|
+
};
|
|
3813
|
+
})();
|
|
3814
|
+
}
|
|
3815
|
+
async function buildOpenclawStatus(runner) {
|
|
3816
|
+
const state = await getOpenclawInstallState(runner);
|
|
3817
|
+
if (state.installed) {
|
|
3818
|
+
return {
|
|
3819
|
+
target: "openclaw",
|
|
3820
|
+
available: true,
|
|
3821
|
+
installed: true,
|
|
3822
|
+
message: "OpenClaw plugin is installed."
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
return {
|
|
3826
|
+
target: "openclaw",
|
|
3827
|
+
available: state.available,
|
|
3828
|
+
installed: false,
|
|
3829
|
+
message: state.available ? "OpenClaw is available and ready for plugin install." : "OpenClaw was not detected."
|
|
3830
|
+
};
|
|
3831
|
+
}
|
|
3832
|
+
async function resolveOpenclawTarball(fetchImpl) {
|
|
3833
|
+
const response = await fetchImpl(
|
|
3834
|
+
`${NPM_REGISTRY_BASE_URL}/${encodeURIComponent(ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME)}`,
|
|
3835
|
+
{
|
|
3836
|
+
headers: {
|
|
3837
|
+
Accept: "application/json"
|
|
3838
|
+
},
|
|
3839
|
+
signal: AbortSignal.timeout(1e4)
|
|
3840
|
+
}
|
|
3841
|
+
);
|
|
3842
|
+
if (!response.ok) {
|
|
3843
|
+
throw new Error(
|
|
3844
|
+
`npm registry returned HTTP ${response.status} while resolving ${ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME}.`
|
|
3845
|
+
);
|
|
3846
|
+
}
|
|
3847
|
+
const data = await response.json();
|
|
3848
|
+
const latestVersion = data["dist-tags"]?.latest;
|
|
3849
|
+
const tarballUrl = latestVersion ? data.versions?.[latestVersion]?.dist?.tarball : void 0;
|
|
3850
|
+
if (!latestVersion || !tarballUrl) {
|
|
3851
|
+
throw new Error(`Could not resolve a tarball URL for ${ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME}.`);
|
|
3852
|
+
}
|
|
3853
|
+
return {
|
|
3854
|
+
tarballUrl,
|
|
3855
|
+
version: latestVersion
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
async function installClaudePlugin(paths, fetchImpl, runner) {
|
|
3859
|
+
const state = await getClaudeInstallState(runner);
|
|
3860
|
+
if (!state.available) {
|
|
3861
|
+
return {
|
|
3862
|
+
target: "claude",
|
|
3863
|
+
changed: false,
|
|
3864
|
+
message: "Claude Code is not available on this machine."
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
const syncResult = await syncManagedRepoTree(CLAUDE_PLUGIN_SYNC_SPEC, paths.claudePluginDir, fetchImpl);
|
|
3868
|
+
const manifestChanged = writeJsonIfChanged(
|
|
3869
|
+
paths.claudeMarketplaceManifestPath,
|
|
3870
|
+
buildClaudeMarketplaceManifest()
|
|
3871
|
+
);
|
|
3872
|
+
const marketplaceAdd = await runner("claude", ["plugin", "marketplace", "add", paths.claudeMarketplaceDir]);
|
|
3873
|
+
if (marketplaceAdd.exitCode !== 0) {
|
|
3874
|
+
throw new Error(formatCommandFailure("claude", ["plugin", "marketplace", "add", paths.claudeMarketplaceDir], marketplaceAdd));
|
|
3875
|
+
}
|
|
3876
|
+
let installedChanged = false;
|
|
3877
|
+
if (!state.installed) {
|
|
3878
|
+
const install = await runner("claude", [
|
|
3879
|
+
"plugin",
|
|
3880
|
+
"install",
|
|
3881
|
+
`${ORGX_CLAUDE_PLUGIN_NAME}@${ORGX_CLAUDE_MARKETPLACE_NAME}`,
|
|
3882
|
+
"--scope",
|
|
3883
|
+
"user"
|
|
3884
|
+
]);
|
|
3885
|
+
if (install.exitCode !== 0) {
|
|
3886
|
+
throw new Error(
|
|
3887
|
+
formatCommandFailure(
|
|
3888
|
+
"claude",
|
|
3889
|
+
[
|
|
3890
|
+
"plugin",
|
|
3891
|
+
"install",
|
|
3892
|
+
`${ORGX_CLAUDE_PLUGIN_NAME}@${ORGX_CLAUDE_MARKETPLACE_NAME}`,
|
|
3893
|
+
"--scope",
|
|
3894
|
+
"user"
|
|
3895
|
+
],
|
|
3896
|
+
install
|
|
3897
|
+
)
|
|
3898
|
+
);
|
|
3899
|
+
}
|
|
3900
|
+
installedChanged = true;
|
|
3901
|
+
}
|
|
3902
|
+
const changed = syncResult.changed || manifestChanged || installedChanged;
|
|
3903
|
+
return {
|
|
3904
|
+
target: "claude",
|
|
3905
|
+
changed,
|
|
3906
|
+
message: changed ? `Synced ${syncResult.fileCount} Claude plugin files and ensured the plugin is installed.` : "Claude Code plugin is already installed and up to date."
|
|
3907
|
+
};
|
|
3908
|
+
}
|
|
3909
|
+
async function installCodexPlugin(paths, fetchImpl, runner) {
|
|
3910
|
+
const available = detectSurface("codex").detected || await commandExists("codex", runner);
|
|
3911
|
+
if (!available) {
|
|
3912
|
+
return {
|
|
3913
|
+
target: "codex",
|
|
3914
|
+
changed: false,
|
|
3915
|
+
message: "Codex is not available on this machine."
|
|
3916
|
+
};
|
|
3917
|
+
}
|
|
3918
|
+
const syncResult = await syncManagedRepoTree(CODEX_PLUGIN_SYNC_SPEC, paths.codexPluginDir, fetchImpl);
|
|
3919
|
+
const marketplaceChanged = upsertCodexMarketplaceEntry(paths.codexMarketplacePath);
|
|
3920
|
+
const changed = syncResult.changed || marketplaceChanged;
|
|
3921
|
+
return {
|
|
3922
|
+
target: "codex",
|
|
3923
|
+
changed,
|
|
3924
|
+
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."
|
|
3925
|
+
};
|
|
3926
|
+
}
|
|
3927
|
+
async function installOpenclawPlugin(fetchImpl, runner) {
|
|
3928
|
+
const state = await getOpenclawInstallState(runner);
|
|
3929
|
+
if (!state.available) {
|
|
3930
|
+
return {
|
|
3931
|
+
target: "openclaw",
|
|
3932
|
+
changed: false,
|
|
3933
|
+
message: "OpenClaw is not available on this machine."
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
if (state.installed) {
|
|
3937
|
+
return {
|
|
3938
|
+
target: "openclaw",
|
|
3939
|
+
changed: false,
|
|
3940
|
+
message: "OpenClaw plugin is already installed."
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3943
|
+
const { tarballUrl, version } = await resolveOpenclawTarball(fetchImpl);
|
|
3944
|
+
const tarballBytes = await fetchRemoteBytes(tarballUrl, fetchImpl);
|
|
3945
|
+
const tempRoot = mkdtempSync(join3(tmpdir(), "orgx-wizard-openclaw-"));
|
|
3946
|
+
const archivePath = join3(tempRoot, `orgx-openclaw-plugin-${version}.tgz`);
|
|
3947
|
+
try {
|
|
3948
|
+
writeFileSync2(archivePath, tarballBytes);
|
|
3949
|
+
const install = await runner("openclaw", ["plugins", "install", archivePath]);
|
|
3950
|
+
if (install.exitCode !== 0) {
|
|
3951
|
+
throw new Error(
|
|
3952
|
+
formatCommandFailure("openclaw", ["plugins", "install", archivePath], install)
|
|
3953
|
+
);
|
|
3954
|
+
}
|
|
3955
|
+
} finally {
|
|
3956
|
+
rmSync(tempRoot, { force: true, recursive: true });
|
|
3957
|
+
}
|
|
3958
|
+
return {
|
|
3959
|
+
target: "openclaw",
|
|
3960
|
+
changed: true,
|
|
3961
|
+
message: `Installed OpenClaw plugin ${ORGX_OPENCLAW_PLUGIN_ID} from ${ORGX_OPENCLAW_PLUGIN_PACKAGE_NAME}@${version}.`
|
|
3962
|
+
};
|
|
3963
|
+
}
|
|
3964
|
+
async function uninstallClaudePlugin(paths, runner) {
|
|
3965
|
+
const state = await getClaudeInstallState(runner);
|
|
3966
|
+
let changed = false;
|
|
3967
|
+
if (state.available && state.installed) {
|
|
3968
|
+
const uninstall = await runner("claude", [
|
|
3969
|
+
"plugin",
|
|
3970
|
+
"uninstall",
|
|
3971
|
+
ORGX_CLAUDE_PLUGIN_NAME,
|
|
3972
|
+
"--scope",
|
|
3973
|
+
"user"
|
|
3974
|
+
]);
|
|
3975
|
+
if (uninstall.exitCode !== 0) {
|
|
3976
|
+
throw new Error(
|
|
3977
|
+
formatCommandFailure(
|
|
3978
|
+
"claude",
|
|
3979
|
+
["plugin", "uninstall", ORGX_CLAUDE_PLUGIN_NAME, "--scope", "user"],
|
|
3980
|
+
uninstall
|
|
3981
|
+
)
|
|
3982
|
+
);
|
|
3983
|
+
}
|
|
3984
|
+
changed = true;
|
|
3985
|
+
}
|
|
3986
|
+
if (state.available) {
|
|
3987
|
+
const removeMarketplace = await runner("claude", ["plugin", "marketplace", "remove", ORGX_CLAUDE_MARKETPLACE_NAME]);
|
|
3988
|
+
if (removeMarketplace.exitCode === 0) {
|
|
3989
|
+
changed = true;
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
changed = removePathIfExists(paths.claudeMarketplaceDir) || changed;
|
|
3993
|
+
return {
|
|
3994
|
+
target: "claude",
|
|
3995
|
+
changed,
|
|
3996
|
+
message: changed ? "Removed the managed Claude Code plugin and marketplace wrapper." : "Claude Code plugin was not installed."
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3999
|
+
async function uninstallCodexPlugin(paths) {
|
|
4000
|
+
const removedPluginDir = removePathIfExists(paths.codexPluginDir);
|
|
4001
|
+
const removedMarketplaceEntry = removeCodexMarketplaceEntry(paths.codexMarketplacePath);
|
|
4002
|
+
const changed = removedPluginDir || removedMarketplaceEntry;
|
|
4003
|
+
return {
|
|
4004
|
+
target: "codex",
|
|
4005
|
+
changed,
|
|
4006
|
+
message: changed ? "Removed the managed Codex plugin files and marketplace entry." : "Codex plugin was not installed."
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
async function uninstallOpenclawPlugin(runner) {
|
|
4010
|
+
const state = await getOpenclawInstallState(runner);
|
|
4011
|
+
if (!state.available || !state.installed) {
|
|
4012
|
+
return {
|
|
4013
|
+
target: "openclaw",
|
|
4014
|
+
changed: false,
|
|
4015
|
+
message: "OpenClaw plugin was not installed."
|
|
4016
|
+
};
|
|
4017
|
+
}
|
|
4018
|
+
const uninstall = await runner("openclaw", ["plugins", "uninstall", ORGX_OPENCLAW_PLUGIN_ID, "--force"]);
|
|
4019
|
+
if (uninstall.exitCode !== 0) {
|
|
4020
|
+
throw new Error(
|
|
4021
|
+
formatCommandFailure(
|
|
4022
|
+
"openclaw",
|
|
4023
|
+
["plugins", "uninstall", ORGX_OPENCLAW_PLUGIN_ID, "--force"],
|
|
4024
|
+
uninstall
|
|
4025
|
+
)
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
4028
|
+
return {
|
|
4029
|
+
target: "openclaw",
|
|
4030
|
+
changed: true,
|
|
4031
|
+
message: "Removed the managed OpenClaw plugin."
|
|
2749
4032
|
};
|
|
2750
4033
|
}
|
|
2751
|
-
function
|
|
4034
|
+
function resolvePluginTargets(requested = []) {
|
|
2752
4035
|
if (requested.length === 0 || requested.includes("all")) {
|
|
2753
|
-
return [...
|
|
4036
|
+
return [...DEFAULT_ORGX_PLUGIN_TARGETS];
|
|
2754
4037
|
}
|
|
2755
4038
|
const seen = /* @__PURE__ */ new Set();
|
|
2756
4039
|
const normalized = [];
|
|
2757
|
-
for (const
|
|
2758
|
-
const
|
|
2759
|
-
if (
|
|
2760
|
-
|
|
4040
|
+
for (const rawTarget of requested) {
|
|
4041
|
+
const target = rawTarget.trim().toLowerCase();
|
|
4042
|
+
if (target === "" || target === "all") continue;
|
|
4043
|
+
if (!DEFAULT_ORGX_PLUGIN_TARGETS.includes(target)) {
|
|
4044
|
+
throw new Error(
|
|
4045
|
+
`Unknown plugin target '${rawTarget}'. Supported targets: ${DEFAULT_ORGX_PLUGIN_TARGETS.join(", ")}.`
|
|
4046
|
+
);
|
|
2761
4047
|
}
|
|
2762
|
-
|
|
2763
|
-
|
|
4048
|
+
const nextTarget = target;
|
|
4049
|
+
if (seen.has(nextTarget)) continue;
|
|
4050
|
+
seen.add(nextTarget);
|
|
4051
|
+
normalized.push(nextTarget);
|
|
2764
4052
|
}
|
|
2765
4053
|
return normalized;
|
|
2766
4054
|
}
|
|
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 = {}) {
|
|
4055
|
+
async function listOrgxPluginStatuses(options = {}) {
|
|
4056
|
+
const paths = resolvePluginPaths(options.paths);
|
|
4057
|
+
const runner = resolveCommandRunner(options.commandRunner);
|
|
4058
|
+
return await Promise.all([
|
|
4059
|
+
buildClaudeStatus(paths, runner),
|
|
4060
|
+
buildCodexStatus(paths, runner),
|
|
4061
|
+
buildOpenclawStatus(runner)
|
|
4062
|
+
]);
|
|
4063
|
+
}
|
|
4064
|
+
async function installOrgxPlugins(options = {}) {
|
|
4065
|
+
const targets = resolvePluginTargets(options.targets ?? []);
|
|
2793
4066
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
2794
4067
|
if (!fetchImpl) {
|
|
2795
4068
|
throw new Error("Global fetch is unavailable in this runtime.");
|
|
2796
4069
|
}
|
|
2797
|
-
const
|
|
2798
|
-
const
|
|
2799
|
-
const
|
|
2800
|
-
const
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
4070
|
+
const paths = resolvePluginPaths(options.paths);
|
|
4071
|
+
const runner = resolveCommandRunner(options.commandRunner);
|
|
4072
|
+
const results = [];
|
|
4073
|
+
for (const target of targets) {
|
|
4074
|
+
switch (target) {
|
|
4075
|
+
case "claude":
|
|
4076
|
+
results.push(await installClaudePlugin(paths, fetchImpl, runner));
|
|
4077
|
+
break;
|
|
4078
|
+
case "codex":
|
|
4079
|
+
results.push(await installCodexPlugin(paths, fetchImpl, runner));
|
|
4080
|
+
break;
|
|
4081
|
+
case "openclaw":
|
|
4082
|
+
results.push(await installOpenclawPlugin(fetchImpl, runner));
|
|
4083
|
+
break;
|
|
2808
4084
|
}
|
|
2809
|
-
} else {
|
|
2810
|
-
skillNames = resolveSkillPackNames(requestedNames);
|
|
2811
4085
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
];
|
|
2816
|
-
const
|
|
2817
|
-
|
|
2818
|
-
|
|
4086
|
+
return { results };
|
|
4087
|
+
}
|
|
4088
|
+
async function uninstallOrgxPlugins(options = {}) {
|
|
4089
|
+
const targets = resolvePluginTargets(options.targets ?? []);
|
|
4090
|
+
const paths = resolvePluginPaths(options.paths);
|
|
4091
|
+
const runner = resolveCommandRunner(options.commandRunner);
|
|
4092
|
+
const results = [];
|
|
4093
|
+
for (const target of targets) {
|
|
4094
|
+
switch (target) {
|
|
4095
|
+
case "claude":
|
|
4096
|
+
results.push(await uninstallClaudePlugin(paths, runner));
|
|
4097
|
+
break;
|
|
4098
|
+
case "codex":
|
|
4099
|
+
results.push(await uninstallCodexPlugin(paths));
|
|
4100
|
+
break;
|
|
4101
|
+
case "openclaw":
|
|
4102
|
+
results.push(await uninstallOpenclawPlugin(runner));
|
|
4103
|
+
break;
|
|
4104
|
+
}
|
|
2819
4105
|
}
|
|
2820
|
-
return {
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
4106
|
+
return { results };
|
|
4107
|
+
}
|
|
4108
|
+
function countPluginReportChanges(report) {
|
|
4109
|
+
return report.results.filter((result) => result.changed).length;
|
|
2824
4110
|
}
|
|
2825
4111
|
|
|
2826
4112
|
// src/lib/setup-workspace.ts
|
|
@@ -3010,7 +4296,9 @@ async function runWorkspaceSetup(client, prompts, options) {
|
|
|
3010
4296
|
// src/lib/founder-preset.ts
|
|
3011
4297
|
async function runFounderPreset(prompts, options) {
|
|
3012
4298
|
const surfaceResults = await setupDetectedSurfaces();
|
|
4299
|
+
const pluginTargets = options.pluginTargets ?? (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
|
|
3013
4300
|
const skillReport = await installOrgxSkills({
|
|
4301
|
+
pluginTargets,
|
|
3014
4302
|
skillNames: [...DEFAULT_ORGX_SKILL_PACKS]
|
|
3015
4303
|
});
|
|
3016
4304
|
let workspaceSetup;
|
|
@@ -3058,6 +4346,179 @@ function summarizeMutationResults(results) {
|
|
|
3058
4346
|
}));
|
|
3059
4347
|
}
|
|
3060
4348
|
|
|
4349
|
+
// src/lib/setup-verification.ts
|
|
4350
|
+
var HOSTED_MCP_OUTAGE_TITLE = "Hosted OrgX MCP is unreachable.";
|
|
4351
|
+
function getSetupVerificationHeadline(summary) {
|
|
4352
|
+
switch (summary.status) {
|
|
4353
|
+
case "degraded":
|
|
4354
|
+
return "Hosted OrgX MCP is down; local setup can still continue.";
|
|
4355
|
+
case "error":
|
|
4356
|
+
return "Issues detected";
|
|
4357
|
+
case "warning":
|
|
4358
|
+
return "Warnings detected";
|
|
4359
|
+
case "ok":
|
|
4360
|
+
return "All systems ready.";
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
function summarizeSetupVerification(assessment) {
|
|
4364
|
+
const errors = assessment.issues.filter((issue) => issue.level === "error");
|
|
4365
|
+
if (errors.length === 0) {
|
|
4366
|
+
return {
|
|
4367
|
+
hostedMcpDegraded: false,
|
|
4368
|
+
status: assessment.issues.length > 0 ? "warning" : "ok"
|
|
4369
|
+
};
|
|
4370
|
+
}
|
|
4371
|
+
const hostedMcpDegraded = errors.every((issue) => issue.title === HOSTED_MCP_OUTAGE_TITLE);
|
|
4372
|
+
return {
|
|
4373
|
+
hostedMcpDegraded,
|
|
4374
|
+
status: hostedMcpDegraded ? "degraded" : "error"
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
4377
|
+
|
|
4378
|
+
// src/lib/telemetry-events.ts
|
|
4379
|
+
function withBaseProperties(properties, base) {
|
|
4380
|
+
return {
|
|
4381
|
+
...base,
|
|
4382
|
+
...properties
|
|
4383
|
+
};
|
|
4384
|
+
}
|
|
4385
|
+
function buildWorkspaceSetupTelemetryProperties(result, base = {}) {
|
|
4386
|
+
return withBaseProperties(
|
|
4387
|
+
{
|
|
4388
|
+
created: result.created === true,
|
|
4389
|
+
default_changed: result.defaultChanged === true,
|
|
4390
|
+
has_workspace: Boolean(result.workspace),
|
|
4391
|
+
status: result.status
|
|
4392
|
+
},
|
|
4393
|
+
base
|
|
4394
|
+
);
|
|
4395
|
+
}
|
|
4396
|
+
function buildOnboardingTaskTelemetryProperties(input, base = {}) {
|
|
4397
|
+
return withBaseProperties(
|
|
4398
|
+
{
|
|
4399
|
+
created: input.created,
|
|
4400
|
+
has_initiative: input.hasInitiative,
|
|
4401
|
+
task_status: input.task.status ?? "unknown"
|
|
4402
|
+
},
|
|
4403
|
+
base
|
|
4404
|
+
);
|
|
4405
|
+
}
|
|
4406
|
+
function buildAgentRosterTelemetryProperties(input, base = {}) {
|
|
4407
|
+
return withBaseProperties(
|
|
4408
|
+
{
|
|
4409
|
+
agent_count: input.roster.agents.length,
|
|
4410
|
+
domain_count: new Set(input.roster.agents.map((agent) => agent.domain)).size,
|
|
4411
|
+
strategy: input.strategy
|
|
4412
|
+
},
|
|
4413
|
+
base
|
|
4414
|
+
);
|
|
4415
|
+
}
|
|
4416
|
+
function buildFounderDemoTelemetryProperties(result, base = {}) {
|
|
4417
|
+
return withBaseProperties(
|
|
4418
|
+
{
|
|
4419
|
+
created: result.created,
|
|
4420
|
+
decision_status: result.decision.status ?? "unknown",
|
|
4421
|
+
has_artifact_url: Boolean(result.artifact.url)
|
|
4422
|
+
},
|
|
4423
|
+
base
|
|
4424
|
+
);
|
|
4425
|
+
}
|
|
4426
|
+
function buildDoctorTelemetryProperties(report, assessment, verification, base = {}) {
|
|
4427
|
+
const errorCount = assessment.issues.filter((issue) => issue.level === "error").length;
|
|
4428
|
+
const warningCount = assessment.issues.filter((issue) => issue.level === "warning").length;
|
|
4429
|
+
return withBaseProperties(
|
|
4430
|
+
{
|
|
4431
|
+
auth_configured: report.auth.configured,
|
|
4432
|
+
auth_ok: report.auth.ok,
|
|
4433
|
+
configured_surface_count: report.surfaces.filter((surface) => surface.configured).length,
|
|
4434
|
+
detected_surface_count: report.surfaces.filter((surface) => surface.detected).length,
|
|
4435
|
+
error_count: errorCount,
|
|
4436
|
+
hosted_mcp_degraded: verification.hostedMcpDegraded,
|
|
4437
|
+
hosted_mcp_ok: report.hostedMcp.ok,
|
|
4438
|
+
hosted_mcp_tool_ok: report.hostedMcpTool.ok,
|
|
4439
|
+
issue_count: assessment.issues.length,
|
|
4440
|
+
openclaw_available: !report.openclaw.skipped,
|
|
4441
|
+
openclaw_ok: report.openclaw.ok,
|
|
4442
|
+
status: verification.status,
|
|
4443
|
+
warning_count: warningCount,
|
|
4444
|
+
workspace_configured: report.workspace.configured,
|
|
4445
|
+
workspace_ok: report.workspace.ok
|
|
4446
|
+
},
|
|
4447
|
+
base
|
|
4448
|
+
);
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
// src/lib/telemetry.ts
|
|
4452
|
+
var POSTHOG_DEFAULT_HOST = "https://us.i.posthog.com";
|
|
4453
|
+
var WIZARD_LIB = "@useorgx/wizard";
|
|
4454
|
+
function isTruthyEnv(value) {
|
|
4455
|
+
if (!value) return false;
|
|
4456
|
+
switch (value.trim().toLowerCase()) {
|
|
4457
|
+
case "1":
|
|
4458
|
+
case "true":
|
|
4459
|
+
case "yes":
|
|
4460
|
+
case "y":
|
|
4461
|
+
case "on":
|
|
4462
|
+
return true;
|
|
4463
|
+
default:
|
|
4464
|
+
return false;
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
function isWizardTelemetryDisabled() {
|
|
4468
|
+
const explicitEnable = isTruthyEnv(process.env.ORGX_TELEMETRY_ENABLED);
|
|
4469
|
+
if (!explicitEnable) return true;
|
|
4470
|
+
return isTruthyEnv(process.env.ORGX_TELEMETRY_DISABLED) || isTruthyEnv(process.env.OPENCLAW_TELEMETRY_DISABLED) || isTruthyEnv(process.env.POSTHOG_DISABLED);
|
|
4471
|
+
}
|
|
4472
|
+
function resolvePosthogApiKey() {
|
|
4473
|
+
const value = process.env.ORGX_POSTHOG_API_KEY ?? process.env.POSTHOG_API_KEY ?? process.env.ORGX_POSTHOG_KEY ?? process.env.POSTHOG_KEY ?? "";
|
|
4474
|
+
const trimmed = value.trim();
|
|
4475
|
+
return trimmed || null;
|
|
4476
|
+
}
|
|
4477
|
+
function resolvePosthogHost() {
|
|
4478
|
+
const value = process.env.ORGX_POSTHOG_HOST ?? process.env.POSTHOG_HOST ?? process.env.ORGX_POSTHOG_API_HOST ?? process.env.POSTHOG_API_HOST ?? "";
|
|
4479
|
+
const trimmed = value.trim();
|
|
4480
|
+
return trimmed || POSTHOG_DEFAULT_HOST;
|
|
4481
|
+
}
|
|
4482
|
+
function toPosthogBatchUrl(host) {
|
|
4483
|
+
try {
|
|
4484
|
+
return new URL("/batch/", host).toString();
|
|
4485
|
+
} catch {
|
|
4486
|
+
return `${POSTHOG_DEFAULT_HOST}/batch/`;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
async function trackWizardTelemetry(event, properties = {}, options = {}) {
|
|
4490
|
+
if (isWizardTelemetryDisabled()) return false;
|
|
4491
|
+
const apiKey = resolvePosthogApiKey();
|
|
4492
|
+
if (!apiKey) return false;
|
|
4493
|
+
const installationId = getOrCreateWizardInstallationId(options.statePath);
|
|
4494
|
+
const timestamp = options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4495
|
+
const response = await fetch(toPosthogBatchUrl(resolvePosthogHost()), {
|
|
4496
|
+
method: "POST",
|
|
4497
|
+
headers: {
|
|
4498
|
+
"Content-Type": "application/json"
|
|
4499
|
+
},
|
|
4500
|
+
body: JSON.stringify({
|
|
4501
|
+
api_key: apiKey,
|
|
4502
|
+
batch: [
|
|
4503
|
+
{
|
|
4504
|
+
type: "capture",
|
|
4505
|
+
event,
|
|
4506
|
+
distinct_id: installationId,
|
|
4507
|
+
properties: {
|
|
4508
|
+
$lib: WIZARD_LIB,
|
|
4509
|
+
source: WIZARD_LIB,
|
|
4510
|
+
wizard_installation_id: installationId,
|
|
4511
|
+
...properties
|
|
4512
|
+
},
|
|
4513
|
+
timestamp
|
|
4514
|
+
}
|
|
4515
|
+
],
|
|
4516
|
+
sent_at: timestamp
|
|
4517
|
+
})
|
|
4518
|
+
}).catch(() => null);
|
|
4519
|
+
return response?.ok === true;
|
|
4520
|
+
}
|
|
4521
|
+
|
|
3061
4522
|
// src/spinner.ts
|
|
3062
4523
|
import ora from "ora";
|
|
3063
4524
|
import pc2 from "picocolors";
|
|
@@ -3095,12 +4556,34 @@ function formatAuthSource(source) {
|
|
|
3095
4556
|
return "not configured";
|
|
3096
4557
|
}
|
|
3097
4558
|
}
|
|
4559
|
+
function formatPluginTargetLabel(target) {
|
|
4560
|
+
switch (target) {
|
|
4561
|
+
case "claude":
|
|
4562
|
+
return "Claude Code";
|
|
4563
|
+
case "codex":
|
|
4564
|
+
return "Codex";
|
|
4565
|
+
case "openclaw":
|
|
4566
|
+
return "OpenClaw";
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
3098
4569
|
function printSurfaceTable(statuses) {
|
|
3099
4570
|
for (const status of statuses) {
|
|
3100
4571
|
const icon = status.configured ? ICON.ok : status.detected ? ICON.warn : ICON.skip;
|
|
3101
4572
|
const state = status.configured ? pc3.green("configured") : status.detected ? pc3.yellow("detected") : pc3.dim("not found");
|
|
3102
4573
|
const mode = pc3.dim(status.mode === "automated" ? "auto " : "manual");
|
|
3103
4574
|
console.log(` ${icon} ${pc3.bold(status.name.padEnd(10))} ${mode} ${state}`);
|
|
4575
|
+
if (status.mode === "manual") {
|
|
4576
|
+
console.log(` ${pc3.dim(status.summary)}`);
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
function printPluginStatusReport(statuses) {
|
|
4581
|
+
for (const status of statuses) {
|
|
4582
|
+
const icon = status.installed ? ICON.ok : status.available ? ICON.warn : ICON.skip;
|
|
4583
|
+
const state = status.installed ? pc3.green("installed") : status.available ? pc3.yellow("available") : pc3.dim("not found");
|
|
4584
|
+
console.log(
|
|
4585
|
+
` ${icon} ${pc3.bold(formatPluginTargetLabel(status.target).padEnd(12))} ${state} ${pc3.dim(status.message)}`
|
|
4586
|
+
);
|
|
3104
4587
|
}
|
|
3105
4588
|
}
|
|
3106
4589
|
function printMutationResults(results) {
|
|
@@ -3110,6 +4593,15 @@ function printMutationResults(results) {
|
|
|
3110
4593
|
console.log(` ${icon} ${pc3.bold(result.name.padEnd(10))} ${state} ${pc3.dim(result.message)}`);
|
|
3111
4594
|
}
|
|
3112
4595
|
}
|
|
4596
|
+
function printPluginMutationReport(report) {
|
|
4597
|
+
for (const result of report.results) {
|
|
4598
|
+
const icon = result.changed ? ICON.ok : ICON.skip;
|
|
4599
|
+
const state = result.changed ? pc3.green("updated ") : pc3.dim("unchanged");
|
|
4600
|
+
console.log(
|
|
4601
|
+
` ${icon} ${pc3.bold(formatPluginTargetLabel(result.target).padEnd(12))} ${state} ${pc3.dim(result.message)}`
|
|
4602
|
+
);
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
3113
4605
|
function printSkillInstallReport(report) {
|
|
3114
4606
|
for (const write of report.writes) {
|
|
3115
4607
|
const icon = write.changed ? ICON.ok : ICON.skip;
|
|
@@ -3122,6 +4614,23 @@ function printSkillInstallReport(report) {
|
|
|
3122
4614
|
const state = changedCount > 0 ? pc3.green(`${changedCount} updated `) : pc3.dim("unchanged");
|
|
3123
4615
|
console.log(` ${icon} ${pc3.bold(pack.name.padEnd(10))} ${state} ${pc3.dim(`${pack.files.length} files`)}`);
|
|
3124
4616
|
}
|
|
4617
|
+
for (const note of report.notes) {
|
|
4618
|
+
console.log(` ${ICON.skip} ${pc3.dim(note)}`);
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
function countSkillReportChanges(report) {
|
|
4622
|
+
const writeChanges = report.writes.filter((write) => write.changed).length;
|
|
4623
|
+
const packChanges = report.packs.reduce(
|
|
4624
|
+
(count, pack) => count + pack.files.filter((file) => file.changed).length,
|
|
4625
|
+
0
|
|
4626
|
+
);
|
|
4627
|
+
return writeChanges + packChanges;
|
|
4628
|
+
}
|
|
4629
|
+
async function safeTrackWizardTelemetry(event, properties = {}) {
|
|
4630
|
+
try {
|
|
4631
|
+
await trackWizardTelemetry(event, properties);
|
|
4632
|
+
} catch {
|
|
4633
|
+
}
|
|
3125
4634
|
}
|
|
3126
4635
|
function printWorkspace(workspace) {
|
|
3127
4636
|
console.log(
|
|
@@ -3170,6 +4679,12 @@ function printFounderPresetResult(result) {
|
|
|
3170
4679
|
` ${result.demoInitiative.created ? pc3.green("created") : pc3.yellow("unchanged")} ${result.demoInitiative.initiative.title}`
|
|
3171
4680
|
);
|
|
3172
4681
|
console.log(` live: ${result.demoInitiative.liveUrl}`);
|
|
4682
|
+
console.log(
|
|
4683
|
+
` decision: ${result.demoInitiative.decision.title} ${pc3.dim(`(${result.demoInitiative.decision.status ?? "pending"})`)}`
|
|
4684
|
+
);
|
|
4685
|
+
console.log(
|
|
4686
|
+
` artifact: ${result.demoInitiative.artifact.name}${result.demoInitiative.artifact.url ? ` ${pc3.dim(result.demoInitiative.artifact.url)}` : ""}`
|
|
4687
|
+
);
|
|
3173
4688
|
}
|
|
3174
4689
|
}
|
|
3175
4690
|
function normalizePromptResult(value) {
|
|
@@ -3187,6 +4702,64 @@ async function textPrompt(input) {
|
|
|
3187
4702
|
};
|
|
3188
4703
|
return normalizePromptResult(await clack.text(promptInput));
|
|
3189
4704
|
}
|
|
4705
|
+
async function multiselectPrompt(input) {
|
|
4706
|
+
const promptInput = {
|
|
4707
|
+
message: input.message,
|
|
4708
|
+
options: input.options,
|
|
4709
|
+
...input.required !== void 0 ? { required: input.required } : {}
|
|
4710
|
+
};
|
|
4711
|
+
return normalizePromptResult(await clack.multiselect(promptInput));
|
|
4712
|
+
}
|
|
4713
|
+
function printAgentRoster(roster) {
|
|
4714
|
+
console.log(` ${ICON.ok} ${pc3.green("agent roster")} ${pc3.bold(`${roster.agents.length} agents`)}`);
|
|
4715
|
+
for (const agent of roster.agents) {
|
|
4716
|
+
console.log(
|
|
4717
|
+
` ${pc3.dim(agent.domain.padEnd(12))} ${pc3.bold(agent.name)} ${pc3.dim(agent.agentType)}`
|
|
4718
|
+
);
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
async function promptForGuidedAgentRoster(workspace) {
|
|
4722
|
+
const remainingAgents = listAgentRosterProfiles();
|
|
4723
|
+
const assignments = [];
|
|
4724
|
+
for (const domain of AGENT_ROSTER_DOMAINS) {
|
|
4725
|
+
if (remainingAgents.length === 0) {
|
|
4726
|
+
break;
|
|
4727
|
+
}
|
|
4728
|
+
const defaultAgent = remainingAgents.find((agent) => agent.defaultDomain === domain);
|
|
4729
|
+
const choice = await selectPrompt({
|
|
4730
|
+
initialValue: defaultAgent?.name ?? "skip",
|
|
4731
|
+
message: `Assign an OrgX agent to the ${domain} domain`,
|
|
4732
|
+
options: [
|
|
4733
|
+
...remainingAgents.map((agent) => ({
|
|
4734
|
+
value: agent.name,
|
|
4735
|
+
label: agent.name,
|
|
4736
|
+
hint: `${agent.agentType} \xB7 default ${agent.defaultDomain}`
|
|
4737
|
+
})),
|
|
4738
|
+
{ value: "skip", label: `Leave ${domain} unassigned`, hint: "optional" }
|
|
4739
|
+
]
|
|
4740
|
+
});
|
|
4741
|
+
if (clack.isCancel(choice)) {
|
|
4742
|
+
clack.cancel("Setup cancelled.");
|
|
4743
|
+
return "cancelled";
|
|
4744
|
+
}
|
|
4745
|
+
if (choice === "skip") {
|
|
4746
|
+
continue;
|
|
4747
|
+
}
|
|
4748
|
+
const index = remainingAgents.findIndex((agent) => agent.name === choice);
|
|
4749
|
+
if (index === -1) {
|
|
4750
|
+
continue;
|
|
4751
|
+
}
|
|
4752
|
+
const [selectedAgent] = remainingAgents.splice(index, 1);
|
|
4753
|
+
if (!selectedAgent) {
|
|
4754
|
+
continue;
|
|
4755
|
+
}
|
|
4756
|
+
assignments.push({ domain, name: selectedAgent.name });
|
|
4757
|
+
}
|
|
4758
|
+
if (assignments.length === 0) {
|
|
4759
|
+
return null;
|
|
4760
|
+
}
|
|
4761
|
+
return configureAgentRoster(workspace, assignments);
|
|
4762
|
+
}
|
|
3190
4763
|
async function verifyAndPersistAuth(apiKey, options) {
|
|
3191
4764
|
const trimmedKey = apiKey.trim();
|
|
3192
4765
|
if (!trimmedKey.toLowerCase().startsWith("oxk_")) {
|
|
@@ -3207,6 +4780,10 @@ async function verifyAndPersistAuth(apiKey, options) {
|
|
|
3207
4780
|
baseUrl,
|
|
3208
4781
|
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3209
4782
|
});
|
|
4783
|
+
await safeTrackWizardTelemetry("auth_completed", {
|
|
4784
|
+
auth_source: options.telemetrySource ?? "manual_api_key",
|
|
4785
|
+
has_base_url: Boolean(baseUrl)
|
|
4786
|
+
});
|
|
3210
4787
|
const openclawResults = detectSurface("openclaw").detected ? await addSurface("openclaw") : [];
|
|
3211
4788
|
return { openclawResults, stored, verification };
|
|
3212
4789
|
}
|
|
@@ -3228,6 +4805,174 @@ function parseTimeoutSeconds(value) {
|
|
|
3228
4805
|
}
|
|
3229
4806
|
return parsed;
|
|
3230
4807
|
}
|
|
4808
|
+
async function maybeConfigureOptionalWorkspaceAddOns(input) {
|
|
4809
|
+
if (!input.interactive || !input.workspace) {
|
|
4810
|
+
return "skipped";
|
|
4811
|
+
}
|
|
4812
|
+
const existingState = readWizardState();
|
|
4813
|
+
const effectiveInitiativeId = input.initiativeId?.trim() || (existingState?.demoInitiative?.workspaceId === input.workspace.id ? existingState.demoInitiative.id : void 0);
|
|
4814
|
+
const hasOnboardingTask = existingState?.onboardingTask?.workspaceId === input.workspace.id;
|
|
4815
|
+
if (!hasOnboardingTask && effectiveInitiativeId) {
|
|
4816
|
+
const onboardingChoice = await selectPrompt({
|
|
4817
|
+
message: `Create a starter onboarding task for ${input.workspace.name}?`,
|
|
4818
|
+
options: [
|
|
4819
|
+
{ value: "yes", label: "Create onboarding task", hint: "recommended" },
|
|
4820
|
+
{ value: "no", label: "Skip task creation" }
|
|
4821
|
+
]
|
|
4822
|
+
});
|
|
4823
|
+
if (clack.isCancel(onboardingChoice)) {
|
|
4824
|
+
clack.cancel("Setup cancelled.");
|
|
4825
|
+
return "cancelled";
|
|
4826
|
+
}
|
|
4827
|
+
if (onboardingChoice === "yes") {
|
|
4828
|
+
try {
|
|
4829
|
+
const task = await ensureOnboardingTask(input.workspace, {
|
|
4830
|
+
initiativeId: effectiveInitiativeId
|
|
4831
|
+
});
|
|
4832
|
+
console.log(` ${ICON.ok} ${pc3.green("onboarding")} ${pc3.bold(task.title)}`);
|
|
4833
|
+
await safeTrackWizardTelemetry(
|
|
4834
|
+
"onboarding_task_created",
|
|
4835
|
+
buildOnboardingTaskTelemetryProperties(
|
|
4836
|
+
{
|
|
4837
|
+
created: true,
|
|
4838
|
+
hasInitiative: true,
|
|
4839
|
+
task
|
|
4840
|
+
},
|
|
4841
|
+
{
|
|
4842
|
+
command: input.telemetry?.command ?? "setup",
|
|
4843
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
|
|
4844
|
+
}
|
|
4845
|
+
)
|
|
4846
|
+
);
|
|
4847
|
+
} catch (error) {
|
|
4848
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4849
|
+
console.log(` ${ICON.warn} ${pc3.yellow("onboarding")} ${pc3.dim(message)}`);
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
const refreshedState = readWizardState();
|
|
4854
|
+
const hasAgentRoster = refreshedState?.agentRoster?.workspaceId === input.workspace.id;
|
|
4855
|
+
if (!hasAgentRoster) {
|
|
4856
|
+
const rosterChoice = await selectPrompt({
|
|
4857
|
+
message: `Set up an OrgX agent roster for ${input.workspace.name}?`,
|
|
4858
|
+
options: [
|
|
4859
|
+
{ value: "guided", label: "Choose agents and domains", hint: "recommended" },
|
|
4860
|
+
{ value: "default", label: "Install the full default roster" },
|
|
4861
|
+
{ value: "no", label: "Skip roster setup" }
|
|
4862
|
+
]
|
|
4863
|
+
});
|
|
4864
|
+
if (clack.isCancel(rosterChoice)) {
|
|
4865
|
+
clack.cancel("Setup cancelled.");
|
|
4866
|
+
return "cancelled";
|
|
4867
|
+
}
|
|
4868
|
+
if (rosterChoice === "guided") {
|
|
4869
|
+
const roster = await promptForGuidedAgentRoster(input.workspace);
|
|
4870
|
+
if (roster === "cancelled") {
|
|
4871
|
+
return "cancelled";
|
|
4872
|
+
}
|
|
4873
|
+
if (roster) {
|
|
4874
|
+
printAgentRoster(roster);
|
|
4875
|
+
await safeTrackWizardTelemetry(
|
|
4876
|
+
"agent_roster_configured",
|
|
4877
|
+
buildAgentRosterTelemetryProperties(
|
|
4878
|
+
{
|
|
4879
|
+
roster,
|
|
4880
|
+
strategy: "guided"
|
|
4881
|
+
},
|
|
4882
|
+
{
|
|
4883
|
+
command: input.telemetry?.command ?? "setup",
|
|
4884
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
|
|
4885
|
+
}
|
|
4886
|
+
)
|
|
4887
|
+
);
|
|
4888
|
+
} else {
|
|
4889
|
+
console.log(` ${ICON.skip} ${pc3.dim("agent roster skipped")} ${pc3.dim("(no agents selected)")}`);
|
|
4890
|
+
}
|
|
4891
|
+
} else if (rosterChoice === "default") {
|
|
4892
|
+
const roster = ensureDefaultAgentRoster(input.workspace);
|
|
4893
|
+
printAgentRoster(roster);
|
|
4894
|
+
await safeTrackWizardTelemetry(
|
|
4895
|
+
"agent_roster_configured",
|
|
4896
|
+
buildAgentRosterTelemetryProperties(
|
|
4897
|
+
{
|
|
4898
|
+
roster,
|
|
4899
|
+
strategy: "default"
|
|
4900
|
+
},
|
|
4901
|
+
{
|
|
4902
|
+
command: input.telemetry?.command ?? "setup",
|
|
4903
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
|
|
4904
|
+
}
|
|
4905
|
+
)
|
|
4906
|
+
);
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
return "configured";
|
|
4910
|
+
}
|
|
4911
|
+
async function promptOptionalCompanionPluginTargets(input) {
|
|
4912
|
+
if (!input.interactive) {
|
|
4913
|
+
return [];
|
|
4914
|
+
}
|
|
4915
|
+
const statuses = await listOrgxPluginStatuses();
|
|
4916
|
+
const installable = statuses.filter((status) => status.available && !status.installed);
|
|
4917
|
+
if (installable.length === 0) {
|
|
4918
|
+
return [];
|
|
4919
|
+
}
|
|
4920
|
+
const selection = await multiselectPrompt({
|
|
4921
|
+
message: "Install companion OrgX plugins into detected tools?",
|
|
4922
|
+
options: installable.map((status) => ({
|
|
4923
|
+
value: status.target,
|
|
4924
|
+
label: `Install ${formatPluginTargetLabel(status.target)}`,
|
|
4925
|
+
hint: status.message
|
|
4926
|
+
})),
|
|
4927
|
+
required: false
|
|
4928
|
+
});
|
|
4929
|
+
if (clack.isCancel(selection)) {
|
|
4930
|
+
clack.cancel("Setup cancelled.");
|
|
4931
|
+
return "cancelled";
|
|
4932
|
+
}
|
|
4933
|
+
return selection;
|
|
4934
|
+
}
|
|
4935
|
+
function printPluginSkillOwnershipNote(targets) {
|
|
4936
|
+
if (!targets.some((target) => target === "claude" || target === "codex")) {
|
|
4937
|
+
return;
|
|
4938
|
+
}
|
|
4939
|
+
console.log(
|
|
4940
|
+
` ${ICON.skip} ${pc3.dim(
|
|
4941
|
+
"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."
|
|
4942
|
+
)}`
|
|
4943
|
+
);
|
|
4944
|
+
}
|
|
4945
|
+
async function installSelectedCompanionPlugins(input) {
|
|
4946
|
+
if (input.targets.length === 0) {
|
|
4947
|
+
return "skipped";
|
|
4948
|
+
}
|
|
4949
|
+
const spinner = createOrgxSpinner("Installing OrgX companion plugins");
|
|
4950
|
+
spinner.start();
|
|
4951
|
+
const report = await installOrgxPlugins({ targets: input.targets });
|
|
4952
|
+
spinner.succeed("OrgX companion plugins processed");
|
|
4953
|
+
printPluginMutationReport(report);
|
|
4954
|
+
printPluginSkillOwnershipNote(input.targets);
|
|
4955
|
+
await safeTrackWizardTelemetry("plugins_installed", {
|
|
4956
|
+
changed_count: countPluginReportChanges(report),
|
|
4957
|
+
command: input.telemetry?.command ?? "setup",
|
|
4958
|
+
...input.telemetry?.preset ? { preset: input.telemetry.preset } : {},
|
|
4959
|
+
requested_target_count: input.targets.length,
|
|
4960
|
+
target_count: report.results.length
|
|
4961
|
+
});
|
|
4962
|
+
return countPluginReportChanges(report) > 0 ? "configured" : "skipped";
|
|
4963
|
+
}
|
|
4964
|
+
async function maybeInstallOptionalCompanionPlugins(input) {
|
|
4965
|
+
const selection = await promptOptionalCompanionPluginTargets({
|
|
4966
|
+
interactive: input.interactive
|
|
4967
|
+
});
|
|
4968
|
+
if (selection === "cancelled") {
|
|
4969
|
+
return "cancelled";
|
|
4970
|
+
}
|
|
4971
|
+
return await installSelectedCompanionPlugins({
|
|
4972
|
+
targets: selection,
|
|
4973
|
+
...input.telemetry ? { telemetry: input.telemetry } : {}
|
|
4974
|
+
});
|
|
4975
|
+
}
|
|
3231
4976
|
function printAuthStatus(status) {
|
|
3232
4977
|
if (!status.configured) {
|
|
3233
4978
|
console.log(` ${ICON.warn} ${pc3.yellow("no account")} run ${pc3.cyan(`${getCmd()} auth login`)} to connect`);
|
|
@@ -3242,6 +4987,7 @@ function printAuthStatus(status) {
|
|
|
3242
4987
|
}
|
|
3243
4988
|
}
|
|
3244
4989
|
function printDoctorReport(report, assessment) {
|
|
4990
|
+
const verification = summarizeSetupVerification(assessment);
|
|
3245
4991
|
console.log(pc3.dim(" surfaces"));
|
|
3246
4992
|
printSurfaceTable(report.surfaces);
|
|
3247
4993
|
console.log("");
|
|
@@ -3290,11 +5036,12 @@ function printDoctorReport(report, assessment) {
|
|
|
3290
5036
|
console.log(` ${pc3.dim("\u2192")} ${pc3.dim(`OrgX is active across ${configuredCount} editor${configuredCount !== 1 ? "s" : ""}`)}`);
|
|
3291
5037
|
}
|
|
3292
5038
|
} else {
|
|
3293
|
-
const
|
|
3294
|
-
const headline =
|
|
5039
|
+
const headlineText = getSetupVerificationHeadline(verification);
|
|
5040
|
+
const headline = verification.status === "error" ? `${ICON.err} ${pc3.red(headlineText)}` : `${ICON.warn} ${pc3.yellow(headlineText)}`;
|
|
3295
5041
|
console.log(` ${headline}`);
|
|
3296
5042
|
for (const issue of assessment.issues) {
|
|
3297
|
-
const
|
|
5043
|
+
const degradedHostedMcpIssue = verification.hostedMcpDegraded && issue.title === HOSTED_MCP_OUTAGE_TITLE;
|
|
5044
|
+
const label = degradedHostedMcpIssue ? pc3.yellow("degr ") : issue.level === "error" ? pc3.red("error") : pc3.yellow("warn ");
|
|
3298
5045
|
console.log(`
|
|
3299
5046
|
${label} ${issue.title}`);
|
|
3300
5047
|
console.log(` ${pc3.dim("\u2192")} ${issue.suggestion}`);
|
|
@@ -3304,16 +5051,28 @@ function printDoctorReport(report, assessment) {
|
|
|
3304
5051
|
async function main() {
|
|
3305
5052
|
const program = new Command();
|
|
3306
5053
|
program.name("orgx-wizard").description("One-line CLI onboarding for OrgX surfaces.").showHelpAfterError();
|
|
3307
|
-
const pkgVersion = true ? "0.1.
|
|
5054
|
+
const pkgVersion = true ? "0.1.9" : void 0;
|
|
3308
5055
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
3309
5056
|
program.hook("preAction", () => {
|
|
3310
5057
|
console.log(renderBanner(pkgVersion));
|
|
3311
5058
|
});
|
|
3312
5059
|
program.command("setup").description("Patch all detected automated OrgX surfaces.").option("--preset <name>", "run a setup bundle (currently: founder)").action(async (options) => {
|
|
5060
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
5061
|
+
await safeTrackWizardTelemetry("wizard_started", {
|
|
5062
|
+
command: "setup",
|
|
5063
|
+
interactive,
|
|
5064
|
+
preset: options.preset ?? "standard"
|
|
5065
|
+
});
|
|
3313
5066
|
if (options.preset) {
|
|
3314
5067
|
if (options.preset !== "founder") {
|
|
3315
5068
|
throw new Error(`Unknown setup preset '${options.preset}'. Supported presets: founder.`);
|
|
3316
5069
|
}
|
|
5070
|
+
const founderPluginTargets = await promptOptionalCompanionPluginTargets({
|
|
5071
|
+
interactive
|
|
5072
|
+
});
|
|
5073
|
+
if (founderPluginTargets === "cancelled") {
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
3317
5076
|
const spinner2 = createOrgxSpinner("Running founder setup preset");
|
|
3318
5077
|
spinner2.start();
|
|
3319
5078
|
const presetResult = await runFounderPreset(
|
|
@@ -3324,7 +5083,8 @@ async function main() {
|
|
|
3324
5083
|
text: textPrompt
|
|
3325
5084
|
},
|
|
3326
5085
|
{
|
|
3327
|
-
interactive
|
|
5086
|
+
interactive,
|
|
5087
|
+
pluginTargets: founderPluginTargets
|
|
3328
5088
|
}
|
|
3329
5089
|
);
|
|
3330
5090
|
if ("status" in presetResult) {
|
|
@@ -3337,11 +5097,62 @@ async function main() {
|
|
|
3337
5097
|
}
|
|
3338
5098
|
spinner2.succeed("Founder setup preset complete");
|
|
3339
5099
|
printFounderPresetResult(presetResult);
|
|
5100
|
+
await safeTrackWizardTelemetry("mcp_injected", {
|
|
5101
|
+
changed_count: presetResult.surfaceResults.filter((result) => result.changed).length,
|
|
5102
|
+
preset: "founder",
|
|
5103
|
+
surface_count: presetResult.surfaceResults.length
|
|
5104
|
+
});
|
|
5105
|
+
await safeTrackWizardTelemetry("skills_installed", {
|
|
5106
|
+
changed_count: countSkillReportChanges(presetResult.skillReport),
|
|
5107
|
+
pack_count: presetResult.skillReport.packs.length,
|
|
5108
|
+
preset: "founder",
|
|
5109
|
+
write_count: presetResult.skillReport.writes.length
|
|
5110
|
+
});
|
|
5111
|
+
if (presetResult.workspaceSetup) {
|
|
5112
|
+
await safeTrackWizardTelemetry(
|
|
5113
|
+
"workspace_bootstrapped",
|
|
5114
|
+
buildWorkspaceSetupTelemetryProperties(presetResult.workspaceSetup, {
|
|
5115
|
+
command: "setup",
|
|
5116
|
+
interactive,
|
|
5117
|
+
preset: "founder"
|
|
5118
|
+
})
|
|
5119
|
+
);
|
|
5120
|
+
}
|
|
5121
|
+
if (presetResult.demoInitiative) {
|
|
5122
|
+
await safeTrackWizardTelemetry(
|
|
5123
|
+
"founder_demo_ready",
|
|
5124
|
+
buildFounderDemoTelemetryProperties(presetResult.demoInitiative, {
|
|
5125
|
+
command: "setup",
|
|
5126
|
+
preset: "founder"
|
|
5127
|
+
})
|
|
5128
|
+
);
|
|
5129
|
+
}
|
|
5130
|
+
const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
|
|
5131
|
+
interactive,
|
|
5132
|
+
telemetry: { command: "setup", preset: "founder" },
|
|
5133
|
+
workspace: presetResult.workspace,
|
|
5134
|
+
...presetResult.demoInitiative ? { initiativeId: presetResult.demoInitiative.initiative.id } : {}
|
|
5135
|
+
});
|
|
5136
|
+
if (addOnResult === "cancelled") {
|
|
5137
|
+
return;
|
|
5138
|
+
}
|
|
5139
|
+
await installSelectedCompanionPlugins({
|
|
5140
|
+
telemetry: { command: "setup", preset: "founder" },
|
|
5141
|
+
targets: founderPluginTargets
|
|
5142
|
+
});
|
|
3340
5143
|
console.log("");
|
|
3341
5144
|
const doctor2 = await runDoctor();
|
|
3342
5145
|
const assessment2 = assessDoctorReport(doctor2);
|
|
5146
|
+
const verification2 = summarizeSetupVerification(assessment2);
|
|
5147
|
+
await safeTrackWizardTelemetry(
|
|
5148
|
+
"setup_verified",
|
|
5149
|
+
buildDoctorTelemetryProperties(doctor2, assessment2, verification2, {
|
|
5150
|
+
command: "setup",
|
|
5151
|
+
preset: "founder"
|
|
5152
|
+
})
|
|
5153
|
+
);
|
|
3343
5154
|
printDoctorReport(doctor2, assessment2);
|
|
3344
|
-
if (
|
|
5155
|
+
if (verification2.status === "error") {
|
|
3345
5156
|
process.exitCode = 1;
|
|
3346
5157
|
}
|
|
3347
5158
|
return;
|
|
@@ -3351,10 +5162,14 @@ async function main() {
|
|
|
3351
5162
|
const results = await setupDetectedSurfaces();
|
|
3352
5163
|
spinner.succeed("Detected surfaces configured");
|
|
3353
5164
|
printMutationResults(results);
|
|
5165
|
+
await safeTrackWizardTelemetry("mcp_injected", {
|
|
5166
|
+
changed_count: results.filter((result) => result.changed).length,
|
|
5167
|
+
preset: "standard",
|
|
5168
|
+
surface_count: results.length
|
|
5169
|
+
});
|
|
3354
5170
|
let resolvedAuth = await resolveOrgxAuth();
|
|
3355
5171
|
if (!resolvedAuth) {
|
|
3356
5172
|
console.log("");
|
|
3357
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
3358
5173
|
if (interactive) {
|
|
3359
5174
|
const choice = await selectPrompt({
|
|
3360
5175
|
message: "Connect your OrgX account to enable workspace and AI tool access",
|
|
@@ -3368,7 +5183,7 @@ async function main() {
|
|
|
3368
5183
|
return;
|
|
3369
5184
|
}
|
|
3370
5185
|
if (choice === "login") {
|
|
3371
|
-
const loginOk = await runBrowserLogin();
|
|
5186
|
+
const loginOk = await runBrowserLogin({ telemetrySource: "browser_pairing" });
|
|
3372
5187
|
if (!loginOk) {
|
|
3373
5188
|
console.log(`
|
|
3374
5189
|
${pc3.dim("\u2192")} ${pc3.cyan(`${getCmd()} auth login`)} ${pc3.dim("to try again")}`);
|
|
@@ -3404,25 +5219,60 @@ async function main() {
|
|
|
3404
5219
|
text: textPrompt
|
|
3405
5220
|
},
|
|
3406
5221
|
{
|
|
3407
|
-
interactive
|
|
5222
|
+
interactive
|
|
3408
5223
|
}
|
|
3409
5224
|
);
|
|
3410
5225
|
if (workspaceSetup.status === "cancelled") {
|
|
3411
5226
|
return;
|
|
3412
5227
|
}
|
|
5228
|
+
await safeTrackWizardTelemetry(
|
|
5229
|
+
"workspace_bootstrapped",
|
|
5230
|
+
buildWorkspaceSetupTelemetryProperties(workspaceSetup, {
|
|
5231
|
+
command: "setup",
|
|
5232
|
+
interactive,
|
|
5233
|
+
preset: "standard"
|
|
5234
|
+
})
|
|
5235
|
+
);
|
|
5236
|
+
const resolvedWorkspace = workspaceSetup.workspace ?? await getCurrentWorkspace().catch(() => null);
|
|
5237
|
+
if (resolvedWorkspace) {
|
|
5238
|
+
persistContinuityDefaults({ workspace: resolvedWorkspace });
|
|
5239
|
+
}
|
|
3413
5240
|
if (workspaceSetup.workspace) {
|
|
3414
5241
|
console.log(` ${ICON.ok} ${pc3.green("workspace ")} ${pc3.bold(workspaceSetup.workspace.name)}`);
|
|
3415
5242
|
}
|
|
5243
|
+
const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
|
|
5244
|
+
interactive,
|
|
5245
|
+
telemetry: { command: "setup", preset: "standard" },
|
|
5246
|
+
workspace: resolvedWorkspace
|
|
5247
|
+
});
|
|
5248
|
+
if (addOnResult === "cancelled") {
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
5251
|
+
const pluginInstallResult = await maybeInstallOptionalCompanionPlugins({
|
|
5252
|
+
interactive,
|
|
5253
|
+
telemetry: { command: "setup", preset: "standard" }
|
|
5254
|
+
});
|
|
5255
|
+
if (pluginInstallResult === "cancelled") {
|
|
5256
|
+
return;
|
|
5257
|
+
}
|
|
3416
5258
|
}
|
|
3417
5259
|
const doctor = await runDoctor();
|
|
3418
5260
|
const assessment = assessDoctorReport(doctor);
|
|
5261
|
+
const verification = summarizeSetupVerification(assessment);
|
|
5262
|
+
await safeTrackWizardTelemetry(
|
|
5263
|
+
"setup_verified",
|
|
5264
|
+
buildDoctorTelemetryProperties(doctor, assessment, verification, {
|
|
5265
|
+
command: "setup",
|
|
5266
|
+
preset: "standard"
|
|
5267
|
+
})
|
|
5268
|
+
);
|
|
3419
5269
|
const configuredCount = doctor.surfaces.filter((s) => s.configured).length;
|
|
3420
5270
|
console.log("");
|
|
3421
5271
|
if (assessment.issues.length === 0) {
|
|
3422
5272
|
console.log(` ${ICON.ok} ${pc3.green("You're all set.")} ${pc3.dim(`OrgX is active across ${configuredCount} editor${configuredCount !== 1 ? "s" : ""}`)}`);
|
|
3423
5273
|
} else {
|
|
3424
5274
|
printDoctorReport(doctor, assessment);
|
|
3425
|
-
if (
|
|
5275
|
+
if (verification.status === "error") {
|
|
3426
5276
|
process.exitCode = 1;
|
|
3427
5277
|
}
|
|
3428
5278
|
}
|
|
@@ -3460,7 +5310,8 @@ async function main() {
|
|
|
3460
5310
|
});
|
|
3461
5311
|
spinner.text = "Verifying API key...";
|
|
3462
5312
|
const result = await verifyAndPersistAuth(ready.key, {
|
|
3463
|
-
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {}
|
|
5313
|
+
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
|
|
5314
|
+
...opts.telemetrySource ? { telemetrySource: opts.telemetrySource } : {}
|
|
3464
5315
|
});
|
|
3465
5316
|
if (!("stored" in result)) {
|
|
3466
5317
|
spinner.fail("Pairing completed but key was rejected");
|
|
@@ -3509,7 +5360,10 @@ async function main() {
|
|
|
3509
5360
|
if (options.apiKey) {
|
|
3510
5361
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|
|
3511
5362
|
spinner.start();
|
|
3512
|
-
const result = await verifyAndPersistAuth(options.apiKey,
|
|
5363
|
+
const result = await verifyAndPersistAuth(options.apiKey, {
|
|
5364
|
+
...options,
|
|
5365
|
+
telemetrySource: "manual_api_key"
|
|
5366
|
+
});
|
|
3513
5367
|
if (!("stored" in result)) {
|
|
3514
5368
|
spinner.fail("OrgX API key verification failed");
|
|
3515
5369
|
printAuthStatus(result.verification);
|
|
@@ -3528,13 +5382,17 @@ async function main() {
|
|
|
3528
5382
|
...options.baseUrl !== void 0 ? { baseUrl: options.baseUrl } : {},
|
|
3529
5383
|
...options.deviceName !== void 0 ? { deviceName: options.deviceName } : {},
|
|
3530
5384
|
...options.open !== void 0 ? { open: options.open } : {},
|
|
5385
|
+
telemetrySource: "browser_pairing",
|
|
3531
5386
|
timeout: options.timeout
|
|
3532
5387
|
});
|
|
3533
5388
|
});
|
|
3534
5389
|
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
5390
|
const spinner = createOrgxSpinner("Verifying OrgX API key");
|
|
3536
5391
|
spinner.start();
|
|
3537
|
-
const result = await verifyAndPersistAuth(apiKey,
|
|
5392
|
+
const result = await verifyAndPersistAuth(apiKey, {
|
|
5393
|
+
...options,
|
|
5394
|
+
telemetrySource: "manual_api_key"
|
|
5395
|
+
});
|
|
3538
5396
|
if (!("stored" in result)) {
|
|
3539
5397
|
spinner.fail("OrgX API key verification failed");
|
|
3540
5398
|
printAuthStatus(result.verification);
|
|
@@ -3583,6 +5441,41 @@ async function main() {
|
|
|
3583
5441
|
const results = removeMcpSurface(names);
|
|
3584
5442
|
printMutationResults(results);
|
|
3585
5443
|
});
|
|
5444
|
+
const plugins = program.command("plugins").description("Install or remove companion OrgX plugins for Claude Code, Codex, and OpenClaw.");
|
|
5445
|
+
plugins.command("list").description("Show companion plugin availability and install status.").action(async () => {
|
|
5446
|
+
const spinner = createOrgxSpinner("Checking companion plugin status");
|
|
5447
|
+
spinner.start();
|
|
5448
|
+
const statuses = await listOrgxPluginStatuses();
|
|
5449
|
+
spinner.stop();
|
|
5450
|
+
printPluginStatusReport(statuses);
|
|
5451
|
+
});
|
|
5452
|
+
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) => {
|
|
5453
|
+
const spinner = createOrgxSpinner("Installing OrgX companion plugins");
|
|
5454
|
+
spinner.start();
|
|
5455
|
+
const report = await installOrgxPlugins({ targets });
|
|
5456
|
+
spinner.succeed("OrgX companion plugins processed");
|
|
5457
|
+
printPluginMutationReport(report);
|
|
5458
|
+
printPluginSkillOwnershipNote(report.results.map((result) => result.target));
|
|
5459
|
+
await safeTrackWizardTelemetry("plugins_installed", {
|
|
5460
|
+
changed_count: countPluginReportChanges(report),
|
|
5461
|
+
command: "plugins:add",
|
|
5462
|
+
requested_target_count: targets.length,
|
|
5463
|
+
target_count: report.results.length
|
|
5464
|
+
});
|
|
5465
|
+
});
|
|
5466
|
+
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) => {
|
|
5467
|
+
const spinner = createOrgxSpinner("Removing OrgX companion plugins");
|
|
5468
|
+
spinner.start();
|
|
5469
|
+
const report = await uninstallOrgxPlugins({ targets });
|
|
5470
|
+
spinner.succeed("OrgX companion plugins removed");
|
|
5471
|
+
printPluginMutationReport(report);
|
|
5472
|
+
await safeTrackWizardTelemetry("plugins_removed", {
|
|
5473
|
+
changed_count: countPluginReportChanges(report),
|
|
5474
|
+
command: "plugins:remove",
|
|
5475
|
+
requested_target_count: targets.length,
|
|
5476
|
+
target_count: report.results.length
|
|
5477
|
+
});
|
|
5478
|
+
});
|
|
3586
5479
|
const workspace = program.command("workspace").description("Inspect and create OrgX workspaces.");
|
|
3587
5480
|
workspace.command("current").description("Show the current OrgX workspace.").action(async () => {
|
|
3588
5481
|
const spinner = createOrgxSpinner("Loading current OrgX workspace");
|
|
@@ -3612,6 +5505,9 @@ async function main() {
|
|
|
3612
5505
|
}
|
|
3613
5506
|
);
|
|
3614
5507
|
spinner.succeed("OrgX workspace created");
|
|
5508
|
+
if (created.isDefault) {
|
|
5509
|
+
persistContinuityDefaults({ workspace: created });
|
|
5510
|
+
}
|
|
3615
5511
|
printWorkspace(created);
|
|
3616
5512
|
if (!created.isDefault) {
|
|
3617
5513
|
console.log("");
|
|
@@ -3625,6 +5521,7 @@ async function main() {
|
|
|
3625
5521
|
spinner.succeed(
|
|
3626
5522
|
result.changed ? "Default OrgX workspace updated" : "OrgX workspace already set as default"
|
|
3627
5523
|
);
|
|
5524
|
+
persistContinuityDefaults({ workspace: result.workspace });
|
|
3628
5525
|
printWorkspace(result.workspace);
|
|
3629
5526
|
});
|
|
3630
5527
|
program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
|
|
@@ -3633,18 +5530,37 @@ async function main() {
|
|
|
3633
5530
|
const report = await runDoctor();
|
|
3634
5531
|
spinner.stop();
|
|
3635
5532
|
const assessment = assessDoctorReport(report);
|
|
5533
|
+
const verification = summarizeSetupVerification(assessment);
|
|
5534
|
+
await safeTrackWizardTelemetry(
|
|
5535
|
+
"doctor_ran",
|
|
5536
|
+
buildDoctorTelemetryProperties(report, assessment, verification, {
|
|
5537
|
+
command: "doctor"
|
|
5538
|
+
})
|
|
5539
|
+
);
|
|
3636
5540
|
printDoctorReport(report, assessment);
|
|
3637
|
-
if (
|
|
5541
|
+
if (verification.status === "error") {
|
|
3638
5542
|
process.exitCode = 1;
|
|
3639
5543
|
}
|
|
3640
5544
|
});
|
|
3641
5545
|
const skills = program.command("skills").description("Install OrgX rules and Claude skill packs.");
|
|
3642
|
-
skills.command("add").description("Write
|
|
5546
|
+
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) => {
|
|
5547
|
+
const pluginTargets = (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
|
|
3643
5548
|
const spinner = createOrgxSpinner("Installing OrgX rules and skills");
|
|
3644
5549
|
spinner.start();
|
|
3645
|
-
const report = await installOrgxSkills({
|
|
5550
|
+
const report = await installOrgxSkills({
|
|
5551
|
+
pluginTargets,
|
|
5552
|
+
skillNames: packs
|
|
5553
|
+
});
|
|
3646
5554
|
spinner.succeed("OrgX rules and skills installed");
|
|
3647
5555
|
printSkillInstallReport(report);
|
|
5556
|
+
await safeTrackWizardTelemetry("skills_installed", {
|
|
5557
|
+
changed_count: countSkillReportChanges(report),
|
|
5558
|
+
command: "skills:add",
|
|
5559
|
+
pack_count: report.packs.length,
|
|
5560
|
+
plugin_managed_target_count: pluginTargets.length,
|
|
5561
|
+
requested_pack_count: packs.length,
|
|
5562
|
+
write_count: report.writes.length
|
|
5563
|
+
});
|
|
3648
5564
|
});
|
|
3649
5565
|
await program.parseAsync(process.argv);
|
|
3650
5566
|
}
|