@pleri/olam-cli 0.1.48 → 0.1.50

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.
Files changed (60) hide show
  1. package/dist/__tests__/mcp-import.test.d.ts +11 -0
  2. package/dist/__tests__/mcp-import.test.d.ts.map +1 -0
  3. package/dist/__tests__/mcp-import.test.js +134 -0
  4. package/dist/__tests__/mcp-import.test.js.map +1 -0
  5. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts +20 -0
  6. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts.map +1 -0
  7. package/dist/commands/__tests__/upgrade.compose-path.test.js +152 -0
  8. package/dist/commands/__tests__/upgrade.compose-path.test.js.map +1 -0
  9. package/dist/commands/host-cp.d.ts +6 -0
  10. package/dist/commands/host-cp.d.ts.map +1 -1
  11. package/dist/commands/host-cp.js +1 -1
  12. package/dist/commands/host-cp.js.map +1 -1
  13. package/dist/commands/mcp/add.d.ts +9 -0
  14. package/dist/commands/mcp/add.d.ts.map +1 -0
  15. package/dist/commands/mcp/add.js +87 -0
  16. package/dist/commands/mcp/add.js.map +1 -0
  17. package/dist/commands/mcp/client.d.ts +60 -0
  18. package/dist/commands/mcp/client.d.ts.map +1 -0
  19. package/dist/commands/mcp/client.js +70 -0
  20. package/dist/commands/mcp/client.js.map +1 -0
  21. package/dist/commands/mcp/import-discovery.d.ts +25 -0
  22. package/dist/commands/mcp/import-discovery.d.ts.map +1 -0
  23. package/dist/commands/mcp/import-discovery.js +135 -0
  24. package/dist/commands/mcp/import-discovery.js.map +1 -0
  25. package/dist/commands/mcp/import-validate.d.ts +15 -0
  26. package/dist/commands/mcp/import-validate.d.ts.map +1 -0
  27. package/dist/commands/mcp/import-validate.js +55 -0
  28. package/dist/commands/mcp/import-validate.js.map +1 -0
  29. package/dist/commands/mcp/import.d.ts +12 -0
  30. package/dist/commands/mcp/import.d.ts.map +1 -0
  31. package/dist/commands/mcp/import.js +126 -0
  32. package/dist/commands/mcp/import.js.map +1 -0
  33. package/dist/commands/mcp/index.d.ts +11 -0
  34. package/dist/commands/mcp/index.d.ts.map +1 -0
  35. package/dist/commands/mcp/index.js +26 -0
  36. package/dist/commands/mcp/index.js.map +1 -0
  37. package/dist/commands/mcp/list.d.ts +6 -0
  38. package/dist/commands/mcp/list.d.ts.map +1 -0
  39. package/dist/commands/mcp/list.js +56 -0
  40. package/dist/commands/mcp/list.js.map +1 -0
  41. package/dist/commands/mcp/login.d.ts +6 -0
  42. package/dist/commands/mcp/login.d.ts.map +1 -0
  43. package/dist/commands/mcp/login.js +38 -0
  44. package/dist/commands/mcp/login.js.map +1 -0
  45. package/dist/commands/mcp/remove.d.ts +6 -0
  46. package/dist/commands/mcp/remove.d.ts.map +1 -0
  47. package/dist/commands/mcp/remove.js +21 -0
  48. package/dist/commands/mcp/remove.js.map +1 -0
  49. package/dist/commands/mcp/status.d.ts +6 -0
  50. package/dist/commands/mcp/status.d.ts.map +1 -0
  51. package/dist/commands/mcp/status.js +57 -0
  52. package/dist/commands/mcp/status.js.map +1 -0
  53. package/dist/commands/upgrade.d.ts +3 -3
  54. package/dist/commands/upgrade.d.ts.map +1 -1
  55. package/dist/commands/upgrade.js +4 -5
  56. package/dist/commands/upgrade.js.map +1 -1
  57. package/dist/image-digests.json +2 -2
  58. package/dist/index.js +606 -61
  59. package/dist/index.js.map +1 -1
  60. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -454,8 +454,8 @@ var init_parseUtil = __esm({
454
454
  init_errors();
455
455
  init_en();
456
456
  makeIssue = (params) => {
457
- const { data, path: path43, errorMaps, issueData } = params;
458
- const fullPath = [...path43, ...issueData.path || []];
457
+ const { data, path: path44, errorMaps, issueData } = params;
458
+ const fullPath = [...path44, ...issueData.path || []];
459
459
  const fullIssue = {
460
460
  ...issueData,
461
461
  path: fullPath
@@ -763,11 +763,11 @@ var init_types = __esm({
763
763
  init_parseUtil();
764
764
  init_util();
765
765
  ParseInputLazyPath = class {
766
- constructor(parent, value, path43, key) {
766
+ constructor(parent, value, path44, key) {
767
767
  this._cachedPath = [];
768
768
  this.parent = parent;
769
769
  this.data = value;
770
- this._path = path43;
770
+ this._path = path44;
771
771
  this._key = key;
772
772
  }
773
773
  get path() {
@@ -4248,7 +4248,7 @@ import YAML from "yaml";
4248
4248
  function bootstrapStepCmd(entry) {
4249
4249
  return typeof entry === "string" ? entry : entry.cmd;
4250
4250
  }
4251
- function refineForbiddenKeys(value, path43, ctx, rejectSource) {
4251
+ function refineForbiddenKeys(value, path44, ctx, rejectSource) {
4252
4252
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4253
4253
  return;
4254
4254
  }
@@ -4256,12 +4256,12 @@ function refineForbiddenKeys(value, path43, ctx, rejectSource) {
4256
4256
  if (FORBIDDEN_KEYS.has(key)) {
4257
4257
  ctx.addIssue({
4258
4258
  code: external_exports.ZodIssueCode.custom,
4259
- path: [...path43, key],
4259
+ path: [...path44, key],
4260
4260
  message: `forbidden key "${key}" (prototype-pollution surface)`
4261
4261
  });
4262
4262
  continue;
4263
4263
  }
4264
- if (rejectSource && path43.length === 0 && key === "source") {
4264
+ if (rejectSource && path44.length === 0 && key === "source") {
4265
4265
  ctx.addIssue({
4266
4266
  code: external_exports.ZodIssueCode.custom,
4267
4267
  path: ["source"],
@@ -4269,21 +4269,21 @@ function refineForbiddenKeys(value, path43, ctx, rejectSource) {
4269
4269
  });
4270
4270
  continue;
4271
4271
  }
4272
- refineForbiddenKeys(value[key], [...path43, key], ctx, false);
4272
+ refineForbiddenKeys(value[key], [...path44, key], ctx, false);
4273
4273
  }
4274
4274
  }
4275
- function rejectForbiddenKeys(value, path43, rejectSource) {
4275
+ function rejectForbiddenKeys(value, path44, rejectSource) {
4276
4276
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4277
4277
  return;
4278
4278
  }
4279
4279
  for (const key of Object.keys(value)) {
4280
4280
  if (FORBIDDEN_KEYS.has(key)) {
4281
- throw new Error(`[manifest] ${path43}: forbidden key "${key}" (prototype-pollution surface)`);
4281
+ throw new Error(`[manifest] ${path44}: forbidden key "${key}" (prototype-pollution surface)`);
4282
4282
  }
4283
4283
  if (rejectSource && key === "source") {
4284
- throw new Error(`[manifest] ${path43}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4284
+ throw new Error(`[manifest] ${path44}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4285
4285
  }
4286
- rejectForbiddenKeys(value[key], `${path43}.${key}`, false);
4286
+ rejectForbiddenKeys(value[key], `${path44}.${key}`, false);
4287
4287
  }
4288
4288
  }
4289
4289
  function unknownTopLevelKeys(parsed) {
@@ -5224,8 +5224,8 @@ var init_client = __esm({
5224
5224
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
5225
5225
  }
5226
5226
  }
5227
- async request(method, path43, body, attempt = 0) {
5228
- const url = `${this.baseUrl}${path43}`;
5227
+ async request(method, path44, body, attempt = 0) {
5228
+ const url = `${this.baseUrl}${path44}`;
5229
5229
  const controller = new AbortController();
5230
5230
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
5231
5231
  const headers = {};
@@ -5243,7 +5243,7 @@ var init_client = __esm({
5243
5243
  } catch (err) {
5244
5244
  if (attempt < RETRY_COUNT && isTransient(err)) {
5245
5245
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
5246
- return this.request(method, path43, body, attempt + 1);
5246
+ return this.request(method, path44, body, attempt + 1);
5247
5247
  }
5248
5248
  throw err;
5249
5249
  } finally {
@@ -6722,8 +6722,8 @@ var init_provider3 = __esm({
6722
6722
  // -----------------------------------------------------------------------
6723
6723
  // Internal fetch helper
6724
6724
  // -----------------------------------------------------------------------
6725
- async request(path43, method, body) {
6726
- const url = `${this.config.workerUrl}${path43}`;
6725
+ async request(path44, method, body) {
6726
+ const url = `${this.config.workerUrl}${path44}`;
6727
6727
  const bearer = await this.config.mintToken();
6728
6728
  const headers = {
6729
6729
  Authorization: `Bearer ${bearer}`
@@ -7983,8 +7983,8 @@ import { execFileSync as execFileSync3 } from "node:child_process";
7983
7983
  import * as fs13 from "node:fs";
7984
7984
  import * as os9 from "node:os";
7985
7985
  import * as path14 from "node:path";
7986
- function expandHome(p, homedir21) {
7987
- return p.replace(/^~(?=$|\/|\\)/, homedir21());
7986
+ function expandHome(p, homedir22) {
7987
+ return p.replace(/^~(?=$|\/|\\)/, homedir22());
7988
7988
  }
7989
7989
  function sanitizeRepoFilename(name) {
7990
7990
  const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -8007,7 +8007,7 @@ ${stderr}`;
8007
8007
  }
8008
8008
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8009
8009
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
8010
- const homedir21 = deps.homedir ?? (() => os9.homedir());
8010
+ const homedir22 = deps.homedir ?? (() => os9.homedir());
8011
8011
  const baselineDir = path14.join(workspacePath, ".olam", "baseline");
8012
8012
  try {
8013
8013
  fs13.mkdirSync(baselineDir, { recursive: true });
@@ -8023,7 +8023,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8023
8023
  continue;
8024
8024
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
8025
8025
  const outPath = path14.join(baselineDir, filename);
8026
- const repoPath = expandHome(repo.path, homedir21);
8026
+ const repoPath = expandHome(repo.path, homedir22);
8027
8027
  if (!fs13.existsSync(repoPath)) {
8028
8028
  writeBaselineFile(outPath, `# repo: ${repo.name}
8029
8029
  # (skipped: path ${repoPath} does not exist)
@@ -11938,6 +11938,7 @@ __export(host_cp_exports, {
11938
11938
  callHostCpProxy: () => callHostCpProxy,
11939
11939
  callHostCpRegistry: () => callHostCpRegistry,
11940
11940
  captureGhToken: () => captureGhToken,
11941
+ findComposeFile: () => findComposeFile,
11941
11942
  findHostCpContainer: () => findHostCpContainer,
11942
11943
  gatherProbeFailureDiagnostics: () => gatherProbeFailureDiagnostics,
11943
11944
  openHostCpUrl: () => openUrl,
@@ -12419,10 +12420,10 @@ async function readHostCpToken2() {
12419
12420
  if (!fs19.existsSync(tp)) return null;
12420
12421
  return fs19.readFileSync(tp, "utf-8").trim();
12421
12422
  }
12422
- async function callHostCpProxy(method, worldId, path43, body) {
12423
+ async function callHostCpProxy(method, worldId, path44, body) {
12423
12424
  const token = await readHostCpToken2();
12424
12425
  if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
12425
- const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path43}`;
12426
+ const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path44}`;
12426
12427
  try {
12427
12428
  const headers = {
12428
12429
  Authorization: `Bearer ${token}`
@@ -12930,8 +12931,8 @@ var init_machine_schema = __esm({
12930
12931
 
12931
12932
  // src/index.ts
12932
12933
  import { Command } from "commander";
12933
- import * as fs38 from "node:fs";
12934
- import * as path42 from "node:path";
12934
+ import * as fs39 from "node:fs";
12935
+ import * as path43 from "node:path";
12935
12936
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12936
12937
 
12937
12938
  // src/commands/init.ts
@@ -13308,9 +13309,9 @@ var UnknownArchetypeError = class extends Error {
13308
13309
  };
13309
13310
  var ArchetypeCycleError = class extends Error {
13310
13311
  path;
13311
- constructor(path43) {
13312
- super(`Archetype inheritance cycle detected: ${path43.join(" \u2192 ")} \u2192 ${path43[0] ?? "?"}`);
13313
- this.path = path43;
13312
+ constructor(path44) {
13313
+ super(`Archetype inheritance cycle detected: ${path44.join(" \u2192 ")} \u2192 ${path44[0] ?? "?"}`);
13314
+ this.path = path44;
13314
13315
  this.name = "ArchetypeCycleError";
13315
13316
  }
13316
13317
  };
@@ -14082,8 +14083,8 @@ function runStep(label, cmd, args, opts = {}) {
14082
14083
  }
14083
14084
  async function confirm(message) {
14084
14085
  if (!process.stdin.isTTY) return true;
14085
- const { createInterface: createInterface2 } = await import("node:readline");
14086
- const rl = createInterface2({ input: process.stdin, output: process.stdout });
14086
+ const { createInterface: createInterface4 } = await import("node:readline");
14087
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
14087
14088
  return new Promise((resolve8) => {
14088
14089
  rl.question(`${message} [y/N] `, (answer) => {
14089
14090
  rl.close();
@@ -14653,9 +14654,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
14653
14654
  "These source files have changed since the image was built; the",
14654
14655
  "changes will NOT take effect in fresh worlds until you rebuild:"
14655
14656
  ];
14656
- for (const { path: path43, mtimeMs } of result.newerSources) {
14657
+ for (const { path: path44, mtimeMs } of result.newerSources) {
14657
14658
  const when = new Date(mtimeMs).toISOString();
14658
- lines.push(` \u2022 ${path43} (modified ${when})`);
14659
+ lines.push(` \u2022 ${path44} (modified ${when})`);
14659
14660
  }
14660
14661
  lines.push("");
14661
14662
  lines.push("Rebuild with:");
@@ -14814,15 +14815,15 @@ init_host_cp();
14814
14815
  var HOST_CP_URL = "http://127.0.0.1:19000";
14815
14816
  async function readHostCpTokenForCreate() {
14816
14817
  try {
14817
- const { default: fs39 } = await import("node:fs");
14818
- const { default: os23 } = await import("node:os");
14819
- const { default: path43 } = await import("node:path");
14820
- const tp = path43.join(
14821
- process.env.OLAM_HOME ?? path43.join(os23.homedir(), ".olam"),
14818
+ const { default: fs40 } = await import("node:fs");
14819
+ const { default: os24 } = await import("node:os");
14820
+ const { default: path44 } = await import("node:path");
14821
+ const tp = path44.join(
14822
+ process.env.OLAM_HOME ?? path44.join(os24.homedir(), ".olam"),
14822
14823
  "host-cp.token"
14823
14824
  );
14824
- if (!fs39.existsSync(tp)) return null;
14825
- return fs39.readFileSync(tp, "utf-8").trim();
14825
+ if (!fs40.existsSync(tp)) return null;
14826
+ return fs40.readFileSync(tp, "utf-8").trim();
14826
14827
  } catch {
14827
14828
  return null;
14828
14829
  }
@@ -15184,12 +15185,12 @@ function defaultNameFromPrompt(prompt) {
15184
15185
  }
15185
15186
  async function readHostCpToken3() {
15186
15187
  try {
15187
- const { default: fs39 } = await import("node:fs");
15188
- const { default: os23 } = await import("node:os");
15189
- const { default: path43 } = await import("node:path");
15190
- const tp = path43.join(os23.homedir(), ".olam", "host-cp.token");
15191
- if (!fs39.existsSync(tp)) return null;
15192
- const raw = fs39.readFileSync(tp, "utf-8").trim();
15188
+ const { default: fs40 } = await import("node:fs");
15189
+ const { default: os24 } = await import("node:os");
15190
+ const { default: path44 } = await import("node:path");
15191
+ const tp = path44.join(os24.homedir(), ".olam", "host-cp.token");
15192
+ if (!fs40.existsSync(tp)) return null;
15193
+ const raw = fs40.readFileSync(tp, "utf-8").trim();
15193
15194
  return raw.length > 0 ? raw : null;
15194
15195
  } catch {
15195
15196
  return null;
@@ -18780,8 +18781,8 @@ function performRollbackSwap(plan) {
18780
18781
  }
18781
18782
  async function confirm2(message) {
18782
18783
  if (!process.stdin.isTTY) return true;
18783
- const { createInterface: createInterface2 } = await import("node:readline");
18784
- const rl = createInterface2({ input: process.stdin, output: process.stdout });
18784
+ const { createInterface: createInterface4 } = await import("node:readline");
18785
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
18785
18786
  return new Promise((resolve8) => {
18786
18787
  rl.question(`${message} [y/N] `, (answer) => {
18787
18788
  rl.close();
@@ -18982,7 +18983,7 @@ async function runUpgradePullByDigest(deps = {}) {
18982
18983
  }
18983
18984
  }
18984
18985
  tagSpinner.succeed("Re-tagged 3 images to canonical refs");
18985
- const composeFile = deps.composeFile ?? path31.join(process.cwd(), "packages/host-cp/compose.yaml");
18986
+ const composeFile = deps.composeFile ?? findComposeFile();
18986
18987
  const authSecret = deps.authSecret ?? readAuthSecret2();
18987
18988
  const composeRunner = deps.runComposeImpl ?? runCompose;
18988
18989
  const composeSpinner = ora7("docker compose recreate host-cp").start();
@@ -19182,8 +19183,7 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
19182
19183
  return;
19183
19184
  }
19184
19185
  printInfo("Rollback", swapResult.summary);
19185
- const cwd = process.cwd();
19186
- const composeFile = path31.join(cwd, "packages/host-cp/compose.yaml");
19186
+ const composeFile = findComposeFile();
19187
19187
  const authSecret = readAuthSecret2();
19188
19188
  process.stdout.write(` ${pc16.dim("docker compose recreate host-cp".padEnd(34))}`);
19189
19189
  const composeStart = Date.now();
@@ -19457,7 +19457,7 @@ Recovery options:
19457
19457
  return;
19458
19458
  }
19459
19459
  printInfo("Swap", swapResult.summary);
19460
- const composeFile = path31.join(cwd, "packages/host-cp/compose.yaml");
19460
+ const composeFile = findComposeFile();
19461
19461
  process.stdout.write(` ${pc16.dim("docker compose recreate".padEnd(34))}`);
19462
19462
  const composeStart = Date.now();
19463
19463
  const composeResult = runCompose(
@@ -21137,19 +21137,563 @@ function registerWorldUpgrade(program2) {
21137
21137
  });
21138
21138
  }
21139
21139
 
21140
- // src/pleri-config.ts
21140
+ // src/commands/mcp/login.ts
21141
+ init_output();
21142
+
21143
+ // src/commands/mcp/client.ts
21144
+ var BASE_URL = process.env["OLAM_MCP_AUTH_SERVICE_URL"] ?? "http://127.0.0.1:9998";
21145
+ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
21146
+ function authHeaders() {
21147
+ return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
21148
+ }
21149
+ async function apiFetch(path44, init = {}) {
21150
+ const res = await fetch(`${BASE_URL}${path44}`, {
21151
+ ...init,
21152
+ headers: {
21153
+ "Content-Type": "application/json",
21154
+ ...authHeaders(),
21155
+ ...init.headers
21156
+ }
21157
+ });
21158
+ if (!res.ok) {
21159
+ let msg = `HTTP ${res.status}`;
21160
+ try {
21161
+ const body = await res.json();
21162
+ if (body.error) msg = body.error;
21163
+ } catch {
21164
+ }
21165
+ return { ok: false, error: msg };
21166
+ }
21167
+ return res.json();
21168
+ }
21169
+ function getMcpAuthClient() {
21170
+ return {
21171
+ async status() {
21172
+ const res = await apiFetch("/credentials/status");
21173
+ return res;
21174
+ },
21175
+ async getCredential(service) {
21176
+ return apiFetch(`/credentials/${encodeURIComponent(service)}`);
21177
+ },
21178
+ async staticAdd({ service, token, label, envName }) {
21179
+ return apiFetch("/credentials/static/add", {
21180
+ method: "POST",
21181
+ body: JSON.stringify({ service, token, label, envName })
21182
+ });
21183
+ },
21184
+ async oauthStart({ service, label }) {
21185
+ return apiFetch("/credentials/oauth/start", {
21186
+ method: "POST",
21187
+ body: JSON.stringify({ service, label })
21188
+ });
21189
+ },
21190
+ async oauthComplete({ state, accessToken, refreshToken }) {
21191
+ return apiFetch("/credentials/oauth/complete", {
21192
+ method: "POST",
21193
+ body: JSON.stringify({ state, accessToken, refreshToken })
21194
+ });
21195
+ },
21196
+ async remove(id) {
21197
+ return apiFetch(`/credentials/${encodeURIComponent(id)}`, {
21198
+ method: "DELETE"
21199
+ });
21200
+ }
21201
+ };
21202
+ }
21203
+
21204
+ // src/commands/mcp/login.ts
21205
+ import { spawn as spawn4 } from "node:child_process";
21206
+ function openBrowser2(url) {
21207
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
21208
+ try {
21209
+ const child = spawn4(cmd, [url], { detached: true, stdio: "ignore" });
21210
+ child.unref();
21211
+ } catch {
21212
+ }
21213
+ }
21214
+ function registerMcpLogin(cmd) {
21215
+ cmd.command("login <service>").description("Start OAuth login flow for an MCP service (linear, slack)").option("--label <label>", "Label for this credential").action(async (service, opts) => {
21216
+ const client = getMcpAuthClient();
21217
+ const result = await client.oauthStart({ service, label: opts.label });
21218
+ if (!result.ok && result.error) {
21219
+ printError(result.error);
21220
+ process.exitCode = 1;
21221
+ return;
21222
+ }
21223
+ if (result.loginUrl) {
21224
+ printInfo("Login URL", result.loginUrl);
21225
+ openBrowser2(result.loginUrl);
21226
+ printInfo("State", result.state ?? "");
21227
+ printSuccess(`OAuth flow started for ${service}. Complete in your browser, then run:`);
21228
+ console.log(` olam mcp complete ${service} --state ${result.state ?? "<state>"} --token <access_token>`);
21229
+ }
21230
+ });
21231
+ }
21232
+
21233
+ // src/commands/mcp/add.ts
21234
+ init_output();
21235
+ import * as readline2 from "node:readline";
21236
+ async function readTokenSilent(prompt) {
21237
+ return new Promise((resolve8, reject) => {
21238
+ const rl = readline2.createInterface({
21239
+ input: process.stdin,
21240
+ output: process.stdout,
21241
+ terminal: true
21242
+ });
21243
+ process.stdout.write(prompt);
21244
+ if (process.stdin.isTTY) {
21245
+ process.stdin.setRawMode(true);
21246
+ }
21247
+ let token = "";
21248
+ process.stdin.on("data", function onData(chunk) {
21249
+ const char = chunk.toString("utf8");
21250
+ if (char === "\r" || char === "\n") {
21251
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
21252
+ process.stdin.removeListener("data", onData);
21253
+ process.stdout.write("\n");
21254
+ rl.close();
21255
+ resolve8(token);
21256
+ } else if (char === "") {
21257
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
21258
+ process.stdin.removeListener("data", onData);
21259
+ rl.close();
21260
+ reject(new Error("Cancelled"));
21261
+ } else if (char === "\x7F") {
21262
+ token = token.slice(0, -1);
21263
+ } else {
21264
+ token += char;
21265
+ }
21266
+ });
21267
+ });
21268
+ }
21269
+ function registerMcpAdd(cmd) {
21270
+ cmd.command("add <service>").description("Add a static-key MCP credential (token input hidden from terminal)").option("--token <token>", "Token value (use stdin prompt if omitted)").option("--label <label>", "Human-friendly label").option("--env-name <envName>", "Environment variable name to inject into worlds").action(async (service, opts) => {
21271
+ let token = opts.token;
21272
+ if (!token) {
21273
+ try {
21274
+ token = await readTokenSilent(`Enter token for ${service} (hidden): `);
21275
+ } catch (err) {
21276
+ printError(err instanceof Error ? err.message : "Cancelled");
21277
+ process.exitCode = 1;
21278
+ return;
21279
+ }
21280
+ }
21281
+ if (!token) {
21282
+ printError("Token is required");
21283
+ process.exitCode = 1;
21284
+ return;
21285
+ }
21286
+ const client = getMcpAuthClient();
21287
+ const result = await client.staticAdd({ service, token, label: opts.label, envName: opts.envName });
21288
+ if (!result.ok) {
21289
+ printError(result.error ?? "Failed to add credential");
21290
+ process.exitCode = 1;
21291
+ return;
21292
+ }
21293
+ printSuccess(`Added ${service} credential`);
21294
+ if (opts.envName) {
21295
+ printInfo("Env var", opts.envName);
21296
+ }
21297
+ });
21298
+ }
21299
+
21300
+ // src/commands/mcp/list.ts
21301
+ init_output();
21302
+ import pc23 from "picocolors";
21303
+ function registerMcpList(cmd) {
21304
+ cmd.command("list").description("List all registered MCP credentials").action(async () => {
21305
+ const client = getMcpAuthClient();
21306
+ let data;
21307
+ try {
21308
+ data = await client.status();
21309
+ } catch (err) {
21310
+ printError(`Could not reach mcp-auth service: ${err instanceof Error ? err.message : "unknown error"}`);
21311
+ printError("Is mcp-auth running? (OLAM_MCP_AUTH_SERVICE_URL defaults to http://127.0.0.1:9998)");
21312
+ process.exitCode = 1;
21313
+ return;
21314
+ }
21315
+ const mcps = data.mcps ?? [];
21316
+ if (mcps.length === 0) {
21317
+ console.log(pc23.dim("No MCP credentials registered."));
21318
+ console.log(pc23.dim("Add one with: olam mcp add <service>"));
21319
+ return;
21320
+ }
21321
+ const [c0, c1, c2, c3] = [16, 20, 10, 24];
21322
+ const header = [
21323
+ "SERVICE".padEnd(c0),
21324
+ "LABEL".padEnd(c1),
21325
+ "TYPE".padEnd(c2),
21326
+ "ENV VAR".padEnd(c3),
21327
+ "STATUS"
21328
+ ].join(" ");
21329
+ console.log(pc23.dim(header));
21330
+ console.log(pc23.dim("\u2500".repeat(header.length)));
21331
+ for (const m of mcps) {
21332
+ const status = !m.validated ? pc23.yellow("unvalidated") : m.expired ? pc23.red("expired") : pc23.green("active");
21333
+ const row = [
21334
+ pc23.cyan(m.service.padEnd(c0)),
21335
+ m.label.padEnd(c1).slice(0, c1),
21336
+ m.type.padEnd(c2),
21337
+ (m.envName ?? "\u2014").padEnd(c3).slice(0, c3),
21338
+ status
21339
+ ].join(" ");
21340
+ console.log(row);
21341
+ }
21342
+ });
21343
+ }
21344
+
21345
+ // src/commands/mcp/remove.ts
21346
+ init_output();
21347
+ function registerMcpRemove(cmd) {
21348
+ cmd.command("remove <service>").description("Remove an MCP credential by service ID").action(async (service) => {
21349
+ const client = getMcpAuthClient();
21350
+ const result = await client.remove(service);
21351
+ if (!result.ok) {
21352
+ printError(result.error ?? `No credential found for: ${service}`);
21353
+ process.exitCode = 1;
21354
+ return;
21355
+ }
21356
+ printSuccess(`Removed credential for ${service}`);
21357
+ });
21358
+ }
21359
+
21360
+ // src/commands/mcp/status.ts
21361
+ init_output();
21362
+ import pc24 from "picocolors";
21363
+ function formatExpiry(expiresAt) {
21364
+ if (!expiresAt) return "\u2014";
21365
+ const ms = expiresAt - Date.now();
21366
+ if (ms <= 0) return pc24.red("expired");
21367
+ const hours = ms / (1e3 * 60 * 60);
21368
+ if (hours < 1) return pc24.yellow(`${Math.ceil(ms / 6e4)}m`);
21369
+ return `${hours.toFixed(1)}h`;
21370
+ }
21371
+ function registerMcpStatus(cmd) {
21372
+ cmd.command("status").description("Show status of all MCP credentials").action(async () => {
21373
+ const client = getMcpAuthClient();
21374
+ let data;
21375
+ try {
21376
+ data = await client.status();
21377
+ } catch (err) {
21378
+ printError(`Could not reach mcp-auth service: ${err instanceof Error ? err.message : "unknown error"}`);
21379
+ process.exitCode = 1;
21380
+ return;
21381
+ }
21382
+ const mcps = data.mcps ?? [];
21383
+ printHeader(`MCP Credentials (${mcps.length})`);
21384
+ if (mcps.length === 0) {
21385
+ console.log(pc24.dim("No credentials registered."));
21386
+ return;
21387
+ }
21388
+ for (const m of mcps) {
21389
+ const stateColor = !m.validated ? pc24.yellow : m.expired ? pc24.red : pc24.green;
21390
+ const stateLabel = !m.validated ? "unvalidated" : m.expired ? "expired" : "active";
21391
+ console.log(`
21392
+ ${pc24.cyan(m.service)} ${stateColor(`[${stateLabel}]`)}`);
21393
+ console.log(` label: ${m.label}`);
21394
+ console.log(` type: ${m.type}`);
21395
+ if (m.envName) console.log(` env var: ${m.envName}`);
21396
+ if (m.importedFrom) console.log(` imported: ${m.importedFrom}`);
21397
+ if (m.type === "oauth") console.log(` expires in: ${formatExpiry(m.expiresAt)}`);
21398
+ if (m.lastUsed) console.log(` last used: ${new Date(m.lastUsed).toLocaleString()}`);
21399
+ }
21400
+ console.log("");
21401
+ });
21402
+ }
21403
+
21404
+ // src/commands/mcp/import.ts
21405
+ init_output();
21406
+ import * as readline3 from "node:readline";
21407
+ import pc25 from "picocolors";
21408
+
21409
+ // src/commands/mcp/import-discovery.ts
21141
21410
  import * as fs37 from "node:fs";
21411
+ import * as os23 from "node:os";
21142
21412
  import * as path41 from "node:path";
21413
+ function readJsonFile(filePath) {
21414
+ try {
21415
+ const raw = fs37.readFileSync(filePath, "utf-8");
21416
+ return JSON.parse(raw);
21417
+ } catch {
21418
+ return null;
21419
+ }
21420
+ }
21421
+ function extractMcpServers(obj, source, sourceLabel) {
21422
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return [];
21423
+ const record = obj;
21424
+ const mcpServers = record["mcpServers"] ?? record["mcps"];
21425
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) return [];
21426
+ const result = [];
21427
+ for (const [name, entry] of Object.entries(mcpServers)) {
21428
+ if (!entry || typeof entry !== "object") continue;
21429
+ const command = entry.command;
21430
+ if (!command || typeof command !== "string") continue;
21431
+ result.push({
21432
+ name,
21433
+ command,
21434
+ args: Array.isArray(entry.args) ? entry.args : [],
21435
+ env: entry.env && typeof entry.env === "object" ? entry.env : void 0,
21436
+ source,
21437
+ sourceLabel
21438
+ });
21439
+ }
21440
+ return result;
21441
+ }
21442
+ function getClaudeDesktopPath() {
21443
+ if (process.platform === "darwin") {
21444
+ return path41.join(os23.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
21445
+ }
21446
+ if (process.platform === "win32") {
21447
+ const appData = process.env["APPDATA"] ?? path41.join(os23.homedir(), "AppData", "Roaming");
21448
+ return path41.join(appData, "Claude", "claude_desktop_config.json");
21449
+ }
21450
+ return path41.join(os23.homedir(), ".config", "Claude", "claude_desktop_config.json");
21451
+ }
21452
+ function getOlamRepoPaths() {
21453
+ const configPaths = [
21454
+ path41.join(os23.homedir(), ".olam", "config.yaml"),
21455
+ path41.join(process.cwd(), ".olam", "config.yaml")
21456
+ ];
21457
+ const paths = [];
21458
+ for (const configPath of configPaths) {
21459
+ if (!fs37.existsSync(configPath)) continue;
21460
+ try {
21461
+ const raw = fs37.readFileSync(configPath, "utf-8");
21462
+ const repoMatches = [...raw.matchAll(/path:\s*["']?([^\s"'\n]+)/g)];
21463
+ for (const m of repoMatches) {
21464
+ if (m[1]) paths.push(m[1]);
21465
+ }
21466
+ } catch {
21467
+ }
21468
+ }
21469
+ return paths;
21470
+ }
21471
+ async function discoverMcpSources(repoPaths) {
21472
+ const start = performance.now();
21473
+ const allEntries = [];
21474
+ const sources = [];
21475
+ const sourceDefs = [
21476
+ {
21477
+ path: path41.join(os23.homedir(), ".claude.json"),
21478
+ label: "Claude Code (~/.claude.json)"
21479
+ },
21480
+ {
21481
+ path: getClaudeDesktopPath(),
21482
+ label: "Claude Desktop"
21483
+ },
21484
+ {
21485
+ path: path41.join(os23.homedir(), ".cursor", "mcp.json"),
21486
+ label: "Cursor (~/.cursor/mcp.json)"
21487
+ },
21488
+ {
21489
+ path: path41.join(os23.homedir(), ".codeium", "windsurf", "mcp_config.json"),
21490
+ label: "Windsurf (~/.codeium/windsurf/mcp_config.json)"
21491
+ }
21492
+ ];
21493
+ const resolvedRepoPaths = repoPaths ?? getOlamRepoPaths();
21494
+ for (const repoPath of resolvedRepoPaths) {
21495
+ sourceDefs.push({
21496
+ path: path41.join(repoPath, ".mcp.json"),
21497
+ label: `.mcp.json (${path41.basename(repoPath)})`
21498
+ });
21499
+ }
21500
+ const reads = await Promise.all(
21501
+ sourceDefs.map(async ({ path: filePath, label }) => ({
21502
+ filePath,
21503
+ label,
21504
+ data: await Promise.resolve(readJsonFile(filePath))
21505
+ }))
21506
+ );
21507
+ const seen = /* @__PURE__ */ new Set();
21508
+ for (const { filePath, label, data } of reads) {
21509
+ if (!data) continue;
21510
+ sources.push(label);
21511
+ const entries = extractMcpServers(data, filePath, label);
21512
+ for (const entry of entries) {
21513
+ if (!seen.has(entry.name)) {
21514
+ seen.add(entry.name);
21515
+ allEntries.push(entry);
21516
+ }
21517
+ }
21518
+ }
21519
+ return {
21520
+ entries: allEntries,
21521
+ sources,
21522
+ durationMs: performance.now() - start
21523
+ };
21524
+ }
21525
+
21526
+ // src/commands/mcp/import-validate.ts
21527
+ import { spawn as spawn5 } from "node:child_process";
21528
+ var VALIDATION_TIMEOUT_MS = 5e3;
21529
+ async function validateMcpEntry(entry) {
21530
+ return new Promise((resolve8) => {
21531
+ let stdout = "";
21532
+ let timedOut = false;
21533
+ let child;
21534
+ try {
21535
+ child = spawn5(entry.command, [...entry.args ?? []], {
21536
+ stdio: ["ignore", "pipe", "ignore"],
21537
+ env: { ...process.env, ...entry.env ?? {} }
21538
+ });
21539
+ } catch (err) {
21540
+ resolve8({
21541
+ name: entry.name,
21542
+ validated: false,
21543
+ reason: err instanceof Error ? err.message : "spawn failed"
21544
+ });
21545
+ return;
21546
+ }
21547
+ const timer = setTimeout(() => {
21548
+ timedOut = true;
21549
+ child.kill("SIGKILL");
21550
+ }, VALIDATION_TIMEOUT_MS);
21551
+ child.stdout?.on("data", (chunk) => {
21552
+ stdout += chunk.toString();
21553
+ });
21554
+ child.on("close", (code) => {
21555
+ clearTimeout(timer);
21556
+ if (timedOut) {
21557
+ resolve8({ name: entry.name, validated: false, reason: "timeout (5s)" });
21558
+ return;
21559
+ }
21560
+ const validated = code === 0 && stdout.trim().length > 0;
21561
+ resolve8({
21562
+ name: entry.name,
21563
+ validated,
21564
+ reason: validated ? "ok" : `exit code ${code ?? "null"}`
21565
+ });
21566
+ });
21567
+ child.on("error", (err) => {
21568
+ clearTimeout(timer);
21569
+ resolve8({ name: entry.name, validated: false, reason: err.message });
21570
+ });
21571
+ });
21572
+ }
21573
+
21574
+ // src/commands/mcp/import.ts
21575
+ async function multiSelectPicker(entries) {
21576
+ if (entries.length === 0) return [];
21577
+ console.log("\n" + pc25.bold("Discovered MCP servers:"));
21578
+ entries.forEach((e, i) => {
21579
+ console.log(
21580
+ ` ${pc25.dim(`[${i + 1}]`)} ${pc25.cyan(e.name.padEnd(20))} ${pc25.dim(e.sourceLabel)}`
21581
+ );
21582
+ });
21583
+ console.log("\n" + pc25.dim('Enter numbers to import (e.g. 1,2,3 or "all" or Enter to skip):'));
21584
+ const answer = await new Promise((resolve8) => {
21585
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
21586
+ rl.question("> ", (ans) => {
21587
+ rl.close();
21588
+ resolve8(ans.trim());
21589
+ });
21590
+ });
21591
+ if (!answer || answer === "") return [];
21592
+ if (answer.toLowerCase() === "all") return entries;
21593
+ const indices = answer.split(/[\s,]+/).map((s) => parseInt(s, 10) - 1).filter((i) => !isNaN(i) && i >= 0 && i < entries.length);
21594
+ const unique = [...new Set(indices)];
21595
+ return unique.map((i) => entries[i]).filter((e) => e != null);
21596
+ }
21597
+ function registerMcpImport(cmd) {
21598
+ cmd.command("import").description("Discover MCP servers from Claude Code, Desktop, Cursor, Windsurf, and .mcp.json").option("--reimport", "Re-import services that are already registered").option("--no-validate", "Skip validation probes").option("--repo-paths <paths>", "Comma-separated list of repo paths to scan for .mcp.json").action(async (opts) => {
21599
+ const client = getMcpAuthClient();
21600
+ let existingIds = /* @__PURE__ */ new Set();
21601
+ try {
21602
+ const data = await client.status();
21603
+ existingIds = new Set((data.mcps ?? []).map((m) => m.id));
21604
+ } catch {
21605
+ }
21606
+ printInfo("Scanning", "Claude Code, Claude Desktop, Cursor, Windsurf, .mcp.json\u2026");
21607
+ const repoPaths = opts.repoPaths ? opts.repoPaths.split(",").map((s) => s.trim()) : void 0;
21608
+ const { entries, sources, durationMs } = await discoverMcpSources(repoPaths);
21609
+ if (entries.length === 0) {
21610
+ console.log(pc25.dim("No MCP servers found in any source path."));
21611
+ return;
21612
+ }
21613
+ printInfo("Sources", sources.length > 0 ? sources.join(", ") : "none");
21614
+ printInfo("Found", `${entries.length} MCP server(s) in ${Math.round(durationMs)}ms`);
21615
+ let candidates = entries;
21616
+ let skippedCount = 0;
21617
+ if (!opts.reimport) {
21618
+ const filtered = entries.filter((e) => !existingIds.has(e.name));
21619
+ skippedCount = entries.length - filtered.length;
21620
+ candidates = filtered;
21621
+ }
21622
+ if (skippedCount > 0) {
21623
+ console.log(pc25.dim(`skipped: ${skippedCount} already registered`));
21624
+ }
21625
+ if (candidates.length === 0) {
21626
+ console.log(pc25.dim("Nothing new to import. Use --reimport to force."));
21627
+ return;
21628
+ }
21629
+ const selected = await multiSelectPicker(candidates);
21630
+ if (selected.length === 0) {
21631
+ console.log(pc25.dim("No servers selected."));
21632
+ return;
21633
+ }
21634
+ console.log(`
21635
+ Importing ${selected.length} server(s)\u2026`);
21636
+ let importedCount = 0;
21637
+ let validatedCount = 0;
21638
+ for (const entry of selected) {
21639
+ let validated = false;
21640
+ let validationReason = "skipped";
21641
+ if (opts.validate !== false) {
21642
+ process.stdout.write(` ${pc25.dim("\u2192")} ${entry.name} validating\u2026 `);
21643
+ const vr = await validateMcpEntry(entry);
21644
+ validated = vr.validated;
21645
+ validationReason = vr.reason;
21646
+ process.stdout.write(
21647
+ validated ? pc25.green("ok\n") : pc25.yellow(`unvalidated (${vr.reason})
21648
+ `)
21649
+ );
21650
+ } else {
21651
+ console.log(` ${pc25.dim("\u2192")} ${entry.name} ${pc25.dim("(validation skipped)")}`);
21652
+ }
21653
+ if (validated) validatedCount++;
21654
+ const result = await client.staticAdd({
21655
+ service: entry.name,
21656
+ token: `mcp://${entry.command}`,
21657
+ label: entry.name
21658
+ });
21659
+ if (result.ok) {
21660
+ importedCount++;
21661
+ } else {
21662
+ printError(`Failed to register ${entry.name}: ${result.error ?? "unknown error"}`);
21663
+ }
21664
+ }
21665
+ console.log("");
21666
+ console.log(pc25.green(`\u2713 Imported ${importedCount}/${selected.length}`));
21667
+ if (validatedCount > 0) {
21668
+ console.log(pc25.dim(` ${validatedCount} validated, ${importedCount - validatedCount} unvalidated`));
21669
+ }
21670
+ });
21671
+ }
21672
+
21673
+ // src/commands/mcp/index.ts
21674
+ function registerMcp(program2) {
21675
+ const mcp = program2.command("mcp").description("Manage MCP server credentials (login, add, list, remove, import)");
21676
+ registerMcpLogin(mcp);
21677
+ registerMcpAdd(mcp);
21678
+ registerMcpList(mcp);
21679
+ registerMcpRemove(mcp);
21680
+ registerMcpStatus(mcp);
21681
+ registerMcpImport(mcp);
21682
+ }
21683
+
21684
+ // src/pleri-config.ts
21685
+ import * as fs38 from "node:fs";
21686
+ import * as path42 from "node:path";
21143
21687
  function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
21144
21688
  if (process.env.PLERI_BASE_URL) {
21145
21689
  return true;
21146
21690
  }
21147
- const configPath = path41.join(configDir, "config.yaml");
21148
- if (!fs37.existsSync(configPath)) {
21691
+ const configPath = path42.join(configDir, "config.yaml");
21692
+ if (!fs38.existsSync(configPath)) {
21149
21693
  return false;
21150
21694
  }
21151
21695
  try {
21152
- const contents = fs37.readFileSync(configPath, "utf8");
21696
+ const contents = fs38.readFileSync(configPath, "utf8");
21153
21697
  return /^[^#\n]*\bpleri:/m.test(contents);
21154
21698
  } catch {
21155
21699
  return false;
@@ -21160,14 +21704,14 @@ function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
21160
21704
  var program = new Command();
21161
21705
  function readCliVersion() {
21162
21706
  try {
21163
- const here = path42.dirname(fileURLToPath4(import.meta.url));
21707
+ const here = path43.dirname(fileURLToPath4(import.meta.url));
21164
21708
  for (const candidate of [
21165
- path42.join(here, "package.json"),
21166
- path42.join(here, "..", "package.json"),
21167
- path42.join(here, "..", "..", "package.json")
21709
+ path43.join(here, "package.json"),
21710
+ path43.join(here, "..", "package.json"),
21711
+ path43.join(here, "..", "..", "package.json")
21168
21712
  ]) {
21169
- if (fs38.existsSync(candidate)) {
21170
- const pkg = JSON.parse(fs38.readFileSync(candidate, "utf-8"));
21713
+ if (fs39.existsSync(candidate)) {
21714
+ const pkg = JSON.parse(fs39.readFileSync(candidate, "utf-8"));
21171
21715
  if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
21172
21716
  }
21173
21717
  }
@@ -21205,4 +21749,5 @@ registerUpdate(program);
21205
21749
  registerBegin(program);
21206
21750
  registerStop(program);
21207
21751
  registerWorldUpgrade(program);
21752
+ registerMcp(program);
21208
21753
  program.parse();