@caplets/core 0.27.0 → 0.28.0
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/dist/attach/api.d.ts +8 -0
- package/dist/cli/code-mode.d.ts +3 -2
- package/dist/cli/commands.d.ts +3 -1
- package/dist/cli.d.ts +4 -0
- package/dist/code-mode/types.d.ts +1 -0
- package/dist/{completion-1wDjwHkC.js → completion-CjE0EnbF.js} +13 -2
- package/dist/config/paths.d.ts +9 -0
- package/dist/config.d.ts +3 -1
- package/dist/engine.d.ts +18 -0
- package/dist/esm-Db9dhnIG.js +7488 -0
- package/dist/index.js +829 -481
- package/dist/native/options.d.ts +1 -0
- package/dist/native/remote.d.ts +2 -1
- package/dist/native/service.d.ts +14 -0
- package/dist/native.js +1 -1
- package/dist/node-BgWIvSVP.js +17214 -0
- package/dist/rolldown-runtime-CE-6LUnI.js +44 -0
- package/dist/serve/http.d.ts +16 -1
- package/dist/serve/options.d.ts +2 -0
- package/dist/{service-BGGiZLHa.js → service-BfPCRxQ9.js} +1688 -121
- package/dist/src-Cd2QIUm1.js +813 -0
- package/dist/telemetry/context.d.ts +13 -0
- package/dist/telemetry/debug.d.ts +10 -0
- package/dist/telemetry/delivery.d.ts +1 -0
- package/dist/telemetry/events.d.ts +70 -0
- package/dist/telemetry/identity.d.ts +1 -0
- package/dist/telemetry/index.d.ts +8 -0
- package/dist/telemetry/intake.generated.d.ts +2 -0
- package/dist/telemetry/notice.d.ts +8 -0
- package/dist/telemetry/privacy.d.ts +3 -0
- package/dist/telemetry/providers.d.ts +21 -0
- package/dist/telemetry/runtime.d.ts +40 -0
- package/dist/telemetry/state.d.ts +53 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as redactCodeModeLogText, $n as getParseErrorMessage, $t as readTokenBundle, A as resolveRemoteMode, An as GetPromptRequestSchema, At as loadProjectConfig, Bn as LoggingLevelSchema, Bt as discoverCapletFiles, C as CapletsCloudClient, Cn as CreateMessageResultSchema, Ct as ServerRegistry, D as normalizeRemoteProfileHostUrl, Dn as ElicitResultSchema, Dt as loadConfigWithSources, E as isCapletsCloudUrl, Et as loadConfig, F as resolveCapletsServer, Fn as ListPromptsRequestSchema, Ft as FileVaultStore, G as runCodeMode, Gn as assertCompleteRequestPrompt, Gt as markdownStructuredContent, H as codeModeRunInputSchema, Hn as ReadResourceRequestSchema, Ht as loadCapletFilesFromMap, I as nativeCapletPromptGuidance, In as ListResourceTemplatesRequestSchema, It as VAULT_MAX_VALUE_BYTES, J as diagnoseCodeModeTypeScript, Jn as isJSONRPCErrorResponse, Jt as runOAuthFlow, K as CodeModeSessionManager, Kn as assertCompleteRequestResourceTemplate, Kt as refreshOAuthTokenBundle, L as nativeCapletToolDescription, Ln as ListResourcesRequestSchema, Lt as validateVaultKeyName, M as controlUrlForBase, Mn as InitializedNotificationSchema, Mt as vaultBootstrapResolver, N as isLoopbackHost, Nn as JSONRPCMessageSchema, Nt as vaultResolverForAuthDir, O as resolveCapletsRemote, On as EmptyResultSchema, Ot as loadGlobalConfig, P as parseServerBaseUrl, Pn as LATEST_PROTOCOL_VERSION, Pt as vaultStoreForAuthDir, Q as CodeModeLogStore, Qn as getObjectShape, Qt as isTokenBundleExpired, R as nativeCapletToolName, Rn as ListRootsResultSchema, Rt as decryptVaultValue, S as buildProjectSyncManifest, Sn as CompleteRequestSchema, St as handleServerTool, T as hostedCloudWorkspaceFromRemoteUrl, Tn as CreateTaskResultSchema, Tt as GoogleDiscoveryManager, U as codeModeRunParamsSchema, Un as SUPPORTED_PROTOCOL_VERSIONS, Ut as hasRenderableStructuredContent, Vn as McpError, Vt as validateCapletFile, W as emptyCodeModeRunMeta, Wn as SetLevelRequestSchema, Wt as markdownCallToolResultContent, X as listCodeModeCallableCaplets, Xn as isJSONRPCResultResponse, Xt as startOAuthFlow, Y as createCodeModeCapletsApi, Yn as isJSONRPCRequest, Yt as startGenericOAuthFlow, Z as CodeModeJournalStore, Zn as getLiteralValue, Zt as deleteTokenBundle, _ as attachErrorResponse, _n as Protocol, _t as rotateTelemetryIdentity, a as CloudAuthStore, an as defaultConfigPath, ar as safeParse, at as version, b as invokeAttachExport, bn as CallToolRequestSchema, bt as findProjectRoot, c as redactedCloudAuthStatus, cn as resolveCapletsRoot, ct as buildProductTelemetryEvent, d as projectBindingError, dn as resolveProjectConfigPath, dt as maybePrintTelemetryNotice, en as DEFAULT_AUTH_DIR, er as getSchemaDescription, et as codeModeDeclarationHash, f as projectBindingRecovery, fn as ReadBuffer, ft as resolveTelemetryState, g as CAPLETS_ATTACH_SESSION_HEADER, gn as AjvJsonSchemaValidator, gt as readTelemetryNotice, hn as assertToolsCallTaskCapability, ht as readTelemetryIdentity, i as createRemoteProfileStore, in as defaultConfigBaseDir, ir as objectFromShape, it as CapletsEngine, j as appendBasePath, jn as InitializeRequestSchema, jt as parseConfig, k as resolveHostedCloudRemote, kn as ErrorCode, kt as loadLocalOverlayConfigWithSources, l as PROJECT_BINDING_ERROR_CODES, ln as resolveConfigPath, lt as buildReliabilityTelemetryEvent, mn as assertClientRequestTaskCapability, mt as readTelemetryDeliveryHealth, n as resolveRemoteSelection, nn as DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR, nr as isZ4Schema, nt as generateCodeModeRunToolDescription, o as cloudAuthPath, on as defaultStateBaseDir, or as safeParseAsync, ot as createTelemetryDispatcher, p as CloudAuthClient, pn as serializeMessage, pt as deleteTelemetryIdentity, q as QuickJsCodeModeSandbox, qn as isInitializeRequest, qt as runGenericOAuthFlow, r as cloudCredentialsFromRemoteProfile, rn as defaultCacheBaseDir, rr as normalizeObjectSchema, rt as minifyCodeModeDeclarationText, s as migrateCredentials, sn as defaultTelemetryStateDir, st as TelemetryDebugSink, t as createNativeCapletsService, tr as isSchemaOptional, tt as generateCodeModeDeclarations, u as ProjectBindingError, un as resolveProjectCapletsRoot, ut as durationBucket, v as buildAttachProjection, vn as mergeCapabilities, vt as resolveExposure, wn as CreateMessageResultWithToolsSchema, wt as capabilityDescription, x as invokeNativeAttachExport, xn as CallToolResultSchema, xt as fingerprintProjectRoot, y as buildNativeAttachProjection, yn as toJsonSchemaCompat, yt as decodeDirectResourceUri, zn as ListToolsRequestSchema, zt as encryptVaultValue } from "./service-BfPCRxQ9.js";
|
|
2
2
|
import { _ as record, b as unknown, d as literal, m as object, n as ZodOptional, o as array, p as number, r as _enum, s as boolean, v as string, x as url } from "./schemas-BoqMu4MG.js";
|
|
3
3
|
import { a as SERVER_ID_PATTERN, d as CapletsError, m as toSafeError, p as redactSecrets$1, u as CAPLETS_ERROR_CODES } from "./validation-CWzd2gtn.js";
|
|
4
|
-
import {
|
|
5
|
-
import { f as observedOutputShapeKey,
|
|
6
|
-
import { a as formatCapletList, c as resolveCliConfigPaths, l as cliCommands$1, n as completionScript, o as formatConfigPaths, s as listCaplets, t as completeCliWords, u as completionShells } from "./completion-
|
|
4
|
+
import { generatedToolInputSchema, generatedToolInputSchemaForCaplet } from "./generated-tool-input-schema.js";
|
|
5
|
+
import { f as observedOutputShapeKey, i as observeOutputShape, u as FileObservedOutputShapeStore } from "./observed-output-shapes-DuP7mJQf.js";
|
|
6
|
+
import { a as formatCapletList, c as resolveCliConfigPaths, l as cliCommands$1, n as completionScript, o as formatConfigPaths, s as listCaplets, t as completeCliWords, u as completionShells } from "./completion-CjE0EnbF.js";
|
|
7
7
|
import { n as normalizeCapletSourcePath, t as FilesystemCapletSource } from "./filesystem-Kkg32TOJ.js";
|
|
8
8
|
import { parseConfig as parseConfig$1 } from "./config-runtime.js";
|
|
9
9
|
import fs, { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
|
|
@@ -13,7 +13,7 @@ import process$1, { stdin, stdout } from "node:process";
|
|
|
13
13
|
import { Readable, Writable } from "node:stream";
|
|
14
14
|
import { STATUS_CODES, createServer } from "node:http";
|
|
15
15
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
16
|
-
import { homedir, tmpdir, userInfo } from "node:os";
|
|
16
|
+
import { arch, homedir, platform, tmpdir, userInfo } from "node:os";
|
|
17
17
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
18
18
|
import { EventEmitter } from "node:events";
|
|
19
19
|
import { promisify, stripVTControlCharacters } from "node:util";
|
|
@@ -1552,9 +1552,6 @@ const EMPTY_COMPLETION_RESULT = { completion: {
|
|
|
1552
1552
|
hasMore: false
|
|
1553
1553
|
} };
|
|
1554
1554
|
//#endregion
|
|
1555
|
-
//#region package.json
|
|
1556
|
-
var version = "0.27.0";
|
|
1557
|
-
//#endregion
|
|
1558
1555
|
//#region src/serve/session.ts
|
|
1559
1556
|
var CapletsMcpSession = class {
|
|
1560
1557
|
engine;
|
|
@@ -1634,7 +1631,7 @@ var CapletsMcpSession = class {
|
|
|
1634
1631
|
downstreamName: entry.downstreamName,
|
|
1635
1632
|
exposure: "direct"
|
|
1636
1633
|
} },
|
|
1637
|
-
callback: async (request) => this.engine.executeDirectTool(entry.caplet.server, entry.downstreamName, isRecord$
|
|
1634
|
+
callback: async (request) => this.engine.executeDirectTool(entry.caplet.server, entry.downstreamName, isRecord$3(request) ? request : {}),
|
|
1638
1635
|
enabled: true
|
|
1639
1636
|
})
|
|
1640
1637
|
});
|
|
@@ -1672,6 +1669,7 @@ var CapletsMcpSession = class {
|
|
|
1672
1669
|
}, async (request) => this.handleCodeModeRunTool(request));
|
|
1673
1670
|
}
|
|
1674
1671
|
async handleCodeModeRunTool(request) {
|
|
1672
|
+
const started = Date.now();
|
|
1675
1673
|
const parsed = codeModeRunInputSchema.safeParse(request);
|
|
1676
1674
|
const envelope = parsed.success ? await runCodeMode({
|
|
1677
1675
|
code: parsed.data.code,
|
|
@@ -1697,6 +1695,10 @@ var CapletsMcpSession = class {
|
|
|
1697
1695
|
},
|
|
1698
1696
|
meta: emptyCodeModeRunMeta()
|
|
1699
1697
|
};
|
|
1698
|
+
this.engine.captureCodeModeOutcome(envelope, {
|
|
1699
|
+
started,
|
|
1700
|
+
...parsed.success && parsed.data.timeoutMs !== void 0 ? { timeoutMs: parsed.data.timeoutMs } : {}
|
|
1701
|
+
}).catch(() => void 0);
|
|
1700
1702
|
return {
|
|
1701
1703
|
content: [{
|
|
1702
1704
|
type: "text",
|
|
@@ -1725,7 +1727,7 @@ var CapletsMcpSession = class {
|
|
|
1725
1727
|
downstreamName: entry.downstreamName,
|
|
1726
1728
|
exposure: "direct"
|
|
1727
1729
|
} }
|
|
1728
|
-
}, async (request) => this.engine.executeDirectTool(entry.caplet.server, entry.downstreamName, isRecord$
|
|
1730
|
+
}, async (request) => this.engine.executeDirectTool(entry.caplet.server, entry.downstreamName, isRecord$3(request) ? request : {}));
|
|
1729
1731
|
}
|
|
1730
1732
|
registerDirectResource(entry) {
|
|
1731
1733
|
if (!this.server.registerResource) throw new Error("MCP server does not support resource registration");
|
|
@@ -1744,11 +1746,11 @@ var CapletsMcpSession = class {
|
|
|
1744
1746
|
title: entry.prompt.name,
|
|
1745
1747
|
...entry.prompt.description ? { description: entry.prompt.description } : {},
|
|
1746
1748
|
argsSchema: promptArgsSchema(entry.prompt.arguments)
|
|
1747
|
-
}, async (args) => await this.engine.getDirectPrompt(entry.caplet.server, entry.downstreamName, isRecord$
|
|
1749
|
+
}, async (args) => await this.engine.getDirectPrompt(entry.caplet.server, entry.downstreamName, isRecord$3(args) ? stringifyRecord(args) : {}));
|
|
1748
1750
|
}
|
|
1749
1751
|
async directResourceResult(serverId, downstreamUri) {
|
|
1750
1752
|
const result = await this.engine.readDirectResource(serverId, downstreamUri);
|
|
1751
|
-
if (isRecord$
|
|
1753
|
+
if (isRecord$3(result) && "contents" in result) return result;
|
|
1752
1754
|
return { contents: [{
|
|
1753
1755
|
uri: downstreamUri,
|
|
1754
1756
|
mimeType: "application/json",
|
|
@@ -1819,10 +1821,10 @@ function promptArgsSchema(args) {
|
|
|
1819
1821
|
for (const arg of args ?? []) shape[arg.name] = string().optional();
|
|
1820
1822
|
return shape;
|
|
1821
1823
|
}
|
|
1822
|
-
function stringifyRecord
|
|
1824
|
+
function stringifyRecord(value) {
|
|
1823
1825
|
return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, nested === void 0 ? "" : String(nested)]));
|
|
1824
1826
|
}
|
|
1825
|
-
function isRecord$
|
|
1827
|
+
function isRecord$3(value) {
|
|
1826
1828
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1827
1829
|
}
|
|
1828
1830
|
function staticExposureSnapshot(config, caplets) {
|
|
@@ -5524,7 +5526,12 @@ async function runCodeModeCli(options) {
|
|
|
5524
5526
|
mode: "local",
|
|
5525
5527
|
...options.configPath ? { configPath: options.configPath } : {},
|
|
5526
5528
|
...options.projectConfigPath ? { projectConfigPath: options.projectConfigPath } : {},
|
|
5527
|
-
...options.authDir ? { authDir: options.authDir } : {}
|
|
5529
|
+
...options.authDir ? { authDir: options.authDir } : {},
|
|
5530
|
+
telemetryEnv: options.env,
|
|
5531
|
+
telemetryStateDir: options.telemetryStateDir ?? defaultTelemetryStateDir(options.env),
|
|
5532
|
+
telemetrySurface: "code_mode",
|
|
5533
|
+
telemetryVisibility: "visible",
|
|
5534
|
+
telemetryRuntimeMode: runtimeScope(options.env) === "local" ? "local" : "unknown"
|
|
5528
5535
|
});
|
|
5529
5536
|
try {
|
|
5530
5537
|
if (options.sessionId !== void 0) {
|
|
@@ -5547,12 +5554,18 @@ async function runCodeModeCli(options) {
|
|
|
5547
5554
|
options.setExitCode(1);
|
|
5548
5555
|
return;
|
|
5549
5556
|
}
|
|
5557
|
+
const code = await readCodeModeCliCode(options);
|
|
5558
|
+
const started = Date.now();
|
|
5550
5559
|
const result = await runCodeMode({
|
|
5551
|
-
code
|
|
5552
|
-
service: service.codeModeService?.() ?? service,
|
|
5560
|
+
code,
|
|
5553
5561
|
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs },
|
|
5562
|
+
service: service.codeModeService?.() ?? service,
|
|
5554
5563
|
runtimeScope: "cli-one-shot"
|
|
5555
5564
|
});
|
|
5565
|
+
await service.captureCodeModeOutcome?.(result, {
|
|
5566
|
+
started,
|
|
5567
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
5568
|
+
}).catch(() => void 0);
|
|
5556
5569
|
if (options.json) options.writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
5557
5570
|
else if (result.ok) options.writeOut(`${formatHumanValue(result.value)}\n`);
|
|
5558
5571
|
else {
|
|
@@ -5587,7 +5600,8 @@ async function codeModeTypesCli(options) {
|
|
|
5587
5600
|
const engine = new CapletsEngine({
|
|
5588
5601
|
...options.configPath ? { configPath: options.configPath } : {},
|
|
5589
5602
|
...options.projectConfigPath ? { projectConfigPath: options.projectConfigPath } : {},
|
|
5590
|
-
...options.authDir ? { authDir: options.authDir } : {}
|
|
5603
|
+
...options.authDir ? { authDir: options.authDir } : {},
|
|
5604
|
+
telemetryStateDir: options.telemetryStateDir ?? defaultTelemetryStateDir(options.env)
|
|
5591
5605
|
});
|
|
5592
5606
|
try {
|
|
5593
5607
|
const caplets = listCodeModeCallableCaplets$1(engine);
|
|
@@ -6919,14 +6933,24 @@ const HTTP_ONLY_OPTIONS = [
|
|
|
6919
6933
|
"port",
|
|
6920
6934
|
"path",
|
|
6921
6935
|
"remoteStatePath",
|
|
6936
|
+
"upstreamUrl",
|
|
6922
6937
|
"allowUnauthenticatedHttp",
|
|
6923
6938
|
"trustProxy"
|
|
6924
6939
|
];
|
|
6940
|
+
const HTTP_ONLY_OPTION_FLAGS = {
|
|
6941
|
+
host: "--host",
|
|
6942
|
+
port: "--port",
|
|
6943
|
+
path: "--path",
|
|
6944
|
+
remoteStatePath: "--remote-state-path",
|
|
6945
|
+
upstreamUrl: "--upstream-url",
|
|
6946
|
+
allowUnauthenticatedHttp: "--allow-unauthenticated-http",
|
|
6947
|
+
trustProxy: "--trust-proxy"
|
|
6948
|
+
};
|
|
6925
6949
|
function resolveServeOptions(raw, env = process.env) {
|
|
6926
6950
|
const transport = parseTransport(raw.transport ?? "stdio");
|
|
6927
6951
|
if (transport === "stdio") {
|
|
6928
6952
|
const invalid = HTTP_ONLY_OPTIONS.filter((key) => raw[key] !== void 0);
|
|
6929
|
-
if (invalid.length > 0) throw new CapletsError("REQUEST_INVALID", `${invalid.map((key) =>
|
|
6953
|
+
if (invalid.length > 0) throw new CapletsError("REQUEST_INVALID", `${invalid.map((key) => HTTP_ONLY_OPTION_FLAGS[key]).join(", ")} ${invalid.length === 1 ? "is" : "are"} only valid with --transport http`);
|
|
6930
6954
|
return { transport };
|
|
6931
6955
|
}
|
|
6932
6956
|
const serverUrl = env.CAPLETS_SERVER_URL ? parseServeServerUrl(nonEmpty$2(env.CAPLETS_SERVER_URL, "CAPLETS_SERVER_URL")) : void 0;
|
|
@@ -6934,6 +6958,13 @@ function resolveServeOptions(raw, env = process.env) {
|
|
|
6934
6958
|
const port = parsePort(raw.port ?? (serverUrl?.port ? Number(serverUrl.port) : 5387));
|
|
6935
6959
|
const path = normalizeHttpPath(raw.path ?? serverUrl?.pathname ?? "/");
|
|
6936
6960
|
const remoteCredentialStateDir = nonEmpty$2(raw.remoteStatePath, "--remote-state-path") ?? nonEmpty$2(env.CAPLETS_REMOTE_SERVER_STATE_DIR, "CAPLETS_REMOTE_SERVER_STATE_DIR") ?? join(DEFAULT_AUTH_DIR, "remote-server");
|
|
6961
|
+
const upstreamUrl = nonEmpty$2(raw.upstreamUrl, "--upstream-url");
|
|
6962
|
+
if (upstreamUrl) rejectSelfReferentialUpstream(upstreamUrl, {
|
|
6963
|
+
...serverUrl ? { origin: serverUrl.origin } : {},
|
|
6964
|
+
host,
|
|
6965
|
+
port,
|
|
6966
|
+
path
|
|
6967
|
+
});
|
|
6937
6968
|
const loopback = isLoopbackHost(host);
|
|
6938
6969
|
const auth = raw.allowUnauthenticatedHttp === true ? { type: "development_unauthenticated" } : { type: "remote_credentials" };
|
|
6939
6970
|
return {
|
|
@@ -6944,6 +6975,7 @@ function resolveServeOptions(raw, env = process.env) {
|
|
|
6944
6975
|
...serverUrl ? { publicOrigin: serverUrl.origin } : {},
|
|
6945
6976
|
auth,
|
|
6946
6977
|
...auth.type === "remote_credentials" ? { remoteCredentialStateDir } : {},
|
|
6978
|
+
...upstreamUrl ? { upstreamUrl } : {},
|
|
6947
6979
|
allowUnauthenticatedHttp: raw.allowUnauthenticatedHttp === true,
|
|
6948
6980
|
warnUnauthenticatedNetwork: !loopback && auth.type === "development_unauthenticated",
|
|
6949
6981
|
loopback,
|
|
@@ -6975,6 +7007,42 @@ function normalizeHttpPath(value) {
|
|
|
6975
7007
|
function serverUrlHost(url) {
|
|
6976
7008
|
return url?.hostname.replace(/^\[(.*)\]$/u, "$1");
|
|
6977
7009
|
}
|
|
7010
|
+
function rejectSelfReferentialUpstream(upstreamUrl, local) {
|
|
7011
|
+
if (sameServerBase(parseServerBaseUrl(upstreamUrl), localServeBaseUrl(local))) throw new CapletsError("REQUEST_INVALID", "--upstream-url must not point back to this runtime.");
|
|
7012
|
+
}
|
|
7013
|
+
function localServeBaseUrl(local) {
|
|
7014
|
+
const origin = local.origin ?? `http://${formatHost$3(local.host)}:${local.port}`;
|
|
7015
|
+
const url = new URL(origin);
|
|
7016
|
+
url.pathname = local.path;
|
|
7017
|
+
url.search = "";
|
|
7018
|
+
url.hash = "";
|
|
7019
|
+
return url;
|
|
7020
|
+
}
|
|
7021
|
+
function sameServerBase(left, right) {
|
|
7022
|
+
return left.protocol === right.protocol && sameHost(left.hostname, right.hostname) && effectivePort(left) === effectivePort(right) && normalizePath(left.pathname) === normalizePath(right.pathname);
|
|
7023
|
+
}
|
|
7024
|
+
function sameHost(left, right) {
|
|
7025
|
+
if (left === right) return true;
|
|
7026
|
+
const normalizedLeft = normalizeLoopbackHost(left);
|
|
7027
|
+
const normalizedRight = normalizeLoopbackHost(right);
|
|
7028
|
+
return normalizedLeft !== void 0 && normalizedLeft === normalizedRight;
|
|
7029
|
+
}
|
|
7030
|
+
function normalizeLoopbackHost(host) {
|
|
7031
|
+
const normalized = host.toLowerCase().replace(/^\[(.*)\]$/u, "$1");
|
|
7032
|
+
if (normalized === "localhost" || normalized === "::1") return "loopback";
|
|
7033
|
+
if (normalized === "0.0.0.0" || normalized === "::") return "loopback";
|
|
7034
|
+
if (/^127(?:\.\d{1,3}){3}$/u.test(normalized)) return "loopback";
|
|
7035
|
+
}
|
|
7036
|
+
function effectivePort(url) {
|
|
7037
|
+
if (url.port) return url.port;
|
|
7038
|
+
return url.protocol === "https:" ? "443" : "80";
|
|
7039
|
+
}
|
|
7040
|
+
function normalizePath(path) {
|
|
7041
|
+
return path === "/" ? "/" : path.replace(/\/+$/u, "");
|
|
7042
|
+
}
|
|
7043
|
+
function formatHost$3(host) {
|
|
7044
|
+
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
7045
|
+
}
|
|
6978
7046
|
function nonEmpty$2(value, label) {
|
|
6979
7047
|
if (value === void 0) return;
|
|
6980
7048
|
const trimmed = value.trim();
|
|
@@ -10481,291 +10549,6 @@ var logger = (fn = console.log) => {
|
|
|
10481
10549
|
};
|
|
10482
10550
|
};
|
|
10483
10551
|
//#endregion
|
|
10484
|
-
//#region src/attach/api.ts
|
|
10485
|
-
async function buildAttachProjection(engine) {
|
|
10486
|
-
const snapshot = await engine.exposureSnapshot();
|
|
10487
|
-
const partial = sortAttachProjectionInput({
|
|
10488
|
-
caplets: snapshot.progressiveCaplets.map(progressiveCapletExport),
|
|
10489
|
-
tools: snapshot.directTools.map(toolExport),
|
|
10490
|
-
resources: snapshot.directResources.map(resourceExport),
|
|
10491
|
-
resourceTemplates: snapshot.directResourceTemplates.map(resourceTemplateExport),
|
|
10492
|
-
prompts: snapshot.directPrompts.map(promptExport),
|
|
10493
|
-
completions: completionExports(snapshot),
|
|
10494
|
-
codeModeCaplets: snapshot.codeModeCaplets.map(codeModeCapletExport),
|
|
10495
|
-
diagnostics: snapshot.hiddenCaplets.map((hidden) => ({
|
|
10496
|
-
code: `ATTACH_CAPLET_${hidden.reason.toUpperCase()}`,
|
|
10497
|
-
message: `Caplet ${hidden.capletId} is not exported: ${hidden.reason}.`,
|
|
10498
|
-
capletId: hidden.capletId,
|
|
10499
|
-
...hidden.error ? { details: hidden.error } : {}
|
|
10500
|
-
}))
|
|
10501
|
-
});
|
|
10502
|
-
const revision = revisionFor(partial);
|
|
10503
|
-
const manifest = {
|
|
10504
|
-
version: 1,
|
|
10505
|
-
revision,
|
|
10506
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10507
|
-
...withRevisionExportIds(revision, partial)
|
|
10508
|
-
};
|
|
10509
|
-
return {
|
|
10510
|
-
manifest,
|
|
10511
|
-
routes: routesFor(manifest)
|
|
10512
|
-
};
|
|
10513
|
-
}
|
|
10514
|
-
async function invokeAttachExport(engine, projection, request) {
|
|
10515
|
-
if (request.revision !== projection.manifest.revision) throw new CapletsError("ATTACH_MANIFEST_STALE", "Attach manifest revision is stale.");
|
|
10516
|
-
const route = projection.routes.get(request.exportId);
|
|
10517
|
-
if (!route || route.kind !== request.kind) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach export was not found.");
|
|
10518
|
-
if (route.kind === "caplet") return await engine.execute(route.capletId, request.input);
|
|
10519
|
-
if (route.kind === "tool") return await engine.executeDirectTool(route.capletId, route.downstreamName, isRecord$3(request.input) ? request.input : {});
|
|
10520
|
-
if (route.kind === "resource") return await engine.readDirectResource(route.capletId, route.downstreamUri);
|
|
10521
|
-
if (route.kind === "resourceTemplate") {
|
|
10522
|
-
const uri = isRecord$3(request.input) && typeof request.input.uri === "string" ? request.input.uri : void 0;
|
|
10523
|
-
if (!uri) throw new CapletsError("REQUEST_INVALID", "Attach resource template invoke requires input.uri.");
|
|
10524
|
-
const downstreamUri = downstreamResourceUri(route.capletId, uri);
|
|
10525
|
-
if (!directResourceUriMatchesTemplate(downstreamUri, route.downstreamUriTemplate)) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach resource URI does not match the exported resource template.");
|
|
10526
|
-
return await engine.readDirectResource(route.capletId, downstreamUri);
|
|
10527
|
-
}
|
|
10528
|
-
if (route.kind === "prompt") return await engine.getDirectPrompt(route.capletId, route.downstreamName, isRecord$3(request.input) ? stringifyRecord(request.input) : {});
|
|
10529
|
-
if (route.kind === "completion") return await engine.execute(route.capletId, {
|
|
10530
|
-
...normalizeCompletionInput(projection.manifest, route.capletId, request.input),
|
|
10531
|
-
operation: "complete"
|
|
10532
|
-
});
|
|
10533
|
-
throw new CapletsError("REQUEST_INVALID", "Attach export kind is not invokable via /v1/attach/invoke.");
|
|
10534
|
-
}
|
|
10535
|
-
function attachErrorResponse(error) {
|
|
10536
|
-
const safe = toSafeError(error, "INTERNAL_ERROR");
|
|
10537
|
-
return {
|
|
10538
|
-
status: safe.code === "ATTACH_MANIFEST_STALE" ? 409 : safe.code === "ATTACH_EXPORT_NOT_FOUND" ? 404 : safe.code === "REQUEST_INVALID" ? 400 : 500,
|
|
10539
|
-
body: {
|
|
10540
|
-
ok: false,
|
|
10541
|
-
error: safe
|
|
10542
|
-
}
|
|
10543
|
-
};
|
|
10544
|
-
}
|
|
10545
|
-
function progressiveCapletExport(entry) {
|
|
10546
|
-
const inputSchema = generatedToolInputJsonSchemaForCaplet(entry.caplet);
|
|
10547
|
-
return {
|
|
10548
|
-
stableId: `progressive:${entry.caplet.server}`,
|
|
10549
|
-
kind: "caplet",
|
|
10550
|
-
name: entry.caplet.server,
|
|
10551
|
-
title: entry.caplet.name,
|
|
10552
|
-
description: entry.caplet.description,
|
|
10553
|
-
inputSchema,
|
|
10554
|
-
schemaHash: schemaHash(inputSchema),
|
|
10555
|
-
capletId: entry.caplet.server,
|
|
10556
|
-
shadowing: shadowingPolicy(entry.caplet)
|
|
10557
|
-
};
|
|
10558
|
-
}
|
|
10559
|
-
function codeModeCapletExport(entry) {
|
|
10560
|
-
return {
|
|
10561
|
-
stableId: `code_mode:${entry.caplet.server}`,
|
|
10562
|
-
kind: "caplet",
|
|
10563
|
-
name: entry.caplet.name,
|
|
10564
|
-
title: entry.caplet.name,
|
|
10565
|
-
description: entry.caplet.description,
|
|
10566
|
-
schemaHash: null,
|
|
10567
|
-
capletId: entry.caplet.server,
|
|
10568
|
-
shadowing: shadowingPolicy(entry.caplet)
|
|
10569
|
-
};
|
|
10570
|
-
}
|
|
10571
|
-
function toolExport(entry) {
|
|
10572
|
-
return {
|
|
10573
|
-
stableId: `tool:${entry.caplet.server}:${entry.downstreamName}`,
|
|
10574
|
-
kind: "tool",
|
|
10575
|
-
name: entry.name,
|
|
10576
|
-
downstreamName: entry.downstreamName,
|
|
10577
|
-
title: entry.tool.name,
|
|
10578
|
-
description: entry.tool.description,
|
|
10579
|
-
inputSchema: entry.tool.inputSchema,
|
|
10580
|
-
outputSchema: entry.tool.outputSchema,
|
|
10581
|
-
annotations: entry.tool.annotations,
|
|
10582
|
-
schemaHash: schemaHash({
|
|
10583
|
-
input: entry.tool.inputSchema,
|
|
10584
|
-
output: entry.tool.outputSchema
|
|
10585
|
-
}),
|
|
10586
|
-
capletId: entry.caplet.server,
|
|
10587
|
-
shadowing: shadowingPolicy(entry.caplet)
|
|
10588
|
-
};
|
|
10589
|
-
}
|
|
10590
|
-
function resourceExport(entry) {
|
|
10591
|
-
return {
|
|
10592
|
-
stableId: `resource:${entry.caplet.server}:${entry.downstreamUri}`,
|
|
10593
|
-
kind: "resource",
|
|
10594
|
-
uri: entry.uri,
|
|
10595
|
-
downstreamUri: entry.downstreamUri,
|
|
10596
|
-
title: entry.resource.name,
|
|
10597
|
-
description: entry.resource.description,
|
|
10598
|
-
...entry.resource.mimeType ? { mimeType: entry.resource.mimeType } : {},
|
|
10599
|
-
...typeof entry.resource.size === "number" ? { size: entry.resource.size } : {},
|
|
10600
|
-
schemaHash: null,
|
|
10601
|
-
capletId: entry.caplet.server,
|
|
10602
|
-
shadowing: shadowingPolicy(entry.caplet)
|
|
10603
|
-
};
|
|
10604
|
-
}
|
|
10605
|
-
function resourceTemplateExport(entry) {
|
|
10606
|
-
return {
|
|
10607
|
-
stableId: `resourceTemplate:${entry.caplet.server}:${entry.downstreamUriTemplate}`,
|
|
10608
|
-
kind: "resourceTemplate",
|
|
10609
|
-
uriTemplate: entry.uriTemplate,
|
|
10610
|
-
downstreamUriTemplate: entry.downstreamUriTemplate,
|
|
10611
|
-
title: entry.resourceTemplate.name,
|
|
10612
|
-
description: entry.resourceTemplate.description,
|
|
10613
|
-
...entry.resourceTemplate.mimeType ? { mimeType: entry.resourceTemplate.mimeType } : {},
|
|
10614
|
-
schemaHash: null,
|
|
10615
|
-
capletId: entry.caplet.server,
|
|
10616
|
-
shadowing: shadowingPolicy(entry.caplet)
|
|
10617
|
-
};
|
|
10618
|
-
}
|
|
10619
|
-
function promptExport(entry) {
|
|
10620
|
-
const inputSchema = { arguments: entry.prompt.arguments ?? [] };
|
|
10621
|
-
return {
|
|
10622
|
-
stableId: `prompt:${entry.caplet.server}:${entry.downstreamName}`,
|
|
10623
|
-
kind: "prompt",
|
|
10624
|
-
name: entry.name,
|
|
10625
|
-
downstreamName: entry.downstreamName,
|
|
10626
|
-
title: entry.prompt.name,
|
|
10627
|
-
description: entry.prompt.description,
|
|
10628
|
-
inputSchema,
|
|
10629
|
-
schemaHash: schemaHash(inputSchema),
|
|
10630
|
-
capletId: entry.caplet.server,
|
|
10631
|
-
shadowing: shadowingPolicy(entry.caplet)
|
|
10632
|
-
};
|
|
10633
|
-
}
|
|
10634
|
-
function completionExports(snapshot) {
|
|
10635
|
-
return [...new Map([...snapshot.directPrompts, ...snapshot.directResourceTemplates].map((entry) => [entry.caplet.server, entry.caplet])).entries()].sort(([left], [right]) => left.localeCompare(right)).map(([capletId, caplet]) => ({
|
|
10636
|
-
stableId: `completion:${capletId}`,
|
|
10637
|
-
kind: "completion",
|
|
10638
|
-
name: `${capletId}:complete`,
|
|
10639
|
-
title: "Complete",
|
|
10640
|
-
description: `MCP completion for ${capletId}.`,
|
|
10641
|
-
schemaHash: null,
|
|
10642
|
-
capletId,
|
|
10643
|
-
shadowing: shadowingPolicy(caplet)
|
|
10644
|
-
}));
|
|
10645
|
-
}
|
|
10646
|
-
function shadowingPolicy(caplet) {
|
|
10647
|
-
return caplet.shadowing ?? "forbid";
|
|
10648
|
-
}
|
|
10649
|
-
function sortAttachProjectionInput(partial) {
|
|
10650
|
-
return {
|
|
10651
|
-
caplets: sortByStableId(partial.caplets),
|
|
10652
|
-
tools: sortByStableId(partial.tools),
|
|
10653
|
-
resources: sortByStableId(partial.resources),
|
|
10654
|
-
resourceTemplates: sortByStableId(partial.resourceTemplates),
|
|
10655
|
-
prompts: sortByStableId(partial.prompts),
|
|
10656
|
-
completions: sortByStableId(partial.completions),
|
|
10657
|
-
codeModeCaplets: sortByStableId(partial.codeModeCaplets),
|
|
10658
|
-
diagnostics: [...partial.diagnostics].sort((left, right) => diagnosticSortKey(left).localeCompare(diagnosticSortKey(right)))
|
|
10659
|
-
};
|
|
10660
|
-
}
|
|
10661
|
-
function sortByStableId(entries) {
|
|
10662
|
-
return [...entries].sort((left, right) => left.stableId.localeCompare(right.stableId));
|
|
10663
|
-
}
|
|
10664
|
-
function diagnosticSortKey(diagnostic) {
|
|
10665
|
-
return stableJsonStringify({
|
|
10666
|
-
code: diagnostic.code,
|
|
10667
|
-
capletId: diagnostic.capletId ?? "",
|
|
10668
|
-
message: diagnostic.message
|
|
10669
|
-
});
|
|
10670
|
-
}
|
|
10671
|
-
function revisionFor(value) {
|
|
10672
|
-
return `sha256:${createHash("sha256").update(stableJsonStringify(value)).digest("hex")}`;
|
|
10673
|
-
}
|
|
10674
|
-
function withRevisionExportIds(revision, partial) {
|
|
10675
|
-
return {
|
|
10676
|
-
...partial,
|
|
10677
|
-
caplets: partial.caplets.map((entry) => withExportId(revision, entry)),
|
|
10678
|
-
tools: partial.tools.map((entry) => withExportId(revision, entry)),
|
|
10679
|
-
resources: partial.resources.map((entry) => withExportId(revision, entry)),
|
|
10680
|
-
resourceTemplates: partial.resourceTemplates.map((entry) => withExportId(revision, entry)),
|
|
10681
|
-
prompts: partial.prompts.map((entry) => withExportId(revision, entry)),
|
|
10682
|
-
completions: partial.completions.map((entry) => withExportId(revision, entry)),
|
|
10683
|
-
codeModeCaplets: partial.codeModeCaplets.map((entry) => withExportId(revision, entry))
|
|
10684
|
-
};
|
|
10685
|
-
}
|
|
10686
|
-
function withExportId(revision, entry) {
|
|
10687
|
-
return {
|
|
10688
|
-
...entry,
|
|
10689
|
-
exportId: `${revision}:${entry.stableId}`
|
|
10690
|
-
};
|
|
10691
|
-
}
|
|
10692
|
-
function routesFor(manifest) {
|
|
10693
|
-
const routes = /* @__PURE__ */ new Map();
|
|
10694
|
-
for (const entry of manifest.caplets) routes.set(entry.exportId, {
|
|
10695
|
-
kind: "caplet",
|
|
10696
|
-
capletId: entry.capletId
|
|
10697
|
-
});
|
|
10698
|
-
for (const entry of manifest.tools) routes.set(entry.exportId, {
|
|
10699
|
-
kind: "tool",
|
|
10700
|
-
capletId: entry.capletId,
|
|
10701
|
-
downstreamName: entry.downstreamName
|
|
10702
|
-
});
|
|
10703
|
-
for (const entry of manifest.resources) routes.set(entry.exportId, {
|
|
10704
|
-
kind: "resource",
|
|
10705
|
-
capletId: entry.capletId,
|
|
10706
|
-
downstreamUri: entry.downstreamUri
|
|
10707
|
-
});
|
|
10708
|
-
for (const entry of manifest.resourceTemplates) routes.set(entry.exportId, {
|
|
10709
|
-
kind: "resourceTemplate",
|
|
10710
|
-
capletId: entry.capletId,
|
|
10711
|
-
downstreamUriTemplate: entry.downstreamUriTemplate
|
|
10712
|
-
});
|
|
10713
|
-
for (const entry of manifest.prompts) routes.set(entry.exportId, {
|
|
10714
|
-
kind: "prompt",
|
|
10715
|
-
capletId: entry.capletId,
|
|
10716
|
-
downstreamName: entry.downstreamName
|
|
10717
|
-
});
|
|
10718
|
-
for (const entry of manifest.completions) routes.set(entry.exportId, {
|
|
10719
|
-
kind: "completion",
|
|
10720
|
-
capletId: entry.capletId
|
|
10721
|
-
});
|
|
10722
|
-
for (const entry of manifest.codeModeCaplets) routes.set(entry.exportId, {
|
|
10723
|
-
kind: "caplet",
|
|
10724
|
-
capletId: entry.capletId
|
|
10725
|
-
});
|
|
10726
|
-
return routes;
|
|
10727
|
-
}
|
|
10728
|
-
function normalizeCompletionInput(manifest, capletId, input) {
|
|
10729
|
-
if (!isRecord$3(input)) return {};
|
|
10730
|
-
const ref = input.ref;
|
|
10731
|
-
if (!isRecord$3(ref)) return input;
|
|
10732
|
-
if (ref.type === "prompt" && typeof ref.name === "string") {
|
|
10733
|
-
const prompt = manifest.prompts.find((entry) => entry.capletId === capletId && (entry.name === ref.name || entry.downstreamName === ref.name));
|
|
10734
|
-
if (!prompt) return input;
|
|
10735
|
-
return {
|
|
10736
|
-
...input,
|
|
10737
|
-
ref: {
|
|
10738
|
-
...ref,
|
|
10739
|
-
name: prompt.downstreamName
|
|
10740
|
-
}
|
|
10741
|
-
};
|
|
10742
|
-
}
|
|
10743
|
-
if (ref.type === "resourceTemplate" && typeof ref.uri === "string") {
|
|
10744
|
-
const resourceTemplate = manifest.resourceTemplates.find((entry) => entry.capletId === capletId && (entry.uriTemplate === ref.uri || entry.downstreamUriTemplate === ref.uri));
|
|
10745
|
-
if (!resourceTemplate) return input;
|
|
10746
|
-
return {
|
|
10747
|
-
...input,
|
|
10748
|
-
ref: {
|
|
10749
|
-
...ref,
|
|
10750
|
-
uri: resourceTemplate.downstreamUriTemplate
|
|
10751
|
-
}
|
|
10752
|
-
};
|
|
10753
|
-
}
|
|
10754
|
-
return input;
|
|
10755
|
-
}
|
|
10756
|
-
function isRecord$3(value) {
|
|
10757
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10758
|
-
}
|
|
10759
|
-
function stringifyRecord(record) {
|
|
10760
|
-
return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)]));
|
|
10761
|
-
}
|
|
10762
|
-
function downstreamResourceUri(capletId, uri) {
|
|
10763
|
-
if (!uri.startsWith("caplets://")) return uri;
|
|
10764
|
-
const decoded = decodeDirectResourceUri(uri);
|
|
10765
|
-
if (decoded.capletId !== capletId) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach resource template URI belongs to a different Caplet.");
|
|
10766
|
-
return decoded.downstreamUri;
|
|
10767
|
-
}
|
|
10768
|
-
//#endregion
|
|
10769
10552
|
//#region src/cli/install.ts
|
|
10770
10553
|
function installCaplets(repo, options = {}) {
|
|
10771
10554
|
const source = resolveInstallSource(repo);
|
|
@@ -12109,14 +11892,24 @@ function parseSupersededRefreshTokens(value) {
|
|
|
12109
11892
|
}
|
|
12110
11893
|
//#endregion
|
|
12111
11894
|
//#region src/serve/http.ts
|
|
11895
|
+
const CAPLETS_STACK_CHAIN_HEADER = "caplets-stack-chain";
|
|
11896
|
+
const ATTACH_SESSION_IDLE_TIMEOUT_MS = 10 * 6e4;
|
|
11897
|
+
const ATTACH_SESSION_PRUNE_INTERVAL_MS = 6e4;
|
|
12112
11898
|
function createHttpServeApp(options, engine, io = {}) {
|
|
12113
11899
|
const app = new Hono();
|
|
12114
11900
|
const sessions = /* @__PURE__ */ new Map();
|
|
11901
|
+
const attachSessions = /* @__PURE__ */ new Map();
|
|
11902
|
+
const defaultAttachSessions = /* @__PURE__ */ new Map();
|
|
11903
|
+
const defaultAttachSessionPromises = /* @__PURE__ */ new Map();
|
|
12115
11904
|
const attachEventStreams = /* @__PURE__ */ new Set();
|
|
11905
|
+
const attachSessionPruneTimer = setInterval(() => pruneIdleAttachSessions(), ATTACH_SESSION_PRUNE_INTERVAL_MS);
|
|
11906
|
+
attachSessionPruneTimer.unref?.();
|
|
12116
11907
|
const writeErr = io.writeErr ?? process.stderr.write.bind(process.stderr);
|
|
12117
11908
|
const paths = servicePaths(options.path);
|
|
11909
|
+
const stackIdentity = httpStackIdentity(options);
|
|
12118
11910
|
const authFlowStore = io.authFlowStore ?? new RemoteAuthFlowStore();
|
|
12119
11911
|
const exposeAttach = io.exposeAttach ?? true;
|
|
11912
|
+
const exposeAttachSessions = exposeAttach && Boolean(io.attachSessionFactory);
|
|
12120
11913
|
const remoteCredentialStore = remoteCredentialStoreForOptions(options, io.remoteCredentialStore);
|
|
12121
11914
|
if (options.auth.type === "remote_credentials" && options.trustProxy === true && options.publicOrigin === void 0) throw new CapletsError("REQUEST_INVALID", "Remote credential auth with --trust-proxy requires CAPLETS_SERVER_URL.");
|
|
12122
11915
|
const protectedRouteAuth = routeAuth(options, remoteCredentialStore, paths.base);
|
|
@@ -12130,14 +11923,20 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12130
11923
|
name: "caplets",
|
|
12131
11924
|
transport: "http",
|
|
12132
11925
|
base: paths.base,
|
|
12133
|
-
versions: [versionDiscovery(paths,
|
|
11926
|
+
versions: [versionDiscovery(paths, {
|
|
11927
|
+
exposeAttach,
|
|
11928
|
+
exposeAttachSessions
|
|
11929
|
+
}, remote)],
|
|
12134
11930
|
auth: { type: options.auth.type },
|
|
12135
11931
|
...remote ? { remote } : {}
|
|
12136
11932
|
});
|
|
12137
11933
|
});
|
|
12138
11934
|
app.get(paths.version, (c) => {
|
|
12139
11935
|
const remote = remoteCredentialStore ? remoteHostMetadata(c.req.url, paths.base, options, (name) => c.req.header(name)) : void 0;
|
|
12140
|
-
return c.json(versionDiscovery(paths,
|
|
11936
|
+
return c.json(versionDiscovery(paths, {
|
|
11937
|
+
exposeAttach,
|
|
11938
|
+
exposeAttachSessions
|
|
11939
|
+
}, remote));
|
|
12141
11940
|
});
|
|
12142
11941
|
app.get(paths.health, (c) => c.json({ status: "ok" }));
|
|
12143
11942
|
if (remoteCredentialStore) {
|
|
@@ -12272,14 +12071,68 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12272
12071
|
return session.transport.handleRequest(c);
|
|
12273
12072
|
});
|
|
12274
12073
|
if (exposeAttach) {
|
|
12074
|
+
if (io.attachSessionFactory) {
|
|
12075
|
+
app.post(paths.attachSessions, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12076
|
+
try {
|
|
12077
|
+
const metadata = parseAttachSessionMetadata(await parseJsonObject(c.req.json(), "Attach session request"), { allowProjectContext: allowAttachSessionProjectContext(options, c.req.url, (name) => c.req.header(name)) });
|
|
12078
|
+
const context = attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER));
|
|
12079
|
+
const sessionId = randomUUID();
|
|
12080
|
+
const session = await io.attachSessionFactory(metadata, context);
|
|
12081
|
+
attachSessions.set(sessionId, {
|
|
12082
|
+
session,
|
|
12083
|
+
lastUsedAt: Date.now()
|
|
12084
|
+
});
|
|
12085
|
+
pruneIdleAttachSessions();
|
|
12086
|
+
return c.json({ sessionId }, 201);
|
|
12087
|
+
} catch (error) {
|
|
12088
|
+
const response = attachErrorResponse(error);
|
|
12089
|
+
return c.json(response.body, response.status);
|
|
12090
|
+
}
|
|
12091
|
+
});
|
|
12092
|
+
app.delete(routePath(paths.attachSessions, ":sessionId"), attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12093
|
+
const sessionId = c.req.param("sessionId");
|
|
12094
|
+
if (!sessionId) return c.json({
|
|
12095
|
+
ok: false,
|
|
12096
|
+
error: { code: "REQUEST_INVALID" }
|
|
12097
|
+
}, 400);
|
|
12098
|
+
const record = attachSessions.get(sessionId);
|
|
12099
|
+
attachSessions.delete(sessionId);
|
|
12100
|
+
await record?.session.close();
|
|
12101
|
+
return c.json({ ok: true });
|
|
12102
|
+
});
|
|
12103
|
+
}
|
|
12275
12104
|
app.get(paths.attachManifest, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12276
|
-
|
|
12277
|
-
|
|
12105
|
+
try {
|
|
12106
|
+
const attachSessionId = c.req.header(CAPLETS_ATTACH_SESSION_HEADER);
|
|
12107
|
+
const attachSession = attachSessionId ? attachSessionForRequest(attachSessionId) : await fallbackAttachSession(attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER)));
|
|
12108
|
+
if (attachSession) return c.json(await attachSession.manifest());
|
|
12109
|
+
const attachProjection = await buildAttachProjection(engine);
|
|
12110
|
+
return c.json(attachProjection.manifest);
|
|
12111
|
+
} catch (error) {
|
|
12112
|
+
const response = attachErrorResponse(error);
|
|
12113
|
+
return c.json(response.body, response.status);
|
|
12114
|
+
}
|
|
12115
|
+
});
|
|
12116
|
+
app.get(paths.attachEvents, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12117
|
+
try {
|
|
12118
|
+
const attachSessionId = c.req.header(CAPLETS_ATTACH_SESSION_HEADER);
|
|
12119
|
+
return attachEventsResponse(attachEventSource(engine, attachSessionId ? attachSessionForRequest(attachSessionId) : await fallbackAttachSession(attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER)))), attachEventStreams, { onActivity: () => {
|
|
12120
|
+
if (attachSessionId) touchAttachSession(attachSessionId);
|
|
12121
|
+
} });
|
|
12122
|
+
} catch (error) {
|
|
12123
|
+
const response = attachErrorResponse(error);
|
|
12124
|
+
return c.json(response.body, response.status);
|
|
12125
|
+
}
|
|
12278
12126
|
});
|
|
12279
|
-
app.get(paths.attachEvents, attachHostProtection, protectedRouteAuth, () => attachEventsResponse(engine, attachEventStreams));
|
|
12280
12127
|
app.post(paths.attachInvoke, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12281
12128
|
try {
|
|
12282
12129
|
const request = await parseAttachInvokeRequest(c.req.json());
|
|
12130
|
+
const attachSessionId = c.req.header(CAPLETS_ATTACH_SESSION_HEADER);
|
|
12131
|
+
const attachSession = attachSessionId ? attachSessionForRequest(attachSessionId) : await fallbackAttachSession(attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER)));
|
|
12132
|
+
if (attachSession) return c.json({
|
|
12133
|
+
ok: true,
|
|
12134
|
+
data: await attachSession.invoke(request)
|
|
12135
|
+
});
|
|
12283
12136
|
const result = await invokeAttachExport(engine, await buildAttachProjection(engine), request);
|
|
12284
12137
|
return c.json({
|
|
12285
12138
|
ok: true,
|
|
@@ -12291,6 +12144,52 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12291
12144
|
}
|
|
12292
12145
|
});
|
|
12293
12146
|
}
|
|
12147
|
+
function attachSessionForRequest(sessionId) {
|
|
12148
|
+
pruneIdleAttachSessions();
|
|
12149
|
+
if (!sessionId) return void 0;
|
|
12150
|
+
const record = attachSessions.get(sessionId);
|
|
12151
|
+
if (!record) throw new CapletsError("REQUEST_INVALID", "Attach session was not found.");
|
|
12152
|
+
record.lastUsedAt = Date.now();
|
|
12153
|
+
return record.session;
|
|
12154
|
+
}
|
|
12155
|
+
async function fallbackAttachSession(context) {
|
|
12156
|
+
if (!io.defaultAttachSessionFactory) return void 0;
|
|
12157
|
+
const key = context.stackChain.join("\0");
|
|
12158
|
+
const existing = defaultAttachSessions.get(key);
|
|
12159
|
+
if (existing) return existing;
|
|
12160
|
+
let pending = defaultAttachSessionPromises.get(key);
|
|
12161
|
+
if (!pending) {
|
|
12162
|
+
pending = Promise.resolve(io.defaultAttachSessionFactory({}, context)).then((session) => {
|
|
12163
|
+
defaultAttachSessions.set(key, session);
|
|
12164
|
+
defaultAttachSessionPromises.delete(key);
|
|
12165
|
+
return session;
|
|
12166
|
+
}, (error) => {
|
|
12167
|
+
defaultAttachSessionPromises.delete(key);
|
|
12168
|
+
throw error;
|
|
12169
|
+
});
|
|
12170
|
+
defaultAttachSessionPromises.set(key, pending);
|
|
12171
|
+
}
|
|
12172
|
+
return await pending;
|
|
12173
|
+
}
|
|
12174
|
+
function attachSessionContext(header) {
|
|
12175
|
+
const incoming = stackChainFromHeader(header);
|
|
12176
|
+
if (incoming.includes(stackIdentity)) throw new CapletsError("REQUEST_INVALID", "Stacked runtime upstream cycle detected.");
|
|
12177
|
+
return { stackChain: [...incoming, stackIdentity] };
|
|
12178
|
+
}
|
|
12179
|
+
function touchAttachSession(sessionId) {
|
|
12180
|
+
const record = attachSessions.get(sessionId);
|
|
12181
|
+
if (record) record.lastUsedAt = Date.now();
|
|
12182
|
+
}
|
|
12183
|
+
function pruneIdleAttachSessions() {
|
|
12184
|
+
const expiresBefore = Date.now() - ATTACH_SESSION_IDLE_TIMEOUT_MS;
|
|
12185
|
+
for (const [sessionId, record] of attachSessions) {
|
|
12186
|
+
if (record.lastUsedAt >= expiresBefore) continue;
|
|
12187
|
+
attachSessions.delete(sessionId);
|
|
12188
|
+
record.session.close().catch((error) => {
|
|
12189
|
+
writeErr(`Could not close idle attach session: ${errorMessage$1(error)}\n`);
|
|
12190
|
+
});
|
|
12191
|
+
}
|
|
12192
|
+
}
|
|
12294
12193
|
app.post(paths.control, protectedRouteAuth, async (c) => {
|
|
12295
12194
|
let request;
|
|
12296
12195
|
try {
|
|
@@ -12314,31 +12213,9 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12314
12213
|
bindingId: c.req.param("bindingId"),
|
|
12315
12214
|
state: "not_attached"
|
|
12316
12215
|
}));
|
|
12317
|
-
app.post(routePath(paths.projectBindings, "sessions"), protectedRouteAuth, (c) =>
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
binding: {
|
|
12321
|
-
bindingId,
|
|
12322
|
-
state: "attaching",
|
|
12323
|
-
syncState: "pending"
|
|
12324
|
-
},
|
|
12325
|
-
sessionId: randomUUID()
|
|
12326
|
-
}, 201);
|
|
12327
|
-
});
|
|
12328
|
-
app.post(routePath(paths.projectBindings, ":bindingId/heartbeat"), protectedRouteAuth, (c) => c.json({
|
|
12329
|
-
ok: true,
|
|
12330
|
-
binding: {
|
|
12331
|
-
bindingId: c.req.param("bindingId"),
|
|
12332
|
-
state: "ready"
|
|
12333
|
-
}
|
|
12334
|
-
}));
|
|
12335
|
-
app.delete(routePath(paths.projectBindings, ":bindingId/session"), protectedRouteAuth, (c) => c.json({
|
|
12336
|
-
ok: true,
|
|
12337
|
-
binding: {
|
|
12338
|
-
bindingId: c.req.param("bindingId"),
|
|
12339
|
-
state: "ended"
|
|
12340
|
-
}
|
|
12341
|
-
}));
|
|
12216
|
+
app.post(routePath(paths.projectBindings, "sessions"), protectedRouteAuth, (c) => c.json(projectBindingUnsupported(), 501));
|
|
12217
|
+
app.post(routePath(paths.projectBindings, ":bindingId/heartbeat"), protectedRouteAuth, (c) => c.json(projectBindingUnsupported(c.req.param("bindingId")), 501));
|
|
12218
|
+
app.delete(routePath(paths.projectBindings, ":bindingId/session"), protectedRouteAuth, (c) => c.json(projectBindingUnsupported(c.req.param("bindingId")), 501));
|
|
12342
12219
|
app.get(routePath(paths.control, "auth/callback/:flowId"), async (c) => {
|
|
12343
12220
|
const flowId = c.req.param("flowId");
|
|
12344
12221
|
const result = await dispatchRemoteCliRequest({
|
|
@@ -12353,11 +12230,17 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12353
12230
|
});
|
|
12354
12231
|
app.notFound((c) => c.json({ error: "not_found" }, 404));
|
|
12355
12232
|
app.closeCapletsSessions = async () => {
|
|
12233
|
+
clearInterval(attachSessionPruneTimer);
|
|
12356
12234
|
for (const stream of attachEventStreams) stream.close();
|
|
12357
12235
|
await Promise.allSettled([...sessions.values()].map(async (session) => {
|
|
12358
12236
|
await session.server.close();
|
|
12359
12237
|
}));
|
|
12360
12238
|
sessions.clear();
|
|
12239
|
+
await Promise.allSettled([...attachSessions.values()].map((record) => record.session.close()));
|
|
12240
|
+
attachSessions.clear();
|
|
12241
|
+
await Promise.allSettled([...defaultAttachSessions.values()].map((session) => session.close()));
|
|
12242
|
+
defaultAttachSessions.clear();
|
|
12243
|
+
defaultAttachSessionPromises.clear();
|
|
12361
12244
|
};
|
|
12362
12245
|
if (options.warnUnauthenticatedNetwork) writeErr(`Warning: Caplets MCP HTTP server is listening on ${options.host} without authentication.\n`);
|
|
12363
12246
|
return app;
|
|
@@ -12390,9 +12273,36 @@ function remoteCredentialSourceHint(trustProxy, header) {
|
|
|
12390
12273
|
const sourceHint = firstForwardedValue(header("x-forwarded-for")) ?? firstForwardedValue(header("x-real-ip")) ?? firstForwardedValue(header("cf-connecting-ip"));
|
|
12391
12274
|
return sourceHint ? { sourceHint } : {};
|
|
12392
12275
|
}
|
|
12276
|
+
function projectBindingUnsupported(bindingId) {
|
|
12277
|
+
return {
|
|
12278
|
+
ok: false,
|
|
12279
|
+
error: {
|
|
12280
|
+
code: "UNSUPPORTED_CAPABILITY",
|
|
12281
|
+
message: "Self-hosted Project Binding sessions are not implemented by this runtime."
|
|
12282
|
+
},
|
|
12283
|
+
...bindingId ? { binding: {
|
|
12284
|
+
bindingId,
|
|
12285
|
+
state: "not_attached"
|
|
12286
|
+
} } : {}
|
|
12287
|
+
};
|
|
12288
|
+
}
|
|
12393
12289
|
function firstForwardedValue(value) {
|
|
12394
12290
|
return value?.split(",", 1)[0]?.trim() || void 0;
|
|
12395
12291
|
}
|
|
12292
|
+
function errorMessage$1(error) {
|
|
12293
|
+
return error instanceof Error ? error.message : String(error);
|
|
12294
|
+
}
|
|
12295
|
+
function httpStackIdentity(options) {
|
|
12296
|
+
const origin = options.publicOrigin ?? `http://${formatHost$2(options.host)}:${options.port}`;
|
|
12297
|
+
const url = new URL(origin);
|
|
12298
|
+
url.pathname = options.path;
|
|
12299
|
+
url.search = "";
|
|
12300
|
+
url.hash = "";
|
|
12301
|
+
return url.toString();
|
|
12302
|
+
}
|
|
12303
|
+
function stackChainFromHeader(header) {
|
|
12304
|
+
return (header ?? "").split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
12305
|
+
}
|
|
12396
12306
|
function remoteHostMetadata(requestUrl, basePath, options, header) {
|
|
12397
12307
|
const audience = remoteCredentialHostUrl(requestUrl, basePath, options.publicOrigin, options.trustProxy, header);
|
|
12398
12308
|
return {
|
|
@@ -12400,7 +12310,9 @@ function remoteHostMetadata(requestUrl, basePath, options, header) {
|
|
|
12400
12310
|
audience
|
|
12401
12311
|
};
|
|
12402
12312
|
}
|
|
12403
|
-
function versionDiscovery(paths,
|
|
12313
|
+
function versionDiscovery(paths, options = {}, remote) {
|
|
12314
|
+
const exposeAttach = options.exposeAttach ?? true;
|
|
12315
|
+
const exposeAttachSessions = options.exposeAttachSessions ?? false;
|
|
12404
12316
|
return {
|
|
12405
12317
|
version: 1,
|
|
12406
12318
|
path: paths.version,
|
|
@@ -12409,6 +12321,7 @@ function versionDiscovery(paths, exposeAttach = true, remote) {
|
|
|
12409
12321
|
mcp: paths.mcp,
|
|
12410
12322
|
admin: paths.control,
|
|
12411
12323
|
...exposeAttach ? {
|
|
12324
|
+
...exposeAttachSessions ? { attachSessions: paths.attachSessions } : {},
|
|
12412
12325
|
attachManifest: paths.attachManifest,
|
|
12413
12326
|
attachEvents: paths.attachEvents,
|
|
12414
12327
|
attachInvoke: paths.attachInvoke
|
|
@@ -12438,10 +12351,82 @@ async function parseAttachInvokeRequest(input) {
|
|
|
12438
12351
|
function isAttachExportKind(value) {
|
|
12439
12352
|
return value === "caplet" || value === "tool" || value === "resource" || value === "resourceTemplate" || value === "prompt" || value === "completion";
|
|
12440
12353
|
}
|
|
12441
|
-
function
|
|
12354
|
+
function parseAttachSessionMetadata(input, options) {
|
|
12355
|
+
const rawProjectRoot = optionalStringField(input, "projectRoot");
|
|
12356
|
+
const rawProjectConfigPath = optionalStringField(input, "projectConfigPath");
|
|
12357
|
+
if (!options.allowProjectContext && (rawProjectRoot || rawProjectConfigPath)) throw new CapletsError("REQUEST_INVALID", "Attach session project context is only accepted by loopback runtimes.");
|
|
12358
|
+
const projectRoot = canonicalProjectRoot(rawProjectRoot);
|
|
12359
|
+
const projectConfigPath = canonicalProjectConfigPath(rawProjectConfigPath, projectRoot);
|
|
12360
|
+
return {
|
|
12361
|
+
...projectRoot ? { projectRoot } : {},
|
|
12362
|
+
...projectConfigPath ? { projectConfigPath } : {}
|
|
12363
|
+
};
|
|
12364
|
+
}
|
|
12365
|
+
function canonicalProjectRoot(projectRoot) {
|
|
12366
|
+
if (!projectRoot) return void 0;
|
|
12367
|
+
try {
|
|
12368
|
+
const canonical = realpathSync(projectRoot);
|
|
12369
|
+
if (!statSync(canonical).isDirectory()) throw new Error("projectRoot is not a directory.");
|
|
12370
|
+
return canonical;
|
|
12371
|
+
} catch (error) {
|
|
12372
|
+
throw new CapletsError("REQUEST_INVALID", "projectRoot must be an existing directory.", error);
|
|
12373
|
+
}
|
|
12374
|
+
}
|
|
12375
|
+
function canonicalProjectConfigPath(projectConfigPath, projectRoot) {
|
|
12376
|
+
if (!projectRoot) {
|
|
12377
|
+
if (!projectConfigPath) return void 0;
|
|
12378
|
+
throw new CapletsError("REQUEST_INVALID", "projectConfigPath requires projectRoot.");
|
|
12379
|
+
}
|
|
12380
|
+
const expectedProjectConfigPath = resolve(projectRoot, ".caplets", "config.json");
|
|
12381
|
+
const lexicalConfigPath = projectConfigPath === void 0 ? expectedProjectConfigPath : isAbsolute(projectConfigPath) ? projectConfigPath : resolve(projectRoot, projectConfigPath);
|
|
12382
|
+
if (resolve(projectConfigPath === void 0 ? expectedProjectConfigPath : canonicalizeExistingParentPath(lexicalConfigPath)) !== expectedProjectConfigPath) throw new CapletsError("REQUEST_INVALID", "projectConfigPath must be <projectRoot>/.caplets/config.json.");
|
|
12383
|
+
if (!existsSync(expectedProjectConfigPath)) return expectedProjectConfigPath;
|
|
12384
|
+
const expected = realpathSync(expectedProjectConfigPath);
|
|
12385
|
+
if (!pathIsInside(expected, projectRoot)) throw new CapletsError("REQUEST_INVALID", "projectConfigPath must resolve inside projectRoot.");
|
|
12386
|
+
return expected;
|
|
12387
|
+
}
|
|
12388
|
+
function canonicalizeExistingParentPath(path) {
|
|
12389
|
+
const parent = dirname(path);
|
|
12390
|
+
try {
|
|
12391
|
+
return resolve(realpathSync(parent), path.slice(parent.length + 1));
|
|
12392
|
+
} catch {
|
|
12393
|
+
return resolve(path);
|
|
12394
|
+
}
|
|
12395
|
+
}
|
|
12396
|
+
function pathIsInside(candidate, root) {
|
|
12397
|
+
const rel = relative(root, candidate);
|
|
12398
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
12399
|
+
}
|
|
12400
|
+
function allowAttachSessionProjectContext(options, requestUrl, header) {
|
|
12401
|
+
if (!options.loopback) return false;
|
|
12402
|
+
return isLoopbackHost(attachRequestHost(options, requestUrl, header));
|
|
12403
|
+
}
|
|
12404
|
+
function attachRequestHost(options, requestUrl, header) {
|
|
12405
|
+
const fallback = new URL(requestUrl).host;
|
|
12406
|
+
const host = (options.trustProxy ? firstForwardedValue(header("x-forwarded-host")) : void 0) ?? header("host") ?? fallback;
|
|
12407
|
+
try {
|
|
12408
|
+
return new URL(`http://${host}`).hostname;
|
|
12409
|
+
} catch {
|
|
12410
|
+
return host.split(":")[0] ?? host;
|
|
12411
|
+
}
|
|
12412
|
+
}
|
|
12413
|
+
function attachEventSource(engine, session) {
|
|
12414
|
+
if (session) return {
|
|
12415
|
+
manifestRevision: async () => (await session.manifest()).revision,
|
|
12416
|
+
onManifestChanged: (listener) => session.onManifestChanged(listener)
|
|
12417
|
+
};
|
|
12418
|
+
return {
|
|
12419
|
+
manifestRevision: async () => (await buildAttachProjection(engine)).manifest.revision,
|
|
12420
|
+
onManifestChanged: (listener) => engine.onReload(() => {
|
|
12421
|
+
listener();
|
|
12422
|
+
})
|
|
12423
|
+
};
|
|
12424
|
+
}
|
|
12425
|
+
function attachEventsResponse(source, activeStreams, options = {}) {
|
|
12442
12426
|
const encoder = new TextEncoder();
|
|
12443
12427
|
let unsubscribe = () => void 0;
|
|
12444
12428
|
let activeStream;
|
|
12429
|
+
let keepAliveTimer;
|
|
12445
12430
|
let closed = false;
|
|
12446
12431
|
const readable = new ReadableStream({
|
|
12447
12432
|
start(controller) {
|
|
@@ -12449,17 +12434,30 @@ function attachEventsResponse(engine, activeStreams) {
|
|
|
12449
12434
|
if (closed) return;
|
|
12450
12435
|
closed = true;
|
|
12451
12436
|
unsubscribe();
|
|
12437
|
+
if (keepAliveTimer) clearInterval(keepAliveTimer);
|
|
12452
12438
|
if (activeStream) activeStreams.delete(activeStream);
|
|
12453
12439
|
try {
|
|
12454
12440
|
controller.close();
|
|
12455
12441
|
} catch {}
|
|
12456
12442
|
} };
|
|
12457
12443
|
activeStreams.add(activeStream);
|
|
12444
|
+
options.onActivity?.();
|
|
12458
12445
|
controller.enqueue(encoder.encode(": connected\n\n"));
|
|
12459
|
-
|
|
12460
|
-
|
|
12446
|
+
keepAliveTimer = setInterval(() => {
|
|
12447
|
+
if (closed) return;
|
|
12448
|
+
options.onActivity?.();
|
|
12449
|
+
try {
|
|
12450
|
+
controller.enqueue(encoder.encode(": keepalive\n\n"));
|
|
12451
|
+
} catch {
|
|
12452
|
+
activeStream?.close();
|
|
12453
|
+
}
|
|
12454
|
+
}, 3e4);
|
|
12455
|
+
keepAliveTimer.unref?.();
|
|
12456
|
+
unsubscribe = source.onManifestChanged(() => {
|
|
12457
|
+
options.onActivity?.();
|
|
12458
|
+
source.manifestRevision().then((revision) => {
|
|
12461
12459
|
if (closed) return;
|
|
12462
|
-
controller.enqueue(encoder.encode(`event: manifest_changed\ndata: ${JSON.stringify({ revision
|
|
12460
|
+
controller.enqueue(encoder.encode(`event: manifest_changed\ndata: ${JSON.stringify({ revision })}\n\n`));
|
|
12463
12461
|
}).catch(() => void 0);
|
|
12464
12462
|
});
|
|
12465
12463
|
},
|
|
@@ -12489,7 +12487,7 @@ async function serveHttp(options, engineOptions = {}, writeErr = (value) => proc
|
|
|
12489
12487
|
}
|
|
12490
12488
|
});
|
|
12491
12489
|
const paths = servicePaths(options.path);
|
|
12492
|
-
const origin = `http://${formatHost$
|
|
12490
|
+
const origin = `http://${formatHost$2(options.host)}:${options.port}`;
|
|
12493
12491
|
const baseUrl = `${origin}${paths.base === "/" ? "" : paths.base}`;
|
|
12494
12492
|
installHttpSignalHandlers(serve({
|
|
12495
12493
|
fetch: app.fetch,
|
|
@@ -12504,20 +12502,26 @@ async function serveHttp(options, engineOptions = {}, writeErr = (value) => proc
|
|
|
12504
12502
|
writeErr(`Auth: ${authDescription(options)}\n`);
|
|
12505
12503
|
}), app, engine, writeErr);
|
|
12506
12504
|
}
|
|
12507
|
-
async function serveHttpWithSessionFactory(options, createSession, writeErr = (value) => process.stderr.write(value)) {
|
|
12508
|
-
const resolvedEngineOptions = {
|
|
12505
|
+
async function serveHttpWithSessionFactory(options, createSession, writeErr = (value) => process.stderr.write(value), io = {}, engineOptions = {}) {
|
|
12506
|
+
const resolvedEngineOptions = {
|
|
12507
|
+
exposeLocalArtifactPaths: false,
|
|
12508
|
+
vaultRecoveryTarget: "remote",
|
|
12509
|
+
...engineOptions
|
|
12510
|
+
};
|
|
12509
12511
|
const engine = new CapletsEngine(resolvedEngineOptions);
|
|
12510
12512
|
const app = createHttpServeApp(options, engine, {
|
|
12511
12513
|
writeErr,
|
|
12512
|
-
exposeAttach: false,
|
|
12514
|
+
exposeAttach: io.exposeAttach ?? false,
|
|
12513
12515
|
sessionFactory: createSession,
|
|
12516
|
+
...io.attachSessionFactory ? { attachSessionFactory: io.attachSessionFactory } : {},
|
|
12517
|
+
...io.defaultAttachSessionFactory ? { defaultAttachSessionFactory: io.defaultAttachSessionFactory } : {},
|
|
12514
12518
|
control: {
|
|
12515
12519
|
...resolvedEngineOptions,
|
|
12516
|
-
projectCapletsRoot:
|
|
12520
|
+
projectCapletsRoot: projectCapletsRootForEngineOptions(resolvedEngineOptions)
|
|
12517
12521
|
}
|
|
12518
12522
|
});
|
|
12519
12523
|
const paths = servicePaths(options.path);
|
|
12520
|
-
const origin = `http://${formatHost$
|
|
12524
|
+
const origin = `http://${formatHost$2(options.host)}:${options.port}`;
|
|
12521
12525
|
const baseUrl = `${origin}${paths.base === "/" ? "" : paths.base}`;
|
|
12522
12526
|
installHttpSignalHandlers(serve({
|
|
12523
12527
|
fetch: app.fetch,
|
|
@@ -12549,6 +12553,7 @@ function servicePaths(base) {
|
|
|
12549
12553
|
version,
|
|
12550
12554
|
mcp: routePath(version, "mcp"),
|
|
12551
12555
|
control: routePath(version, "admin"),
|
|
12556
|
+
attachSessions: routePath(attach, "sessions"),
|
|
12552
12557
|
attachManifest: routePath(attach, "manifest"),
|
|
12553
12558
|
attachEvents: routePath(attach, "events"),
|
|
12554
12559
|
attachInvoke: routePath(attach, "invoke"),
|
|
@@ -12692,7 +12697,7 @@ function installHttpSignalHandlers(server, app, engine, writeErr) {
|
|
|
12692
12697
|
function closeAllServerConnections(server) {
|
|
12693
12698
|
server.closeAllConnections?.call(server);
|
|
12694
12699
|
}
|
|
12695
|
-
function formatHost$
|
|
12700
|
+
function formatHost$2(host) {
|
|
12696
12701
|
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
12697
12702
|
}
|
|
12698
12703
|
//#endregion
|
|
@@ -12823,11 +12828,11 @@ async function allocateLoopbackPort() {
|
|
|
12823
12828
|
if (!address || typeof address === "string") throw new CapletsError("SERVER_UNAVAILABLE", "Could not allocate a validation port.");
|
|
12824
12829
|
return address.port;
|
|
12825
12830
|
}
|
|
12826
|
-
function formatHost(host) {
|
|
12831
|
+
function formatHost$1(host) {
|
|
12827
12832
|
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
12828
12833
|
}
|
|
12829
12834
|
function healthUrl(config, port = config.serve.port) {
|
|
12830
|
-
return `http://${formatHost(config.serve.host)}:${port}${servicePaths(config.serve.path).health}`;
|
|
12835
|
+
return `http://${formatHost$1(config.serve.host)}:${port}${servicePaths(config.serve.path).health}`;
|
|
12831
12836
|
}
|
|
12832
12837
|
function validationSpawnCommand(config) {
|
|
12833
12838
|
const planned = serviceCommand(config);
|
|
@@ -14708,6 +14713,7 @@ function createAttachNativeService(options, io) {
|
|
|
14708
14713
|
return createNativeCapletsService({
|
|
14709
14714
|
mode: options.selection.kind === "hosted_cloud" ? "cloud" : "remote",
|
|
14710
14715
|
configPath: options.configPath,
|
|
14716
|
+
projectRoot: options.projectRoot,
|
|
14711
14717
|
projectConfigPath: options.projectConfigPath,
|
|
14712
14718
|
...options.authDir ? { authDir: options.authDir } : {},
|
|
14713
14719
|
remote: {
|
|
@@ -15325,15 +15331,95 @@ async function serveResolvedCaplets(resolved, engineOptions = {}, writeErr) {
|
|
|
15325
15331
|
});
|
|
15326
15332
|
return;
|
|
15327
15333
|
}
|
|
15334
|
+
if (resolved.upstreamUrl) {
|
|
15335
|
+
await serveHttpWithUpstream(resolved, resolved.upstreamUrl, engineOptions, writeErr);
|
|
15336
|
+
return;
|
|
15337
|
+
}
|
|
15328
15338
|
await serveHttp(resolved, {
|
|
15329
15339
|
...engineOptions,
|
|
15330
15340
|
...writeErr ? { writeErr } : {}
|
|
15331
15341
|
}, writeErr);
|
|
15332
15342
|
}
|
|
15343
|
+
async function serveHttpWithUpstream(resolved, upstreamUrl, engineOptions, writeErr) {
|
|
15344
|
+
const stackChain = [serveStackIdentity(resolved)];
|
|
15345
|
+
await serveHttpWithSessionFactory(resolved, async () => new NativeCapletsMcpSession(await createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, {}, stackChain)), writeErr, {
|
|
15346
|
+
exposeAttach: true,
|
|
15347
|
+
defaultAttachSessionFactory: async (_metadata, context) => nativeAttachSession(await createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, {}, context.stackChain)),
|
|
15348
|
+
attachSessionFactory: async (metadata, context) => {
|
|
15349
|
+
return nativeAttachSession(await createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, metadata, context.stackChain));
|
|
15350
|
+
}
|
|
15351
|
+
}, engineOptions);
|
|
15352
|
+
}
|
|
15353
|
+
async function createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, metadata = {}, stackChain = []) {
|
|
15354
|
+
const service = createUpstreamNativeService(upstreamUrl, engineOptions, writeErr, metadata, stackChain);
|
|
15355
|
+
try {
|
|
15356
|
+
await service.reload();
|
|
15357
|
+
return service;
|
|
15358
|
+
} catch (error) {
|
|
15359
|
+
await service.close().catch(() => void 0);
|
|
15360
|
+
throw error;
|
|
15361
|
+
}
|
|
15362
|
+
}
|
|
15363
|
+
function createUpstreamNativeService(upstreamUrl, engineOptions, writeErr, metadata = {}, stackChain = []) {
|
|
15364
|
+
return createNativeCapletsService({
|
|
15365
|
+
...engineOptions,
|
|
15366
|
+
...metadata.projectRoot ? { projectRoot: metadata.projectRoot } : {},
|
|
15367
|
+
...metadata.projectConfigPath ? { projectConfigPath: metadata.projectConfigPath } : {},
|
|
15368
|
+
mode: isCapletsCloudUrl(upstreamUrl) ? "cloud" : "remote",
|
|
15369
|
+
remote: {
|
|
15370
|
+
url: upstreamUrl,
|
|
15371
|
+
...stackChain.length > 0 ? { requestHeaders: { [CAPLETS_STACK_CHAIN_HEADER]: stackChain.join(",") } } : {}
|
|
15372
|
+
},
|
|
15373
|
+
...writeErr ? { writeErr } : {}
|
|
15374
|
+
});
|
|
15375
|
+
}
|
|
15376
|
+
function serveStackIdentity(options) {
|
|
15377
|
+
const origin = options.publicOrigin ?? `http://${formatHost(options.host)}:${options.port}`;
|
|
15378
|
+
const url = new URL(origin);
|
|
15379
|
+
url.pathname = options.path;
|
|
15380
|
+
url.search = "";
|
|
15381
|
+
url.hash = "";
|
|
15382
|
+
return url.toString();
|
|
15383
|
+
}
|
|
15384
|
+
function formatHost(host) {
|
|
15385
|
+
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
15386
|
+
}
|
|
15387
|
+
function nativeAttachSession(service) {
|
|
15388
|
+
let cachedProjection;
|
|
15389
|
+
const getProjection = async () => {
|
|
15390
|
+
cachedProjection ??= await buildNativeAttachProjection(service);
|
|
15391
|
+
return cachedProjection;
|
|
15392
|
+
};
|
|
15393
|
+
const unsubscribe = service.onToolsChanged(() => {
|
|
15394
|
+
cachedProjection = void 0;
|
|
15395
|
+
});
|
|
15396
|
+
return {
|
|
15397
|
+
manifest: async () => (await getProjection()).manifest,
|
|
15398
|
+
invoke: async (request) => await invokeNativeAttachExport(service, await getProjection(), request),
|
|
15399
|
+
onManifestChanged: (listener) => service.onToolsChanged(() => listener()),
|
|
15400
|
+
close: async () => {
|
|
15401
|
+
unsubscribe();
|
|
15402
|
+
await service.close();
|
|
15403
|
+
}
|
|
15404
|
+
};
|
|
15405
|
+
}
|
|
15333
15406
|
//#endregion
|
|
15334
15407
|
//#region src/cli.ts
|
|
15335
15408
|
async function runCli(args, io = {}) {
|
|
15336
|
-
|
|
15409
|
+
let observedExitCode = 0;
|
|
15410
|
+
const wrappedIo = {
|
|
15411
|
+
...io,
|
|
15412
|
+
setExitCode: (code) => {
|
|
15413
|
+
observedExitCode = code;
|
|
15414
|
+
if (io.setExitCode) io.setExitCode(code);
|
|
15415
|
+
else process.exitCode = code;
|
|
15416
|
+
}
|
|
15417
|
+
};
|
|
15418
|
+
const program = createProgram(wrappedIo);
|
|
15419
|
+
const trackedCommand = telemetryCommandFamilyFromArgs(args);
|
|
15420
|
+
const startedAt = Date.now();
|
|
15421
|
+
const telemetryContext = telemetryContextForIo(wrappedIo);
|
|
15422
|
+
const dispatcher = createTelemetryDispatcher({ stateDir: telemetryContext.stateDir });
|
|
15337
15423
|
try {
|
|
15338
15424
|
if (args.length === 0) {
|
|
15339
15425
|
program.outputHelp();
|
|
@@ -15344,12 +15430,35 @@ async function runCli(args, io = {}) {
|
|
|
15344
15430
|
"caplets",
|
|
15345
15431
|
...args
|
|
15346
15432
|
]);
|
|
15433
|
+
if (trackedCommand) await captureCliTelemetry(telemetryContext, {
|
|
15434
|
+
debugSink: wrappedIo.telemetryDebugSink,
|
|
15435
|
+
dispatcher,
|
|
15436
|
+
commandFamily: trackedCommand.commandFamily,
|
|
15437
|
+
surface: trackedCommand.surface,
|
|
15438
|
+
outcome: observedExitCode === 0 ? "success" : "failure",
|
|
15439
|
+
startedAt
|
|
15440
|
+
});
|
|
15347
15441
|
} catch (error) {
|
|
15442
|
+
let normalizedError = error;
|
|
15443
|
+
let captureProductEvent = true;
|
|
15348
15444
|
if (error instanceof CommanderError) {
|
|
15349
15445
|
if (error.code === "commander.helpDisplayed" || error.code === "commander.version" || error.message === "(outputHelp)") return;
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15446
|
+
normalizedError = new CapletsError("REQUEST_INVALID", error.message);
|
|
15447
|
+
captureProductEvent = false;
|
|
15448
|
+
}
|
|
15449
|
+
if (trackedCommand) await captureCliTelemetry(telemetryContext, {
|
|
15450
|
+
debugSink: wrappedIo.telemetryDebugSink,
|
|
15451
|
+
dispatcher,
|
|
15452
|
+
commandFamily: trackedCommand.commandFamily,
|
|
15453
|
+
surface: trackedCommand.surface,
|
|
15454
|
+
outcome: "failure",
|
|
15455
|
+
startedAt,
|
|
15456
|
+
error: normalizedError,
|
|
15457
|
+
productEvent: captureProductEvent
|
|
15458
|
+
}).catch(() => void 0);
|
|
15459
|
+
throw normalizedError;
|
|
15460
|
+
} finally {
|
|
15461
|
+
await dispatcher.shutdown();
|
|
15353
15462
|
}
|
|
15354
15463
|
}
|
|
15355
15464
|
function normalizeCompletionWords(words) {
|
|
@@ -15370,6 +15479,249 @@ function collectValues(value, previous) {
|
|
|
15370
15479
|
return [...previous, value];
|
|
15371
15480
|
}
|
|
15372
15481
|
const HIDDEN_INPUT_PROMPT_LABELS = { vaultValue: "Value: " };
|
|
15482
|
+
function telemetryContextForIo(io) {
|
|
15483
|
+
const env = io.env ?? process.env;
|
|
15484
|
+
return {
|
|
15485
|
+
env,
|
|
15486
|
+
configPath: envConfigPath(env),
|
|
15487
|
+
projectConfigPath: envProjectConfigPath(env),
|
|
15488
|
+
stateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(env),
|
|
15489
|
+
stderrIsTTY: io.stderrIsTTY ?? process.stderr.isTTY === true,
|
|
15490
|
+
writeErr: io.writeErr ?? ((value) => process.stderr.write(value))
|
|
15491
|
+
};
|
|
15492
|
+
}
|
|
15493
|
+
function telemetryCommandFamilyFromArgs(args) {
|
|
15494
|
+
const command = args[0];
|
|
15495
|
+
if (command === void 0 || command === "--help" || command === "-h" || command === "--version" || command === "-V" || command === cliCommands$1.telemetry || command === cliCommands$1.completion || command === cliCommands$1.completeHidden) return;
|
|
15496
|
+
if (command === cliCommands$1.serve) return {
|
|
15497
|
+
commandFamily: "serve",
|
|
15498
|
+
surface: "serve"
|
|
15499
|
+
};
|
|
15500
|
+
if (command === cliCommands$1.attach) return {
|
|
15501
|
+
commandFamily: "attach",
|
|
15502
|
+
surface: "attach"
|
|
15503
|
+
};
|
|
15504
|
+
if (command === cliCommands$1.daemon) return {
|
|
15505
|
+
commandFamily: "daemon",
|
|
15506
|
+
surface: "daemon"
|
|
15507
|
+
};
|
|
15508
|
+
if (command === cliCommands$1.codeMode) return {
|
|
15509
|
+
commandFamily: "code_mode",
|
|
15510
|
+
surface: "code_mode"
|
|
15511
|
+
};
|
|
15512
|
+
if (command === cliCommands$1.setup) return {
|
|
15513
|
+
commandFamily: "setup",
|
|
15514
|
+
surface: "cli"
|
|
15515
|
+
};
|
|
15516
|
+
if (command === cliCommands$1.init) return {
|
|
15517
|
+
commandFamily: "init",
|
|
15518
|
+
surface: "cli"
|
|
15519
|
+
};
|
|
15520
|
+
if (command === cliCommands$1.install) return {
|
|
15521
|
+
commandFamily: "install",
|
|
15522
|
+
surface: "cli"
|
|
15523
|
+
};
|
|
15524
|
+
if (command === cliCommands$1.add) return {
|
|
15525
|
+
commandFamily: "add",
|
|
15526
|
+
surface: "cli"
|
|
15527
|
+
};
|
|
15528
|
+
if (command === cliCommands$1.doctor) return {
|
|
15529
|
+
commandFamily: "doctor",
|
|
15530
|
+
surface: "cli"
|
|
15531
|
+
};
|
|
15532
|
+
if (command === cliCommands$1.auth) return {
|
|
15533
|
+
commandFamily: "auth",
|
|
15534
|
+
surface: "cli"
|
|
15535
|
+
};
|
|
15536
|
+
if (command === cliCommands$1.remote || command === cliCommands$1.cloud) return {
|
|
15537
|
+
commandFamily: "remote",
|
|
15538
|
+
surface: "cli"
|
|
15539
|
+
};
|
|
15540
|
+
if (command === cliCommands$1.inspect) return {
|
|
15541
|
+
commandFamily: "inspect",
|
|
15542
|
+
surface: "cli"
|
|
15543
|
+
};
|
|
15544
|
+
if (command === cliCommands$1.checkBackend) return {
|
|
15545
|
+
commandFamily: "check",
|
|
15546
|
+
surface: "cli"
|
|
15547
|
+
};
|
|
15548
|
+
if (command === cliCommands$1.listTools || command === cliCommands$1.searchTools || command === cliCommands$1.getTool || command === cliCommands$1.callTool) return {
|
|
15549
|
+
commandFamily: "tools",
|
|
15550
|
+
surface: "cli"
|
|
15551
|
+
};
|
|
15552
|
+
if (command === cliCommands$1.listResources || command === cliCommands$1.searchResources || command === cliCommands$1.listResourceTemplates || command === cliCommands$1.readResource) return {
|
|
15553
|
+
commandFamily: "resources",
|
|
15554
|
+
surface: "cli"
|
|
15555
|
+
};
|
|
15556
|
+
if (command === cliCommands$1.listPrompts || command === cliCommands$1.searchPrompts || command === cliCommands$1.getPrompt) return {
|
|
15557
|
+
commandFamily: "prompts",
|
|
15558
|
+
surface: "cli"
|
|
15559
|
+
};
|
|
15560
|
+
if (command === cliCommands$1.complete) return {
|
|
15561
|
+
commandFamily: "complete",
|
|
15562
|
+
surface: "cli"
|
|
15563
|
+
};
|
|
15564
|
+
return {
|
|
15565
|
+
commandFamily: "unknown",
|
|
15566
|
+
surface: "cli"
|
|
15567
|
+
};
|
|
15568
|
+
}
|
|
15569
|
+
function telemetryConfigForCli(context) {
|
|
15570
|
+
try {
|
|
15571
|
+
return loadConfigWithSources(context.configPath, context.projectConfigPath, { vaultResolver: vaultBootstrapResolver }).config;
|
|
15572
|
+
} catch (error) {
|
|
15573
|
+
if (error instanceof CapletsError && error.code !== "CONFIG_INVALID") return {};
|
|
15574
|
+
return telemetryOnlyConfigForCli(resolveConfigPath(context.configPath)) ?? { telemetry: false };
|
|
15575
|
+
}
|
|
15576
|
+
}
|
|
15577
|
+
function telemetryOnlyConfigForCli(path) {
|
|
15578
|
+
try {
|
|
15579
|
+
const config = readUserConfigObject(path);
|
|
15580
|
+
return typeof config.telemetry === "boolean" ? { telemetry: config.telemetry } : void 0;
|
|
15581
|
+
} catch {
|
|
15582
|
+
return;
|
|
15583
|
+
}
|
|
15584
|
+
}
|
|
15585
|
+
function maybePrintCliTelemetryNotice(context, surface) {
|
|
15586
|
+
const state = resolveTelemetryState({
|
|
15587
|
+
config: telemetryConfigForCli(context),
|
|
15588
|
+
env: context.env,
|
|
15589
|
+
stateDir: context.stateDir,
|
|
15590
|
+
surface,
|
|
15591
|
+
visibility: "visible",
|
|
15592
|
+
allowWithoutNotice: true,
|
|
15593
|
+
createIdentity: false
|
|
15594
|
+
});
|
|
15595
|
+
if (state.status !== "enabled" || state.notice.shown) return;
|
|
15596
|
+
maybePrintTelemetryNotice({
|
|
15597
|
+
stateDir: context.stateDir,
|
|
15598
|
+
surface,
|
|
15599
|
+
stderrIsTTY: context.stderrIsTTY,
|
|
15600
|
+
writeErr: context.writeErr
|
|
15601
|
+
});
|
|
15602
|
+
}
|
|
15603
|
+
async function captureCliTelemetry(context, options) {
|
|
15604
|
+
try {
|
|
15605
|
+
maybePrintCliTelemetryNotice(context, options.surface ?? "cli");
|
|
15606
|
+
} catch {}
|
|
15607
|
+
const state = resolveTelemetryState({
|
|
15608
|
+
config: telemetryConfigForCli(context),
|
|
15609
|
+
env: context.env,
|
|
15610
|
+
stateDir: context.stateDir,
|
|
15611
|
+
surface: options.surface ?? "cli",
|
|
15612
|
+
visibility: "visible",
|
|
15613
|
+
debug: context.env.CAPLETS_TELEMETRY_DEBUG === "1"
|
|
15614
|
+
});
|
|
15615
|
+
if (state.status !== "enabled" && state.status !== "debug") return;
|
|
15616
|
+
const identity = state.status === "debug" ? readTelemetryIdentity({
|
|
15617
|
+
stateDir: context.stateDir,
|
|
15618
|
+
create: false
|
|
15619
|
+
}) : state.identity ?? readTelemetryIdentity({
|
|
15620
|
+
stateDir: context.stateDir,
|
|
15621
|
+
create: true
|
|
15622
|
+
});
|
|
15623
|
+
if (options.productEvent !== false) {
|
|
15624
|
+
const product = buildProductTelemetryEvent({
|
|
15625
|
+
name: "caplets_cli_command",
|
|
15626
|
+
distinctId: identity.id,
|
|
15627
|
+
properties: {
|
|
15628
|
+
package: "@caplets/core",
|
|
15629
|
+
version,
|
|
15630
|
+
surface: options.surface ?? "cli",
|
|
15631
|
+
runtime_mode: runtimeModeForEnv(context.env),
|
|
15632
|
+
execution_context: state.executionContext,
|
|
15633
|
+
command_family: options.commandFamily,
|
|
15634
|
+
outcome: options.outcome,
|
|
15635
|
+
duration_bucket: durationBucket(Date.now() - options.startedAt)
|
|
15636
|
+
}
|
|
15637
|
+
});
|
|
15638
|
+
if (state.status === "debug") options.debugSink?.capture("debug", product);
|
|
15639
|
+
else await (options.dispatcher ?? createTelemetryDispatcher({ stateDir: context.stateDir })).capture(state, product);
|
|
15640
|
+
}
|
|
15641
|
+
if (options.outcome !== "failure") return;
|
|
15642
|
+
if (options.error === void 0) return;
|
|
15643
|
+
const reliability = buildReliabilityTelemetryEvent({
|
|
15644
|
+
name: "caplets_reliability_error",
|
|
15645
|
+
properties: {
|
|
15646
|
+
package: "@caplets/core",
|
|
15647
|
+
version,
|
|
15648
|
+
surface: options.surface ?? "cli",
|
|
15649
|
+
runtime_mode: runtimeModeForEnv(context.env),
|
|
15650
|
+
command_family: options.commandFamily,
|
|
15651
|
+
error_code: errorCodeForTelemetry(options.error),
|
|
15652
|
+
diagnostic_category: diagnosticCategoryForError(options.error),
|
|
15653
|
+
os_family: platform(),
|
|
15654
|
+
arch: arch(),
|
|
15655
|
+
node_major: Number(process.versions.node.split(".")[0] ?? 0)
|
|
15656
|
+
}
|
|
15657
|
+
});
|
|
15658
|
+
if (state.status === "debug") {
|
|
15659
|
+
options.debugSink?.capture("debug", reliability);
|
|
15660
|
+
return;
|
|
15661
|
+
}
|
|
15662
|
+
await (options.dispatcher ?? createTelemetryDispatcher({ stateDir: context.stateDir })).capture(state, reliability);
|
|
15663
|
+
}
|
|
15664
|
+
function readUserConfigObject(path) {
|
|
15665
|
+
if (!existsSync(path)) return {};
|
|
15666
|
+
try {
|
|
15667
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
15668
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
15669
|
+
} catch (error) {
|
|
15670
|
+
throw new CapletsError("CONFIG_INVALID", `Caplets config at ${path} is not valid JSON`, error);
|
|
15671
|
+
}
|
|
15672
|
+
}
|
|
15673
|
+
function runtimeModeForEnv(env) {
|
|
15674
|
+
const mode = env.CAPLETS_MODE;
|
|
15675
|
+
return mode === "remote" || mode === "cloud" || mode === "local" ? mode : "unknown";
|
|
15676
|
+
}
|
|
15677
|
+
function errorCodeForTelemetry(error) {
|
|
15678
|
+
if (error instanceof CapletsError) return error.code;
|
|
15679
|
+
return "UNKNOWN";
|
|
15680
|
+
}
|
|
15681
|
+
function diagnosticCategoryForError(error) {
|
|
15682
|
+
if (!(error instanceof CapletsError)) return "unknown";
|
|
15683
|
+
if (error.code.startsWith("CONFIG")) return "config";
|
|
15684
|
+
if (error.code.startsWith("AUTH")) return "auth";
|
|
15685
|
+
if (error.code.includes("NETWORK") || error.code.includes("UNAVAILABLE")) return "network";
|
|
15686
|
+
if (error.code.includes("VALID") || error.code.includes("REQUEST")) return "validation";
|
|
15687
|
+
return "runtime";
|
|
15688
|
+
}
|
|
15689
|
+
function writeTelemetryConfig(path, enabled) {
|
|
15690
|
+
const config = {
|
|
15691
|
+
...readUserConfigObject(path),
|
|
15692
|
+
$schema: "https://caplets.dev/config.schema.json",
|
|
15693
|
+
telemetry: enabled
|
|
15694
|
+
};
|
|
15695
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
15696
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
|
|
15697
|
+
}
|
|
15698
|
+
function formatTelemetryStatus(context) {
|
|
15699
|
+
const config = telemetryConfigForCli(context);
|
|
15700
|
+
const state = resolveTelemetryState({
|
|
15701
|
+
config,
|
|
15702
|
+
env: context.env,
|
|
15703
|
+
stateDir: context.stateDir,
|
|
15704
|
+
surface: "cli",
|
|
15705
|
+
visibility: "visible",
|
|
15706
|
+
createIdentity: false
|
|
15707
|
+
});
|
|
15708
|
+
const notice = readTelemetryNotice({ stateDir: context.stateDir });
|
|
15709
|
+
const identity = readTelemetryIdentity({
|
|
15710
|
+
stateDir: context.stateDir,
|
|
15711
|
+
create: false
|
|
15712
|
+
});
|
|
15713
|
+
const health = readTelemetryDeliveryHealth({ stateDir: context.stateDir });
|
|
15714
|
+
return `${[
|
|
15715
|
+
`Telemetry: ${state.status}`,
|
|
15716
|
+
`Decision: ${state.decider}`,
|
|
15717
|
+
`Config: ${config.telemetry === false ? "disabled" : "enabled"}`,
|
|
15718
|
+
`Environment: ${context.env.CAPLETS_DISABLE_TELEMETRY === "1" ? "disabled" : "enabled"}`,
|
|
15719
|
+
`Notice shown: ${notice.shown ? `yes (${notice.surface})` : "no"}`,
|
|
15720
|
+
`Anonymous ID: ${identity.kind === "stable" ? "present" : "not stored"}`,
|
|
15721
|
+
`Delivery health: ${Object.keys(health).length === 0 ? "none" : JSON.stringify(health)}`,
|
|
15722
|
+
"Disable with CAPLETS_DISABLE_TELEMETRY=1 or `caplets telemetry disable`."
|
|
15723
|
+
].join("\n")}\n`;
|
|
15724
|
+
}
|
|
15373
15725
|
function remoteProfileStore(authDir, env) {
|
|
15374
15726
|
return createRemoteProfileStore({
|
|
15375
15727
|
authDir,
|
|
@@ -15380,6 +15732,21 @@ function attachRemoteUrlFromArgs(positionalUrl, legacyRemoteUrl) {
|
|
|
15380
15732
|
if (positionalUrl && legacyRemoteUrl && positionalUrl !== legacyRemoteUrl) throw new CapletsError("REQUEST_INVALID", "Pass either attach URL or --remote-url, not both. Use caplets attach <url> for new configs.");
|
|
15381
15733
|
return positionalUrl ?? legacyRemoteUrl;
|
|
15382
15734
|
}
|
|
15735
|
+
function rejectAttachHttpServeFlags(options) {
|
|
15736
|
+
const invalid = [
|
|
15737
|
+
options.transport !== void 0 ? "--transport" : void 0,
|
|
15738
|
+
options.host !== void 0 ? "--host" : void 0,
|
|
15739
|
+
options.port !== void 0 ? "--port" : void 0,
|
|
15740
|
+
options.path !== void 0 ? "--path" : void 0,
|
|
15741
|
+
options.allowUnauthenticatedHttp === true ? "--allow-unauthenticated-http" : void 0,
|
|
15742
|
+
options.trustProxy === true ? "--trust-proxy" : void 0
|
|
15743
|
+
].filter((value) => value !== void 0);
|
|
15744
|
+
if (invalid.length === 0) return;
|
|
15745
|
+
throw new CapletsError("REQUEST_INVALID", `caplets attach is stdio-only; ${invalid.join(", ")} ${invalid.length === 1 ? "is" : "are"} no longer supported. Use caplets serve --transport http --upstream-url <url> to start an HTTP stacked runtime.`);
|
|
15746
|
+
}
|
|
15747
|
+
function hiddenOption(flags, description) {
|
|
15748
|
+
return new Option(flags, description).hideHelp();
|
|
15749
|
+
}
|
|
15383
15750
|
function remoteServerCredentialStore(statePath, env) {
|
|
15384
15751
|
return new RemoteServerCredentialStore({ dir: statePath ?? env.CAPLETS_REMOTE_SERVER_STATE_DIR ?? join(DEFAULT_AUTH_DIR, "remote-server") });
|
|
15385
15752
|
}
|
|
@@ -15801,9 +16168,33 @@ function createProgram(io = {}) {
|
|
|
15801
16168
|
const writeErr = io.writeErr ?? ((value) => process.stderr.write(value));
|
|
15802
16169
|
const env = io.env ?? process.env;
|
|
15803
16170
|
const currentConfigPath = () => envConfigPath(env);
|
|
16171
|
+
const telemetryContext = () => ({
|
|
16172
|
+
env,
|
|
16173
|
+
configPath: currentConfigPath(),
|
|
16174
|
+
projectConfigPath: envProjectConfigPath(env),
|
|
16175
|
+
stateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(env),
|
|
16176
|
+
stderrIsTTY: io.stderrIsTTY ?? process.stderr.isTTY === true,
|
|
16177
|
+
writeErr
|
|
16178
|
+
});
|
|
16179
|
+
const printTelemetryNotice = (surface) => {
|
|
16180
|
+
try {
|
|
16181
|
+
maybePrintCliTelemetryNotice(telemetryContext(), surface);
|
|
16182
|
+
} catch {}
|
|
16183
|
+
};
|
|
15804
16184
|
const setExitCode = io.setExitCode ?? ((code) => {
|
|
15805
16185
|
process.exitCode = code;
|
|
15806
16186
|
});
|
|
16187
|
+
const executeOperationIo = (format) => ({
|
|
16188
|
+
writeOut,
|
|
16189
|
+
writeErr,
|
|
16190
|
+
setExitCode,
|
|
16191
|
+
authDir: io.authDir,
|
|
16192
|
+
env,
|
|
16193
|
+
remote: remoteClientForCli(io),
|
|
16194
|
+
format,
|
|
16195
|
+
telemetryStateDir: telemetryContext().stateDir,
|
|
16196
|
+
telemetryDebugSink: io.telemetryDebugSink
|
|
16197
|
+
});
|
|
15807
16198
|
const program = new Command();
|
|
15808
16199
|
program.name("caplets").description("Progressive-disclosure gateway for MCP servers.").version(io.version ?? version).exitOverride().configureOutput({
|
|
15809
16200
|
writeOut,
|
|
@@ -15847,7 +16238,51 @@ function createProgram(io = {}) {
|
|
|
15847
16238
|
}
|
|
15848
16239
|
if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
|
|
15849
16240
|
});
|
|
16241
|
+
const telemetry = program.command(cliCommands$1.telemetry).description("Inspect and control anonymous Caplets telemetry.");
|
|
16242
|
+
telemetry.command("status").description("Show anonymous telemetry status.").action(() => {
|
|
16243
|
+
writeOut(formatTelemetryStatus(telemetryContext()));
|
|
16244
|
+
});
|
|
16245
|
+
telemetry.command("enable").description("Enable anonymous telemetry in the user config.").action(() => {
|
|
16246
|
+
const path = resolveConfigPath(currentConfigPath());
|
|
16247
|
+
writeTelemetryConfig(path, true);
|
|
16248
|
+
writeOut(`Enabled anonymous telemetry in ${path}.\n`);
|
|
16249
|
+
if (env.CAPLETS_DISABLE_TELEMETRY === "1") writeOut("CAPLETS_DISABLE_TELEMETRY=1 still disables telemetry for this process.\n");
|
|
16250
|
+
});
|
|
16251
|
+
telemetry.command("disable").description("Disable anonymous telemetry in the user config.").action(() => {
|
|
16252
|
+
const path = resolveConfigPath(currentConfigPath());
|
|
16253
|
+
writeTelemetryConfig(path, false);
|
|
16254
|
+
writeOut(`Disabled anonymous telemetry in ${path}.\n`);
|
|
16255
|
+
});
|
|
16256
|
+
telemetry.command("delete-id").description("Delete the local anonymous telemetry ID.").action(() => {
|
|
16257
|
+
deleteTelemetryIdentity({ stateDir: telemetryContext().stateDir });
|
|
16258
|
+
writeOut("Deleted the local anonymous telemetry ID. This does not delete provider-side historical anonymous events; provider retention controls historical data.\n");
|
|
16259
|
+
});
|
|
16260
|
+
telemetry.command("rotate-id").description("Rotate the local anonymous telemetry ID.").action(() => {
|
|
16261
|
+
rotateTelemetryIdentity({ stateDir: telemetryContext().stateDir });
|
|
16262
|
+
writeOut("Rotated the local anonymous telemetry ID. This does not delete provider-side historical anonymous events; provider retention controls historical data.\n");
|
|
16263
|
+
});
|
|
16264
|
+
telemetry.command("debug").description("Run a Caplets command with local telemetry debug output.").allowUnknownOption(true).argument("[args...]", "Caplets command and arguments after --").action(async (args) => {
|
|
16265
|
+
const nestedArgs = args[0] === "--" ? args.slice(1) : args;
|
|
16266
|
+
const sink = new TelemetryDebugSink();
|
|
16267
|
+
try {
|
|
16268
|
+
if (nestedArgs.length > 0) await runCli(nestedArgs, {
|
|
16269
|
+
...io,
|
|
16270
|
+
env: {
|
|
16271
|
+
...env,
|
|
16272
|
+
CAPLETS_TELEMETRY_DEBUG: "1"
|
|
16273
|
+
},
|
|
16274
|
+
telemetryDebugSink: sink,
|
|
16275
|
+
writeOut,
|
|
16276
|
+
writeErr
|
|
16277
|
+
});
|
|
16278
|
+
} finally {
|
|
16279
|
+
writeOut(`${JSON.stringify({ telemetryDebug: sink.toJSON() }, null, 2)}\n`);
|
|
16280
|
+
}
|
|
16281
|
+
});
|
|
15850
16282
|
program.command(cliCommands$1.codeMode).description("Run, inspect, and debug Caplets Code Mode.").argument("[code]", "inline TypeScript code to run").option("--file <path>", "read TypeScript code from a file relative to the current directory").option("--session-id <id>", "optional Code Mode session identifier").option("--recover <ref>", "recover a prior Code Mode REPL session when supported").option("--timeout-ms <ms>", "execution timeout in milliseconds", parsePositiveInteger).option("--json", "print the structured run envelope").action(async (code, options) => {
|
|
16283
|
+
try {
|
|
16284
|
+
maybePrintCliTelemetryNotice(telemetryContext(), "code_mode");
|
|
16285
|
+
} catch {}
|
|
15851
16286
|
if (code === "repl" && options.file === void 0) {
|
|
15852
16287
|
await runCodeModeReplCli({
|
|
15853
16288
|
env,
|
|
@@ -15881,6 +16316,7 @@ function createProgram(io = {}) {
|
|
|
15881
16316
|
...currentConfigPath() ? { configPath: currentConfigPath() } : {},
|
|
15882
16317
|
projectConfigPath: envProjectConfigPath(env),
|
|
15883
16318
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
16319
|
+
telemetryStateDir: telemetryContext().stateDir,
|
|
15884
16320
|
...code === void 0 ? {} : { inlineCode: code },
|
|
15885
16321
|
...options.file === void 0 ? {} : { file: options.file },
|
|
15886
16322
|
...options.sessionId === void 0 ? {} : { sessionId: options.sessionId },
|
|
@@ -15897,16 +16333,23 @@ function createProgram(io = {}) {
|
|
|
15897
16333
|
...currentConfigPath() ? { configPath: currentConfigPath() } : {},
|
|
15898
16334
|
projectConfigPath: envProjectConfigPath(env),
|
|
15899
16335
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
16336
|
+
telemetryStateDir: telemetryContext().stateDir,
|
|
15900
16337
|
...options.json === void 0 && parentOptions.json === void 0 ? {} : { json: options.json ?? parentOptions.json },
|
|
15901
16338
|
writeOut
|
|
15902
16339
|
});
|
|
15903
16340
|
});
|
|
15904
|
-
const serve = program.command(cliCommands$1.serve).description("Serve configured Caplets as an MCP server.").option("--transport <transport>", "server transport: stdio or http").option("--host <host>", "HTTP bind host").option("--port <port>", "HTTP bind port").option("--path <path>", "HTTP service base path").option("--remote-state-path <path>", "server-owned remote credential state directory").option("--allow-unauthenticated-http", "allow unauthenticated HTTP serving on non-loopback hosts").option("--trust-proxy", "trust X-Forwarded-* headers from a reverse proxy").action(async (options) => {
|
|
16341
|
+
const serve = program.command(cliCommands$1.serve).description("Serve configured Caplets as an MCP server.").option("--transport <transport>", "server transport: stdio or http").option("--host <host>", "HTTP bind host").option("--port <port>", "HTTP bind port").option("--path <path>", "HTTP service base path").option("--remote-state-path <path>", "server-owned remote credential state directory").option("--upstream-url <url>", "upstream Caplets runtime URL to compose with this HTTP service").option("--allow-unauthenticated-http", "allow unauthenticated HTTP serving on non-loopback hosts").option("--trust-proxy", "trust X-Forwarded-* headers from a reverse proxy").action(async (options) => {
|
|
16342
|
+
printTelemetryNotice("serve");
|
|
15905
16343
|
const resolved = resolveServeOptions(options);
|
|
15906
16344
|
const configPath = currentConfigPath();
|
|
15907
16345
|
await (io.serve ?? ((serveOptions) => serveResolvedCaplets(serveOptions, {
|
|
15908
16346
|
...configPath ? { configPath } : {},
|
|
15909
|
-
...io.authDir ? { authDir: io.authDir } : {}
|
|
16347
|
+
...io.authDir ? { authDir: io.authDir } : {},
|
|
16348
|
+
telemetryEnv: env,
|
|
16349
|
+
telemetryStateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(env),
|
|
16350
|
+
telemetrySurface: "serve",
|
|
16351
|
+
telemetryVisibility: "visible",
|
|
16352
|
+
telemetryRuntimeMode: runtimeModeForEnv(env)
|
|
15910
16353
|
}, writeErr)))(resolved);
|
|
15911
16354
|
});
|
|
15912
16355
|
const daemonOptions = () => ({
|
|
@@ -15923,6 +16366,7 @@ function createProgram(io = {}) {
|
|
|
15923
16366
|
})) addServeMigrationCommand(serve, name, replacement);
|
|
15924
16367
|
const daemon = program.command(cliCommands$1.daemon).description("Install and manage the default Caplets daemon.");
|
|
15925
16368
|
addDaemonInstallOptions(daemon.command("install").description("Install or update the default Caplets daemon.")).action(async (options) => {
|
|
16369
|
+
printTelemetryNotice("daemon");
|
|
15926
16370
|
const prompt = options.json ? void 0 : createSetupPromptHandle(io, writeOut);
|
|
15927
16371
|
try {
|
|
15928
16372
|
const result = await installDaemon(daemonInstallOptions(options), {
|
|
@@ -15946,6 +16390,7 @@ function createProgram(io = {}) {
|
|
|
15946
16390
|
}
|
|
15947
16391
|
});
|
|
15948
16392
|
addJsonOption(daemon.command("uninstall").description("Uninstall the default Caplets daemon.").option("--purge", "remove daemon config, state, and logs").option("--dry-run", "preview uninstall actions without mutation")).action(async (options) => {
|
|
16393
|
+
printTelemetryNotice("daemon");
|
|
15949
16394
|
const result = await uninstallDaemon({
|
|
15950
16395
|
purge: options.purge === true,
|
|
15951
16396
|
dryRun: options.dryRun === true
|
|
@@ -15957,6 +16402,7 @@ function createProgram(io = {}) {
|
|
|
15957
16402
|
writeOut(result.dryRun ? "Would uninstall Caplets daemon.\n" : "Uninstalled Caplets daemon.\n");
|
|
15958
16403
|
});
|
|
15959
16404
|
addJsonOption(daemon.command("start").description("Start the default Caplets daemon.")).action(async (options) => {
|
|
16405
|
+
printTelemetryNotice("daemon");
|
|
15960
16406
|
const result = await startDaemon(daemonOptions());
|
|
15961
16407
|
if (options.json) {
|
|
15962
16408
|
writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -15965,6 +16411,7 @@ function createProgram(io = {}) {
|
|
|
15965
16411
|
writeOut(result.action === "restart" ? "Restarted Caplets daemon.\n" : "Started Caplets daemon.\n");
|
|
15966
16412
|
});
|
|
15967
16413
|
addJsonOption(daemon.command("restart").description("Restart the default Caplets daemon.")).action(async (options) => {
|
|
16414
|
+
printTelemetryNotice("daemon");
|
|
15968
16415
|
const result = await restartDaemon(daemonOptions());
|
|
15969
16416
|
if (options.json) {
|
|
15970
16417
|
writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -15973,6 +16420,7 @@ function createProgram(io = {}) {
|
|
|
15973
16420
|
writeOut("Restarted Caplets daemon.\n");
|
|
15974
16421
|
});
|
|
15975
16422
|
addJsonOption(daemon.command("stop").description("Stop the default Caplets daemon.")).action(async (options) => {
|
|
16423
|
+
printTelemetryNotice("daemon");
|
|
15976
16424
|
const result = await stopDaemon(daemonOptions());
|
|
15977
16425
|
if (options.json) {
|
|
15978
16426
|
writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -15981,6 +16429,7 @@ function createProgram(io = {}) {
|
|
|
15981
16429
|
writeOut("Stopped Caplets daemon.\n");
|
|
15982
16430
|
});
|
|
15983
16431
|
addJsonOption(daemon.command("status").description("Show the default Caplets daemon status.")).action(async (options) => {
|
|
16432
|
+
printTelemetryNotice("daemon");
|
|
15984
16433
|
const status = await daemonStatus(daemonOptions());
|
|
15985
16434
|
if (options.json) {
|
|
15986
16435
|
writeOut(`${JSON.stringify(status, null, 2)}\n`);
|
|
@@ -15989,6 +16438,7 @@ function createProgram(io = {}) {
|
|
|
15989
16438
|
writeOut(formatDaemonStatus(status));
|
|
15990
16439
|
});
|
|
15991
16440
|
addJsonOption(daemon.command("logs").description("Show Caplets daemon logs.").option("--follow", "follow appended log lines").option("--tail <lines>", "show the last number of lines").option("--stream <stream>", "log stream: stdout, stderr, or all")).action(async (options) => {
|
|
16441
|
+
printTelemetryNotice("daemon");
|
|
15992
16442
|
const tail = options.tail === void 0 ? 10 : parseNonNegativeInteger(options.tail, "--tail");
|
|
15993
16443
|
const stream = parseLogStream(options.stream);
|
|
15994
16444
|
if (options.follow) {
|
|
@@ -16023,8 +16473,10 @@ function createProgram(io = {}) {
|
|
|
16023
16473
|
}
|
|
16024
16474
|
for (const entry of result.entries) writeOut(stream === "all" ? `[${entry.stream}] ${entry.line}\n` : `${entry.line}\n`);
|
|
16025
16475
|
});
|
|
16026
|
-
program.command(cliCommands$1.attach).description("Start a remote-backed Caplets MCP server.").argument("[url]", "remote Caplets service base URL").
|
|
16476
|
+
program.command(cliCommands$1.attach).description("Start a remote-backed Caplets MCP server.").argument("[url]", "remote Caplets service base URL").addOption(hiddenOption("--transport <transport>", "server transport: stdio or http")).addOption(hiddenOption("--host <host>", "HTTP bind host")).addOption(hiddenOption("--port <port>", "HTTP bind port")).addOption(hiddenOption("--path <path>", "HTTP service base path")).addOption(new Option("--remote-url <url>", "legacy alias for the remote Caplets service base URL").hideHelp()).option("--workspace <workspace>", "hosted Cloud workspace ID or slug").addOption(hiddenOption("--allow-unauthenticated-http", "allow unauthenticated HTTP serving on non-loopback hosts")).addOption(hiddenOption("--trust-proxy", "trust X-Forwarded-* headers from a reverse proxy")).option("--json", "print JSON status events").option("--verbose", "print detailed attach diagnostics").option("--once", "validate Project Binding once and exit").option("--project-root <path>", "test-only project root override").action(async (url, options) => {
|
|
16477
|
+
printTelemetryNotice("attach");
|
|
16027
16478
|
try {
|
|
16479
|
+
rejectAttachHttpServeFlags(options);
|
|
16028
16480
|
const remoteUrl = attachRemoteUrlFromArgs(url, options.remoteUrl);
|
|
16029
16481
|
const attachOptions = {
|
|
16030
16482
|
...options,
|
|
@@ -16396,6 +16848,7 @@ function createProgram(io = {}) {
|
|
|
16396
16848
|
writeOut(`Created ${localMutationTargetLabel(target, io)}Caplets config at ${path}\n`);
|
|
16397
16849
|
});
|
|
16398
16850
|
program.command(cliCommands$1.setup).description("Install or configure an agent integration for Caplets.").argument("[integration]", "integration: codex, claude-code, opencode, pi, or mcp-client").option("--remote", "configure for a remote Caplets server").option("--remote-url <url>", "remote Caplets service base URL").option("--server-url <url>", "remote Caplets service base URL").option("--output <path>", "config path to write for generic MCP setup").option("--dry-run", "print actions without running commands or writing files").option("--yes", "approve Caplet setup commands for the exact current content hash").option("--target <target>", "Caplet setup target: local, remote, or cloud", parseSetupTarget).option("--format <format>", "output format: plain or json", parseSetupFormat).action(async (integration, options) => {
|
|
16851
|
+
printTelemetryNotice("cli");
|
|
16399
16852
|
const setupOptions = {
|
|
16400
16853
|
...options,
|
|
16401
16854
|
env
|
|
@@ -16584,6 +17037,7 @@ function createProgram(io = {}) {
|
|
|
16584
17037
|
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
16585
17038
|
});
|
|
16586
17039
|
program.command(cliCommands$1.install).description("Install Caplets from a repo's caplets directory.").argument("<repo>", "local repo path, Git URL, or GitHub owner/repo").argument("[caplets...]", "optional Caplet IDs to install").option("--project", "install to the project Caplets root").option("-g, --global", "install to the user Caplets root").option("--remote", "install through remote control").option("--force", "overwrite installed Caplets").action(async (repo, capletIds, options) => {
|
|
17040
|
+
printTelemetryNotice("cli");
|
|
16587
17041
|
const target = parseMutationTarget(options);
|
|
16588
17042
|
if (target === "remote") {
|
|
16589
17043
|
const result = await requireRemoteClientForTarget(io).request("install", {
|
|
@@ -16703,37 +17157,13 @@ function createProgram(io = {}) {
|
|
|
16703
17157
|
writeAddResult(writeOut, `${localMutationTargetLabel(target, io)}HTTP`, result);
|
|
16704
17158
|
});
|
|
16705
17159
|
program.command(cliCommands$1.inspect).description("Print a configured Caplet card.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
|
|
16706
|
-
await executeOperation(caplet, { operation: "inspect" },
|
|
16707
|
-
writeOut,
|
|
16708
|
-
writeErr,
|
|
16709
|
-
setExitCode,
|
|
16710
|
-
authDir: io.authDir,
|
|
16711
|
-
env,
|
|
16712
|
-
remote: remoteClientForCli(io),
|
|
16713
|
-
format: options.format
|
|
16714
|
-
});
|
|
17160
|
+
await executeOperation(caplet, { operation: "inspect" }, executeOperationIo(options.format));
|
|
16715
17161
|
});
|
|
16716
17162
|
program.command(cliCommands$1.checkBackend).description("Check backend availability for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
|
|
16717
|
-
await executeOperation(caplet, { operation: "check" },
|
|
16718
|
-
writeOut,
|
|
16719
|
-
writeErr,
|
|
16720
|
-
setExitCode,
|
|
16721
|
-
authDir: io.authDir,
|
|
16722
|
-
env,
|
|
16723
|
-
remote: remoteClientForCli(io),
|
|
16724
|
-
format: options.format
|
|
16725
|
-
});
|
|
17163
|
+
await executeOperation(caplet, { operation: "check" }, executeOperationIo(options.format));
|
|
16726
17164
|
});
|
|
16727
17165
|
program.command(cliCommands$1.listTools).description("List downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
|
|
16728
|
-
await executeOperation(caplet, { operation: "tools" },
|
|
16729
|
-
writeOut,
|
|
16730
|
-
writeErr,
|
|
16731
|
-
setExitCode,
|
|
16732
|
-
authDir: io.authDir,
|
|
16733
|
-
env,
|
|
16734
|
-
remote: remoteClientForCli(io),
|
|
16735
|
-
format: options.format
|
|
16736
|
-
});
|
|
17166
|
+
await executeOperation(caplet, { operation: "tools" }, executeOperationIo(options.format));
|
|
16737
17167
|
});
|
|
16738
17168
|
program.command(cliCommands$1.searchTools).description("Search downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").argument("<query>", "search query").option("--limit <n>", "maximum number of tools to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => {
|
|
16739
17169
|
await executeOperation(caplet, options.limit === void 0 ? {
|
|
@@ -16743,30 +17173,14 @@ function createProgram(io = {}) {
|
|
|
16743
17173
|
operation: "search_tools",
|
|
16744
17174
|
query,
|
|
16745
17175
|
limit: options.limit
|
|
16746
|
-
},
|
|
16747
|
-
writeOut,
|
|
16748
|
-
writeErr,
|
|
16749
|
-
setExitCode,
|
|
16750
|
-
authDir: io.authDir,
|
|
16751
|
-
env,
|
|
16752
|
-
remote: remoteClientForCli(io),
|
|
16753
|
-
format: options.format
|
|
16754
|
-
});
|
|
17176
|
+
}, executeOperationIo(options.format));
|
|
16755
17177
|
});
|
|
16756
17178
|
program.command(cliCommands$1.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) => {
|
|
16757
17179
|
const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
|
|
16758
17180
|
await executeOperation(caplet, {
|
|
16759
17181
|
operation: "describe_tool",
|
|
16760
17182
|
name: tool
|
|
16761
|
-
},
|
|
16762
|
-
writeOut,
|
|
16763
|
-
writeErr,
|
|
16764
|
-
setExitCode,
|
|
16765
|
-
authDir: io.authDir,
|
|
16766
|
-
env,
|
|
16767
|
-
remote: remoteClientForCli(io),
|
|
16768
|
-
format: options.format
|
|
16769
|
-
});
|
|
17183
|
+
}, executeOperationIo(options.format));
|
|
16770
17184
|
});
|
|
16771
17185
|
program.command(cliCommands$1.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) => {
|
|
16772
17186
|
const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
|
|
@@ -16775,28 +17189,12 @@ function createProgram(io = {}) {
|
|
|
16775
17189
|
name: tool,
|
|
16776
17190
|
args: parseCallToolArgs(options.args),
|
|
16777
17191
|
...options.field && options.field.length > 0 ? { fields: options.field } : {}
|
|
16778
|
-
},
|
|
16779
|
-
writeOut,
|
|
16780
|
-
writeErr,
|
|
16781
|
-
setExitCode,
|
|
16782
|
-
authDir: io.authDir,
|
|
16783
|
-
env,
|
|
16784
|
-
remote: remoteClientForCli(io),
|
|
16785
|
-
format: options.format
|
|
16786
|
-
});
|
|
17192
|
+
}, executeOperationIo(options.format));
|
|
16787
17193
|
});
|
|
16788
17194
|
program.command(cliCommands$1.listResources).description("List MCP resources for a configured MCP Caplet.").argument("<caplet>").option("--limit <n>", "maximum number of resources to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, options.limit === void 0 ? { operation: "resources" } : {
|
|
16789
17195
|
operation: "resources",
|
|
16790
17196
|
limit: options.limit
|
|
16791
|
-
},
|
|
16792
|
-
writeOut,
|
|
16793
|
-
writeErr,
|
|
16794
|
-
setExitCode,
|
|
16795
|
-
authDir: io.authDir,
|
|
16796
|
-
env,
|
|
16797
|
-
remote: remoteClientForCli(io),
|
|
16798
|
-
format: options.format
|
|
16799
|
-
}));
|
|
17197
|
+
}, executeOperationIo(options.format)));
|
|
16800
17198
|
program.command(cliCommands$1.searchResources).description("Search MCP resources and resource templates for a configured MCP Caplet.").argument("<caplet>").argument("<query>").option("--limit <n>", "maximum number of matches to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => executeOperation(caplet, options.limit === void 0 ? {
|
|
16801
17199
|
operation: "search_resources",
|
|
16802
17200
|
query
|
|
@@ -16804,51 +17202,19 @@ function createProgram(io = {}) {
|
|
|
16804
17202
|
operation: "search_resources",
|
|
16805
17203
|
query,
|
|
16806
17204
|
limit: options.limit
|
|
16807
|
-
},
|
|
16808
|
-
writeOut,
|
|
16809
|
-
writeErr,
|
|
16810
|
-
setExitCode,
|
|
16811
|
-
authDir: io.authDir,
|
|
16812
|
-
env,
|
|
16813
|
-
remote: remoteClientForCli(io),
|
|
16814
|
-
format: options.format
|
|
16815
|
-
}));
|
|
17205
|
+
}, executeOperationIo(options.format)));
|
|
16816
17206
|
program.command(cliCommands$1.listResourceTemplates).description("List MCP resource templates for a configured MCP Caplet.").argument("<caplet>").option("--limit <n>", "maximum number of templates to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, options.limit === void 0 ? { operation: "resource_templates" } : {
|
|
16817
17207
|
operation: "resource_templates",
|
|
16818
17208
|
limit: options.limit
|
|
16819
|
-
},
|
|
16820
|
-
writeOut,
|
|
16821
|
-
writeErr,
|
|
16822
|
-
setExitCode,
|
|
16823
|
-
authDir: io.authDir,
|
|
16824
|
-
env,
|
|
16825
|
-
remote: remoteClientForCli(io),
|
|
16826
|
-
format: options.format
|
|
16827
|
-
}));
|
|
17209
|
+
}, executeOperationIo(options.format)));
|
|
16828
17210
|
program.command(cliCommands$1.readResource).description("Read one MCP resource by URI.").argument("<caplet>").argument("<uri>").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, uri, options) => executeOperation(caplet, {
|
|
16829
17211
|
operation: "read_resource",
|
|
16830
17212
|
uri
|
|
16831
|
-
},
|
|
16832
|
-
writeOut,
|
|
16833
|
-
writeErr,
|
|
16834
|
-
setExitCode,
|
|
16835
|
-
authDir: io.authDir,
|
|
16836
|
-
env,
|
|
16837
|
-
remote: remoteClientForCli(io),
|
|
16838
|
-
format: options.format
|
|
16839
|
-
}));
|
|
17213
|
+
}, executeOperationIo(options.format)));
|
|
16840
17214
|
program.command(cliCommands$1.listPrompts).description("List MCP prompts for a configured MCP Caplet.").argument("<caplet>").option("--limit <n>", "maximum number of prompts to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, options.limit === void 0 ? { operation: "prompts" } : {
|
|
16841
17215
|
operation: "prompts",
|
|
16842
17216
|
limit: options.limit
|
|
16843
|
-
},
|
|
16844
|
-
writeOut,
|
|
16845
|
-
writeErr,
|
|
16846
|
-
setExitCode,
|
|
16847
|
-
authDir: io.authDir,
|
|
16848
|
-
env,
|
|
16849
|
-
remote: remoteClientForCli(io),
|
|
16850
|
-
format: options.format
|
|
16851
|
-
}));
|
|
17217
|
+
}, executeOperationIo(options.format)));
|
|
16852
17218
|
program.command(cliCommands$1.searchPrompts).description("Search MCP prompts for a configured MCP Caplet.").argument("<caplet>").argument("<query>").option("--limit <n>", "maximum number of prompts to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => executeOperation(caplet, options.limit === void 0 ? {
|
|
16853
17219
|
operation: "search_prompts",
|
|
16854
17220
|
query
|
|
@@ -16856,30 +17222,14 @@ function createProgram(io = {}) {
|
|
|
16856
17222
|
operation: "search_prompts",
|
|
16857
17223
|
query,
|
|
16858
17224
|
limit: options.limit
|
|
16859
|
-
},
|
|
16860
|
-
writeOut,
|
|
16861
|
-
writeErr,
|
|
16862
|
-
setExitCode,
|
|
16863
|
-
authDir: io.authDir,
|
|
16864
|
-
env,
|
|
16865
|
-
remote: remoteClientForCli(io),
|
|
16866
|
-
format: options.format
|
|
16867
|
-
}));
|
|
17225
|
+
}, executeOperationIo(options.format)));
|
|
16868
17226
|
program.command(cliCommands$1.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) => {
|
|
16869
17227
|
const { caplet, tool: prompt } = parseQualifiedTarget(capletOrTarget, promptArgument);
|
|
16870
17228
|
await executeOperation(caplet, {
|
|
16871
17229
|
operation: "get_prompt",
|
|
16872
17230
|
name: prompt,
|
|
16873
17231
|
args: parseJsonObjectOption(options.args, "get-prompt --args")
|
|
16874
|
-
},
|
|
16875
|
-
writeOut,
|
|
16876
|
-
writeErr,
|
|
16877
|
-
setExitCode,
|
|
16878
|
-
authDir: io.authDir,
|
|
16879
|
-
env,
|
|
16880
|
-
remote: remoteClientForCli(io),
|
|
16881
|
-
format: options.format
|
|
16882
|
-
});
|
|
17232
|
+
}, executeOperationIo(options.format));
|
|
16883
17233
|
});
|
|
16884
17234
|
program.command(cliCommands$1.complete).description("Complete an MCP prompt or resource-template argument.").argument("<caplet>").requiredOption("--argument <name>", "argument name").option("--value <value>", "argument prefix", "").option("--prompt <name>", "prompt name to complete").option("--resource-template <uri-template>", "resource template URI to complete").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, {
|
|
16885
17235
|
operation: "complete",
|
|
@@ -16888,15 +17238,7 @@ function createProgram(io = {}) {
|
|
|
16888
17238
|
name: options.argument,
|
|
16889
17239
|
value: options.value
|
|
16890
17240
|
}
|
|
16891
|
-
},
|
|
16892
|
-
writeOut,
|
|
16893
|
-
writeErr,
|
|
16894
|
-
setExitCode,
|
|
16895
|
-
authDir: io.authDir,
|
|
16896
|
-
env,
|
|
16897
|
-
remote: remoteClientForCli(io),
|
|
16898
|
-
format: options.format
|
|
16899
|
-
}));
|
|
17241
|
+
}, executeOperationIo(options.format)));
|
|
16900
17242
|
const config = program.command(cliCommands$1.config).description("Inspect Caplets config locations.");
|
|
16901
17243
|
config.command("path").description("Print the effective user config path.").action(() => {
|
|
16902
17244
|
writeOut(`${resolveConfigPath(currentConfigPath())}\n`);
|
|
@@ -17521,6 +17863,12 @@ async function executeLocalOperation(caplet, request, io, config) {
|
|
|
17521
17863
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
17522
17864
|
watch: false,
|
|
17523
17865
|
writeErr: io.writeErr,
|
|
17866
|
+
telemetryEnv: io.env ?? process.env,
|
|
17867
|
+
telemetryStateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(io.env ?? process.env),
|
|
17868
|
+
telemetrySurface: "cli",
|
|
17869
|
+
telemetryVisibility: "visible",
|
|
17870
|
+
telemetryRuntimeMode: runtimeModeForEnv(io.env ?? process.env),
|
|
17871
|
+
telemetryDebugSink: io.telemetryDebugSink,
|
|
17524
17872
|
...config ? { configLoader: () => config } : {}
|
|
17525
17873
|
});
|
|
17526
17874
|
try {
|