@corenel/sidecar 0.8.0 → 0.8.2

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 (2) hide show
  1. package/dist/cli.js +139 -17
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -25280,9 +25280,10 @@ function oauthConfigFromEnv(env = process.env) {
25280
25280
  return { issuer, clientId };
25281
25281
  }
25282
25282
  function openBrowser(url) {
25283
- const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
25284
- const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
25283
+ const [cmd, args] = process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
25285
25284
  const child = spawn6(cmd, args, { detached: true, stdio: "ignore" });
25285
+ child.on("error", () => {
25286
+ });
25286
25287
  child.unref();
25287
25288
  }
25288
25289
  async function exchangeCode2(p2) {
@@ -46192,7 +46193,7 @@ var FLAG_TO_KEY = {
46192
46193
  "--default-model": "defaultModel",
46193
46194
  "--name": "name"
46194
46195
  };
46195
- var DEFAULT_GUEST_IMAGE2 = "prompd/corenel-sandbox:0.7.0";
46196
+ var DEFAULT_GUEST_IMAGE2 = "prompd/corenel-sandbox:0.8.2";
46196
46197
  function parseArgsWithProvenance(argv) {
46197
46198
  const explicit = /* @__PURE__ */ new Set();
46198
46199
  for (const a2 of argv) {
@@ -46880,6 +46881,49 @@ async function readMcpCredential(stateDir, server) {
46880
46881
  const cred = section[server];
46881
46882
  return isUsableCredential(cred) ? cred : null;
46882
46883
  }
46884
+ function isUsableClient(v2) {
46885
+ if (typeof v2 !== "object" || v2 === null) return false;
46886
+ const r2 = v2;
46887
+ return typeof r2.clientId === "string" && r2.clientId.length > 0 && typeof r2.authServer === "string" && r2.authServer.length > 0 && (r2.clientSecret === void 0 || typeof r2.clientSecret === "string") && (r2.redirectPort === void 0 || typeof r2.redirectPort === "number" && Number.isInteger(r2.redirectPort) && r2.redirectPort > 0 && r2.redirectPort < 65536);
46888
+ }
46889
+ async function readMcpClient(stateDir, server) {
46890
+ const doc = await readAuthJson(stateDir);
46891
+ const section = doc.mcpClients;
46892
+ if (typeof section !== "object" || section === null || Array.isArray(section)) return null;
46893
+ const rec5 = section[server];
46894
+ return isUsableClient(rec5) ? rec5 : null;
46895
+ }
46896
+ async function writeMcpClient(stateDir, server, client) {
46897
+ const doc = await readAuthJson(stateDir);
46898
+ const prev = doc.mcpClients;
46899
+ const section = typeof prev === "object" && prev !== null && !Array.isArray(prev) ? { ...prev } : {};
46900
+ section[server] = client;
46901
+ const next = { ...doc, mcpClients: section };
46902
+ const path = credentialFilePath(stateDir);
46903
+ await mkdir4(credentialDir(stateDir), { recursive: true });
46904
+ await writeFile2(path, `${JSON.stringify(next, null, 2)}
46905
+ `, { mode: 384 });
46906
+ try {
46907
+ await chmod(path, 384);
46908
+ } catch {
46909
+ }
46910
+ }
46911
+ async function clearMcpClient(stateDir, server) {
46912
+ const doc = await readAuthJson(stateDir);
46913
+ const prev = doc.mcpClients;
46914
+ if (typeof prev !== "object" || prev === null || Array.isArray(prev)) return;
46915
+ const section = { ...prev };
46916
+ if (!(server in section)) return;
46917
+ delete section[server];
46918
+ const path = credentialFilePath(stateDir);
46919
+ await mkdir4(credentialDir(stateDir), { recursive: true });
46920
+ await writeFile2(path, `${JSON.stringify({ ...doc, mcpClients: section }, null, 2)}
46921
+ `, { mode: 384 });
46922
+ try {
46923
+ await chmod(path, 384);
46924
+ } catch {
46925
+ }
46926
+ }
46883
46927
  async function writeMcpCredential(stateDir, server, cred) {
46884
46928
  const doc = await readAuthJson(stateDir);
46885
46929
  const section = isMcpAuthSection(doc.mcp) ? { ...doc.mcp } : {};
@@ -46921,6 +46965,15 @@ function firstAuthServer(doc) {
46921
46965
  const first = list[0];
46922
46966
  return typeof first === "string" && first.length > 0 ? first : null;
46923
46967
  }
46968
+ function scopesFrom(doc) {
46969
+ const list = doc.scopes_supported;
46970
+ if (!Array.isArray(list) || list.length === 0) return void 0;
46971
+ return list.every((s2) => typeof s2 === "string") ? list : void 0;
46972
+ }
46973
+ function resourceResult(doc, authServer, mcpUrl) {
46974
+ const scopes = scopesFrom(doc);
46975
+ return { authServer, resource: resourceFor(doc, mcpUrl), ...scopes ? { scopes } : {} };
46976
+ }
46924
46977
  function resourceMetadataUrlFromHeader(headerValue) {
46925
46978
  if (!headerValue) return null;
46926
46979
  const match = /resource_metadata="([^"]+)"/.exec(headerValue);
@@ -46934,9 +46987,22 @@ async function discoverAuthServer(mcpUrl, fetchImpl = fetch) {
46934
46987
  const wellKnownUrl = new URL(PROTECTED_RESOURCE_PATH, origin).toString();
46935
46988
  const direct = await tryFetchJson(wellKnownUrl, fetchImpl);
46936
46989
  if (direct) {
46937
- const authServer2 = firstAuthServer(direct);
46938
- if (authServer2) return { authServer: authServer2, resource: resourceFor(direct, mcpUrl) };
46990
+ const authServer = firstAuthServer(direct);
46991
+ if (authServer) return resourceResult(direct, authServer, mcpUrl);
46939
46992
  }
46993
+ const viaHint = await hintedResource(mcpUrl, fetchImpl);
46994
+ if (viaHint) {
46995
+ const authServer = firstAuthServer(viaHint);
46996
+ if (authServer) return resourceResult(viaHint, authServer, mcpUrl);
46997
+ }
46998
+ const own = await tryFetchJson(authServerMetadataUrl(origin), fetchImpl);
46999
+ if (own && typeof own.authorization_endpoint === "string" && own.authorization_endpoint.length > 0) {
47000
+ const issuer = typeof own.issuer === "string" && own.issuer.length > 0 ? own.issuer : origin;
47001
+ return resourceResult(own, issuer, mcpUrl);
47002
+ }
47003
+ return null;
47004
+ }
47005
+ async function hintedResource(mcpUrl, fetchImpl) {
46940
47006
  let probe;
46941
47007
  try {
46942
47008
  probe = await fetchImpl(mcpUrl);
@@ -46946,10 +47012,7 @@ async function discoverAuthServer(mcpUrl, fetchImpl = fetch) {
46946
47012
  if (probe.status !== 401) return null;
46947
47013
  const hinted = resourceMetadataUrlFromHeader(probe.headers.get("www-authenticate"));
46948
47014
  if (!hinted) return null;
46949
- const viaHint = await tryFetchJson(hinted, fetchImpl);
46950
- if (!viaHint) return null;
46951
- const authServer = firstAuthServer(viaHint);
46952
- return authServer ? { authServer, resource: resourceFor(viaHint, mcpUrl) } : null;
47015
+ return tryFetchJson(hinted, fetchImpl);
46953
47016
  }
46954
47017
  function authServerMetadataUrl(authServer) {
46955
47018
  const u2 = new URL(authServer);
@@ -47208,23 +47271,32 @@ async function loginToMcpServer(stateDir, server, deps = {}) {
47208
47271
  message: `"${server.name}" does not advertise an OAuth authorization server. If it takes a static token instead, store that as a header.`
47209
47272
  };
47210
47273
  }
47211
- const meta = await discoverAuthMetadata(
47274
+ const asMeta = await discoverAuthMetadata(
47212
47275
  discovered.authServer,
47213
47276
  fetchImpl
47214
47277
  );
47215
- const port = await getFreePort();
47278
+ const meta = discovered.scopes ? { ...asMeta, scopesSupported: discovered.scopes } : asMeta;
47279
+ const cached = await readMcpClient(stateDir, server.name);
47280
+ const port = cached?.redirectPort ?? await getFreePort();
47216
47281
  const redirectUri = `http://localhost:${port}/callback`;
47217
- const registered2 = await registerClient(
47282
+ const registered2 = cached && cached.authServer === discovered.authServer ? { clientId: cached.clientId, ...cached.clientSecret ? { clientSecret: cached.clientSecret } : {} } : await registerClient(
47218
47283
  meta,
47219
47284
  redirectUri,
47220
47285
  fetchImpl
47221
47286
  );
47287
+ if (registered2 && !cached) {
47288
+ await writeMcpClient(stateDir, server.name, {
47289
+ clientId: registered2.clientId,
47290
+ ...registered2.clientSecret ? { clientSecret: registered2.clientSecret } : {},
47291
+ authServer: discovered.authServer
47292
+ });
47293
+ }
47222
47294
  if (!registered2) {
47223
47295
  return {
47224
47296
  ok: false,
47225
47297
  reason: "no-dcr",
47226
47298
  authServer: discovered.authServer,
47227
- message: `"${server.name}"'s authorization server (${discovered.authServer}) does not support dynamic client registration, and there is no configured client id to fall back to. Store a static token as a header instead, if the server offers one.`
47299
+ message: `"${server.name}"'s authorization server (${discovered.authServer}) supports OAuth but not dynamic client registration, so no client can be created on demand. Store a static token as a header instead -- a server in this position normally issues one from its own settings.`
47228
47300
  };
47229
47301
  }
47230
47302
  const pkce = makePkce();
@@ -47259,12 +47331,17 @@ async function loginToMcpServer(stateDir, server, deps = {}) {
47259
47331
  await writeMcpCredential(stateDir, server.name, cred);
47260
47332
  return { ok: true };
47261
47333
  } catch (e2) {
47262
- return { ok: false, reason: "failed", message: e2 instanceof Error ? e2.message : String(e2) };
47334
+ const message = e2 instanceof Error ? e2.message : String(e2);
47335
+ if (/invalid_client|unauthorized_client|client.{0,20}not.{0,10}found/i.test(message)) {
47336
+ await clearMcpClient(stateDir, server.name).catch(() => {
47337
+ });
47338
+ }
47339
+ return { ok: false, reason: "failed", message };
47263
47340
  }
47264
47341
  }
47265
47342
 
47266
47343
  // src/config/mcpCmd.ts
47267
- var USAGE2 = "Use: corenel mcp add <name> <url> [--hosted] | remove <name> | list | set-header <name> <header> <value> | login <name> | refresh <name>";
47344
+ var USAGE2 = "Use: corenel mcp add <name> <url> [--hosted] | remove <name> | list | set-header <name> <header> <value> | set-client <name> <client-id> [<client-secret>] --auth-server <url> [--redirect-port <port>] | login <name> | refresh <name>";
47268
47345
  function isHttpUrl(url) {
47269
47346
  try {
47270
47347
  const parsed = new URL(url);
@@ -47277,7 +47354,7 @@ async function runMcp(raw, io, deps = {}) {
47277
47354
  const argv = stripStateDir(raw);
47278
47355
  const sub = argv[0];
47279
47356
  const { corrupt } = await readDaemonConfig(io.stateDir);
47280
- const isWrite = sub === "add" || sub === "remove" || sub === "set-header" || sub === "login";
47357
+ const isWrite = sub === "add" || sub === "remove" || sub === "set-header" || sub === "set-client" || sub === "login";
47281
47358
  if (corrupt && isWrite) {
47282
47359
  io.err(`daemon.json in ${io.stateDir} is not valid JSON; fix or delete it before writing.`);
47283
47360
  return 2;
@@ -47332,6 +47409,51 @@ async function runMcp(raw, io, deps = {}) {
47332
47409
  if (corrupt) io.err(`daemon.json in ${io.stateDir} is not valid JSON; these are the defaults, not what the file says.`);
47333
47410
  return 0;
47334
47411
  }
47412
+ if (sub === "set-client") {
47413
+ const flagAt = argv.indexOf("--auth-server");
47414
+ const authServer = flagAt === -1 ? void 0 : argv[flagAt + 1];
47415
+ const portAt = argv.indexOf("--redirect-port");
47416
+ const portRaw = portAt === -1 ? void 0 : argv[portAt + 1];
47417
+ const consumed = /* @__PURE__ */ new Set([0, flagAt, flagAt + 1, portAt, portAt + 1]);
47418
+ const positional = argv.filter((a2, i2) => !consumed.has(i2));
47419
+ const name = positional[0];
47420
+ const clientId = positional[1];
47421
+ const clientSecret = positional[2];
47422
+ if (!name || !clientId) {
47423
+ io.err(`corenel mcp set-client needs a name and a client id. ${USAGE2}`);
47424
+ return 2;
47425
+ }
47426
+ if (!authServer || !isHttpUrl(authServer)) {
47427
+ io.err(`corenel mcp set-client needs --auth-server <url> -- the authorization server the client was registered with, copied from the service's own app settings. ${USAGE2}`);
47428
+ return 2;
47429
+ }
47430
+ let redirectPort;
47431
+ if (portRaw !== void 0) {
47432
+ const parsed = Number(portRaw);
47433
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed >= 65536) {
47434
+ io.err(`"${portRaw}" is not a usable port for --redirect-port. ${USAGE2}`);
47435
+ return 2;
47436
+ }
47437
+ redirectPort = parsed;
47438
+ }
47439
+ const servers = await readMcpServers(io.stateDir);
47440
+ if (!servers.some((s2) => s2.name === name)) {
47441
+ io.err(`"${name}" is not a configured MCP server. Add it first: corenel mcp add ${name} <url>`);
47442
+ return 2;
47443
+ }
47444
+ await writeMcpClient(io.stateDir, name, {
47445
+ clientId,
47446
+ ...clientSecret ? { clientSecret } : {},
47447
+ authServer,
47448
+ ...redirectPort ? { redirectPort } : {}
47449
+ });
47450
+ io.out(`Stored an OAuth client for "${name}" (client id ${clientId}${clientSecret ? ", with a secret" : ""}).`);
47451
+ if (redirectPort !== void 0) {
47452
+ io.out(`Register this redirect URL with the service: http://localhost:${redirectPort}/callback`);
47453
+ }
47454
+ io.out(`Run: corenel mcp login ${name}`);
47455
+ return 0;
47456
+ }
47335
47457
  if (sub === "set-header") {
47336
47458
  const name = argv[1];
47337
47459
  const header = argv[2];
@@ -47385,7 +47507,7 @@ async function runMcp(raw, io, deps = {}) {
47385
47507
  return 2;
47386
47508
  }
47387
47509
  if (outcome.reason === "no-dcr") {
47388
- io.err(`"${name}"'s authorization server (${outcome.authServer}) does not support dynamic client registration, and corenel mcp login has no configured client id to fall back to. Use: corenel mcp set-header ${name} <header> <value> instead, if the server also offers a static token.`);
47510
+ io.err(`"${name}"'s authorization server (${outcome.authServer}) supports OAuth but not dynamic client registration, so no client can be created on demand. Use: corenel mcp set-header ${name} <header> <value> with a token from the service's own settings.`);
47389
47511
  return 2;
47390
47512
  }
47391
47513
  io.err(`Sign-in for "${name}" failed: ${outcome.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corenel/sidecar",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "type": "module",
5
5
  "license": "Elastic-2.0",
6
6
  "description": "Prompd sidecar — a local capability server. Run it next to Prompd (prompd.app) so the browser app can reach your machine: local LLMs (Ollama/LM Studio/vLLM) without CORS, your filesystem, and (opt-in) shell.",
@@ -38,8 +38,8 @@
38
38
  "typescript": "^5.9.3",
39
39
  "@corenel/harness": "0.7.0",
40
40
  "@corenel/protocol": "0.7.0",
41
- "@corenel/tools-node": "0.3.2",
42
- "@corenel/crew": "0.0.0"
41
+ "@corenel/crew": "0.0.0",
42
+ "@corenel/tools-node": "0.3.2"
43
43
  },
44
44
  "repository": {
45
45
  "type": "git",