@reticlehq/server 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +130 -813
- package/dist/bridge/bridge.d.ts +23 -0
- package/dist/bridge/bridge.js +80 -2
- package/dist/bridge/bridge.test-harness.d.ts +5 -0
- package/dist/bridge/bridge.test-harness.js +6 -4
- package/dist/capsule/capsule-store.d.ts +2 -2
- package/dist/capsule/causal-summary.d.ts +16 -1
- package/dist/capsule/causal-summary.js +2 -1
- package/dist/cli/cli-doctor.js +41 -13
- package/dist/cli/cli-flow-commands.js +8 -8
- package/dist/cli/cli-launch.d.ts +0 -17
- package/dist/cli/cli-launch.js +0 -28
- package/dist/cli/cli-parse.d.ts +1 -1
- package/dist/cli/cli-parse.js +1 -1
- package/dist/cli/cli-port.d.ts +24 -0
- package/dist/cli/cli-port.js +66 -35
- package/dist/cli/cli-verify.d.ts +21 -0
- package/dist/cli/cli-verify.js +45 -1
- package/dist/cli/config-discovery.d.ts +35 -0
- package/dist/cli/config-discovery.js +163 -0
- package/dist/cli/doctor-daemon-line.js +4 -1
- package/dist/cli/doctor-project-line.d.ts +37 -0
- package/dist/cli/doctor-project-line.js +35 -0
- package/dist/cli/doctor-rows.d.ts +51 -0
- package/dist/cli/doctor-rows.js +54 -0
- package/dist/cli/doctor-sessions-line.js +6 -3
- package/dist/cli/drive-attach.d.ts +99 -0
- package/dist/cli/drive-attach.js +203 -0
- package/dist/cli/drive-command.d.ts +13 -0
- package/dist/cli/drive-command.js +103 -0
- package/dist/cli/open-note.d.ts +20 -0
- package/dist/cli/open-note.js +37 -0
- package/dist/cli/status-next-action.d.ts +37 -0
- package/dist/cli/status-next-action.js +61 -0
- package/dist/cli.js +65 -70
- package/dist/crawl/crawl.d.ts +7 -2
- package/dist/crawl/crawl.js +52 -9
- package/dist/ee/audit-log.js +2 -2
- package/dist/events/body-failures.d.ts +2 -2
- package/dist/events/contradiction-folds.d.ts +45 -0
- package/dist/events/contradiction-folds.js +42 -0
- package/dist/events/contradictions.d.ts +51 -1
- package/dist/events/contradictions.js +26 -7
- package/dist/events/declared.d.ts +45 -0
- package/dist/events/declared.js +73 -0
- package/dist/events/echo-mismatch.d.ts +9 -3
- package/dist/events/echo-mismatch.js +33 -5
- package/dist/events/event-filters.d.ts +1 -1
- package/dist/events/event-filters.js +7 -4
- package/dist/events/predicate-eval.d.ts +17 -91
- package/dist/events/predicate-eval.js +85 -326
- package/dist/events/predicate-parse.js +31 -3
- package/dist/events/predicate-route.d.ts +32 -0
- package/dist/events/predicate-route.js +102 -0
- package/dist/events/predicate-schema.d.ts +99 -0
- package/dist/events/predicate-schema.js +309 -0
- package/dist/events/predicate.d.ts +8 -0
- package/dist/events/predicate.js +127 -5
- package/dist/events/stale-response.d.ts +2 -3
- package/dist/events/stale-response.js +49 -2
- package/dist/events/unit-mismatch.d.ts +2 -2
- package/dist/flows/annotate-tools.js +3 -0
- package/dist/flows/annotate.js +18 -4
- package/dist/flows/coverage.d.ts +38 -5
- package/dist/flows/coverage.js +30 -4
- package/dist/flows/decision.d.ts +15 -0
- package/dist/flows/decision.js +1 -1
- package/dist/flows/flow-replay-run.js +8 -1
- package/dist/flows/flow-tools.js +9 -1
- package/dist/flows/replay.js +3 -0
- package/dist/flows/verify-change-tools.js +1 -1
- package/dist/honesty/accepted-write.d.ts +3 -0
- package/dist/honesty/accepted-write.js +8 -3
- package/dist/honesty/already-true.js +15 -0
- package/dist/honesty/blind-spots.js +4 -0
- package/dist/honesty/honesty.d.ts +11 -0
- package/dist/honesty/honesty.js +1 -0
- package/dist/honesty/uncaptured-bodies.d.ts +4 -2
- package/dist/honesty/uncaptured-bodies.js +9 -2
- package/dist/honesty/unread-outcome.d.ts +8 -0
- package/dist/honesty/unread-outcome.js +36 -9
- package/dist/honesty/unsettled.d.ts +9 -0
- package/dist/honesty/unsettled.js +7 -3
- package/dist/honesty/verified.d.ts +29 -3
- package/dist/honesty/verified.js +67 -8
- package/dist/http-server.d.ts +19 -0
- package/dist/http-server.js +202 -4
- package/dist/impact/impact-recorder.d.ts +23 -0
- package/dist/impact/impact-recorder.js +76 -0
- package/dist/impact/impact-store.d.ts +47 -0
- package/dist/impact/impact-store.js +171 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +60 -10
- package/dist/init/agent-rules.d.ts +20 -0
- package/dist/init/agent-rules.js +36 -0
- package/dist/init/astro-patch.js +27 -1
- package/dist/init/confirm.d.ts +21 -3
- package/dist/init/confirm.js +32 -5
- package/dist/init/connect-steps.d.ts +2 -0
- package/dist/init/connect-steps.js +50 -0
- package/dist/init/csp-check.d.ts +38 -0
- package/dist/init/csp-check.js +92 -0
- package/dist/init/csp-doctor.d.ts +25 -0
- package/dist/init/csp-doctor.js +49 -0
- package/dist/init/cursor.js +6 -9
- package/dist/init/detect.d.ts +8 -0
- package/dist/init/detect.js +33 -3
- package/dist/init/mcp-clients.d.ts +1 -0
- package/dist/init/mcp-clients.js +51 -7
- package/dist/init/mcp.d.ts +16 -2
- package/dist/init/mcp.js +23 -1
- package/dist/init/plan-framework.d.ts +10 -0
- package/dist/init/plan-framework.js +25 -1
- package/dist/init/plan.d.ts +7 -2
- package/dist/init/plan.js +66 -30
- package/dist/init/run.js +11 -5
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +12 -4
- package/dist/init/snippets.d.ts +1 -1
- package/dist/init/snippets.js +35 -7
- package/dist/init/vite-config.js +24 -2
- package/dist/input/network-mock-tools.js +8 -1
- package/dist/input/scroll-find.d.ts +6 -0
- package/dist/input/scroll-find.js +17 -2
- package/dist/input/scroll-tools.js +4 -0
- package/dist/journal/attach-journal.d.ts +9 -1
- package/dist/journal/attach-journal.js +13 -2
- package/dist/journal/journal-query.d.ts +13 -0
- package/dist/journal/journal-query.js +28 -3
- package/dist/journal/workspace-gitignore.d.ts +10 -0
- package/dist/journal/workspace-gitignore.js +59 -0
- package/dist/license/license.d.ts +69 -4
- package/dist/license/license.js +84 -15
- package/dist/mcp/mcp-proxy.d.ts +7 -93
- package/dist/mcp/mcp-proxy.js +183 -250
- package/dist/mcp/mcp.d.ts +27 -2
- package/dist/mcp/mcp.js +77 -36
- package/dist/mcp/proxy-daemon-probe.d.ts +8 -0
- package/dist/mcp/proxy-daemon-probe.js +48 -0
- package/dist/mcp/proxy-handshake.d.ts +82 -1
- package/dist/mcp/proxy-handshake.js +110 -3
- package/dist/mcp/proxy-lifecycle.d.ts +10 -1
- package/dist/mcp/proxy-lifecycle.js +11 -2
- package/dist/mcp/proxy-log.d.ts +63 -0
- package/dist/mcp/proxy-log.js +132 -0
- package/dist/mcp/server-instructions.d.ts +17 -0
- package/dist/mcp/server-instructions.js +56 -0
- package/dist/mcp/shared-params.d.ts +27 -0
- package/dist/mcp/shared-params.js +36 -0
- package/dist/mcp/sse-frame-parser.d.ts +19 -0
- package/dist/mcp/sse-frame-parser.js +41 -0
- package/dist/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- package/dist/session/connection-memory.d.ts +59 -0
- package/dist/session/connection-memory.js +108 -0
- package/dist/session/dev-command.d.ts +26 -0
- package/dist/session/dev-command.js +89 -0
- package/dist/session/dev-server-probe.d.ts +68 -0
- package/dist/session/dev-server-probe.js +102 -7
- package/dist/session/last-act.d.ts +26 -0
- package/dist/session/last-act.js +35 -1
- package/dist/session/live-control-tools.js +42 -5
- package/dist/session/no-session-diagnosis.d.ts +55 -2
- package/dist/session/no-session-diagnosis.js +199 -22
- package/dist/session/no-session-next-action.d.ts +46 -0
- package/dist/session/no-session-next-action.js +85 -0
- package/dist/session/no-session-watch.d.ts +40 -1
- package/dist/session/no-session-watch.js +172 -11
- package/dist/session/pending-commands.d.ts +12 -0
- package/dist/session/pending-commands.js +13 -1
- package/dist/session/session-manager.d.ts +4 -0
- package/dist/session/session-manager.js +35 -5
- package/dist/session/session-readiness.js +1 -1
- package/dist/session/session.d.ts +10 -1
- package/dist/session/session.js +146 -5
- package/dist/telemetry/app-instrumented.d.ts +8 -0
- package/dist/telemetry/app-instrumented.js +10 -0
- package/dist/telemetry/bug-found.d.ts +18 -0
- package/dist/telemetry/bug-found.js +14 -0
- package/dist/telemetry/daemon-telemetry.js +29 -0
- package/dist/telemetry/install-source.d.ts +20 -0
- package/dist/telemetry/install-source.js +34 -0
- package/dist/telemetry/instrumentation-stall.d.ts +57 -0
- package/dist/telemetry/instrumentation-stall.js +103 -0
- package/dist/telemetry/license-activation.d.ts +48 -0
- package/dist/telemetry/license-activation.js +57 -0
- package/dist/telemetry/session-metrics.d.ts +8 -0
- package/dist/telemetry/session-metrics.js +10 -0
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +23 -3
- package/dist/tools/act-tools.js +166 -29
- package/dist/tools/assert-verdict.js +32 -7
- package/dist/tools/browser-tools.js +10 -0
- package/dist/tools/dynamic-tools.js +17 -8
- package/dist/tools/error-recovery.js +10 -6
- package/dist/tools/feedback-tools.js +31 -6
- package/dist/tools/invoke-tool.d.ts +1 -1
- package/dist/tools/invoke-tool.js +30 -8
- package/dist/tools/lease-hint.d.ts +30 -8
- package/dist/tools/lease-hint.js +65 -14
- package/dist/tools/lease-tools.d.ts +11 -0
- package/dist/tools/lease-tools.js +141 -96
- package/dist/tools/observe-tools.js +9 -3
- package/dist/tools/react-grace.d.ts +4 -0
- package/dist/tools/react-grace.js +6 -3
- package/dist/tools/read-tools.js +47 -1
- package/dist/tools/resolve-target.d.ts +33 -0
- package/dist/tools/resolve-target.js +53 -0
- package/dist/tools/settle-in-flight.d.ts +15 -0
- package/dist/tools/settle-in-flight.js +42 -2
- package/dist/tools/snapshot-delta.d.ts +3 -0
- package/dist/tools/snapshot-delta.js +67 -6
- package/dist/tools/tool-kit.d.ts +38 -3
- package/dist/tools/tool-names.d.ts +2 -0
- package/dist/tools/tool-names.js +2 -0
- package/dist/tools/tool-surface.d.ts +60 -0
- package/dist/tools/tool-surface.js +84 -19
- package/dist/tools/tools.js +61 -5
- package/dist/tools/unadvertised-help.js +9 -6
- package/docs/README.md +1 -2
- package/docs/actions.mdx +41 -8
- package/docs/agent-cheatsheet.md +6 -4
- package/docs/architecture.md +6 -0
- package/docs/benchmarks.md +60 -12
- package/docs/best-practices.mdx +11 -6
- package/docs/capabilities.mdx +204 -57
- package/docs/cli/affected.mdx +6 -4
- package/docs/cli/capsules.mdx +9 -8
- package/docs/cli/cloud.mdx +30 -4
- package/docs/cli/doctor.mdx +41 -8
- package/docs/cli/drive.mdx +34 -15
- package/docs/cli/gate.mdx +6 -6
- package/docs/cli/help.mdx +8 -6
- package/docs/cli/hunt.mdx +20 -4
- package/docs/cli/identify.mdx +16 -2
- package/docs/cli/init.mdx +35 -8
- package/docs/cli/kill.mdx +5 -5
- package/docs/cli/license.mdx +1 -1
- package/docs/cli/open.mdx +6 -0
- package/docs/cli/restart.mdx +6 -6
- package/docs/cli/serve.mdx +10 -3
- package/docs/cli/status.mdx +29 -5
- package/docs/cli/stop.mdx +8 -0
- package/docs/cli/verify.mdx +20 -1
- package/docs/cli/version.mdx +1 -1
- package/docs/cli/watch.mdx +9 -2
- package/docs/cli.mdx +44 -22
- package/docs/debugging.md +3 -1
- package/docs/deploy-checks.md +31 -2
- package/docs/desktop.mdx +276 -80
- package/docs/docs.json +13 -12
- package/docs/enterprise.md +9 -3
- package/docs/faq.mdx +49 -35
- package/docs/fixtures.md +2 -0
- package/docs/flows.md +7 -5
- package/docs/for-agents.md +3 -3
- package/docs/frameworks.mdx +46 -7
- package/docs/gate-plan.md +4 -2
- package/docs/gates.md +8 -6
- package/docs/getting-started.md +53 -12
- package/docs/human-control.md +2 -0
- package/docs/index.mdx +43 -1
- package/docs/install-agentic.mdx +13 -4
- package/docs/install-manual.mdx +6 -4
- package/docs/instrumentation.mdx +7 -1
- package/docs/integration-patterns.md +3 -1
- package/docs/local-registry.md +4 -2
- package/docs/multi-agent-testing.md +3 -1
- package/docs/packages/babel-plugin.mdx +15 -6
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/core.mdx +26 -16
- package/docs/packages/electron.mdx +5 -7
- package/docs/packages/eslint-plugin.mdx +5 -3
- package/docs/packages/next.mdx +3 -5
- package/docs/packages/react.mdx +3 -3
- package/docs/packages/server.mdx +2 -2
- package/docs/packages/tauri.mdx +6 -6
- package/docs/packages/test.mdx +18 -7
- package/docs/packages/vite-plugin.mdx +4 -6
- package/docs/packages.mdx +14 -13
- package/docs/platform-integration.md +2 -0
- package/docs/predicates.mdx +76 -7
- package/docs/quickstart.mdx +51 -5
- package/docs/recipes.mdx +63 -23
- package/docs/skill-file.mdx +3 -1
- package/docs/state-management.mdx +2 -0
- package/docs/system-map.md +3 -1
- package/docs/telemetry-contract.md +33 -5
- package/docs/telemetry-events.mdx +63 -18
- package/docs/telemetry.md +8 -5
- package/docs/testing.md +35 -1
- package/docs/token-efficiency.md +8 -2
- package/docs/tools-act-and-wait.mdx +64 -14
- package/docs/tools-act-sequence.mdx +34 -17
- package/docs/tools-act.mdx +24 -11
- package/docs/tools-assert.mdx +69 -10
- package/docs/tools-console.mdx +11 -7
- package/docs/tools-inspect.mdx +11 -8
- package/docs/tools-navigate.mdx +41 -5
- package/docs/tools-network.mdx +42 -9
- package/docs/tools-observe.mdx +66 -10
- package/docs/tools-overview.mdx +26 -23
- package/docs/tools-query.mdx +52 -8
- package/docs/tools-session-and-feedback.mdx +35 -9
- package/docs/tools-sessions.mdx +39 -10
- package/docs/tools-snapshot.mdx +57 -3
- package/docs/tools-state.mdx +24 -1
- package/docs/tools-tools-and-run.mdx +55 -24
- package/docs/tools-wait-for.mdx +34 -15
- package/docs/troubleshooting.mdx +80 -10
- package/docs/usage.md +46 -24
- package/docs/vs-chrome-devtools-mcp.mdx +41 -0
- package/docs/vs-playwright-mcp.mdx +42 -2
- package/docs/vs-screenshots.mdx +42 -1
- package/docs/what-is-reticle.mdx +1 -1
- package/docs/why-reticle.mdx +3 -1
- package/package.json +5 -5
- package/docs/desktop-apps.md +0 -265
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { ReticleDir } from '@reticlehq/core';
|
|
3
|
+
/**
|
|
4
|
+
* The `.reticle/` entries that are LOCAL state, and must never be committed by accident.
|
|
5
|
+
*
|
|
6
|
+
* Split from the durable half deliberately, and the split is the whole design: `contract.json`,
|
|
7
|
+
* `flows/`, `baselines/` and `capsules/` are meant to be checked in — a flow that cannot be shared
|
|
8
|
+
* is not a regression check anybody else can run — while everything below is per-machine churn.
|
|
9
|
+
*
|
|
10
|
+
* The reason this matters beyond tidiness: a session journal carries URLs, request and response
|
|
11
|
+
* bodies, and DOM text from the app under test. `.reticle/` is created by the daemon rather than by
|
|
12
|
+
* the user, so it arrives untracked and unexplained, and one `git add -A` puts an app's traffic into
|
|
13
|
+
* a shared repository. Reticle created the directory; the ignore for it is Reticle's to write.
|
|
14
|
+
*/
|
|
15
|
+
const TRANSIENT = [
|
|
16
|
+
`${ReticleDir.SESSIONS_SUBDIR}/`,
|
|
17
|
+
`${ReticleDir.RUNS_SUBDIR}/`,
|
|
18
|
+
`${ReticleDir.VISUAL_SUBDIR}/`,
|
|
19
|
+
ReticleDir.PROJECT_FILE,
|
|
20
|
+
ReticleDir.AMBIENT_FILE,
|
|
21
|
+
ReticleDir.ENVELOPES_FILE,
|
|
22
|
+
ReticleDir.FLAKE_FILE,
|
|
23
|
+
ReticleDir.TIERS_FILE,
|
|
24
|
+
'*.log',
|
|
25
|
+
'*.tmp',
|
|
26
|
+
];
|
|
27
|
+
const HEADER = [
|
|
28
|
+
'# Written by Reticle, and only about the directory Reticle owns.',
|
|
29
|
+
'#',
|
|
30
|
+
'# The entries below are per-machine state — session journals, run artifacts, learned envelopes.',
|
|
31
|
+
'# Journals contain URLs, request and response bodies and page text from the app under test, so',
|
|
32
|
+
'# they are worth keeping out of a shared repository on their own account.',
|
|
33
|
+
'#',
|
|
34
|
+
`# Deliberately NOT ignored: ${ReticleDir.CONTRACT_FILE}, ${ReticleDir.FLOWS_SUBDIR}/, ${ReticleDir.BASELINES_SUBDIR}/ and`,
|
|
35
|
+
`# ${ReticleDir.CAPSULES_SUBDIR}/. Those are meant to be committed — a saved flow only earns its keep`,
|
|
36
|
+
'# when a teammate or CI can replay it.',
|
|
37
|
+
'#',
|
|
38
|
+
'# Edit freely: Reticle writes this once and never rewrites it.',
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Write `.reticle/.gitignore` if it is not already there.
|
|
42
|
+
*
|
|
43
|
+
* Never overwrites — once the file exists it belongs to whoever edited it last, and silently
|
|
44
|
+
* reverting somebody's change to a file we placed in their repository is worse than not having
|
|
45
|
+
* written it. Best-effort throughout: this runs on the daemon's start path, and a workspace that
|
|
46
|
+
* cannot be written must not stop a daemon coming up. Nothing about verification depends on it.
|
|
47
|
+
*/
|
|
48
|
+
export async function ensureWorkspaceGitignore(fs, root) {
|
|
49
|
+
const path = join(root, '.gitignore');
|
|
50
|
+
try {
|
|
51
|
+
if (await fs.exists(path))
|
|
52
|
+
return;
|
|
53
|
+
await fs.mkdir(root);
|
|
54
|
+
await fs.writeFile(path, `${[...HEADER, '', ...TRANSIENT].join('\n')}\n`);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
/* a workspace we cannot write is not a reason to fail a session */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { type KeyObject } from 'node:crypto';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
+
import { LicenseActivation } from '@reticlehq/core';
|
|
15
16
|
/** Outcome of verifying a license key. */
|
|
16
17
|
export declare const LicenseStatus: {
|
|
17
18
|
readonly VALID: "valid";
|
|
@@ -21,18 +22,31 @@ export declare const LicenseStatus: {
|
|
|
21
22
|
readonly EXPIRED: "expired";
|
|
22
23
|
};
|
|
23
24
|
export type LicenseStatus = (typeof LicenseStatus)[keyof typeof LicenseStatus];
|
|
24
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* The signed claims inside a key. `exp` is epoch ms; `features` (optional) scopes which ee features
|
|
27
|
+
* unlock.
|
|
28
|
+
*
|
|
29
|
+
* `lid` is the STABLE LICENSE ID and the only safe join key for per-customer usage: `org` is a display
|
|
30
|
+
* name a human typed at signing time, so two customers called "Acme" merge into one and a customer who
|
|
31
|
+
* renames splits into two. Every key carries one (minted by the issuer, `scripts/issue-license.mjs`),
|
|
32
|
+
* which is why it is required rather than optional — an optional id is one that goes missing on exactly
|
|
33
|
+
* the keys somebody later needs to attribute, and a key without it should be re-issued, not silently
|
|
34
|
+
* counted as nobody.
|
|
35
|
+
*/
|
|
25
36
|
declare const LicensePayloadSchema: z.ZodObject<{
|
|
37
|
+
lid: z.ZodString;
|
|
26
38
|
org: z.ZodString;
|
|
27
39
|
plan: z.ZodString;
|
|
28
40
|
exp: z.ZodNumber;
|
|
29
41
|
features: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
30
42
|
}, "strip", z.ZodTypeAny, {
|
|
43
|
+
lid: string;
|
|
31
44
|
org: string;
|
|
32
45
|
plan: string;
|
|
33
46
|
exp: number;
|
|
34
47
|
features?: string[] | undefined;
|
|
35
48
|
}, {
|
|
49
|
+
lid: string;
|
|
36
50
|
org: string;
|
|
37
51
|
plan: string;
|
|
38
52
|
exp: number;
|
|
@@ -42,13 +56,44 @@ export type LicensePayload = z.infer<typeof LicensePayloadSchema>;
|
|
|
42
56
|
type LicenseCheck = {
|
|
43
57
|
status: typeof LicenseStatus.VALID;
|
|
44
58
|
payload: LicensePayload;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* EXPIRED carries the payload; the other failures cannot. The line is the SIGNATURE, not the
|
|
62
|
+
* clock: an expired key was genuinely issued by us and its claims are exactly as trustworthy as
|
|
63
|
+
* they were yesterday, so the licence id is safe to use. A malformed or wrongly-signed key has no
|
|
64
|
+
* verified claims at all, and naming a customer from one would mean trusting whatever was pasted.
|
|
65
|
+
*/
|
|
66
|
+
| {
|
|
67
|
+
status: typeof LicenseStatus.EXPIRED;
|
|
68
|
+
payload: LicensePayload;
|
|
45
69
|
} | {
|
|
46
|
-
status: Exclude<LicenseStatus, typeof LicenseStatus.VALID>;
|
|
70
|
+
status: Exclude<LicenseStatus, typeof LicenseStatus.VALID | typeof LicenseStatus.EXPIRED>;
|
|
47
71
|
};
|
|
48
72
|
/** Sign a payload into a license key — the ISSUER side (Reticle's private key). Exposed for the issuer tool + tests. */
|
|
49
73
|
export declare function signLicenseKey(payload: LicensePayload, privateKey: KeyObject): string;
|
|
50
74
|
/** Verify a key against the issuer public key. Never throws — returns a structured status. */
|
|
51
75
|
export declare function verifyLicenseKey(key: string | undefined, publicKey: KeyObject, now: number): LicenseCheck;
|
|
76
|
+
/**
|
|
77
|
+
* Where to get a key. A named constant because it appears in the gate's error AND in what
|
|
78
|
+
* `reticle license` prints, and a contact address that drifts between the two is a support request
|
|
79
|
+
* that reaches nobody.
|
|
80
|
+
*/
|
|
81
|
+
export declare const LICENSE_CONTACT = "hey@reticle.sh";
|
|
82
|
+
/**
|
|
83
|
+
* The features a licence gates in THIS build. The single source: `assertEnterprise` callers name a
|
|
84
|
+
* member rather than a string literal, and `reticle license` reports the list, so what the CLI claims
|
|
85
|
+
* is gated is derived from what is actually gated rather than a second list somebody maintains.
|
|
86
|
+
*
|
|
87
|
+
* It is deliberately short. Reticle's roadmap for enterprise is longer (SSO, SCIM, RBAC, multi-org,
|
|
88
|
+
* policy gates), and none of it is here yet — printing a roadmap as though it were shipped is how a
|
|
89
|
+
* buyer discovers on day two that the thing they paid for does not exist. What is listed is what the
|
|
90
|
+
* running build will actually refuse without a key.
|
|
91
|
+
*/
|
|
92
|
+
export declare const EnterpriseFeature: {
|
|
93
|
+
/** Recording an audit event. */
|
|
94
|
+
readonly AUDIT_LOG: "audit-log";
|
|
95
|
+
};
|
|
96
|
+
export type EnterpriseFeature = (typeof EnterpriseFeature)[keyof typeof EnterpriseFeature];
|
|
52
97
|
/** Thrown when a production-mode enterprise feature is used without a valid license. */
|
|
53
98
|
export declare class EnterpriseLicenseError extends Error {
|
|
54
99
|
readonly feature: string;
|
|
@@ -71,8 +116,28 @@ export declare function assertEnterprise(feature: string, ctx: GateContext): voi
|
|
|
71
116
|
export declare const LICENSE_KEY_ENV = "RETICLE_LICENSE_KEY";
|
|
72
117
|
export declare const LICENSE_PUBLIC_KEY_ENV = "RETICLE_LICENSE_PUBLIC_KEY";
|
|
73
118
|
/** The human-facing state of enterprise activation on this machine (what `reticle license status` shows). */
|
|
74
|
-
interface LicenseReport {
|
|
75
|
-
|
|
119
|
+
export interface LicenseReport {
|
|
120
|
+
/**
|
|
121
|
+
* Typed against core's closed list rather than re-listing the strings here: this status is reported
|
|
122
|
+
* on every telemetry event, so a member added in one place and not the other is a silently
|
|
123
|
+
* miscounted column (telemetry contract, rule 4).
|
|
124
|
+
*/
|
|
125
|
+
status: LicenseActivation;
|
|
126
|
+
/** The stable license id — what usage is attributed to. Present only when `status` is `active`. */
|
|
127
|
+
licenseId?: string;
|
|
128
|
+
/**
|
|
129
|
+
* What a licence unlocks in THIS build, and where to get one. Always reported, including on an
|
|
130
|
+
* unlicensed install: `reticle license` was the one command that talks about licensing and it named
|
|
131
|
+
* neither, so it told an interested reader to set a variable without saying what it would unlock or
|
|
132
|
+
* how to obtain it. A dead end at exactly the moment somebody is asking.
|
|
133
|
+
*/
|
|
134
|
+
gated: readonly string[];
|
|
135
|
+
contact: string;
|
|
136
|
+
/**
|
|
137
|
+
* The key is valid, and scoped to features this build does not gate, so it unlocks nothing here.
|
|
138
|
+
* Present only when true: a `false` on every healthy licence is noise on the common case.
|
|
139
|
+
*/
|
|
140
|
+
coversNothingHere?: boolean;
|
|
76
141
|
org?: string;
|
|
77
142
|
plan?: string;
|
|
78
143
|
expiresAt?: number;
|
package/dist/license/license.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { createPublicKey, sign as edSign, verify as edVerify } from 'node:crypto';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
+
import { LicenseActivation } from '@reticlehq/core';
|
|
15
16
|
/** Outcome of verifying a license key. */
|
|
16
17
|
export const LicenseStatus = {
|
|
17
18
|
VALID: 'valid',
|
|
@@ -20,8 +21,19 @@ export const LicenseStatus = {
|
|
|
20
21
|
BAD_SIGNATURE: 'bad-signature',
|
|
21
22
|
EXPIRED: 'expired',
|
|
22
23
|
};
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The signed claims inside a key. `exp` is epoch ms; `features` (optional) scopes which ee features
|
|
26
|
+
* unlock.
|
|
27
|
+
*
|
|
28
|
+
* `lid` is the STABLE LICENSE ID and the only safe join key for per-customer usage: `org` is a display
|
|
29
|
+
* name a human typed at signing time, so two customers called "Acme" merge into one and a customer who
|
|
30
|
+
* renames splits into two. Every key carries one (minted by the issuer, `scripts/issue-license.mjs`),
|
|
31
|
+
* which is why it is required rather than optional — an optional id is one that goes missing on exactly
|
|
32
|
+
* the keys somebody later needs to attribute, and a key without it should be re-issued, not silently
|
|
33
|
+
* counted as nobody.
|
|
34
|
+
*/
|
|
24
35
|
const LicensePayloadSchema = z.object({
|
|
36
|
+
lid: z.string().min(1),
|
|
25
37
|
org: z.string(),
|
|
26
38
|
plan: z.string(),
|
|
27
39
|
exp: z.number(),
|
|
@@ -68,15 +80,35 @@ export function verifyLicenseKey(key, publicKey, now) {
|
|
|
68
80
|
if (!signatureOk)
|
|
69
81
|
return { status: LicenseStatus.BAD_SIGNATURE };
|
|
70
82
|
if (payload.exp <= now)
|
|
71
|
-
return { status: LicenseStatus.EXPIRED };
|
|
83
|
+
return { status: LicenseStatus.EXPIRED, payload };
|
|
72
84
|
return { status: LicenseStatus.VALID, payload };
|
|
73
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Where to get a key. A named constant because it appears in the gate's error AND in what
|
|
88
|
+
* `reticle license` prints, and a contact address that drifts between the two is a support request
|
|
89
|
+
* that reaches nobody.
|
|
90
|
+
*/
|
|
91
|
+
export const LICENSE_CONTACT = 'hey@reticle.sh';
|
|
92
|
+
/**
|
|
93
|
+
* The features a licence gates in THIS build. The single source: `assertEnterprise` callers name a
|
|
94
|
+
* member rather than a string literal, and `reticle license` reports the list, so what the CLI claims
|
|
95
|
+
* is gated is derived from what is actually gated rather than a second list somebody maintains.
|
|
96
|
+
*
|
|
97
|
+
* It is deliberately short. Reticle's roadmap for enterprise is longer (SSO, SCIM, RBAC, multi-org,
|
|
98
|
+
* policy gates), and none of it is here yet — printing a roadmap as though it were shipped is how a
|
|
99
|
+
* buyer discovers on day two that the thing they paid for does not exist. What is listed is what the
|
|
100
|
+
* running build will actually refuse without a key.
|
|
101
|
+
*/
|
|
102
|
+
export const EnterpriseFeature = {
|
|
103
|
+
/** Recording an audit event. */
|
|
104
|
+
AUDIT_LOG: 'audit-log',
|
|
105
|
+
};
|
|
74
106
|
/** Thrown when a production-mode enterprise feature is used without a valid license. */
|
|
75
107
|
export class EnterpriseLicenseError extends Error {
|
|
76
108
|
feature;
|
|
77
109
|
reason;
|
|
78
110
|
constructor(feature, reason) {
|
|
79
|
-
super(`Reticle Enterprise feature "${feature}" requires a valid license (${reason}). Contact
|
|
111
|
+
super(`Reticle Enterprise feature "${feature}" requires a valid license (${reason}). Contact ${LICENSE_CONTACT}.`);
|
|
80
112
|
this.name = 'EnterpriseLicenseError';
|
|
81
113
|
this.feature = feature;
|
|
82
114
|
this.reason = reason;
|
|
@@ -122,7 +154,7 @@ export const LICENSE_PUBLIC_KEY_ENV = 'RETICLE_LICENSE_PUBLIC_KEY';
|
|
|
122
154
|
* never setting RETICLE_LICENSE_PUBLIC_KEY, because the baked key takes precedence. The public key is
|
|
123
155
|
* safe to ship openly — it can only verify licenses, never mint them (that needs the private key).
|
|
124
156
|
*/
|
|
125
|
-
const BAKED_ISSUER_PUBLIC_KEY_PEM =
|
|
157
|
+
const BAKED_ISSUER_PUBLIC_KEY_PEM = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAcg+u61Fs5pwDHcYxFGhaxVaQoUbAdyl+RUdSViZyxDY=\n-----END PUBLIC KEY-----";
|
|
126
158
|
/**
|
|
127
159
|
* Resolve the issuer public-key PEM enforcement uses: the baked-in release key wins, so it can't be
|
|
128
160
|
* turned off from the environment. Only when nothing is baked (dev/repo) does the env var apply — that
|
|
@@ -134,6 +166,15 @@ function resolveIssuerPublicKeyPem(env, baked = BAKED_ISSUER_PUBLIC_KEY_PEM) {
|
|
|
134
166
|
const envPem = env[LICENSE_PUBLIC_KEY_ENV];
|
|
135
167
|
return envPem !== undefined && envPem.length > 0 ? envPem : undefined;
|
|
136
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* The two fields every report carries, spread into each branch. One object rather than a field on each
|
|
171
|
+
* return, because the value that must never go missing is the one on the branch nobody was thinking
|
|
172
|
+
* about — and the branch an unlicensed reader lands on is exactly that branch.
|
|
173
|
+
*/
|
|
174
|
+
const LICENSE_OFFER = {
|
|
175
|
+
gated: Object.values(EnterpriseFeature),
|
|
176
|
+
contact: LICENSE_CONTACT,
|
|
177
|
+
};
|
|
137
178
|
function loadPublicKey(pem) {
|
|
138
179
|
if (pem === undefined || 0 === pem.length)
|
|
139
180
|
return undefined;
|
|
@@ -156,42 +197,70 @@ export function describeLicense(now, env = process.env, baked = BAKED_ISSUER_PUB
|
|
|
156
197
|
// is off, and assertEnterpriseFromEnv now denies rather than unlocking. Say so loudly here too.
|
|
157
198
|
return isProductionEnv(env)
|
|
158
199
|
? {
|
|
159
|
-
|
|
200
|
+
...LICENSE_OFFER,
|
|
201
|
+
status: LicenseActivation.INVALID,
|
|
160
202
|
detail: 'MISCONFIGURED — running in production with no issuer key baked; enterprise features are DENIED (rebuild with BAKED_ISSUER_PUBLIC_KEY_PEM stamped in)',
|
|
161
203
|
}
|
|
162
204
|
: {
|
|
163
|
-
|
|
205
|
+
...LICENSE_OFFER,
|
|
206
|
+
status: LicenseActivation.EVAL,
|
|
164
207
|
detail: 'evaluation mode: enterprise features run free (no issuer key configured)',
|
|
165
208
|
};
|
|
166
209
|
}
|
|
167
210
|
const publicKey = loadPublicKey(pem);
|
|
168
211
|
if (publicKey === undefined)
|
|
169
|
-
return {
|
|
212
|
+
return {
|
|
213
|
+
...LICENSE_OFFER,
|
|
214
|
+
status: LicenseActivation.INVALID,
|
|
215
|
+
detail: `${LICENSE_PUBLIC_KEY_ENV} is not a valid public key`,
|
|
216
|
+
};
|
|
170
217
|
const check = verifyLicenseKey(env[LICENSE_KEY_ENV], publicKey, now);
|
|
171
218
|
if (check.status === LicenseStatus.VALID) {
|
|
172
|
-
const { org, plan, exp, features } = check.payload;
|
|
219
|
+
const { lid, org, plan, exp, features } = check.payload;
|
|
220
|
+
// A key scoped to features this build does not gate is `active` and unlocks nothing. Both halves
|
|
221
|
+
// were already on screen (`features` against `gated`) and nothing joined them, so the one word a
|
|
222
|
+
// customer actually reads told them they were fine while every gated call refused them.
|
|
223
|
+
const coversNothingHere = features !== undefined && !features.some((f) => LICENSE_OFFER.gated.includes(f));
|
|
224
|
+
const detail = coversNothingHere
|
|
225
|
+
? `licensed to ${org} (${plan}), expires ${new Date(exp).toISOString()}. This key covers ${features?.join(', ')}, and nothing this build gates (${LICENSE_OFFER.gated.join(', ')}) is included. Contact ${LICENSE_CONTACT}`
|
|
226
|
+
: `licensed to ${org} (${plan}), expires ${new Date(exp).toISOString()}`;
|
|
173
227
|
return {
|
|
174
|
-
|
|
228
|
+
...LICENSE_OFFER,
|
|
229
|
+
status: LicenseActivation.ACTIVE,
|
|
230
|
+
licenseId: lid,
|
|
175
231
|
org,
|
|
176
232
|
plan,
|
|
177
233
|
expiresAt: exp,
|
|
178
234
|
...(features !== undefined ? { features } : {}),
|
|
179
|
-
|
|
235
|
+
...(coversNothingHere ? { coversNothingHere } : {}),
|
|
236
|
+
detail,
|
|
180
237
|
};
|
|
181
238
|
}
|
|
182
239
|
if (check.status === LicenseStatus.MISSING) {
|
|
183
240
|
return {
|
|
184
|
-
|
|
185
|
-
|
|
241
|
+
...LICENSE_OFFER,
|
|
242
|
+
status: LicenseActivation.MISSING,
|
|
243
|
+
detail: `set ${LICENSE_KEY_ENV} to activate enterprise features in production. ` +
|
|
244
|
+
`Request a key from ${LICENSE_CONTACT}`,
|
|
186
245
|
};
|
|
187
246
|
}
|
|
188
247
|
if (check.status === LicenseStatus.EXPIRED) {
|
|
248
|
+
// The id rides an expired key so a lapse can be attributed to a CUSTOMER. Without it, telemetry
|
|
249
|
+
// reports that somebody's licence ran out and cannot say whose, which is not a renewal signal.
|
|
189
250
|
return {
|
|
190
|
-
|
|
191
|
-
|
|
251
|
+
...LICENSE_OFFER,
|
|
252
|
+
status: LicenseActivation.EXPIRED,
|
|
253
|
+
licenseId: check.payload.lid,
|
|
254
|
+
org: check.payload.org,
|
|
255
|
+
expiresAt: check.payload.exp,
|
|
256
|
+
detail: `license expired. Renew with ${LICENSE_CONTACT} to keep using enterprise features`,
|
|
192
257
|
};
|
|
193
258
|
}
|
|
194
|
-
return {
|
|
259
|
+
return {
|
|
260
|
+
...LICENSE_OFFER,
|
|
261
|
+
status: LicenseActivation.INVALID,
|
|
262
|
+
detail: `license key rejected (${check.status}). ${LICENSE_CONTACT} can re-issue it`,
|
|
263
|
+
};
|
|
195
264
|
}
|
|
196
265
|
/** True in a production runtime (NODE_ENV=production) — where a missing issuer key is a mis-built
|
|
197
266
|
* release, not an eval session, so the gate must fail CLOSED rather than run features free. */
|
package/dist/mcp/mcp-proxy.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export { SseFrameParser, type SseFrame } from './sse-frame-parser.js';
|
|
2
|
+
export { probeDaemon, waitForDaemon } from './proxy-daemon-probe.js';
|
|
3
|
+
/**
|
|
4
|
+
* Re-exported so every existing caller (and every spec) keeps importing the proxy's log from the
|
|
5
|
+
* proxy. Where the code lives is a file-size decision; where it is imported from is an API.
|
|
6
|
+
*/
|
|
7
|
+
export { accountProxyLogWrite, proxyLog, proxyLogPath, PROXY_LOG_CHECK_BYTES, recoverOversizedProxyLog, setProxyLogPort, } from './proxy-log.js';
|
|
1
8
|
/**
|
|
2
9
|
* Overridable so the survival spec can reach exhaustion in seconds instead of minutes — the same
|
|
3
10
|
* reason the idle windows take an env override. A positive integer only; anything else falls back,
|
|
@@ -7,62 +14,6 @@ export declare const MAX_RECONNECT_ATTEMPTS: number;
|
|
|
7
14
|
export declare function reconnectDelayMs(attempt: number): number;
|
|
8
15
|
/** JSON-RPC id the proxy uses for its own replayed `initialize` — never one the client could send. */
|
|
9
16
|
export declare const RECONNECT_INITIALIZE_ID = "__reticle_proxy_reinit";
|
|
10
|
-
/**
|
|
11
|
-
* How much the proxy may append between size checks.
|
|
12
|
-
*
|
|
13
|
-
* Statting the file on every line would be a syscall per log line on a process that logs from a
|
|
14
|
-
* reconnect loop. Counting what we wrote costs nothing and is exact enough, because this process is
|
|
15
|
-
* the only writer of its own log. Well under the cap, so the cap is a cap rather than a suggestion.
|
|
16
|
-
*/
|
|
17
|
-
export declare const PROXY_LOG_CHECK_BYTES: number;
|
|
18
|
-
/**
|
|
19
|
-
* Keep the proxy log under the same cap as the daemon log, without a stat per write.
|
|
20
|
-
*
|
|
21
|
-
* Returns the new "bytes since the last check" counter, so the accounting is testable without a
|
|
22
|
-
* filesystem. Rotation itself is `rotateDaemonLog` — the proxy log had no cap at all while the
|
|
23
|
-
* daemon log on the SAME machine stayed small, which is the whole of this defect.
|
|
24
|
-
*/
|
|
25
|
-
export declare function accountProxyLogWrite(bytesSinceCheck: number, written: number, path: string, deps: {
|
|
26
|
-
fileSize(p: string): number;
|
|
27
|
-
renameFile(from: string, to: string): void;
|
|
28
|
-
}): number;
|
|
29
|
-
/**
|
|
30
|
-
* Reclaim a proxy log that a previous process already let run away. Returns the bytes reclaimed.
|
|
31
|
-
*
|
|
32
|
-
* Reported from the field: one machine's `proxy-4400.log` reached a third of a 460GB disk and broke
|
|
33
|
-
* builds, Docker and ordinary shell commands with ENOSPC. Nothing in Reticle degraded first, so the
|
|
34
|
-
* failure surfaced as the operating system refusing to write files. Files like that exist on real
|
|
35
|
-
* machines now, and the cap alone does not help them: the user should not have to find one with `du`.
|
|
36
|
-
*
|
|
37
|
-
* TRUNCATED IN PLACE, never renamed or unlinked. A rename moves the bytes without reclaiming a byte,
|
|
38
|
-
* and a file that a running process still holds open keeps its blocks allocated after an unlink
|
|
39
|
-
* until that handle closes — which is why the reporter recovered with `: > ~/.reticle/proxy-4400.log`
|
|
40
|
-
* and why that is the operation to copy here.
|
|
41
|
-
*
|
|
42
|
-
* Best-effort, in the same spirit as `rotateDaemonLog`: refusing to start the MCP server over
|
|
43
|
-
* housekeeping is strictly worse than a large file.
|
|
44
|
-
*/
|
|
45
|
-
export declare function recoverOversizedProxyLog(path: string, deps: {
|
|
46
|
-
fileSize(p: string): number;
|
|
47
|
-
truncateFile(p: string): void;
|
|
48
|
-
}, max?: number): number;
|
|
49
|
-
/** Name the proxy log after the port it serves. Called before anything can log. */
|
|
50
|
-
export declare function setProxyLogPort(port: number): void;
|
|
51
|
-
/** The proxy's own log file, so a silent drop leaves a readable trace the agent can go read. */
|
|
52
|
-
export declare function proxyLogPath(port?: number | undefined): string;
|
|
53
|
-
/**
|
|
54
|
-
* Log to stderr (which the agent host usually swallows) AND to ~/.reticle/proxy-<port>.log, which it
|
|
55
|
-
* does not. A dropped MCP connection is invisible from the agent's side — no message, no exit code —
|
|
56
|
-
* so the one thing that makes it diagnosable at all is a file somebody can read afterwards.
|
|
57
|
-
*
|
|
58
|
-
* EXPORTED because the crash handlers need it. `installProxyResilience` was wired to the bare stderr
|
|
59
|
-
* logger, so the proxy's own uncaught exceptions and unhandled rejections — the exact events that
|
|
60
|
-
* present to a human as "the MCP server disconnected" — were written to a stream the editor throws
|
|
61
|
-
* away. The handler ran, the process kept serving, and the reason went nowhere. Reported as "the
|
|
62
|
-
* proxy has no log file… when it dies the diagnostic dies with it", which was half right: the file
|
|
63
|
-
* existed, and the one path that most needed it was not using it.
|
|
64
|
-
*/
|
|
65
|
-
export declare function proxyLog(event: string, fields?: Record<string, unknown>): void;
|
|
66
17
|
/**
|
|
67
18
|
* JSON-RPC requests forwarded on behalf of the client that have not been answered yet.
|
|
68
19
|
*
|
|
@@ -143,33 +94,6 @@ export declare class HandshakeReplay {
|
|
|
143
94
|
/** True when this inbound daemon line is the echo of our replayed handshake and must not be forwarded. */
|
|
144
95
|
shouldSuppressInbound(line: string): boolean;
|
|
145
96
|
}
|
|
146
|
-
/** One parsed Server-Sent-Events frame: the event name (defaulted to "message") and its data. */
|
|
147
|
-
interface SseFrame {
|
|
148
|
-
event: string;
|
|
149
|
-
data: string;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Incremental SSE frame parser for the MCP front door.
|
|
153
|
-
*
|
|
154
|
-
* A single SSE field can split across TCP reads (`da` in one chunk, `ta: {...}` in the next), and a
|
|
155
|
-
* server may send CRLF or bare CR line endings — so the framing is stateful and edge-case-prone, yet it
|
|
156
|
-
* carried every MCP message the agent sends and was only ever exercised end-to-end. Pulled out of the
|
|
157
|
-
* socket handler as a pure, chunk-fed parser so those boundaries are unit-testable: feed raw chunks,
|
|
158
|
-
* get back each complete frame (a blank line terminates a frame; `event:` names it, `data:` lines
|
|
159
|
-
* accumulate newline-joined; `id:`/`retry:`/comments are ignored — not needed for the bridge).
|
|
160
|
-
*/
|
|
161
|
-
export declare class SseFrameParser {
|
|
162
|
-
#private;
|
|
163
|
-
push(chunk: string): SseFrame[];
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Returns true if something is already listening on the reticle port.
|
|
167
|
-
* Uses a plain TCP probe so we don't create a side-effectful SSE session
|
|
168
|
-
* inside the daemon just to check reachability.
|
|
169
|
-
*/
|
|
170
|
-
export declare function probeDaemon(port: number): Promise<boolean>;
|
|
171
|
-
/** Poll until the daemon's HTTP port accepts connections or the deadline is reached. */
|
|
172
|
-
export declare function waitForDaemon(port: number): Promise<void>;
|
|
173
97
|
/**
|
|
174
98
|
* Turn an `endpoint` frame's data into a POST target, or null when there is nothing usable in it.
|
|
175
99
|
*
|
|
@@ -181,15 +105,6 @@ export declare function waitForDaemon(port: number): Promise<void>;
|
|
|
181
105
|
* to reconnect and how to answer what it cannot send.
|
|
182
106
|
*/
|
|
183
107
|
export declare function buildSessionUrl(rawData: string, port: number): string | null;
|
|
184
|
-
/**
|
|
185
|
-
* Bridge stdio ↔ SSE: connects to the running daemon's MCP endpoint and forwards
|
|
186
|
-
* Claude Code's stdin/stdout JSON-RPC through it. Never resolves — runs until stdin closes.
|
|
187
|
-
*
|
|
188
|
-
* A dropped SSE stream used to exit the process, which is what made the agent's `reticle_*` tools
|
|
189
|
-
* vanish mid-session with no message and no way for the agent to get them back — only a human running
|
|
190
|
-
* `/mcp` could. The daemon demonstrably stays up across these drops, so the stream ending is a
|
|
191
|
-
* transport event, not a shutdown: reconnect to it and replay the handshake instead of dying.
|
|
192
|
-
*/
|
|
193
108
|
export declare function startMcpProxy(port: number,
|
|
194
109
|
/**
|
|
195
110
|
* Bring a daemon back before reconnecting. Without it the proxy retried a DEAD port until the
|
|
@@ -197,4 +112,3 @@ export declare function startMcpProxy(port: number,
|
|
|
197
112
|
* failure mode for a daemon that crashed, was stopped, or shut itself down as idle.
|
|
198
113
|
*/
|
|
199
114
|
ensureDaemon?: () => Promise<void>): Promise<never>;
|
|
200
|
-
export {};
|