@useorgx/wizard 0.1.8 → 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 CHANGED
@@ -22,7 +22,7 @@ For local development, use `pnpm dev -- --help`.
22
22
  - `plugins list` shows companion plugin availability and install status for Claude Code, Codex, and OpenClaw.
23
23
  - `plugins add [target...]` installs the managed OrgX companion plugins into Claude Code, Codex, and/or OpenClaw. Claude Code and Codex plugins carry their own OrgX skills, so they become the source of truth for those hosts.
24
24
  - `plugins remove [target...]` uninstalls the managed OrgX companion plugins from Claude Code, Codex, and/or OpenClaw.
25
- - `doctor` verifies local config, hosted MCP reachability, an authenticated hosted MCP `get_setup_status` tool call when OrgX user auth is configured, npm registry readiness, current-workspace connectivity, local OpenClaw health, and optionally the remote setup status API. It exits non-zero when blocking connectivity issues remain.
25
+ - `doctor` verifies local config, hosted MCP reachability, npm registry readiness, current-workspace connectivity, local OpenClaw health, and optionally the remote setup status API. Hosted MCP tools are OAuth-scoped inside the client connector, so the wizard does not preflight them with `oxk_` API keys. It exits non-zero when blocking connectivity issues remain.
26
26
  - `auth status` shows the resolved OrgX API key source and verifies it against `POST /api/client/sync`.
27
27
  - `auth login [--base-url <url>]` starts browser pairing against OrgX, waits for approval, saves the returned per-user key to the wizard auth store, and bootstraps OpenClaw auth if OpenClaw is detected. Use `--api-key <oxk_...>` for CI or blocked-browser fallback.
28
28
  - `auth set-key <oxk_...> [--base-url <url>]` verifies a per-user OrgX key, saves it to the wizard auth store, and bootstraps OpenClaw auth if OpenClaw is detected.
@@ -40,7 +40,7 @@ For local development, use `pnpm dev -- --help`.
40
40
 
41
41
  - The wizard uses per-user `oxk_...` keys for auth verification and local bootstrap. `auth login` now prefers secure browser pairing and falls back to direct key entry with `--api-key`.
42
42
  - When `keytar` is available, the wizard stores the raw API key in the system keychain and keeps only metadata in `auth.json`. If `keytar` is unavailable or disabled, it falls back to file-backed storage in `auth.json`.
43
- - `doctor` always checks `https://mcp.useorgx.com/health` and, when user auth is configured, initializes an authenticated MCP session against `https://mcp.useorgx.com/mcp` and calls `get_setup_status` before resolving the current workspace through the OrgX API.
43
+ - `doctor` always checks `https://mcp.useorgx.com/health` and resolves the current workspace through the OrgX API. Hosted MCP tool calls are verified by the configured client connector after its OAuth flow completes, not by the wizard's per-user `oxk_` key.
44
44
  - `doctor` also checks npm registry reachability for `@useorgx/wizard` and reports whether the package is already published.
45
45
  - Remote setup status is separate: `doctor` checks `/api/setup/status` only when `ORGX_SERVICE_KEY` is present because that endpoint is still service-key-only.
46
46
  - Auth resolution order is `ORGX_API_KEY`, then the wizard auth store, then OpenClaw config.
package/dist/cli.js CHANGED
@@ -940,6 +940,7 @@ function parseOnboardingTask(value) {
940
940
  ...isNonEmptyString2(record.initiativeId) ? { initiativeId: record.initiativeId.trim() } : {},
941
941
  ...isNonEmptyString2(record.status) ? { status: record.status.trim() } : {},
942
942
  title: record.title.trim(),
943
+ ...isNonEmptyString2(record.workstreamId) ? { workstreamId: record.workstreamId.trim() } : {},
943
944
  ...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
944
945
  ...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
945
946
  };
@@ -1239,39 +1240,6 @@ async function checkHostedMcpHealth() {
1239
1240
 
1240
1241
  // src/lib/hosted-mcp-tool-check.ts
1241
1242
  var DEFAULT_TOOL_NAME = "get_setup_status";
1242
- function buildHeaders(apiKey, sessionId) {
1243
- const headers = {
1244
- Authorization: `Bearer ${apiKey}`,
1245
- "Content-Type": "application/json",
1246
- Accept: "application/json, text/event-stream"
1247
- };
1248
- if (sessionId) {
1249
- headers["Mcp-Session-Id"] = sessionId;
1250
- }
1251
- return headers;
1252
- }
1253
- async function readJsonSafe(response) {
1254
- const text2 = await response.text();
1255
- if (!text2) return null;
1256
- try {
1257
- return JSON.parse(text2);
1258
- } catch {
1259
- return text2;
1260
- }
1261
- }
1262
- function readJsonRpcError(payload) {
1263
- if (!payload || typeof payload !== "object") {
1264
- return void 0;
1265
- }
1266
- const maybeError = payload.error;
1267
- if (!maybeError || typeof maybeError !== "object") {
1268
- return void 0;
1269
- }
1270
- return typeof maybeError.message === "string" ? maybeError.message : "Unknown MCP JSON-RPC error.";
1271
- }
1272
- function readSessionId(response) {
1273
- return response.headers.get("mcp-session-id") ?? response.headers.get("Mcp-Session-Id") ?? void 0;
1274
- }
1275
1243
  async function checkHostedMcpToolAccess(options = {}, toolName = DEFAULT_TOOL_NAME) {
1276
1244
  const auth = await resolveOrgxAuth(options);
1277
1245
  if (!auth) {
@@ -1286,157 +1254,20 @@ async function checkHostedMcpToolAccess(options = {}, toolName = DEFAULT_TOOL_NA
1286
1254
  details: []
1287
1255
  };
1288
1256
  }
1289
- try {
1290
- const initializeResponse = await fetch(ORGX_HOSTED_MCP_URL, {
1291
- method: "POST",
1292
- headers: buildHeaders(auth.apiKey),
1293
- body: JSON.stringify({
1294
- jsonrpc: "2.0",
1295
- id: 1,
1296
- method: "initialize",
1297
- params: {
1298
- protocolVersion: "2024-11-05",
1299
- clientInfo: {
1300
- name: "orgx-wizard",
1301
- version: "0.1.0"
1302
- },
1303
- capabilities: {}
1304
- }
1305
- }),
1306
- signal: AbortSignal.timeout(1e4)
1307
- });
1308
- const initializePayload = await readJsonSafe(initializeResponse);
1309
- const initializeError = readJsonRpcError(initializePayload);
1310
- const sessionId = readSessionId(initializeResponse);
1311
- if (!initializeResponse.ok) {
1312
- return {
1313
- configured: true,
1314
- ok: false,
1315
- skipped: false,
1316
- source: auth.source,
1317
- toolName,
1318
- url: ORGX_HOSTED_MCP_URL,
1319
- baseUrl: auth.baseUrl,
1320
- initializeStatus: initializeResponse.status,
1321
- error: `MCP initialize failed with HTTP ${initializeResponse.status}.`,
1322
- details: [`auth source: ${auth.source}`],
1323
- response: initializePayload
1324
- };
1325
- }
1326
- if (initializeError) {
1327
- return {
1328
- configured: true,
1329
- ok: false,
1330
- skipped: false,
1331
- source: auth.source,
1332
- toolName,
1333
- url: ORGX_HOSTED_MCP_URL,
1334
- baseUrl: auth.baseUrl,
1335
- initializeStatus: initializeResponse.status,
1336
- error: `MCP initialize returned JSON-RPC error: ${initializeError}`,
1337
- details: [`auth source: ${auth.source}`],
1338
- response: initializePayload
1339
- };
1340
- }
1341
- if (!sessionId) {
1342
- return {
1343
- configured: true,
1344
- ok: false,
1345
- skipped: false,
1346
- source: auth.source,
1347
- toolName,
1348
- url: ORGX_HOSTED_MCP_URL,
1349
- baseUrl: auth.baseUrl,
1350
- initializeStatus: initializeResponse.status,
1351
- error: "MCP initialize succeeded but no Mcp-Session-Id header was returned.",
1352
- details: [`auth source: ${auth.source}`],
1353
- response: initializePayload
1354
- };
1355
- }
1356
- const callResponse = await fetch(ORGX_HOSTED_MCP_URL, {
1357
- method: "POST",
1358
- headers: buildHeaders(auth.apiKey, sessionId),
1359
- body: JSON.stringify({
1360
- jsonrpc: "2.0",
1361
- id: 2,
1362
- method: "tools/call",
1363
- params: {
1364
- name: toolName,
1365
- arguments: {}
1366
- }
1367
- }),
1368
- signal: AbortSignal.timeout(1e4)
1369
- });
1370
- const callPayload = await readJsonSafe(callResponse);
1371
- const callError = readJsonRpcError(callPayload);
1372
- if (!callResponse.ok) {
1373
- return {
1374
- configured: true,
1375
- ok: false,
1376
- skipped: false,
1377
- source: auth.source,
1378
- toolName,
1379
- url: ORGX_HOSTED_MCP_URL,
1380
- baseUrl: auth.baseUrl,
1381
- initializeStatus: initializeResponse.status,
1382
- callStatus: callResponse.status,
1383
- error: `MCP tool call failed with HTTP ${callResponse.status}.`,
1384
- details: [
1385
- `auth source: ${auth.source}`,
1386
- `session established: ${sessionId}`
1387
- ],
1388
- response: callPayload
1389
- };
1390
- }
1391
- if (callError) {
1392
- return {
1393
- configured: true,
1394
- ok: false,
1395
- skipped: false,
1396
- source: auth.source,
1397
- toolName,
1398
- url: ORGX_HOSTED_MCP_URL,
1399
- baseUrl: auth.baseUrl,
1400
- initializeStatus: initializeResponse.status,
1401
- callStatus: callResponse.status,
1402
- error: `MCP tool call returned JSON-RPC error: ${callError}`,
1403
- details: [
1404
- `auth source: ${auth.source}`,
1405
- `session established: ${sessionId}`
1406
- ],
1407
- response: callPayload
1408
- };
1409
- }
1410
- return {
1411
- configured: true,
1412
- ok: true,
1413
- skipped: false,
1414
- source: auth.source,
1415
- toolName,
1416
- url: ORGX_HOSTED_MCP_URL,
1417
- baseUrl: auth.baseUrl,
1418
- initializeStatus: initializeResponse.status,
1419
- callStatus: callResponse.status,
1420
- details: [
1421
- `auth source: ${auth.source}`,
1422
- `session established: ${sessionId}`,
1423
- `tool call: ${toolName}`
1424
- ],
1425
- response: callPayload
1426
- };
1427
- } catch (error) {
1428
- return {
1429
- configured: true,
1430
- ok: false,
1431
- skipped: false,
1432
- source: auth.source,
1433
- toolName,
1434
- url: ORGX_HOSTED_MCP_URL,
1435
- baseUrl: auth.baseUrl,
1436
- error: error instanceof Error ? error.message : String(error),
1437
- details: [`auth source: ${auth.source}`]
1438
- };
1439
- }
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
+ };
1440
1271
  }
1441
1272
 
1442
1273
  // src/lib/npm-registry-health.ts
@@ -1633,7 +1464,7 @@ async function getCurrentWorkspace(options = {}) {
1633
1464
  }
1634
1465
  return workspace;
1635
1466
  }
1636
- if (response.status === 404) {
1467
+ if (response.status === 401 || response.status === 404) {
1637
1468
  const workspaces = await listWorkspaces(options);
1638
1469
  return workspaces[0] ?? null;
1639
1470
  }
@@ -2334,7 +2165,10 @@ function buildChatgptManualStatus(detection, authHint) {
2334
2165
  "Manual step: in ChatGPT desktop developer mode, add a custom connector pointing at the hosted OrgX MCP URL."
2335
2166
  );
2336
2167
  details.push(
2337
- "After connecting ChatGPT, run `orgx-wizard doctor` to verify hosted MCP tool access."
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."
2338
2172
  );
2339
2173
  } else {
2340
2174
  details.push(
@@ -2369,7 +2203,7 @@ function buildChatgptAddResult(path, authHint, toolCheck) {
2369
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.`
2370
2204
  };
2371
2205
  }
2372
- if (!toolCheck || !toolCheck.ok) {
2206
+ if (toolCheck && !toolCheck.ok && !toolCheck.skipped) {
2373
2207
  const failure = toolCheck?.error ?? "hosted MCP verification did not complete.";
2374
2208
  return {
2375
2209
  name: "chatgpt",
@@ -2382,7 +2216,7 @@ function buildChatgptAddResult(path, authHint, toolCheck) {
2382
2216
  name: "chatgpt",
2383
2217
  changed: false,
2384
2218
  ...path ? { path } : {},
2385
- message: `OrgX auth and hosted MCP access are verified. Add ${ORGX_HOSTED_MCP_URL} as a custom connector in ChatGPT developer mode, then run \`${DOCTOR_HINT}\` to confirm \`${toolCheck.toolName}\`.`
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.`
2386
2220
  };
2387
2221
  }
2388
2222
  function buildChatgptRemoveResult(path) {
@@ -2520,8 +2354,7 @@ async function addAutomatedSurface(name) {
2520
2354
  if (!authHint) {
2521
2355
  return buildChatgptAddResult(path, null);
2522
2356
  }
2523
- const toolCheck = await checkHostedMcpToolAccess();
2524
- return buildChatgptAddResult(path, authHint, toolCheck);
2357
+ return buildChatgptAddResult(path, authHint);
2525
2358
  }
2526
2359
  default:
2527
2360
  return {
@@ -2827,6 +2660,8 @@ var FOUNDER_DEMO_ARTIFACT_TYPE = "document";
2827
2660
  var FOUNDER_DEMO_ARTIFACT_DESCRIPTION = "Live founder demo generated by @useorgx/wizard after workspace bootstrap completed.";
2828
2661
  var ONBOARDING_TASK_TITLE = "Complete OrgX onboarding";
2829
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.";
2830
2665
  function parseResponseBody3(text2) {
2831
2666
  if (!text2) {
2832
2667
  return null;
@@ -2910,6 +2745,15 @@ function parseTask(payload) {
2910
2745
  ...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
2911
2746
  };
2912
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
+ }
2913
2757
  function toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace) {
2914
2758
  return {
2915
2759
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2927,13 +2771,14 @@ function toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspa
2927
2771
  workspaceName: workspace.name
2928
2772
  };
2929
2773
  }
2930
- function toOnboardingTaskRecord(task, workspace, initiativeId) {
2774
+ function toOnboardingTaskRecord(task, workspace, options = {}) {
2931
2775
  return {
2932
2776
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2933
2777
  id: task.id,
2934
- ...initiativeId ? { initiativeId } : {},
2778
+ ...options.initiativeId ? { initiativeId: options.initiativeId } : {},
2935
2779
  ...task.status ? { status: task.status } : {},
2936
2780
  title: task.title,
2781
+ ...options.workstreamId ? { workstreamId: options.workstreamId } : {},
2937
2782
  workspaceId: workspace.id,
2938
2783
  workspaceName: workspace.name
2939
2784
  };
@@ -3112,14 +2957,33 @@ async function ensureOnboardingTask(workspace, options = {}) {
3112
2957
  ...existingRecord.status ? { status: existingRecord.status } : {}
3113
2958
  };
3114
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
+ );
3115
2978
  const task = await createEntity(
3116
2979
  "task",
3117
2980
  {
2981
+ initiative_id: initiativeId,
3118
2982
  status: "todo",
3119
2983
  summary: ONBOARDING_TASK_SUMMARY,
3120
2984
  title: ONBOARDING_TASK_TITLE,
3121
- workspace_id: workspace.id,
3122
- ...options.initiativeId ? { initiative_id: options.initiativeId } : {}
2985
+ workstream_id: workstream.id,
2986
+ workspace_id: workspace.id
3123
2987
  },
3124
2988
  parseTask,
3125
2989
  options
@@ -3127,7 +2991,10 @@ async function ensureOnboardingTask(workspace, options = {}) {
3127
2991
  updateWizardState(
3128
2992
  (current) => ({
3129
2993
  ...current,
3130
- onboardingTask: toOnboardingTaskRecord(task, workspace, options.initiativeId)
2994
+ onboardingTask: toOnboardingTaskRecord(task, workspace, {
2995
+ initiativeId,
2996
+ workstreamId: workstream.id
2997
+ })
3131
2998
  }),
3132
2999
  options.statePath
3133
3000
  );
@@ -4943,8 +4810,9 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
4943
4810
  return "skipped";
4944
4811
  }
4945
4812
  const existingState = readWizardState();
4813
+ const effectiveInitiativeId = input.initiativeId?.trim() || (existingState?.demoInitiative?.workspaceId === input.workspace.id ? existingState.demoInitiative.id : void 0);
4946
4814
  const hasOnboardingTask = existingState?.onboardingTask?.workspaceId === input.workspace.id;
4947
- if (!hasOnboardingTask) {
4815
+ if (!hasOnboardingTask && effectiveInitiativeId) {
4948
4816
  const onboardingChoice = await selectPrompt({
4949
4817
  message: `Create a starter onboarding task for ${input.workspace.name}?`,
4950
4818
  options: [
@@ -4957,24 +4825,29 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
4957
4825
  return "cancelled";
4958
4826
  }
4959
4827
  if (onboardingChoice === "yes") {
4960
- const task = await ensureOnboardingTask(input.workspace, {
4961
- ...input.initiativeId ? { initiativeId: input.initiativeId } : {}
4962
- });
4963
- console.log(` ${ICON.ok} ${pc3.green("onboarding")} ${pc3.bold(task.title)}`);
4964
- await safeTrackWizardTelemetry(
4965
- "onboarding_task_created",
4966
- buildOnboardingTaskTelemetryProperties(
4967
- {
4968
- created: true,
4969
- hasInitiative: Boolean(input.initiativeId),
4970
- task
4971
- },
4972
- {
4973
- command: input.telemetry?.command ?? "setup",
4974
- ...input.telemetry?.preset ? { preset: input.telemetry.preset } : {}
4975
- }
4976
- )
4977
- );
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
+ }
4978
4851
  }
4979
4852
  }
4980
4853
  const refreshedState = readWizardState();
@@ -5178,7 +5051,7 @@ function printDoctorReport(report, assessment) {
5178
5051
  async function main() {
5179
5052
  const program = new Command();
5180
5053
  program.name("orgx-wizard").description("One-line CLI onboarding for OrgX surfaces.").showHelpAfterError();
5181
- const pkgVersion = true ? "0.1.8" : void 0;
5054
+ const pkgVersion = true ? "0.1.9" : void 0;
5182
5055
  program.version(pkgVersion ?? "unknown", "-V, --version");
5183
5056
  program.hook("preAction", () => {
5184
5057
  console.log(renderBanner(pkgVersion));