@caplets/core 0.18.1 → 0.18.3

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.
@@ -1,4 +1,4 @@
1
- import { Ct as resolveProjectConfigPath, Mt as __exportAll, Ot as CapletsError, St as resolveProjectCapletsRoot, bt as resolveCapletsRoot, gt as loadConfigWithSources, vt as DEFAULT_AUTH_DIR, xt as resolveConfigPath, yt as DEFAULT_COMPLETION_CACHE_DIR } from "./options-BqibJVxq.js";
1
+ import { Ct as resolveProjectConfigPath, Mt as __exportAll, Ot as CapletsError, St as resolveProjectCapletsRoot, bt as resolveCapletsRoot, gt as loadConfigWithSources, vt as DEFAULT_AUTH_DIR, xt as resolveConfigPath, yt as DEFAULT_COMPLETION_CACHE_DIR } from "./options-j9p3L3r1.js";
2
2
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { createHash } from "node:crypto";
@@ -469,9 +469,17 @@ async function completeCliWords(words, options = {}) {
469
469
  if (normalized.length === 1) return prefixFilter([...topLevelCommandNames], current);
470
470
  if (normalized.length === 2 && command in cliSubcommands) return prefixFilter(cliSubcommands[command], current);
471
471
  if (normalized.length === 2 && capletIdCommands.has(command)) return prefixFilter(promptResourceCommands.has(command) ? configuredCapletIds(options, { backend: "mcp" }) : configuredCapletIds(options), current);
472
- if (normalized.length === 2 && (qualifiedToolCommands.has(command) || qualifiedPromptCommands.has(command))) {
473
- if (current.includes(".")) return prefixFilter((await discoverCompletionCandidates(current.slice(0, current.indexOf(".")), qualifiedToolCommands.has(command) ? "tools" : "prompts", discoveryOptions(options))).map((candidate) => candidate.value), current);
474
- return prefixFilter(configuredCapletIds(options, qualifiedPromptCommands.has(command) ? { backend: "mcp" } : void 0).map((id) => `${id}.`), current);
472
+ if (qualifiedToolCommands.has(command) || qualifiedPromptCommands.has(command)) {
473
+ const kind = qualifiedToolCommands.has(command) ? "tools" : "prompts";
474
+ const idFilter = qualifiedPromptCommands.has(command) ? { backend: "mcp" } : void 0;
475
+ if (normalized.length === 2) {
476
+ if (current.includes(".")) return prefixFilter((await discoverCompletionCandidates(current.slice(0, current.indexOf(".")), kind, discoveryOptions(options))).map((candidate) => candidate.value), current);
477
+ return prefixFilter(configuredCapletIds(options, idFilter), current);
478
+ }
479
+ if (normalized.length === 3 && subcommand && !subcommand.includes(".")) {
480
+ if (current.startsWith("-")) return [];
481
+ return prefixFilter((await discoverCompletionCandidates(subcommand, kind, discoveryOptions(options))).map((candidate) => candidate.value.replace(`${subcommand}.`, "")), current);
482
+ }
475
483
  }
476
484
  if (command === cliCommands.readResource && normalized.length === 3) return prefixFilter((await discoverCompletionCandidates(subcommand, "resources", discoveryOptions(options))).map((candidate) => candidate.value), current);
477
485
  if (command === cliCommands.auth && ["login", "logout"].includes(subcommand) && normalized.length === 3) return prefixFilter(configuredCapletIds(options), current);
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { $ as assertCompleteRequestPrompt, A as CreateMessageResultSchema, At as toSafeError, B as JSONRPCMessageSchema, C as AjvJsonSchemaValidator, D as CallToolRequestSchema, Dt as CAPLETS_ERROR_CODES, E as toJsonSchemaCompat, Et as SERVER_ID_PATTERN, F as EmptyResultSchema, G as ListRootsResultSchema, H as ListPromptsRequestSchema, I as ErrorCode, J as McpError, K as ListToolsRequestSchema, L as GetPromptRequestSchema, M as CreateTaskResultSchema, Nt as __require, O as CallToolResultSchema, Ot as CapletsError, P as ElicitResultSchema, Pt as __toESM, R as InitializeRequestSchema, S as assertToolsCallTaskCapability, St as resolveProjectCapletsRoot, T as mergeCapabilities, Tt as validateCapletFile, U as ListResourceTemplatesRequestSchema, V as LATEST_PROTOCOL_VERSION, W as ListResourcesRequestSchema, X as SUPPORTED_PROTOCOL_VERSIONS, Y as ReadResourceRequestSchema, Z as SetLevelRequestSchema, _t as parseConfig, a as resolveCapletsServer, at as getLiteralValue, bt as resolveCapletsRoot, c as ServerRegistry, ct as getSchemaDescription, d as runOAuthFlow, dt as normalizeObjectSchema, et as assertCompleteRequestResourceTemplate, f as startGenericOAuthFlow, ft as objectFromShape, g as readTokenBundle, gt as loadConfigWithSources, h as isTokenBundleExpired, ht as loadConfig, i as resolveCapletsMode, it as isJSONRPCResultResponse, j as CreateMessageResultWithToolsSchema, jt as __commonJSMin, k as CompleteRequestSchema, kt as redactSecrets, l as capabilityDescription, lt as isSchemaOptional, m as deleteTokenBundle, mt as safeParseAsync, nt as isJSONRPCErrorResponse, o as CapletsEngine, ot as getObjectShape, p as startOAuthFlow, pt as safeParse, q as LoggingLevelSchema, r as parseServerBaseUrl, rt as isJSONRPCRequest, s as handleServerTool, st as getParseErrorMessage, t as controlUrlForBase, tt as isInitializeRequest, u as runGenericOAuthFlow, ut as isZ4Schema, v as ReadBuffer, w as Protocol, wt as discoverCapletFiles, x as assertClientRequestTaskCapability, xt as resolveConfigPath, y as serializeMessage, z as InitializedNotificationSchema } from "./options-BqibJVxq.js";
1
+ import { $ as assertCompleteRequestPrompt, A as CreateMessageResultSchema, At as toSafeError, B as JSONRPCMessageSchema, C as AjvJsonSchemaValidator, D as CallToolRequestSchema, Dt as CAPLETS_ERROR_CODES, E as toJsonSchemaCompat, Et as SERVER_ID_PATTERN, F as EmptyResultSchema, G as ListRootsResultSchema, H as ListPromptsRequestSchema, I as ErrorCode, J as McpError, K as ListToolsRequestSchema, L as GetPromptRequestSchema, M as CreateTaskResultSchema, Nt as __require, O as CallToolResultSchema, Ot as CapletsError, P as ElicitResultSchema, Pt as __toESM, R as InitializeRequestSchema, S as assertToolsCallTaskCapability, St as resolveProjectCapletsRoot, T as mergeCapabilities, Tt as validateCapletFile, U as ListResourceTemplatesRequestSchema, V as LATEST_PROTOCOL_VERSION, W as ListResourcesRequestSchema, X as SUPPORTED_PROTOCOL_VERSIONS, Y as ReadResourceRequestSchema, Z as SetLevelRequestSchema, _t as parseConfig, a as resolveCapletsServer, at as getLiteralValue, bt as resolveCapletsRoot, c as ServerRegistry, ct as getSchemaDescription, d as runOAuthFlow, dt as normalizeObjectSchema, et as assertCompleteRequestResourceTemplate, f as startGenericOAuthFlow, ft as objectFromShape, g as readTokenBundle, gt as loadConfigWithSources, h as isTokenBundleExpired, ht as loadConfig, i as resolveCapletsMode, it as isJSONRPCResultResponse, j as CreateMessageResultWithToolsSchema, jt as __commonJSMin, k as CompleteRequestSchema, kt as redactSecrets, l as capabilityDescription, lt as isSchemaOptional, m as deleteTokenBundle, mt as safeParseAsync, nt as isJSONRPCErrorResponse, o as CapletsEngine, ot as getObjectShape, p as startOAuthFlow, pt as safeParse, q as LoggingLevelSchema, r as parseServerBaseUrl, rt as isJSONRPCRequest, s as handleServerTool, st as getParseErrorMessage, t as controlUrlForBase, tt as isInitializeRequest, u as runGenericOAuthFlow, ut as isZ4Schema, v as ReadBuffer, w as Protocol, wt as discoverCapletFiles, x as assertClientRequestTaskCapability, xt as resolveConfigPath, y as serializeMessage, z as InitializedNotificationSchema } from "./options-j9p3L3r1.js";
2
2
  import { A as url, C as object, D as string, b as literal, d as ZodOptional, o as generatedToolInputSchema, s as generatedToolInputSchemaForCaplet } from "./generated-tool-input-schema-BYoyY-l-.js";
3
- import { a as formatCapletList, c as resolveCliConfigPaths, l as cliCommands, n as completionScript, o as formatConfigPaths, s as listCaplets, t as completeCliWords, u as completionShells } from "./completion-dbB1hc97.js";
4
- import { accessSync, chmodSync, closeSync, constants, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
5
- import { basename, dirname, join, parse, relative, resolve } from "node:path";
3
+ import { a as formatCapletList, c as resolveCliConfigPaths, l as cliCommands, n as completionScript, o as formatConfigPaths, s as listCaplets, t as completeCliWords, u as completionShells } from "./completion-Cq1z7ci2.js";
4
+ import { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
5
+ import { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
6
6
  import { execFileSync } from "node:child_process";
7
7
  import process$1, { stdin, stdout } from "node:process";
8
8
  import { tmpdir } from "node:os";
@@ -1320,7 +1320,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
1320
1320
  } };
1321
1321
  //#endregion
1322
1322
  //#region package.json
1323
- var version = "0.18.1";
1323
+ var version = "0.18.3";
1324
1324
  //#endregion
1325
1325
  //#region src/serve/session.ts
1326
1326
  var CapletsMcpSession = class {
@@ -5192,12 +5192,14 @@ function rejectCrossKindDestinationCollision(plan, destinationRoot) {
5192
5192
  function installPlan(caplet, options) {
5193
5193
  const isDirectory = basename(caplet.path) === "CAPLET.md";
5194
5194
  const sourcePath = isDirectory ? dirname(caplet.path) : caplet.path;
5195
+ const sourceBoundary = dirname(sourcePath);
5195
5196
  const sourcePathRelative = relative(options.repoRoot, sourcePath);
5196
5197
  const destination = isDirectory ? join(options.destinationRoot, caplet.id) : join(options.destinationRoot, `${caplet.id}.md`);
5197
5198
  return {
5198
5199
  id: caplet.id,
5199
5200
  source: `${options.sourceId}#${sourcePathRelative}`,
5200
5201
  sourcePath,
5202
+ sourceBoundary,
5201
5203
  destination,
5202
5204
  kind: isDirectory ? "directory" : "file"
5203
5205
  };
@@ -5259,16 +5261,40 @@ function removeInstallPath(path, label, force) {
5259
5261
  }
5260
5262
  function copyInstallPath(plan) {
5261
5263
  try {
5264
+ if (plan.kind === "directory") {
5265
+ copyDirectoryCaplet(plan.sourcePath, plan.destination, realpathSync(plan.sourceBoundary));
5266
+ return;
5267
+ }
5262
5268
  cpSync(plan.sourcePath, plan.destination, {
5263
- recursive: plan.kind === "directory",
5269
+ recursive: false,
5264
5270
  force: false,
5265
5271
  errorOnExist: true
5266
5272
  });
5267
5273
  } catch (error) {
5274
+ if (error instanceof CapletsError) throw error;
5268
5275
  if (isFsError(error, "EEXIST") || isFsError(error, "EISDIR")) throw new CapletsError("CONFIG_EXISTS", `Caplet ${plan.id} already exists at ${plan.destination}; pass --force to overwrite it`, toSafeError(error));
5269
5276
  throw new CapletsError("CONFIG_INVALID", `Could not install Caplet ${plan.id} to ${plan.destination}`, toSafeError(error));
5270
5277
  }
5271
5278
  }
5279
+ function copyDirectoryCaplet(source, destination, sourceBoundary, seenDirectories = /* @__PURE__ */ new Set()) {
5280
+ const resolvedSource = lstatSync(source).isSymbolicLink() ? resolveDirectoryCapletSymlink(source, sourceBoundary) : source;
5281
+ if (statSync(resolvedSource).isDirectory()) {
5282
+ const realDirectory = realpathSync(resolvedSource);
5283
+ if (seenDirectories.has(realDirectory)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} creates a copy cycle`);
5284
+ const childSeenDirectories = new Set(seenDirectories);
5285
+ childSeenDirectories.add(realDirectory);
5286
+ mkdirSync(destination);
5287
+ for (const entry of readdirSync(resolvedSource)) copyDirectoryCaplet(join(resolvedSource, entry), join(destination, entry), sourceBoundary, childSeenDirectories);
5288
+ return;
5289
+ }
5290
+ copyFileSync(resolvedSource, destination);
5291
+ }
5292
+ function resolveDirectoryCapletSymlink(source, sourceBoundary) {
5293
+ const target = readlinkSync(source);
5294
+ const resolvedTarget = realpathSync(isAbsolute(target) ? target : resolve(dirname(source), target));
5295
+ if (resolvedTarget !== sourceBoundary && !resolvedTarget.startsWith(`${sourceBoundary}${sep}`)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} resolves outside source Caplets boundary`);
5296
+ return resolvedTarget;
5297
+ }
5272
5298
  function isFsError(error, code) {
5273
5299
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
5274
5300
  }
@@ -9123,7 +9149,7 @@ function createHttpServeApp(options, engine, io = {}) {
9123
9149
  }
9124
9150
  });
9125
9151
  }
9126
- return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name))));
9152
+ return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.publicOrigin, options.trustProxy, (name) => c.req.header(name))));
9127
9153
  });
9128
9154
  app.get(routePath(paths.control, "auth/callback/:flowId"), async (c) => {
9129
9155
  const flowId = c.req.param("flowId");
@@ -9133,7 +9159,7 @@ function createHttpServeApp(options, engine, io = {}) {
9133
9159
  flowId,
9134
9160
  callbackUrl: c.req.url
9135
9161
  }
9136
- }, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name)));
9162
+ }, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.publicOrigin, options.trustProxy, (name) => c.req.header(name)));
9137
9163
  if (!result.ok) writeErr(`Caplets authentication failed for flow ${flowId}: ${result.error.message}\n`);
9138
9164
  return result.ok ? c.text("Caplets authentication complete. You can return to your terminal.") : c.text("Caplets authentication failed. Check server logs for details.", 400);
9139
9165
  });
@@ -9147,12 +9173,12 @@ function createHttpServeApp(options, engine, io = {}) {
9147
9173
  if (options.warnUnauthenticatedNetwork) writeErr(`Warning: Caplets MCP HTTP server is listening on ${options.host} without authentication.\n`);
9148
9174
  return app;
9149
9175
  }
9150
- function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, trustProxy, header) {
9176
+ function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, publicOrigin, trustProxy, header) {
9151
9177
  return {
9152
9178
  ...io.control,
9153
9179
  projectCapletsRoot: io.control?.projectCapletsRoot ?? resolveProjectCapletsRoot(),
9154
9180
  authFlowStore,
9155
- controlCallbackBaseUrl: new URL(controlPath, publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
9181
+ controlCallbackBaseUrl: new URL(controlPath, publicOrigin ?? publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
9156
9182
  writeErr
9157
9183
  };
9158
9184
  }
@@ -9319,6 +9345,7 @@ function resolveServeOptions(raw, env = process.env) {
9319
9345
  host,
9320
9346
  port,
9321
9347
  path,
9348
+ ...serverUrl ? { publicOrigin: serverUrl.origin } : {},
9322
9349
  auth,
9323
9350
  warnUnauthenticatedNetwork: !loopback && !auth.enabled,
9324
9351
  loopback,
@@ -9519,9 +9546,12 @@ function createProgram(io = {}) {
9519
9546
  suggestions = remote ? await remote.request("complete_cli", {
9520
9547
  shell,
9521
9548
  words: completionWords
9522
- }) : await completeCliWords(completionWords, configPath ? { configPath } : {});
9549
+ }) : await completeCliWordsLocally(completionWords, {
9550
+ ...configPath ? { configPath } : {},
9551
+ ...io.authDir ? { authDir: io.authDir } : {}
9552
+ });
9523
9553
  } catch {
9524
- suggestions = [];
9554
+ suggestions = remote ? [] : await completeCliWords(completionWords, configPath ? { configPath } : {});
9525
9555
  }
9526
9556
  if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
9527
9557
  });
@@ -9715,8 +9745,8 @@ function createProgram(io = {}) {
9715
9745
  format: options.format
9716
9746
  });
9717
9747
  });
9718
- program.command(cliCommands.getTool).description("Print one downstream tool schema.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
9719
- const { caplet, tool } = parseQualifiedTarget(target);
9748
+ program.command(cliCommands.getTool).description("Print one downstream tool schema.").argument("<caplet-or-target>", "Caplet ID or qualified <caplet.tool> target").argument("[tool]", "downstream tool name when caplet is provided separately").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, toolArgument, options) => {
9749
+ const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
9720
9750
  await executeOperation(caplet, {
9721
9751
  operation: "get_tool",
9722
9752
  tool
@@ -9730,8 +9760,8 @@ function createProgram(io = {}) {
9730
9760
  format: options.format
9731
9761
  });
9732
9762
  });
9733
- program.command(cliCommands.callTool).description("Call one downstream tool.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of downstream tool arguments").option("--field <path>", "project a field from structured output", collect, []).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
9734
- const { caplet, tool } = parseQualifiedTarget(target);
9763
+ program.command(cliCommands.callTool).description("Call one downstream tool.").argument("<caplet-or-target>", "Caplet ID or qualified <caplet.tool> target").argument("[tool]", "downstream tool name when caplet is provided separately").option("--args <json-object>", "JSON object of downstream tool arguments").option("--field <path>", "project a field from structured output", collect, []).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, toolArgument, options) => {
9764
+ const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
9735
9765
  await executeOperation(caplet, {
9736
9766
  operation: "call_tool",
9737
9767
  tool,
@@ -9827,8 +9857,8 @@ function createProgram(io = {}) {
9827
9857
  remote: remoteClientForCli(io),
9828
9858
  format: options.format
9829
9859
  }));
9830
- program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet.prompt>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of prompt arguments").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
9831
- const { caplet, tool: prompt } = parseQualifiedTarget(target);
9860
+ program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet-or-target>", "MCP Caplet ID or qualified <caplet.prompt> target").argument("[prompt]", "prompt name when caplet is provided separately").option("--args <json-object>", "JSON object of prompt arguments").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, promptArgument, options) => {
9861
+ const { caplet, tool: prompt } = parseQualifiedTarget(capletOrTarget, promptArgument);
9832
9862
  await executeOperation(caplet, {
9833
9863
  operation: "get_prompt",
9834
9864
  prompt,
@@ -9993,14 +10023,33 @@ function parseOutputFormat(value) {
9993
10023
  default: throw new CapletsError("REQUEST_INVALID", `Expected output format markdown, md, plain, or json; got ${value}`);
9994
10024
  }
9995
10025
  }
9996
- function parseQualifiedTarget(target) {
9997
- const dot = target.indexOf(".");
9998
- if (dot <= 0 || dot === target.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected qualified target in the form <caplet>.<tool>");
10026
+ function parseQualifiedTarget(capletOrTarget, toolArgument) {
10027
+ if (toolArgument !== void 0) {
10028
+ if (capletOrTarget.length === 0 || toolArgument.length === 0) throw new CapletsError("REQUEST_INVALID", "Expected target in the form <caplet> <tool> or <caplet>.<tool>");
10029
+ return {
10030
+ caplet: capletOrTarget,
10031
+ tool: toolArgument
10032
+ };
10033
+ }
10034
+ const dot = capletOrTarget.indexOf(".");
10035
+ if (dot <= 0 || dot === capletOrTarget.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected target in the form <caplet> <tool> or <caplet>.<tool>");
9999
10036
  return {
10000
- caplet: target.slice(0, dot),
10001
- tool: target.slice(dot + 1)
10037
+ caplet: capletOrTarget.slice(0, dot),
10038
+ tool: capletOrTarget.slice(dot + 1)
10002
10039
  };
10003
10040
  }
10041
+ async function completeCliWordsLocally(words, options) {
10042
+ const engine = new CapletsEngine({
10043
+ ...options.configPath ? { configPath: options.configPath } : {},
10044
+ ...options.authDir ? { authDir: options.authDir } : {},
10045
+ watch: false
10046
+ });
10047
+ try {
10048
+ return await engine.completeCliWords(words);
10049
+ } finally {
10050
+ await engine.close();
10051
+ }
10052
+ }
10004
10053
  function parseCallToolArgs(value) {
10005
10054
  if (value === void 0) return {};
10006
10055
  let parsed;
package/dist/native.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Ot as CapletsError, Q as ToolListChangedNotificationSchema, _ as StreamableHTTPClientTransport, a as resolveCapletsServer, b as Client, i as resolveCapletsMode, l as capabilityDescription, n as mcpUrlForBase, o as CapletsEngine } from "./options-BqibJVxq.js";
1
+ import { Ot as CapletsError, Q as ToolListChangedNotificationSchema, _ as StreamableHTTPClientTransport, a as resolveCapletsServer, b as Client, i as resolveCapletsMode, l as capabilityDescription, n as mcpUrlForBase, o as CapletsEngine } from "./options-j9p3L3r1.js";
2
2
  import { a as generatedToolInputJsonSchemaForCaplet, i as generatedToolInputJsonSchema, l as operations, o as generatedToolInputSchema } from "./generated-tool-input-schema-BYoyY-l-.js";
3
3
  //#region src/native/options.ts
4
4
  const DEFAULT_POLL_INTERVAL_MS = 3e4;
@@ -52655,8 +52655,21 @@ async function loadOpenApiSource(endpoint, authDir) {
52655
52655
  if (endpoint.specPath) return endpoint.specPath;
52656
52656
  if (!endpoint.specUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} is missing OpenAPI spec source`);
52657
52657
  if (!endpoint.baseUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} must configure baseUrl when using remote specUrl`);
52658
- const response = await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {});
52659
- return JSON.parse(response);
52658
+ return parseOpenApiSourceText(await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {}));
52659
+ }
52660
+ function parseOpenApiSourceText(source) {
52661
+ let parsed;
52662
+ try {
52663
+ parsed = JSON.parse(source);
52664
+ } catch (jsonError) {
52665
+ try {
52666
+ parsed = (0, import_dist$1.parse)(source);
52667
+ } catch {
52668
+ throw jsonError instanceof Error ? jsonError : new Error(String(jsonError));
52669
+ }
52670
+ }
52671
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("OpenAPI source must parse to an object");
52672
+ return parsed;
52660
52673
  }
52661
52674
  function extractOperations(endpoint, document) {
52662
52675
  const operations = [];
@@ -52674,6 +52687,7 @@ function extractOperations(endpoint, document) {
52674
52687
  const requestBody = requestBodyFor(operation);
52675
52688
  const outputSchema = outputSchemaFor(operation);
52676
52689
  const baseUrl = endpoint.baseUrl ?? firstServerUrl(document);
52690
+ const staticHeaders = staticHeaderDefaultsFor(endpoint, parameters);
52677
52691
  validateOperationBaseUrl(endpoint, baseUrl);
52678
52692
  operations.push({
52679
52693
  name,
@@ -52681,15 +52695,32 @@ function extractOperations(endpoint, document) {
52681
52695
  path,
52682
52696
  ...typeof operation.summary === "string" ? { summary: operation.summary } : {},
52683
52697
  ...typeof operation.description === "string" ? { description: operation.description } : {},
52684
- inputSchema: inputSchemaFor(parameters, requestBody),
52698
+ inputSchema: inputSchemaFor(parameters, requestBody, staticHeaders),
52685
52699
  ...outputSchema ? { outputSchema } : {},
52686
52700
  ...requestBody?.contentType ? { requestBodyContentType: requestBody.contentType } : {},
52687
- ...baseUrl ? { baseUrl } : {}
52701
+ ...baseUrl ? { baseUrl } : {},
52702
+ ...Object.keys(staticHeaders).length ? { staticHeaders } : {}
52688
52703
  });
52689
52704
  }
52690
52705
  }
52691
52706
  return operations.sort((left, right) => left.name.localeCompare(right.name));
52692
52707
  }
52708
+ function staticHeaderDefaultsFor(endpoint, parameters) {
52709
+ const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
52710
+ const headers = {};
52711
+ for (const parameter of parameters) {
52712
+ if (parameter?.in !== "header" || typeof parameter.name !== "string") continue;
52713
+ const normalized = parameter.name.toLowerCase();
52714
+ if (configuredHeaderNames.has(normalized) || FORBIDDEN_ARGUMENT_HEADERS.has(normalized) && normalized !== "accept") continue;
52715
+ const defaultValue = parameter.schema?.default;
52716
+ if ([
52717
+ "string",
52718
+ "number",
52719
+ "boolean"
52720
+ ].includes(typeof defaultValue)) headers[parameter.name] = String(defaultValue);
52721
+ }
52722
+ return headers;
52723
+ }
52693
52724
  function requestBodyFor(operation) {
52694
52725
  const requestBody = operation.requestBody;
52695
52726
  if (!requestBody || typeof requestBody !== "object") return;
@@ -52750,19 +52781,20 @@ function structuredOutputSchema(bodySchema) {
52750
52781
  }
52751
52782
  };
52752
52783
  }
52753
- function inputSchemaFor(parameters, requestBody) {
52784
+ function inputSchemaFor(parameters, requestBody, staticHeaders = {}) {
52754
52785
  const schema = {
52755
52786
  type: "object",
52756
52787
  additionalProperties: false,
52757
52788
  properties: {}
52758
52789
  };
52759
52790
  const required = [];
52791
+ const protectedStaticHeaders = new Set(Object.keys(staticHeaders).map((key) => key.toLowerCase()).filter((key) => FORBIDDEN_ARGUMENT_HEADERS.has(key)));
52760
52792
  for (const location of [
52761
52793
  "path",
52762
52794
  "query",
52763
52795
  "header"
52764
52796
  ]) {
52765
- const locationParameters = parameters.filter((parameter) => parameter?.in === location);
52797
+ const locationParameters = parameters.filter((parameter) => parameter?.in === location && !(location === "header" && protectedStaticHeaders.has(parameter.name?.toLowerCase())));
52766
52798
  if (locationParameters.length === 0) continue;
52767
52799
  const nestedRequired = locationParameters.filter((parameter) => parameter.required === true || location === "path").map((parameter) => parameter.name);
52768
52800
  schema.properties[location] = {
@@ -52801,7 +52833,8 @@ function buildRequest(endpoint, operation, args, authDir) {
52801
52833
  for (const [key, value] of Object.entries(asRecord(args.query))) if (value !== void 0 && value !== null) url.searchParams.append(key, serializeHttpValue("query", key, value));
52802
52834
  const headers = new Headers();
52803
52835
  applyAuth(headers, endpoint, authDir);
52804
- const configuredHeaderNames = endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
52836
+ const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
52837
+ for (const [key, value] of Object.entries(operation.staticHeaders ?? {})) if (!headers.has(key) && !configuredHeaderNames.has(key.toLowerCase())) headers.set(key, value);
52805
52838
  for (const [key, value] of Object.entries(asRecord(args.header))) if (value !== void 0 && value !== null) {
52806
52839
  const normalized = key.toLowerCase();
52807
52840
  if (FORBIDDEN_ARGUMENT_HEADERS.has(normalized) || configuredHeaderNames.has(normalized)) throw new CapletsError("REQUEST_INVALID", `Header ${key} cannot be supplied by arguments`);
@@ -52847,6 +52880,9 @@ function asRecord(value) {
52847
52880
  function applyAuth(headers, endpoint, authDir) {
52848
52881
  for (const [key, value] of Object.entries(authHeaders(endpoint, authDir))) headers.set(key, value);
52849
52882
  }
52883
+ function configuredAuthHeaderNames(endpoint) {
52884
+ return endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
52885
+ }
52850
52886
  function authHeaders(endpoint, authDir) {
52851
52887
  switch (endpoint.auth.type) {
52852
52888
  case "none": return {};
@@ -53853,7 +53889,7 @@ var CapletsEngine = class {
53853
53889
  }
53854
53890
  }
53855
53891
  async completeCliWords(words) {
53856
- const { completeCliWords } = await import("./completion-dbB1hc97.js").then((n) => n.r);
53892
+ const { completeCliWords } = await import("./completion-Cq1z7ci2.js").then((n) => n.r);
53857
53893
  return await completeCliWords(words, {
53858
53894
  config: this.registry.config,
53859
53895
  managers: {
@@ -17,6 +17,7 @@ export type HttpServeOptions = {
17
17
  host: string;
18
18
  port: number;
19
19
  path: string;
20
+ publicOrigin?: string | undefined;
20
21
  auth: HttpBasicAuthOptions;
21
22
  warnUnauthenticatedNetwork: boolean;
22
23
  loopback: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caplets/core",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "Core runtime library for Caplets progressive disclosure gateways.",
5
5
  "keywords": [
6
6
  "caplets",
@@ -53,6 +53,7 @@
53
53
  "hono": "^4.12.21",
54
54
  "vfile": "^6.0.3",
55
55
  "vfile-matter": "^5.0.1",
56
+ "yaml": "^2.9.0",
56
57
  "zod": "^4.4.3"
57
58
  },
58
59
  "devDependencies": {