@caplets/core 0.27.0 → 0.28.1
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-D9253pYs.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 +833 -482
- 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-B6YvNthO.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-B6YvNthO.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-D9253pYs.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();
|
|
@@ -7064,6 +7132,7 @@ function daemonServeArgs(options) {
|
|
|
7064
7132
|
options.path
|
|
7065
7133
|
];
|
|
7066
7134
|
if (options.auth.type === "remote_credentials" && options.remoteCredentialStateDir) args.push("--remote-state-path", options.remoteCredentialStateDir);
|
|
7135
|
+
if (options.upstreamUrl) args.push("--upstream-url", options.upstreamUrl);
|
|
7067
7136
|
if (options.allowUnauthenticatedHttp) args.push("--allow-unauthenticated-http");
|
|
7068
7137
|
if (options.trustProxy) args.push("--trust-proxy");
|
|
7069
7138
|
return args;
|
|
@@ -10481,291 +10550,6 @@ var logger = (fn = console.log) => {
|
|
|
10481
10550
|
};
|
|
10482
10551
|
};
|
|
10483
10552
|
//#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
10553
|
//#region src/cli/install.ts
|
|
10770
10554
|
function installCaplets(repo, options = {}) {
|
|
10771
10555
|
const source = resolveInstallSource(repo);
|
|
@@ -12109,14 +11893,24 @@ function parseSupersededRefreshTokens(value) {
|
|
|
12109
11893
|
}
|
|
12110
11894
|
//#endregion
|
|
12111
11895
|
//#region src/serve/http.ts
|
|
11896
|
+
const CAPLETS_STACK_CHAIN_HEADER = "caplets-stack-chain";
|
|
11897
|
+
const ATTACH_SESSION_IDLE_TIMEOUT_MS = 10 * 6e4;
|
|
11898
|
+
const ATTACH_SESSION_PRUNE_INTERVAL_MS = 6e4;
|
|
12112
11899
|
function createHttpServeApp(options, engine, io = {}) {
|
|
12113
11900
|
const app = new Hono();
|
|
12114
11901
|
const sessions = /* @__PURE__ */ new Map();
|
|
11902
|
+
const attachSessions = /* @__PURE__ */ new Map();
|
|
11903
|
+
const defaultAttachSessions = /* @__PURE__ */ new Map();
|
|
11904
|
+
const defaultAttachSessionPromises = /* @__PURE__ */ new Map();
|
|
12115
11905
|
const attachEventStreams = /* @__PURE__ */ new Set();
|
|
11906
|
+
const attachSessionPruneTimer = setInterval(() => pruneIdleAttachSessions(), ATTACH_SESSION_PRUNE_INTERVAL_MS);
|
|
11907
|
+
attachSessionPruneTimer.unref?.();
|
|
12116
11908
|
const writeErr = io.writeErr ?? process.stderr.write.bind(process.stderr);
|
|
12117
11909
|
const paths = servicePaths(options.path);
|
|
11910
|
+
const stackIdentity = httpStackIdentity(options);
|
|
12118
11911
|
const authFlowStore = io.authFlowStore ?? new RemoteAuthFlowStore();
|
|
12119
11912
|
const exposeAttach = io.exposeAttach ?? true;
|
|
11913
|
+
const exposeAttachSessions = exposeAttach && Boolean(io.attachSessionFactory);
|
|
12120
11914
|
const remoteCredentialStore = remoteCredentialStoreForOptions(options, io.remoteCredentialStore);
|
|
12121
11915
|
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
11916
|
const protectedRouteAuth = routeAuth(options, remoteCredentialStore, paths.base);
|
|
@@ -12130,14 +11924,20 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12130
11924
|
name: "caplets",
|
|
12131
11925
|
transport: "http",
|
|
12132
11926
|
base: paths.base,
|
|
12133
|
-
versions: [versionDiscovery(paths,
|
|
11927
|
+
versions: [versionDiscovery(paths, {
|
|
11928
|
+
exposeAttach,
|
|
11929
|
+
exposeAttachSessions
|
|
11930
|
+
}, remote)],
|
|
12134
11931
|
auth: { type: options.auth.type },
|
|
12135
11932
|
...remote ? { remote } : {}
|
|
12136
11933
|
});
|
|
12137
11934
|
});
|
|
12138
11935
|
app.get(paths.version, (c) => {
|
|
12139
11936
|
const remote = remoteCredentialStore ? remoteHostMetadata(c.req.url, paths.base, options, (name) => c.req.header(name)) : void 0;
|
|
12140
|
-
return c.json(versionDiscovery(paths,
|
|
11937
|
+
return c.json(versionDiscovery(paths, {
|
|
11938
|
+
exposeAttach,
|
|
11939
|
+
exposeAttachSessions
|
|
11940
|
+
}, remote));
|
|
12141
11941
|
});
|
|
12142
11942
|
app.get(paths.health, (c) => c.json({ status: "ok" }));
|
|
12143
11943
|
if (remoteCredentialStore) {
|
|
@@ -12272,14 +12072,68 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12272
12072
|
return session.transport.handleRequest(c);
|
|
12273
12073
|
});
|
|
12274
12074
|
if (exposeAttach) {
|
|
12075
|
+
if (io.attachSessionFactory) {
|
|
12076
|
+
app.post(paths.attachSessions, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12077
|
+
try {
|
|
12078
|
+
const metadata = parseAttachSessionMetadata(await parseJsonObject(c.req.json(), "Attach session request"), { allowProjectContext: allowAttachSessionProjectContext(options, c.req.url, (name) => c.req.header(name)) });
|
|
12079
|
+
const context = attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER));
|
|
12080
|
+
const sessionId = randomUUID();
|
|
12081
|
+
const session = await io.attachSessionFactory(metadata, context);
|
|
12082
|
+
attachSessions.set(sessionId, {
|
|
12083
|
+
session,
|
|
12084
|
+
lastUsedAt: Date.now()
|
|
12085
|
+
});
|
|
12086
|
+
pruneIdleAttachSessions();
|
|
12087
|
+
return c.json({ sessionId }, 201);
|
|
12088
|
+
} catch (error) {
|
|
12089
|
+
const response = attachErrorResponse(error);
|
|
12090
|
+
return c.json(response.body, response.status);
|
|
12091
|
+
}
|
|
12092
|
+
});
|
|
12093
|
+
app.delete(routePath(paths.attachSessions, ":sessionId"), attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12094
|
+
const sessionId = c.req.param("sessionId");
|
|
12095
|
+
if (!sessionId) return c.json({
|
|
12096
|
+
ok: false,
|
|
12097
|
+
error: { code: "REQUEST_INVALID" }
|
|
12098
|
+
}, 400);
|
|
12099
|
+
const record = attachSessions.get(sessionId);
|
|
12100
|
+
attachSessions.delete(sessionId);
|
|
12101
|
+
await record?.session.close();
|
|
12102
|
+
return c.json({ ok: true });
|
|
12103
|
+
});
|
|
12104
|
+
}
|
|
12275
12105
|
app.get(paths.attachManifest, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12276
|
-
|
|
12277
|
-
|
|
12106
|
+
try {
|
|
12107
|
+
const attachSessionId = c.req.header(CAPLETS_ATTACH_SESSION_HEADER);
|
|
12108
|
+
const attachSession = attachSessionId ? attachSessionForRequest(attachSessionId) : await fallbackAttachSession(attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER)));
|
|
12109
|
+
if (attachSession) return c.json(await attachSession.manifest());
|
|
12110
|
+
const attachProjection = await buildAttachProjection(engine);
|
|
12111
|
+
return c.json(attachProjection.manifest);
|
|
12112
|
+
} catch (error) {
|
|
12113
|
+
const response = attachErrorResponse(error);
|
|
12114
|
+
return c.json(response.body, response.status);
|
|
12115
|
+
}
|
|
12116
|
+
});
|
|
12117
|
+
app.get(paths.attachEvents, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12118
|
+
try {
|
|
12119
|
+
const attachSessionId = c.req.header(CAPLETS_ATTACH_SESSION_HEADER);
|
|
12120
|
+
return attachEventsResponse(attachEventSource(engine, attachSessionId ? attachSessionForRequest(attachSessionId) : await fallbackAttachSession(attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER)))), attachEventStreams, { onActivity: () => {
|
|
12121
|
+
if (attachSessionId) touchAttachSession(attachSessionId);
|
|
12122
|
+
} });
|
|
12123
|
+
} catch (error) {
|
|
12124
|
+
const response = attachErrorResponse(error);
|
|
12125
|
+
return c.json(response.body, response.status);
|
|
12126
|
+
}
|
|
12278
12127
|
});
|
|
12279
|
-
app.get(paths.attachEvents, attachHostProtection, protectedRouteAuth, () => attachEventsResponse(engine, attachEventStreams));
|
|
12280
12128
|
app.post(paths.attachInvoke, attachHostProtection, protectedRouteAuth, async (c) => {
|
|
12281
12129
|
try {
|
|
12282
12130
|
const request = await parseAttachInvokeRequest(c.req.json());
|
|
12131
|
+
const attachSessionId = c.req.header(CAPLETS_ATTACH_SESSION_HEADER);
|
|
12132
|
+
const attachSession = attachSessionId ? attachSessionForRequest(attachSessionId) : await fallbackAttachSession(attachSessionContext(c.req.header(CAPLETS_STACK_CHAIN_HEADER)));
|
|
12133
|
+
if (attachSession) return c.json({
|
|
12134
|
+
ok: true,
|
|
12135
|
+
data: await attachSession.invoke(request)
|
|
12136
|
+
});
|
|
12283
12137
|
const result = await invokeAttachExport(engine, await buildAttachProjection(engine), request);
|
|
12284
12138
|
return c.json({
|
|
12285
12139
|
ok: true,
|
|
@@ -12291,6 +12145,52 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12291
12145
|
}
|
|
12292
12146
|
});
|
|
12293
12147
|
}
|
|
12148
|
+
function attachSessionForRequest(sessionId) {
|
|
12149
|
+
pruneIdleAttachSessions();
|
|
12150
|
+
if (!sessionId) return void 0;
|
|
12151
|
+
const record = attachSessions.get(sessionId);
|
|
12152
|
+
if (!record) throw new CapletsError("REQUEST_INVALID", "Attach session was not found.");
|
|
12153
|
+
record.lastUsedAt = Date.now();
|
|
12154
|
+
return record.session;
|
|
12155
|
+
}
|
|
12156
|
+
async function fallbackAttachSession(context) {
|
|
12157
|
+
if (!io.defaultAttachSessionFactory) return void 0;
|
|
12158
|
+
const key = context.stackChain.join("\0");
|
|
12159
|
+
const existing = defaultAttachSessions.get(key);
|
|
12160
|
+
if (existing) return existing;
|
|
12161
|
+
let pending = defaultAttachSessionPromises.get(key);
|
|
12162
|
+
if (!pending) {
|
|
12163
|
+
pending = Promise.resolve(io.defaultAttachSessionFactory({}, context)).then((session) => {
|
|
12164
|
+
defaultAttachSessions.set(key, session);
|
|
12165
|
+
defaultAttachSessionPromises.delete(key);
|
|
12166
|
+
return session;
|
|
12167
|
+
}, (error) => {
|
|
12168
|
+
defaultAttachSessionPromises.delete(key);
|
|
12169
|
+
throw error;
|
|
12170
|
+
});
|
|
12171
|
+
defaultAttachSessionPromises.set(key, pending);
|
|
12172
|
+
}
|
|
12173
|
+
return await pending;
|
|
12174
|
+
}
|
|
12175
|
+
function attachSessionContext(header) {
|
|
12176
|
+
const incoming = stackChainFromHeader(header);
|
|
12177
|
+
if (incoming.includes(stackIdentity)) throw new CapletsError("REQUEST_INVALID", "Stacked runtime upstream cycle detected.");
|
|
12178
|
+
return { stackChain: [...incoming, stackIdentity] };
|
|
12179
|
+
}
|
|
12180
|
+
function touchAttachSession(sessionId) {
|
|
12181
|
+
const record = attachSessions.get(sessionId);
|
|
12182
|
+
if (record) record.lastUsedAt = Date.now();
|
|
12183
|
+
}
|
|
12184
|
+
function pruneIdleAttachSessions() {
|
|
12185
|
+
const expiresBefore = Date.now() - ATTACH_SESSION_IDLE_TIMEOUT_MS;
|
|
12186
|
+
for (const [sessionId, record] of attachSessions) {
|
|
12187
|
+
if (record.lastUsedAt >= expiresBefore) continue;
|
|
12188
|
+
attachSessions.delete(sessionId);
|
|
12189
|
+
record.session.close().catch((error) => {
|
|
12190
|
+
writeErr(`Could not close idle attach session: ${errorMessage$1(error)}\n`);
|
|
12191
|
+
});
|
|
12192
|
+
}
|
|
12193
|
+
}
|
|
12294
12194
|
app.post(paths.control, protectedRouteAuth, async (c) => {
|
|
12295
12195
|
let request;
|
|
12296
12196
|
try {
|
|
@@ -12314,31 +12214,9 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12314
12214
|
bindingId: c.req.param("bindingId"),
|
|
12315
12215
|
state: "not_attached"
|
|
12316
12216
|
}));
|
|
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
|
-
}));
|
|
12217
|
+
app.post(routePath(paths.projectBindings, "sessions"), protectedRouteAuth, (c) => c.json(projectBindingUnsupported(), 501));
|
|
12218
|
+
app.post(routePath(paths.projectBindings, ":bindingId/heartbeat"), protectedRouteAuth, (c) => c.json(projectBindingUnsupported(c.req.param("bindingId")), 501));
|
|
12219
|
+
app.delete(routePath(paths.projectBindings, ":bindingId/session"), protectedRouteAuth, (c) => c.json(projectBindingUnsupported(c.req.param("bindingId")), 501));
|
|
12342
12220
|
app.get(routePath(paths.control, "auth/callback/:flowId"), async (c) => {
|
|
12343
12221
|
const flowId = c.req.param("flowId");
|
|
12344
12222
|
const result = await dispatchRemoteCliRequest({
|
|
@@ -12353,11 +12231,17 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
12353
12231
|
});
|
|
12354
12232
|
app.notFound((c) => c.json({ error: "not_found" }, 404));
|
|
12355
12233
|
app.closeCapletsSessions = async () => {
|
|
12234
|
+
clearInterval(attachSessionPruneTimer);
|
|
12356
12235
|
for (const stream of attachEventStreams) stream.close();
|
|
12357
12236
|
await Promise.allSettled([...sessions.values()].map(async (session) => {
|
|
12358
12237
|
await session.server.close();
|
|
12359
12238
|
}));
|
|
12360
12239
|
sessions.clear();
|
|
12240
|
+
await Promise.allSettled([...attachSessions.values()].map((record) => record.session.close()));
|
|
12241
|
+
attachSessions.clear();
|
|
12242
|
+
await Promise.allSettled([...defaultAttachSessions.values()].map((session) => session.close()));
|
|
12243
|
+
defaultAttachSessions.clear();
|
|
12244
|
+
defaultAttachSessionPromises.clear();
|
|
12361
12245
|
};
|
|
12362
12246
|
if (options.warnUnauthenticatedNetwork) writeErr(`Warning: Caplets MCP HTTP server is listening on ${options.host} without authentication.\n`);
|
|
12363
12247
|
return app;
|
|
@@ -12390,9 +12274,36 @@ function remoteCredentialSourceHint(trustProxy, header) {
|
|
|
12390
12274
|
const sourceHint = firstForwardedValue(header("x-forwarded-for")) ?? firstForwardedValue(header("x-real-ip")) ?? firstForwardedValue(header("cf-connecting-ip"));
|
|
12391
12275
|
return sourceHint ? { sourceHint } : {};
|
|
12392
12276
|
}
|
|
12277
|
+
function projectBindingUnsupported(bindingId) {
|
|
12278
|
+
return {
|
|
12279
|
+
ok: false,
|
|
12280
|
+
error: {
|
|
12281
|
+
code: "UNSUPPORTED_CAPABILITY",
|
|
12282
|
+
message: "Self-hosted Project Binding sessions are not implemented by this runtime."
|
|
12283
|
+
},
|
|
12284
|
+
...bindingId ? { binding: {
|
|
12285
|
+
bindingId,
|
|
12286
|
+
state: "not_attached"
|
|
12287
|
+
} } : {}
|
|
12288
|
+
};
|
|
12289
|
+
}
|
|
12393
12290
|
function firstForwardedValue(value) {
|
|
12394
12291
|
return value?.split(",", 1)[0]?.trim() || void 0;
|
|
12395
12292
|
}
|
|
12293
|
+
function errorMessage$1(error) {
|
|
12294
|
+
return error instanceof Error ? error.message : String(error);
|
|
12295
|
+
}
|
|
12296
|
+
function httpStackIdentity(options) {
|
|
12297
|
+
const origin = options.publicOrigin ?? `http://${formatHost$2(options.host)}:${options.port}`;
|
|
12298
|
+
const url = new URL(origin);
|
|
12299
|
+
url.pathname = options.path;
|
|
12300
|
+
url.search = "";
|
|
12301
|
+
url.hash = "";
|
|
12302
|
+
return url.toString();
|
|
12303
|
+
}
|
|
12304
|
+
function stackChainFromHeader(header) {
|
|
12305
|
+
return (header ?? "").split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
12306
|
+
}
|
|
12396
12307
|
function remoteHostMetadata(requestUrl, basePath, options, header) {
|
|
12397
12308
|
const audience = remoteCredentialHostUrl(requestUrl, basePath, options.publicOrigin, options.trustProxy, header);
|
|
12398
12309
|
return {
|
|
@@ -12400,7 +12311,9 @@ function remoteHostMetadata(requestUrl, basePath, options, header) {
|
|
|
12400
12311
|
audience
|
|
12401
12312
|
};
|
|
12402
12313
|
}
|
|
12403
|
-
function versionDiscovery(paths,
|
|
12314
|
+
function versionDiscovery(paths, options = {}, remote) {
|
|
12315
|
+
const exposeAttach = options.exposeAttach ?? true;
|
|
12316
|
+
const exposeAttachSessions = options.exposeAttachSessions ?? false;
|
|
12404
12317
|
return {
|
|
12405
12318
|
version: 1,
|
|
12406
12319
|
path: paths.version,
|
|
@@ -12409,6 +12322,7 @@ function versionDiscovery(paths, exposeAttach = true, remote) {
|
|
|
12409
12322
|
mcp: paths.mcp,
|
|
12410
12323
|
admin: paths.control,
|
|
12411
12324
|
...exposeAttach ? {
|
|
12325
|
+
...exposeAttachSessions ? { attachSessions: paths.attachSessions } : {},
|
|
12412
12326
|
attachManifest: paths.attachManifest,
|
|
12413
12327
|
attachEvents: paths.attachEvents,
|
|
12414
12328
|
attachInvoke: paths.attachInvoke
|
|
@@ -12438,10 +12352,82 @@ async function parseAttachInvokeRequest(input) {
|
|
|
12438
12352
|
function isAttachExportKind(value) {
|
|
12439
12353
|
return value === "caplet" || value === "tool" || value === "resource" || value === "resourceTemplate" || value === "prompt" || value === "completion";
|
|
12440
12354
|
}
|
|
12441
|
-
function
|
|
12355
|
+
function parseAttachSessionMetadata(input, options) {
|
|
12356
|
+
const rawProjectRoot = optionalStringField(input, "projectRoot");
|
|
12357
|
+
const rawProjectConfigPath = optionalStringField(input, "projectConfigPath");
|
|
12358
|
+
if (!options.allowProjectContext && (rawProjectRoot || rawProjectConfigPath)) throw new CapletsError("REQUEST_INVALID", "Attach session project context is only accepted by loopback runtimes.");
|
|
12359
|
+
const projectRoot = canonicalProjectRoot(rawProjectRoot);
|
|
12360
|
+
const projectConfigPath = canonicalProjectConfigPath(rawProjectConfigPath, projectRoot);
|
|
12361
|
+
return {
|
|
12362
|
+
...projectRoot ? { projectRoot } : {},
|
|
12363
|
+
...projectConfigPath ? { projectConfigPath } : {}
|
|
12364
|
+
};
|
|
12365
|
+
}
|
|
12366
|
+
function canonicalProjectRoot(projectRoot) {
|
|
12367
|
+
if (!projectRoot) return void 0;
|
|
12368
|
+
try {
|
|
12369
|
+
const canonical = realpathSync(projectRoot);
|
|
12370
|
+
if (!statSync(canonical).isDirectory()) throw new Error("projectRoot is not a directory.");
|
|
12371
|
+
return canonical;
|
|
12372
|
+
} catch (error) {
|
|
12373
|
+
throw new CapletsError("REQUEST_INVALID", "projectRoot must be an existing directory.", error);
|
|
12374
|
+
}
|
|
12375
|
+
}
|
|
12376
|
+
function canonicalProjectConfigPath(projectConfigPath, projectRoot) {
|
|
12377
|
+
if (!projectRoot) {
|
|
12378
|
+
if (!projectConfigPath) return void 0;
|
|
12379
|
+
throw new CapletsError("REQUEST_INVALID", "projectConfigPath requires projectRoot.");
|
|
12380
|
+
}
|
|
12381
|
+
const expectedProjectConfigPath = resolve(projectRoot, ".caplets", "config.json");
|
|
12382
|
+
const lexicalConfigPath = projectConfigPath === void 0 ? expectedProjectConfigPath : isAbsolute(projectConfigPath) ? projectConfigPath : resolve(projectRoot, projectConfigPath);
|
|
12383
|
+
if (resolve(projectConfigPath === void 0 ? expectedProjectConfigPath : canonicalizeExistingParentPath(lexicalConfigPath)) !== expectedProjectConfigPath) throw new CapletsError("REQUEST_INVALID", "projectConfigPath must be <projectRoot>/.caplets/config.json.");
|
|
12384
|
+
if (!existsSync(expectedProjectConfigPath)) return expectedProjectConfigPath;
|
|
12385
|
+
const expected = realpathSync(expectedProjectConfigPath);
|
|
12386
|
+
if (!pathIsInside(expected, projectRoot)) throw new CapletsError("REQUEST_INVALID", "projectConfigPath must resolve inside projectRoot.");
|
|
12387
|
+
return expected;
|
|
12388
|
+
}
|
|
12389
|
+
function canonicalizeExistingParentPath(path) {
|
|
12390
|
+
const parent = dirname(path);
|
|
12391
|
+
try {
|
|
12392
|
+
return resolve(realpathSync(parent), path.slice(parent.length + 1));
|
|
12393
|
+
} catch {
|
|
12394
|
+
return resolve(path);
|
|
12395
|
+
}
|
|
12396
|
+
}
|
|
12397
|
+
function pathIsInside(candidate, root) {
|
|
12398
|
+
const rel = relative(root, candidate);
|
|
12399
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
12400
|
+
}
|
|
12401
|
+
function allowAttachSessionProjectContext(options, requestUrl, header) {
|
|
12402
|
+
if (!options.loopback) return false;
|
|
12403
|
+
return isLoopbackHost(attachRequestHost(options, requestUrl, header));
|
|
12404
|
+
}
|
|
12405
|
+
function attachRequestHost(options, requestUrl, header) {
|
|
12406
|
+
const fallback = new URL(requestUrl).host;
|
|
12407
|
+
const host = (options.trustProxy ? firstForwardedValue(header("x-forwarded-host")) : void 0) ?? header("host") ?? fallback;
|
|
12408
|
+
try {
|
|
12409
|
+
return new URL(`http://${host}`).hostname;
|
|
12410
|
+
} catch {
|
|
12411
|
+
return host.split(":")[0] ?? host;
|
|
12412
|
+
}
|
|
12413
|
+
}
|
|
12414
|
+
function attachEventSource(engine, session) {
|
|
12415
|
+
if (session) return {
|
|
12416
|
+
manifestRevision: async () => (await session.manifest()).revision,
|
|
12417
|
+
onManifestChanged: (listener) => session.onManifestChanged(listener)
|
|
12418
|
+
};
|
|
12419
|
+
return {
|
|
12420
|
+
manifestRevision: async () => (await buildAttachProjection(engine)).manifest.revision,
|
|
12421
|
+
onManifestChanged: (listener) => engine.onReload(() => {
|
|
12422
|
+
listener();
|
|
12423
|
+
})
|
|
12424
|
+
};
|
|
12425
|
+
}
|
|
12426
|
+
function attachEventsResponse(source, activeStreams, options = {}) {
|
|
12442
12427
|
const encoder = new TextEncoder();
|
|
12443
12428
|
let unsubscribe = () => void 0;
|
|
12444
12429
|
let activeStream;
|
|
12430
|
+
let keepAliveTimer;
|
|
12445
12431
|
let closed = false;
|
|
12446
12432
|
const readable = new ReadableStream({
|
|
12447
12433
|
start(controller) {
|
|
@@ -12449,17 +12435,30 @@ function attachEventsResponse(engine, activeStreams) {
|
|
|
12449
12435
|
if (closed) return;
|
|
12450
12436
|
closed = true;
|
|
12451
12437
|
unsubscribe();
|
|
12438
|
+
if (keepAliveTimer) clearInterval(keepAliveTimer);
|
|
12452
12439
|
if (activeStream) activeStreams.delete(activeStream);
|
|
12453
12440
|
try {
|
|
12454
12441
|
controller.close();
|
|
12455
12442
|
} catch {}
|
|
12456
12443
|
} };
|
|
12457
12444
|
activeStreams.add(activeStream);
|
|
12445
|
+
options.onActivity?.();
|
|
12458
12446
|
controller.enqueue(encoder.encode(": connected\n\n"));
|
|
12459
|
-
|
|
12460
|
-
|
|
12447
|
+
keepAliveTimer = setInterval(() => {
|
|
12448
|
+
if (closed) return;
|
|
12449
|
+
options.onActivity?.();
|
|
12450
|
+
try {
|
|
12451
|
+
controller.enqueue(encoder.encode(": keepalive\n\n"));
|
|
12452
|
+
} catch {
|
|
12453
|
+
activeStream?.close();
|
|
12454
|
+
}
|
|
12455
|
+
}, 3e4);
|
|
12456
|
+
keepAliveTimer.unref?.();
|
|
12457
|
+
unsubscribe = source.onManifestChanged(() => {
|
|
12458
|
+
options.onActivity?.();
|
|
12459
|
+
source.manifestRevision().then((revision) => {
|
|
12461
12460
|
if (closed) return;
|
|
12462
|
-
controller.enqueue(encoder.encode(`event: manifest_changed\ndata: ${JSON.stringify({ revision
|
|
12461
|
+
controller.enqueue(encoder.encode(`event: manifest_changed\ndata: ${JSON.stringify({ revision })}\n\n`));
|
|
12463
12462
|
}).catch(() => void 0);
|
|
12464
12463
|
});
|
|
12465
12464
|
},
|
|
@@ -12489,7 +12488,7 @@ async function serveHttp(options, engineOptions = {}, writeErr = (value) => proc
|
|
|
12489
12488
|
}
|
|
12490
12489
|
});
|
|
12491
12490
|
const paths = servicePaths(options.path);
|
|
12492
|
-
const origin = `http://${formatHost$
|
|
12491
|
+
const origin = `http://${formatHost$2(options.host)}:${options.port}`;
|
|
12493
12492
|
const baseUrl = `${origin}${paths.base === "/" ? "" : paths.base}`;
|
|
12494
12493
|
installHttpSignalHandlers(serve({
|
|
12495
12494
|
fetch: app.fetch,
|
|
@@ -12504,20 +12503,26 @@ async function serveHttp(options, engineOptions = {}, writeErr = (value) => proc
|
|
|
12504
12503
|
writeErr(`Auth: ${authDescription(options)}\n`);
|
|
12505
12504
|
}), app, engine, writeErr);
|
|
12506
12505
|
}
|
|
12507
|
-
async function serveHttpWithSessionFactory(options, createSession, writeErr = (value) => process.stderr.write(value)) {
|
|
12508
|
-
const resolvedEngineOptions = {
|
|
12506
|
+
async function serveHttpWithSessionFactory(options, createSession, writeErr = (value) => process.stderr.write(value), io = {}, engineOptions = {}) {
|
|
12507
|
+
const resolvedEngineOptions = {
|
|
12508
|
+
exposeLocalArtifactPaths: false,
|
|
12509
|
+
vaultRecoveryTarget: "remote",
|
|
12510
|
+
...engineOptions
|
|
12511
|
+
};
|
|
12509
12512
|
const engine = new CapletsEngine(resolvedEngineOptions);
|
|
12510
12513
|
const app = createHttpServeApp(options, engine, {
|
|
12511
12514
|
writeErr,
|
|
12512
|
-
exposeAttach: false,
|
|
12515
|
+
exposeAttach: io.exposeAttach ?? false,
|
|
12513
12516
|
sessionFactory: createSession,
|
|
12517
|
+
...io.attachSessionFactory ? { attachSessionFactory: io.attachSessionFactory } : {},
|
|
12518
|
+
...io.defaultAttachSessionFactory ? { defaultAttachSessionFactory: io.defaultAttachSessionFactory } : {},
|
|
12514
12519
|
control: {
|
|
12515
12520
|
...resolvedEngineOptions,
|
|
12516
|
-
projectCapletsRoot:
|
|
12521
|
+
projectCapletsRoot: projectCapletsRootForEngineOptions(resolvedEngineOptions)
|
|
12517
12522
|
}
|
|
12518
12523
|
});
|
|
12519
12524
|
const paths = servicePaths(options.path);
|
|
12520
|
-
const origin = `http://${formatHost$
|
|
12525
|
+
const origin = `http://${formatHost$2(options.host)}:${options.port}`;
|
|
12521
12526
|
const baseUrl = `${origin}${paths.base === "/" ? "" : paths.base}`;
|
|
12522
12527
|
installHttpSignalHandlers(serve({
|
|
12523
12528
|
fetch: app.fetch,
|
|
@@ -12549,6 +12554,7 @@ function servicePaths(base) {
|
|
|
12549
12554
|
version,
|
|
12550
12555
|
mcp: routePath(version, "mcp"),
|
|
12551
12556
|
control: routePath(version, "admin"),
|
|
12557
|
+
attachSessions: routePath(attach, "sessions"),
|
|
12552
12558
|
attachManifest: routePath(attach, "manifest"),
|
|
12553
12559
|
attachEvents: routePath(attach, "events"),
|
|
12554
12560
|
attachInvoke: routePath(attach, "invoke"),
|
|
@@ -12692,7 +12698,7 @@ function installHttpSignalHandlers(server, app, engine, writeErr) {
|
|
|
12692
12698
|
function closeAllServerConnections(server) {
|
|
12693
12699
|
server.closeAllConnections?.call(server);
|
|
12694
12700
|
}
|
|
12695
|
-
function formatHost$
|
|
12701
|
+
function formatHost$2(host) {
|
|
12696
12702
|
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
12697
12703
|
}
|
|
12698
12704
|
//#endregion
|
|
@@ -12823,11 +12829,11 @@ async function allocateLoopbackPort() {
|
|
|
12823
12829
|
if (!address || typeof address === "string") throw new CapletsError("SERVER_UNAVAILABLE", "Could not allocate a validation port.");
|
|
12824
12830
|
return address.port;
|
|
12825
12831
|
}
|
|
12826
|
-
function formatHost(host) {
|
|
12832
|
+
function formatHost$1(host) {
|
|
12827
12833
|
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
12828
12834
|
}
|
|
12829
12835
|
function healthUrl(config, port = config.serve.port) {
|
|
12830
|
-
return `http://${formatHost(config.serve.host)}:${port}${servicePaths(config.serve.path).health}`;
|
|
12836
|
+
return `http://${formatHost$1(config.serve.host)}:${port}${servicePaths(config.serve.path).health}`;
|
|
12831
12837
|
}
|
|
12832
12838
|
function validationSpawnCommand(config) {
|
|
12833
12839
|
const planned = serviceCommand(config);
|
|
@@ -13289,6 +13295,7 @@ function mergeServeOptions(existing, install) {
|
|
|
13289
13295
|
...install.port !== void 0 ? { port: install.port } : existing?.serve.port ? { port: existing.serve.port } : {},
|
|
13290
13296
|
...install.path !== void 0 ? { path: install.path } : existing?.serve.path ? { path: existing.serve.path } : {},
|
|
13291
13297
|
...install.remoteStatePath !== void 0 ? { remoteStatePath: install.remoteStatePath } : existing?.serve.remoteCredentialStateDir ? { remoteStatePath: existing.serve.remoteCredentialStateDir } : {},
|
|
13298
|
+
...install.upstreamUrl !== void 0 ? { upstreamUrl: install.upstreamUrl } : existing?.serve.upstreamUrl ? { upstreamUrl: existing.serve.upstreamUrl } : {},
|
|
13292
13299
|
...install.allowUnauthenticatedHttp !== void 0 ? { allowUnauthenticatedHttp: install.allowUnauthenticatedHttp } : existing ? { allowUnauthenticatedHttp: existing.serve.allowUnauthenticatedHttp } : {},
|
|
13293
13300
|
...install.trustProxy !== void 0 ? { trustProxy: install.trustProxy } : existing ? { trustProxy: existing.serve.trustProxy } : {},
|
|
13294
13301
|
...existing && existing.serve.auth.type === "development_unauthenticated" && install.allowUnauthenticatedHttp === void 0 ? { preserveUnauthenticatedAuth: true } : {}
|
|
@@ -14708,6 +14715,7 @@ function createAttachNativeService(options, io) {
|
|
|
14708
14715
|
return createNativeCapletsService({
|
|
14709
14716
|
mode: options.selection.kind === "hosted_cloud" ? "cloud" : "remote",
|
|
14710
14717
|
configPath: options.configPath,
|
|
14718
|
+
projectRoot: options.projectRoot,
|
|
14711
14719
|
projectConfigPath: options.projectConfigPath,
|
|
14712
14720
|
...options.authDir ? { authDir: options.authDir } : {},
|
|
14713
14721
|
remote: {
|
|
@@ -15325,15 +15333,95 @@ async function serveResolvedCaplets(resolved, engineOptions = {}, writeErr) {
|
|
|
15325
15333
|
});
|
|
15326
15334
|
return;
|
|
15327
15335
|
}
|
|
15336
|
+
if (resolved.upstreamUrl) {
|
|
15337
|
+
await serveHttpWithUpstream(resolved, resolved.upstreamUrl, engineOptions, writeErr);
|
|
15338
|
+
return;
|
|
15339
|
+
}
|
|
15328
15340
|
await serveHttp(resolved, {
|
|
15329
15341
|
...engineOptions,
|
|
15330
15342
|
...writeErr ? { writeErr } : {}
|
|
15331
15343
|
}, writeErr);
|
|
15332
15344
|
}
|
|
15345
|
+
async function serveHttpWithUpstream(resolved, upstreamUrl, engineOptions, writeErr) {
|
|
15346
|
+
const stackChain = [serveStackIdentity(resolved)];
|
|
15347
|
+
await serveHttpWithSessionFactory(resolved, async () => new NativeCapletsMcpSession(await createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, {}, stackChain)), writeErr, {
|
|
15348
|
+
exposeAttach: true,
|
|
15349
|
+
defaultAttachSessionFactory: async (_metadata, context) => nativeAttachSession(await createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, {}, context.stackChain)),
|
|
15350
|
+
attachSessionFactory: async (metadata, context) => {
|
|
15351
|
+
return nativeAttachSession(await createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, metadata, context.stackChain));
|
|
15352
|
+
}
|
|
15353
|
+
}, engineOptions);
|
|
15354
|
+
}
|
|
15355
|
+
async function createReloadedUpstreamService(upstreamUrl, engineOptions, writeErr, metadata = {}, stackChain = []) {
|
|
15356
|
+
const service = createUpstreamNativeService(upstreamUrl, engineOptions, writeErr, metadata, stackChain);
|
|
15357
|
+
try {
|
|
15358
|
+
await service.reload();
|
|
15359
|
+
return service;
|
|
15360
|
+
} catch (error) {
|
|
15361
|
+
await service.close().catch(() => void 0);
|
|
15362
|
+
throw error;
|
|
15363
|
+
}
|
|
15364
|
+
}
|
|
15365
|
+
function createUpstreamNativeService(upstreamUrl, engineOptions, writeErr, metadata = {}, stackChain = []) {
|
|
15366
|
+
return createNativeCapletsService({
|
|
15367
|
+
...engineOptions,
|
|
15368
|
+
...metadata.projectRoot ? { projectRoot: metadata.projectRoot } : {},
|
|
15369
|
+
...metadata.projectConfigPath ? { projectConfigPath: metadata.projectConfigPath } : {},
|
|
15370
|
+
mode: isCapletsCloudUrl(upstreamUrl) ? "cloud" : "remote",
|
|
15371
|
+
remote: {
|
|
15372
|
+
url: upstreamUrl,
|
|
15373
|
+
...stackChain.length > 0 ? { requestHeaders: { [CAPLETS_STACK_CHAIN_HEADER]: stackChain.join(",") } } : {}
|
|
15374
|
+
},
|
|
15375
|
+
...writeErr ? { writeErr } : {}
|
|
15376
|
+
});
|
|
15377
|
+
}
|
|
15378
|
+
function serveStackIdentity(options) {
|
|
15379
|
+
const origin = options.publicOrigin ?? `http://${formatHost(options.host)}:${options.port}`;
|
|
15380
|
+
const url = new URL(origin);
|
|
15381
|
+
url.pathname = options.path;
|
|
15382
|
+
url.search = "";
|
|
15383
|
+
url.hash = "";
|
|
15384
|
+
return url.toString();
|
|
15385
|
+
}
|
|
15386
|
+
function formatHost(host) {
|
|
15387
|
+
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
15388
|
+
}
|
|
15389
|
+
function nativeAttachSession(service) {
|
|
15390
|
+
let cachedProjection;
|
|
15391
|
+
const getProjection = async () => {
|
|
15392
|
+
cachedProjection ??= await buildNativeAttachProjection(service);
|
|
15393
|
+
return cachedProjection;
|
|
15394
|
+
};
|
|
15395
|
+
const unsubscribe = service.onToolsChanged(() => {
|
|
15396
|
+
cachedProjection = void 0;
|
|
15397
|
+
});
|
|
15398
|
+
return {
|
|
15399
|
+
manifest: async () => (await getProjection()).manifest,
|
|
15400
|
+
invoke: async (request) => await invokeNativeAttachExport(service, await getProjection(), request),
|
|
15401
|
+
onManifestChanged: (listener) => service.onToolsChanged(() => listener()),
|
|
15402
|
+
close: async () => {
|
|
15403
|
+
unsubscribe();
|
|
15404
|
+
await service.close();
|
|
15405
|
+
}
|
|
15406
|
+
};
|
|
15407
|
+
}
|
|
15333
15408
|
//#endregion
|
|
15334
15409
|
//#region src/cli.ts
|
|
15335
15410
|
async function runCli(args, io = {}) {
|
|
15336
|
-
|
|
15411
|
+
let observedExitCode = 0;
|
|
15412
|
+
const wrappedIo = {
|
|
15413
|
+
...io,
|
|
15414
|
+
setExitCode: (code) => {
|
|
15415
|
+
observedExitCode = code;
|
|
15416
|
+
if (io.setExitCode) io.setExitCode(code);
|
|
15417
|
+
else process.exitCode = code;
|
|
15418
|
+
}
|
|
15419
|
+
};
|
|
15420
|
+
const program = createProgram(wrappedIo);
|
|
15421
|
+
const trackedCommand = telemetryCommandFamilyFromArgs(args);
|
|
15422
|
+
const startedAt = Date.now();
|
|
15423
|
+
const telemetryContext = telemetryContextForIo(wrappedIo);
|
|
15424
|
+
const dispatcher = createTelemetryDispatcher({ stateDir: telemetryContext.stateDir });
|
|
15337
15425
|
try {
|
|
15338
15426
|
if (args.length === 0) {
|
|
15339
15427
|
program.outputHelp();
|
|
@@ -15344,12 +15432,35 @@ async function runCli(args, io = {}) {
|
|
|
15344
15432
|
"caplets",
|
|
15345
15433
|
...args
|
|
15346
15434
|
]);
|
|
15435
|
+
if (trackedCommand) await captureCliTelemetry(telemetryContext, {
|
|
15436
|
+
debugSink: wrappedIo.telemetryDebugSink,
|
|
15437
|
+
dispatcher,
|
|
15438
|
+
commandFamily: trackedCommand.commandFamily,
|
|
15439
|
+
surface: trackedCommand.surface,
|
|
15440
|
+
outcome: observedExitCode === 0 ? "success" : "failure",
|
|
15441
|
+
startedAt
|
|
15442
|
+
});
|
|
15347
15443
|
} catch (error) {
|
|
15444
|
+
let normalizedError = error;
|
|
15445
|
+
let captureProductEvent = true;
|
|
15348
15446
|
if (error instanceof CommanderError) {
|
|
15349
15447
|
if (error.code === "commander.helpDisplayed" || error.code === "commander.version" || error.message === "(outputHelp)") return;
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15448
|
+
normalizedError = new CapletsError("REQUEST_INVALID", error.message);
|
|
15449
|
+
captureProductEvent = false;
|
|
15450
|
+
}
|
|
15451
|
+
if (trackedCommand) await captureCliTelemetry(telemetryContext, {
|
|
15452
|
+
debugSink: wrappedIo.telemetryDebugSink,
|
|
15453
|
+
dispatcher,
|
|
15454
|
+
commandFamily: trackedCommand.commandFamily,
|
|
15455
|
+
surface: trackedCommand.surface,
|
|
15456
|
+
outcome: "failure",
|
|
15457
|
+
startedAt,
|
|
15458
|
+
error: normalizedError,
|
|
15459
|
+
productEvent: captureProductEvent
|
|
15460
|
+
}).catch(() => void 0);
|
|
15461
|
+
throw normalizedError;
|
|
15462
|
+
} finally {
|
|
15463
|
+
await dispatcher.shutdown();
|
|
15353
15464
|
}
|
|
15354
15465
|
}
|
|
15355
15466
|
function normalizeCompletionWords(words) {
|
|
@@ -15359,7 +15470,7 @@ function addJsonOption(command) {
|
|
|
15359
15470
|
return command.option("--json", "print JSON output");
|
|
15360
15471
|
}
|
|
15361
15472
|
function addDaemonInstallOptions(command) {
|
|
15362
|
-
return addJsonOption(command).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").option("--reset", "rebuild daemon configuration from defaults").option("--env <KEY=VALUE>", "set an environment variable for the service", collectValues, []).option("--unset-env <KEY>", "remove an environment variable from the service", collectValues, []).option("--inherit-env", "run the service through the user's shell environment").option("--no-inherit-env", "disable shell environment inheritance").option("--dry-run", "preview actions without writing files or registering a service").option("--no-validate", "skip temporary service command validation").option("--start", "start the service after install").option("--restart", "restart the service after install").option("--no-restart", "do not restart a running service after updating config");
|
|
15473
|
+
return addJsonOption(command).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").option("--reset", "rebuild daemon configuration from defaults").option("--env <KEY=VALUE>", "set an environment variable for the service", collectValues, []).option("--unset-env <KEY>", "remove an environment variable from the service", collectValues, []).option("--inherit-env", "run the service through the user's shell environment").option("--no-inherit-env", "disable shell environment inheritance").option("--dry-run", "preview actions without writing files or registering a service").option("--no-validate", "skip temporary service command validation").option("--start", "start the service after install").option("--restart", "restart the service after install").option("--no-restart", "do not restart a running service after updating config");
|
|
15363
15474
|
}
|
|
15364
15475
|
function addServeMigrationCommand(parent, name, replacement) {
|
|
15365
15476
|
parent.command(name, { hidden: true }).allowUnknownOption(true).action(() => {
|
|
@@ -15370,6 +15481,249 @@ function collectValues(value, previous) {
|
|
|
15370
15481
|
return [...previous, value];
|
|
15371
15482
|
}
|
|
15372
15483
|
const HIDDEN_INPUT_PROMPT_LABELS = { vaultValue: "Value: " };
|
|
15484
|
+
function telemetryContextForIo(io) {
|
|
15485
|
+
const env = io.env ?? process.env;
|
|
15486
|
+
return {
|
|
15487
|
+
env,
|
|
15488
|
+
configPath: envConfigPath(env),
|
|
15489
|
+
projectConfigPath: envProjectConfigPath(env),
|
|
15490
|
+
stateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(env),
|
|
15491
|
+
stderrIsTTY: io.stderrIsTTY ?? process.stderr.isTTY === true,
|
|
15492
|
+
writeErr: io.writeErr ?? ((value) => process.stderr.write(value))
|
|
15493
|
+
};
|
|
15494
|
+
}
|
|
15495
|
+
function telemetryCommandFamilyFromArgs(args) {
|
|
15496
|
+
const command = args[0];
|
|
15497
|
+
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;
|
|
15498
|
+
if (command === cliCommands$1.serve) return {
|
|
15499
|
+
commandFamily: "serve",
|
|
15500
|
+
surface: "serve"
|
|
15501
|
+
};
|
|
15502
|
+
if (command === cliCommands$1.attach) return {
|
|
15503
|
+
commandFamily: "attach",
|
|
15504
|
+
surface: "attach"
|
|
15505
|
+
};
|
|
15506
|
+
if (command === cliCommands$1.daemon) return {
|
|
15507
|
+
commandFamily: "daemon",
|
|
15508
|
+
surface: "daemon"
|
|
15509
|
+
};
|
|
15510
|
+
if (command === cliCommands$1.codeMode) return {
|
|
15511
|
+
commandFamily: "code_mode",
|
|
15512
|
+
surface: "code_mode"
|
|
15513
|
+
};
|
|
15514
|
+
if (command === cliCommands$1.setup) return {
|
|
15515
|
+
commandFamily: "setup",
|
|
15516
|
+
surface: "cli"
|
|
15517
|
+
};
|
|
15518
|
+
if (command === cliCommands$1.init) return {
|
|
15519
|
+
commandFamily: "init",
|
|
15520
|
+
surface: "cli"
|
|
15521
|
+
};
|
|
15522
|
+
if (command === cliCommands$1.install) return {
|
|
15523
|
+
commandFamily: "install",
|
|
15524
|
+
surface: "cli"
|
|
15525
|
+
};
|
|
15526
|
+
if (command === cliCommands$1.add) return {
|
|
15527
|
+
commandFamily: "add",
|
|
15528
|
+
surface: "cli"
|
|
15529
|
+
};
|
|
15530
|
+
if (command === cliCommands$1.doctor) return {
|
|
15531
|
+
commandFamily: "doctor",
|
|
15532
|
+
surface: "cli"
|
|
15533
|
+
};
|
|
15534
|
+
if (command === cliCommands$1.auth) return {
|
|
15535
|
+
commandFamily: "auth",
|
|
15536
|
+
surface: "cli"
|
|
15537
|
+
};
|
|
15538
|
+
if (command === cliCommands$1.remote || command === cliCommands$1.cloud) return {
|
|
15539
|
+
commandFamily: "remote",
|
|
15540
|
+
surface: "cli"
|
|
15541
|
+
};
|
|
15542
|
+
if (command === cliCommands$1.inspect) return {
|
|
15543
|
+
commandFamily: "inspect",
|
|
15544
|
+
surface: "cli"
|
|
15545
|
+
};
|
|
15546
|
+
if (command === cliCommands$1.checkBackend) return {
|
|
15547
|
+
commandFamily: "check",
|
|
15548
|
+
surface: "cli"
|
|
15549
|
+
};
|
|
15550
|
+
if (command === cliCommands$1.listTools || command === cliCommands$1.searchTools || command === cliCommands$1.getTool || command === cliCommands$1.callTool) return {
|
|
15551
|
+
commandFamily: "tools",
|
|
15552
|
+
surface: "cli"
|
|
15553
|
+
};
|
|
15554
|
+
if (command === cliCommands$1.listResources || command === cliCommands$1.searchResources || command === cliCommands$1.listResourceTemplates || command === cliCommands$1.readResource) return {
|
|
15555
|
+
commandFamily: "resources",
|
|
15556
|
+
surface: "cli"
|
|
15557
|
+
};
|
|
15558
|
+
if (command === cliCommands$1.listPrompts || command === cliCommands$1.searchPrompts || command === cliCommands$1.getPrompt) return {
|
|
15559
|
+
commandFamily: "prompts",
|
|
15560
|
+
surface: "cli"
|
|
15561
|
+
};
|
|
15562
|
+
if (command === cliCommands$1.complete) return {
|
|
15563
|
+
commandFamily: "complete",
|
|
15564
|
+
surface: "cli"
|
|
15565
|
+
};
|
|
15566
|
+
return {
|
|
15567
|
+
commandFamily: "unknown",
|
|
15568
|
+
surface: "cli"
|
|
15569
|
+
};
|
|
15570
|
+
}
|
|
15571
|
+
function telemetryConfigForCli(context) {
|
|
15572
|
+
try {
|
|
15573
|
+
return loadConfigWithSources(context.configPath, context.projectConfigPath, { vaultResolver: vaultBootstrapResolver }).config;
|
|
15574
|
+
} catch (error) {
|
|
15575
|
+
if (error instanceof CapletsError && error.code !== "CONFIG_INVALID") return {};
|
|
15576
|
+
return telemetryOnlyConfigForCli(resolveConfigPath(context.configPath)) ?? { telemetry: false };
|
|
15577
|
+
}
|
|
15578
|
+
}
|
|
15579
|
+
function telemetryOnlyConfigForCli(path) {
|
|
15580
|
+
try {
|
|
15581
|
+
const config = readUserConfigObject(path);
|
|
15582
|
+
return typeof config.telemetry === "boolean" ? { telemetry: config.telemetry } : void 0;
|
|
15583
|
+
} catch {
|
|
15584
|
+
return;
|
|
15585
|
+
}
|
|
15586
|
+
}
|
|
15587
|
+
function maybePrintCliTelemetryNotice(context, surface) {
|
|
15588
|
+
const state = resolveTelemetryState({
|
|
15589
|
+
config: telemetryConfigForCli(context),
|
|
15590
|
+
env: context.env,
|
|
15591
|
+
stateDir: context.stateDir,
|
|
15592
|
+
surface,
|
|
15593
|
+
visibility: "visible",
|
|
15594
|
+
allowWithoutNotice: true,
|
|
15595
|
+
createIdentity: false
|
|
15596
|
+
});
|
|
15597
|
+
if (state.status !== "enabled" || state.notice.shown) return;
|
|
15598
|
+
maybePrintTelemetryNotice({
|
|
15599
|
+
stateDir: context.stateDir,
|
|
15600
|
+
surface,
|
|
15601
|
+
stderrIsTTY: context.stderrIsTTY,
|
|
15602
|
+
writeErr: context.writeErr
|
|
15603
|
+
});
|
|
15604
|
+
}
|
|
15605
|
+
async function captureCliTelemetry(context, options) {
|
|
15606
|
+
try {
|
|
15607
|
+
maybePrintCliTelemetryNotice(context, options.surface ?? "cli");
|
|
15608
|
+
} catch {}
|
|
15609
|
+
const state = resolveTelemetryState({
|
|
15610
|
+
config: telemetryConfigForCli(context),
|
|
15611
|
+
env: context.env,
|
|
15612
|
+
stateDir: context.stateDir,
|
|
15613
|
+
surface: options.surface ?? "cli",
|
|
15614
|
+
visibility: "visible",
|
|
15615
|
+
debug: context.env.CAPLETS_TELEMETRY_DEBUG === "1"
|
|
15616
|
+
});
|
|
15617
|
+
if (state.status !== "enabled" && state.status !== "debug") return;
|
|
15618
|
+
const identity = state.status === "debug" ? readTelemetryIdentity({
|
|
15619
|
+
stateDir: context.stateDir,
|
|
15620
|
+
create: false
|
|
15621
|
+
}) : state.identity ?? readTelemetryIdentity({
|
|
15622
|
+
stateDir: context.stateDir,
|
|
15623
|
+
create: true
|
|
15624
|
+
});
|
|
15625
|
+
if (options.productEvent !== false) {
|
|
15626
|
+
const product = buildProductTelemetryEvent({
|
|
15627
|
+
name: "caplets_cli_command",
|
|
15628
|
+
distinctId: identity.id,
|
|
15629
|
+
properties: {
|
|
15630
|
+
package: "@caplets/core",
|
|
15631
|
+
version,
|
|
15632
|
+
surface: options.surface ?? "cli",
|
|
15633
|
+
runtime_mode: runtimeModeForEnv(context.env),
|
|
15634
|
+
execution_context: state.executionContext,
|
|
15635
|
+
command_family: options.commandFamily,
|
|
15636
|
+
outcome: options.outcome,
|
|
15637
|
+
duration_bucket: durationBucket(Date.now() - options.startedAt)
|
|
15638
|
+
}
|
|
15639
|
+
});
|
|
15640
|
+
if (state.status === "debug") options.debugSink?.capture("debug", product);
|
|
15641
|
+
else await (options.dispatcher ?? createTelemetryDispatcher({ stateDir: context.stateDir })).capture(state, product);
|
|
15642
|
+
}
|
|
15643
|
+
if (options.outcome !== "failure") return;
|
|
15644
|
+
if (options.error === void 0) return;
|
|
15645
|
+
const reliability = buildReliabilityTelemetryEvent({
|
|
15646
|
+
name: "caplets_reliability_error",
|
|
15647
|
+
properties: {
|
|
15648
|
+
package: "@caplets/core",
|
|
15649
|
+
version,
|
|
15650
|
+
surface: options.surface ?? "cli",
|
|
15651
|
+
runtime_mode: runtimeModeForEnv(context.env),
|
|
15652
|
+
command_family: options.commandFamily,
|
|
15653
|
+
error_code: errorCodeForTelemetry(options.error),
|
|
15654
|
+
diagnostic_category: diagnosticCategoryForError(options.error),
|
|
15655
|
+
os_family: platform(),
|
|
15656
|
+
arch: arch(),
|
|
15657
|
+
node_major: Number(process.versions.node.split(".")[0] ?? 0)
|
|
15658
|
+
}
|
|
15659
|
+
});
|
|
15660
|
+
if (state.status === "debug") {
|
|
15661
|
+
options.debugSink?.capture("debug", reliability);
|
|
15662
|
+
return;
|
|
15663
|
+
}
|
|
15664
|
+
await (options.dispatcher ?? createTelemetryDispatcher({ stateDir: context.stateDir })).capture(state, reliability);
|
|
15665
|
+
}
|
|
15666
|
+
function readUserConfigObject(path) {
|
|
15667
|
+
if (!existsSync(path)) return {};
|
|
15668
|
+
try {
|
|
15669
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
15670
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
15671
|
+
} catch (error) {
|
|
15672
|
+
throw new CapletsError("CONFIG_INVALID", `Caplets config at ${path} is not valid JSON`, error);
|
|
15673
|
+
}
|
|
15674
|
+
}
|
|
15675
|
+
function runtimeModeForEnv(env) {
|
|
15676
|
+
const mode = env.CAPLETS_MODE;
|
|
15677
|
+
return mode === "remote" || mode === "cloud" || mode === "local" ? mode : "unknown";
|
|
15678
|
+
}
|
|
15679
|
+
function errorCodeForTelemetry(error) {
|
|
15680
|
+
if (error instanceof CapletsError) return error.code;
|
|
15681
|
+
return "UNKNOWN";
|
|
15682
|
+
}
|
|
15683
|
+
function diagnosticCategoryForError(error) {
|
|
15684
|
+
if (!(error instanceof CapletsError)) return "unknown";
|
|
15685
|
+
if (error.code.startsWith("CONFIG")) return "config";
|
|
15686
|
+
if (error.code.startsWith("AUTH")) return "auth";
|
|
15687
|
+
if (error.code.includes("NETWORK") || error.code.includes("UNAVAILABLE")) return "network";
|
|
15688
|
+
if (error.code.includes("VALID") || error.code.includes("REQUEST")) return "validation";
|
|
15689
|
+
return "runtime";
|
|
15690
|
+
}
|
|
15691
|
+
function writeTelemetryConfig(path, enabled) {
|
|
15692
|
+
const config = {
|
|
15693
|
+
...readUserConfigObject(path),
|
|
15694
|
+
$schema: "https://caplets.dev/config.schema.json",
|
|
15695
|
+
telemetry: enabled
|
|
15696
|
+
};
|
|
15697
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
15698
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
|
|
15699
|
+
}
|
|
15700
|
+
function formatTelemetryStatus(context) {
|
|
15701
|
+
const config = telemetryConfigForCli(context);
|
|
15702
|
+
const state = resolveTelemetryState({
|
|
15703
|
+
config,
|
|
15704
|
+
env: context.env,
|
|
15705
|
+
stateDir: context.stateDir,
|
|
15706
|
+
surface: "cli",
|
|
15707
|
+
visibility: "visible",
|
|
15708
|
+
createIdentity: false
|
|
15709
|
+
});
|
|
15710
|
+
const notice = readTelemetryNotice({ stateDir: context.stateDir });
|
|
15711
|
+
const identity = readTelemetryIdentity({
|
|
15712
|
+
stateDir: context.stateDir,
|
|
15713
|
+
create: false
|
|
15714
|
+
});
|
|
15715
|
+
const health = readTelemetryDeliveryHealth({ stateDir: context.stateDir });
|
|
15716
|
+
return `${[
|
|
15717
|
+
`Telemetry: ${state.status}`,
|
|
15718
|
+
`Decision: ${state.decider}`,
|
|
15719
|
+
`Config: ${config.telemetry === false ? "disabled" : "enabled"}`,
|
|
15720
|
+
`Environment: ${context.env.CAPLETS_DISABLE_TELEMETRY === "1" ? "disabled" : "enabled"}`,
|
|
15721
|
+
`Notice shown: ${notice.shown ? `yes (${notice.surface})` : "no"}`,
|
|
15722
|
+
`Anonymous ID: ${identity.kind === "stable" ? "present" : "not stored"}`,
|
|
15723
|
+
`Delivery health: ${Object.keys(health).length === 0 ? "none" : JSON.stringify(health)}`,
|
|
15724
|
+
"Disable with CAPLETS_DISABLE_TELEMETRY=1 or `caplets telemetry disable`."
|
|
15725
|
+
].join("\n")}\n`;
|
|
15726
|
+
}
|
|
15373
15727
|
function remoteProfileStore(authDir, env) {
|
|
15374
15728
|
return createRemoteProfileStore({
|
|
15375
15729
|
authDir,
|
|
@@ -15380,6 +15734,21 @@ function attachRemoteUrlFromArgs(positionalUrl, legacyRemoteUrl) {
|
|
|
15380
15734
|
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
15735
|
return positionalUrl ?? legacyRemoteUrl;
|
|
15382
15736
|
}
|
|
15737
|
+
function rejectAttachHttpServeFlags(options) {
|
|
15738
|
+
const invalid = [
|
|
15739
|
+
options.transport !== void 0 ? "--transport" : void 0,
|
|
15740
|
+
options.host !== void 0 ? "--host" : void 0,
|
|
15741
|
+
options.port !== void 0 ? "--port" : void 0,
|
|
15742
|
+
options.path !== void 0 ? "--path" : void 0,
|
|
15743
|
+
options.allowUnauthenticatedHttp === true ? "--allow-unauthenticated-http" : void 0,
|
|
15744
|
+
options.trustProxy === true ? "--trust-proxy" : void 0
|
|
15745
|
+
].filter((value) => value !== void 0);
|
|
15746
|
+
if (invalid.length === 0) return;
|
|
15747
|
+
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.`);
|
|
15748
|
+
}
|
|
15749
|
+
function hiddenOption(flags, description) {
|
|
15750
|
+
return new Option(flags, description).hideHelp();
|
|
15751
|
+
}
|
|
15383
15752
|
function remoteServerCredentialStore(statePath, env) {
|
|
15384
15753
|
return new RemoteServerCredentialStore({ dir: statePath ?? env.CAPLETS_REMOTE_SERVER_STATE_DIR ?? join(DEFAULT_AUTH_DIR, "remote-server") });
|
|
15385
15754
|
}
|
|
@@ -15405,6 +15774,7 @@ function daemonInstallOptions(options) {
|
|
|
15405
15774
|
...options.port !== void 0 ? { port: options.port } : {},
|
|
15406
15775
|
...options.path !== void 0 ? { path: options.path } : {},
|
|
15407
15776
|
...options.remoteStatePath !== void 0 ? { remoteStatePath: options.remoteStatePath } : {},
|
|
15777
|
+
...options.upstreamUrl !== void 0 ? { upstreamUrl: options.upstreamUrl } : {},
|
|
15408
15778
|
...options.allowUnauthenticatedHttp !== void 0 ? { allowUnauthenticatedHttp: options.allowUnauthenticatedHttp } : {},
|
|
15409
15779
|
...options.trustProxy !== void 0 ? { trustProxy: options.trustProxy } : {},
|
|
15410
15780
|
...options.reset !== void 0 ? { reset: options.reset } : {},
|
|
@@ -15801,9 +16171,33 @@ function createProgram(io = {}) {
|
|
|
15801
16171
|
const writeErr = io.writeErr ?? ((value) => process.stderr.write(value));
|
|
15802
16172
|
const env = io.env ?? process.env;
|
|
15803
16173
|
const currentConfigPath = () => envConfigPath(env);
|
|
16174
|
+
const telemetryContext = () => ({
|
|
16175
|
+
env,
|
|
16176
|
+
configPath: currentConfigPath(),
|
|
16177
|
+
projectConfigPath: envProjectConfigPath(env),
|
|
16178
|
+
stateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(env),
|
|
16179
|
+
stderrIsTTY: io.stderrIsTTY ?? process.stderr.isTTY === true,
|
|
16180
|
+
writeErr
|
|
16181
|
+
});
|
|
16182
|
+
const printTelemetryNotice = (surface) => {
|
|
16183
|
+
try {
|
|
16184
|
+
maybePrintCliTelemetryNotice(telemetryContext(), surface);
|
|
16185
|
+
} catch {}
|
|
16186
|
+
};
|
|
15804
16187
|
const setExitCode = io.setExitCode ?? ((code) => {
|
|
15805
16188
|
process.exitCode = code;
|
|
15806
16189
|
});
|
|
16190
|
+
const executeOperationIo = (format) => ({
|
|
16191
|
+
writeOut,
|
|
16192
|
+
writeErr,
|
|
16193
|
+
setExitCode,
|
|
16194
|
+
authDir: io.authDir,
|
|
16195
|
+
env,
|
|
16196
|
+
remote: remoteClientForCli(io),
|
|
16197
|
+
format,
|
|
16198
|
+
telemetryStateDir: telemetryContext().stateDir,
|
|
16199
|
+
telemetryDebugSink: io.telemetryDebugSink
|
|
16200
|
+
});
|
|
15807
16201
|
const program = new Command();
|
|
15808
16202
|
program.name("caplets").description("Progressive-disclosure gateway for MCP servers.").version(io.version ?? version).exitOverride().configureOutput({
|
|
15809
16203
|
writeOut,
|
|
@@ -15847,7 +16241,51 @@ function createProgram(io = {}) {
|
|
|
15847
16241
|
}
|
|
15848
16242
|
if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
|
|
15849
16243
|
});
|
|
16244
|
+
const telemetry = program.command(cliCommands$1.telemetry).description("Inspect and control anonymous Caplets telemetry.");
|
|
16245
|
+
telemetry.command("status").description("Show anonymous telemetry status.").action(() => {
|
|
16246
|
+
writeOut(formatTelemetryStatus(telemetryContext()));
|
|
16247
|
+
});
|
|
16248
|
+
telemetry.command("enable").description("Enable anonymous telemetry in the user config.").action(() => {
|
|
16249
|
+
const path = resolveConfigPath(currentConfigPath());
|
|
16250
|
+
writeTelemetryConfig(path, true);
|
|
16251
|
+
writeOut(`Enabled anonymous telemetry in ${path}.\n`);
|
|
16252
|
+
if (env.CAPLETS_DISABLE_TELEMETRY === "1") writeOut("CAPLETS_DISABLE_TELEMETRY=1 still disables telemetry for this process.\n");
|
|
16253
|
+
});
|
|
16254
|
+
telemetry.command("disable").description("Disable anonymous telemetry in the user config.").action(() => {
|
|
16255
|
+
const path = resolveConfigPath(currentConfigPath());
|
|
16256
|
+
writeTelemetryConfig(path, false);
|
|
16257
|
+
writeOut(`Disabled anonymous telemetry in ${path}.\n`);
|
|
16258
|
+
});
|
|
16259
|
+
telemetry.command("delete-id").description("Delete the local anonymous telemetry ID.").action(() => {
|
|
16260
|
+
deleteTelemetryIdentity({ stateDir: telemetryContext().stateDir });
|
|
16261
|
+
writeOut("Deleted the local anonymous telemetry ID. This does not delete provider-side historical anonymous events; provider retention controls historical data.\n");
|
|
16262
|
+
});
|
|
16263
|
+
telemetry.command("rotate-id").description("Rotate the local anonymous telemetry ID.").action(() => {
|
|
16264
|
+
rotateTelemetryIdentity({ stateDir: telemetryContext().stateDir });
|
|
16265
|
+
writeOut("Rotated the local anonymous telemetry ID. This does not delete provider-side historical anonymous events; provider retention controls historical data.\n");
|
|
16266
|
+
});
|
|
16267
|
+
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) => {
|
|
16268
|
+
const nestedArgs = args[0] === "--" ? args.slice(1) : args;
|
|
16269
|
+
const sink = new TelemetryDebugSink();
|
|
16270
|
+
try {
|
|
16271
|
+
if (nestedArgs.length > 0) await runCli(nestedArgs, {
|
|
16272
|
+
...io,
|
|
16273
|
+
env: {
|
|
16274
|
+
...env,
|
|
16275
|
+
CAPLETS_TELEMETRY_DEBUG: "1"
|
|
16276
|
+
},
|
|
16277
|
+
telemetryDebugSink: sink,
|
|
16278
|
+
writeOut,
|
|
16279
|
+
writeErr
|
|
16280
|
+
});
|
|
16281
|
+
} finally {
|
|
16282
|
+
writeOut(`${JSON.stringify({ telemetryDebug: sink.toJSON() }, null, 2)}\n`);
|
|
16283
|
+
}
|
|
16284
|
+
});
|
|
15850
16285
|
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) => {
|
|
16286
|
+
try {
|
|
16287
|
+
maybePrintCliTelemetryNotice(telemetryContext(), "code_mode");
|
|
16288
|
+
} catch {}
|
|
15851
16289
|
if (code === "repl" && options.file === void 0) {
|
|
15852
16290
|
await runCodeModeReplCli({
|
|
15853
16291
|
env,
|
|
@@ -15881,6 +16319,7 @@ function createProgram(io = {}) {
|
|
|
15881
16319
|
...currentConfigPath() ? { configPath: currentConfigPath() } : {},
|
|
15882
16320
|
projectConfigPath: envProjectConfigPath(env),
|
|
15883
16321
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
16322
|
+
telemetryStateDir: telemetryContext().stateDir,
|
|
15884
16323
|
...code === void 0 ? {} : { inlineCode: code },
|
|
15885
16324
|
...options.file === void 0 ? {} : { file: options.file },
|
|
15886
16325
|
...options.sessionId === void 0 ? {} : { sessionId: options.sessionId },
|
|
@@ -15897,16 +16336,23 @@ function createProgram(io = {}) {
|
|
|
15897
16336
|
...currentConfigPath() ? { configPath: currentConfigPath() } : {},
|
|
15898
16337
|
projectConfigPath: envProjectConfigPath(env),
|
|
15899
16338
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
16339
|
+
telemetryStateDir: telemetryContext().stateDir,
|
|
15900
16340
|
...options.json === void 0 && parentOptions.json === void 0 ? {} : { json: options.json ?? parentOptions.json },
|
|
15901
16341
|
writeOut
|
|
15902
16342
|
});
|
|
15903
16343
|
});
|
|
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) => {
|
|
16344
|
+
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) => {
|
|
16345
|
+
printTelemetryNotice("serve");
|
|
15905
16346
|
const resolved = resolveServeOptions(options);
|
|
15906
16347
|
const configPath = currentConfigPath();
|
|
15907
16348
|
await (io.serve ?? ((serveOptions) => serveResolvedCaplets(serveOptions, {
|
|
15908
16349
|
...configPath ? { configPath } : {},
|
|
15909
|
-
...io.authDir ? { authDir: io.authDir } : {}
|
|
16350
|
+
...io.authDir ? { authDir: io.authDir } : {},
|
|
16351
|
+
telemetryEnv: env,
|
|
16352
|
+
telemetryStateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(env),
|
|
16353
|
+
telemetrySurface: "serve",
|
|
16354
|
+
telemetryVisibility: "visible",
|
|
16355
|
+
telemetryRuntimeMode: runtimeModeForEnv(env)
|
|
15910
16356
|
}, writeErr)))(resolved);
|
|
15911
16357
|
});
|
|
15912
16358
|
const daemonOptions = () => ({
|
|
@@ -15923,6 +16369,7 @@ function createProgram(io = {}) {
|
|
|
15923
16369
|
})) addServeMigrationCommand(serve, name, replacement);
|
|
15924
16370
|
const daemon = program.command(cliCommands$1.daemon).description("Install and manage the default Caplets daemon.");
|
|
15925
16371
|
addDaemonInstallOptions(daemon.command("install").description("Install or update the default Caplets daemon.")).action(async (options) => {
|
|
16372
|
+
printTelemetryNotice("daemon");
|
|
15926
16373
|
const prompt = options.json ? void 0 : createSetupPromptHandle(io, writeOut);
|
|
15927
16374
|
try {
|
|
15928
16375
|
const result = await installDaemon(daemonInstallOptions(options), {
|
|
@@ -15946,6 +16393,7 @@ function createProgram(io = {}) {
|
|
|
15946
16393
|
}
|
|
15947
16394
|
});
|
|
15948
16395
|
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) => {
|
|
16396
|
+
printTelemetryNotice("daemon");
|
|
15949
16397
|
const result = await uninstallDaemon({
|
|
15950
16398
|
purge: options.purge === true,
|
|
15951
16399
|
dryRun: options.dryRun === true
|
|
@@ -15957,6 +16405,7 @@ function createProgram(io = {}) {
|
|
|
15957
16405
|
writeOut(result.dryRun ? "Would uninstall Caplets daemon.\n" : "Uninstalled Caplets daemon.\n");
|
|
15958
16406
|
});
|
|
15959
16407
|
addJsonOption(daemon.command("start").description("Start the default Caplets daemon.")).action(async (options) => {
|
|
16408
|
+
printTelemetryNotice("daemon");
|
|
15960
16409
|
const result = await startDaemon(daemonOptions());
|
|
15961
16410
|
if (options.json) {
|
|
15962
16411
|
writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -15965,6 +16414,7 @@ function createProgram(io = {}) {
|
|
|
15965
16414
|
writeOut(result.action === "restart" ? "Restarted Caplets daemon.\n" : "Started Caplets daemon.\n");
|
|
15966
16415
|
});
|
|
15967
16416
|
addJsonOption(daemon.command("restart").description("Restart the default Caplets daemon.")).action(async (options) => {
|
|
16417
|
+
printTelemetryNotice("daemon");
|
|
15968
16418
|
const result = await restartDaemon(daemonOptions());
|
|
15969
16419
|
if (options.json) {
|
|
15970
16420
|
writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -15973,6 +16423,7 @@ function createProgram(io = {}) {
|
|
|
15973
16423
|
writeOut("Restarted Caplets daemon.\n");
|
|
15974
16424
|
});
|
|
15975
16425
|
addJsonOption(daemon.command("stop").description("Stop the default Caplets daemon.")).action(async (options) => {
|
|
16426
|
+
printTelemetryNotice("daemon");
|
|
15976
16427
|
const result = await stopDaemon(daemonOptions());
|
|
15977
16428
|
if (options.json) {
|
|
15978
16429
|
writeOut(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -15981,6 +16432,7 @@ function createProgram(io = {}) {
|
|
|
15981
16432
|
writeOut("Stopped Caplets daemon.\n");
|
|
15982
16433
|
});
|
|
15983
16434
|
addJsonOption(daemon.command("status").description("Show the default Caplets daemon status.")).action(async (options) => {
|
|
16435
|
+
printTelemetryNotice("daemon");
|
|
15984
16436
|
const status = await daemonStatus(daemonOptions());
|
|
15985
16437
|
if (options.json) {
|
|
15986
16438
|
writeOut(`${JSON.stringify(status, null, 2)}\n`);
|
|
@@ -15989,6 +16441,7 @@ function createProgram(io = {}) {
|
|
|
15989
16441
|
writeOut(formatDaemonStatus(status));
|
|
15990
16442
|
});
|
|
15991
16443
|
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) => {
|
|
16444
|
+
printTelemetryNotice("daemon");
|
|
15992
16445
|
const tail = options.tail === void 0 ? 10 : parseNonNegativeInteger(options.tail, "--tail");
|
|
15993
16446
|
const stream = parseLogStream(options.stream);
|
|
15994
16447
|
if (options.follow) {
|
|
@@ -16023,8 +16476,10 @@ function createProgram(io = {}) {
|
|
|
16023
16476
|
}
|
|
16024
16477
|
for (const entry of result.entries) writeOut(stream === "all" ? `[${entry.stream}] ${entry.line}\n` : `${entry.line}\n`);
|
|
16025
16478
|
});
|
|
16026
|
-
program.command(cliCommands$1.attach).description("Start a remote-backed Caplets MCP server.").argument("[url]", "remote Caplets service base URL").
|
|
16479
|
+
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) => {
|
|
16480
|
+
printTelemetryNotice("attach");
|
|
16027
16481
|
try {
|
|
16482
|
+
rejectAttachHttpServeFlags(options);
|
|
16028
16483
|
const remoteUrl = attachRemoteUrlFromArgs(url, options.remoteUrl);
|
|
16029
16484
|
const attachOptions = {
|
|
16030
16485
|
...options,
|
|
@@ -16396,6 +16851,7 @@ function createProgram(io = {}) {
|
|
|
16396
16851
|
writeOut(`Created ${localMutationTargetLabel(target, io)}Caplets config at ${path}\n`);
|
|
16397
16852
|
});
|
|
16398
16853
|
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) => {
|
|
16854
|
+
printTelemetryNotice("cli");
|
|
16399
16855
|
const setupOptions = {
|
|
16400
16856
|
...options,
|
|
16401
16857
|
env
|
|
@@ -16584,6 +17040,7 @@ function createProgram(io = {}) {
|
|
|
16584
17040
|
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
16585
17041
|
});
|
|
16586
17042
|
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) => {
|
|
17043
|
+
printTelemetryNotice("cli");
|
|
16587
17044
|
const target = parseMutationTarget(options);
|
|
16588
17045
|
if (target === "remote") {
|
|
16589
17046
|
const result = await requireRemoteClientForTarget(io).request("install", {
|
|
@@ -16703,37 +17160,13 @@ function createProgram(io = {}) {
|
|
|
16703
17160
|
writeAddResult(writeOut, `${localMutationTargetLabel(target, io)}HTTP`, result);
|
|
16704
17161
|
});
|
|
16705
17162
|
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
|
-
});
|
|
17163
|
+
await executeOperation(caplet, { operation: "inspect" }, executeOperationIo(options.format));
|
|
16715
17164
|
});
|
|
16716
17165
|
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
|
-
});
|
|
17166
|
+
await executeOperation(caplet, { operation: "check" }, executeOperationIo(options.format));
|
|
16726
17167
|
});
|
|
16727
17168
|
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
|
-
});
|
|
17169
|
+
await executeOperation(caplet, { operation: "tools" }, executeOperationIo(options.format));
|
|
16737
17170
|
});
|
|
16738
17171
|
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
17172
|
await executeOperation(caplet, options.limit === void 0 ? {
|
|
@@ -16743,30 +17176,14 @@ function createProgram(io = {}) {
|
|
|
16743
17176
|
operation: "search_tools",
|
|
16744
17177
|
query,
|
|
16745
17178
|
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
|
-
});
|
|
17179
|
+
}, executeOperationIo(options.format));
|
|
16755
17180
|
});
|
|
16756
17181
|
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
17182
|
const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
|
|
16758
17183
|
await executeOperation(caplet, {
|
|
16759
17184
|
operation: "describe_tool",
|
|
16760
17185
|
name: tool
|
|
16761
|
-
},
|
|
16762
|
-
writeOut,
|
|
16763
|
-
writeErr,
|
|
16764
|
-
setExitCode,
|
|
16765
|
-
authDir: io.authDir,
|
|
16766
|
-
env,
|
|
16767
|
-
remote: remoteClientForCli(io),
|
|
16768
|
-
format: options.format
|
|
16769
|
-
});
|
|
17186
|
+
}, executeOperationIo(options.format));
|
|
16770
17187
|
});
|
|
16771
17188
|
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
17189
|
const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
|
|
@@ -16775,28 +17192,12 @@ function createProgram(io = {}) {
|
|
|
16775
17192
|
name: tool,
|
|
16776
17193
|
args: parseCallToolArgs(options.args),
|
|
16777
17194
|
...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
|
-
});
|
|
17195
|
+
}, executeOperationIo(options.format));
|
|
16787
17196
|
});
|
|
16788
17197
|
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
17198
|
operation: "resources",
|
|
16790
17199
|
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
|
-
}));
|
|
17200
|
+
}, executeOperationIo(options.format)));
|
|
16800
17201
|
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
17202
|
operation: "search_resources",
|
|
16802
17203
|
query
|
|
@@ -16804,51 +17205,19 @@ function createProgram(io = {}) {
|
|
|
16804
17205
|
operation: "search_resources",
|
|
16805
17206
|
query,
|
|
16806
17207
|
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
|
-
}));
|
|
17208
|
+
}, executeOperationIo(options.format)));
|
|
16816
17209
|
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
17210
|
operation: "resource_templates",
|
|
16818
17211
|
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
|
-
}));
|
|
17212
|
+
}, executeOperationIo(options.format)));
|
|
16828
17213
|
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
17214
|
operation: "read_resource",
|
|
16830
17215
|
uri
|
|
16831
|
-
},
|
|
16832
|
-
writeOut,
|
|
16833
|
-
writeErr,
|
|
16834
|
-
setExitCode,
|
|
16835
|
-
authDir: io.authDir,
|
|
16836
|
-
env,
|
|
16837
|
-
remote: remoteClientForCli(io),
|
|
16838
|
-
format: options.format
|
|
16839
|
-
}));
|
|
17216
|
+
}, executeOperationIo(options.format)));
|
|
16840
17217
|
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
17218
|
operation: "prompts",
|
|
16842
17219
|
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
|
-
}));
|
|
17220
|
+
}, executeOperationIo(options.format)));
|
|
16852
17221
|
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
17222
|
operation: "search_prompts",
|
|
16854
17223
|
query
|
|
@@ -16856,30 +17225,14 @@ function createProgram(io = {}) {
|
|
|
16856
17225
|
operation: "search_prompts",
|
|
16857
17226
|
query,
|
|
16858
17227
|
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
|
-
}));
|
|
17228
|
+
}, executeOperationIo(options.format)));
|
|
16868
17229
|
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
17230
|
const { caplet, tool: prompt } = parseQualifiedTarget(capletOrTarget, promptArgument);
|
|
16870
17231
|
await executeOperation(caplet, {
|
|
16871
17232
|
operation: "get_prompt",
|
|
16872
17233
|
name: prompt,
|
|
16873
17234
|
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
|
-
});
|
|
17235
|
+
}, executeOperationIo(options.format));
|
|
16883
17236
|
});
|
|
16884
17237
|
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
17238
|
operation: "complete",
|
|
@@ -16888,15 +17241,7 @@ function createProgram(io = {}) {
|
|
|
16888
17241
|
name: options.argument,
|
|
16889
17242
|
value: options.value
|
|
16890
17243
|
}
|
|
16891
|
-
},
|
|
16892
|
-
writeOut,
|
|
16893
|
-
writeErr,
|
|
16894
|
-
setExitCode,
|
|
16895
|
-
authDir: io.authDir,
|
|
16896
|
-
env,
|
|
16897
|
-
remote: remoteClientForCli(io),
|
|
16898
|
-
format: options.format
|
|
16899
|
-
}));
|
|
17244
|
+
}, executeOperationIo(options.format)));
|
|
16900
17245
|
const config = program.command(cliCommands$1.config).description("Inspect Caplets config locations.");
|
|
16901
17246
|
config.command("path").description("Print the effective user config path.").action(() => {
|
|
16902
17247
|
writeOut(`${resolveConfigPath(currentConfigPath())}\n`);
|
|
@@ -17521,6 +17866,12 @@ async function executeLocalOperation(caplet, request, io, config) {
|
|
|
17521
17866
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
17522
17867
|
watch: false,
|
|
17523
17868
|
writeErr: io.writeErr,
|
|
17869
|
+
telemetryEnv: io.env ?? process.env,
|
|
17870
|
+
telemetryStateDir: io.telemetryStateDir ?? defaultTelemetryStateDir(io.env ?? process.env),
|
|
17871
|
+
telemetrySurface: "cli",
|
|
17872
|
+
telemetryVisibility: "visible",
|
|
17873
|
+
telemetryRuntimeMode: runtimeModeForEnv(io.env ?? process.env),
|
|
17874
|
+
telemetryDebugSink: io.telemetryDebugSink,
|
|
17524
17875
|
...config ? { configLoader: () => config } : {}
|
|
17525
17876
|
});
|
|
17526
17877
|
try {
|