@gmickel/gno 1.16.0 → 1.18.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/README.md +34 -18
- package/assets/skill/SKILL.md +25 -6
- package/assets/skill/mcp-reference.md +21 -0
- package/package.json +2 -2
- package/src/app/context-agent-projection.ts +303 -0
- package/src/app/context-format.ts +249 -0
- package/src/app/context-runtime-contract.ts +325 -0
- package/src/app/context-runtime-input.ts +362 -0
- package/src/app/context-runtime-types.ts +65 -0
- package/src/app/context-runtime.ts +170 -0
- package/src/app/context-surface.ts +145 -0
- package/src/cli/commands/context-build.ts +149 -0
- package/src/cli/commands/context-verify.ts +90 -0
- package/src/cli/commands/daemon.ts +69 -2
- package/src/cli/commands/models/pull.ts +13 -3
- package/src/cli/commands/status.ts +2 -0
- package/src/cli/detach.ts +37 -20
- package/src/cli/options.ts +4 -0
- package/src/cli/program.ts +252 -27
- package/src/config/index.ts +3 -0
- package/src/config/types.ts +37 -0
- package/src/core/context-budget.ts +461 -0
- package/src/core/context-capsule-index-schema.ts +15 -0
- package/src/core/context-capsule-retrieval-schema.ts +81 -0
- package/src/core/context-capsule-schema.ts +473 -0
- package/src/core/context-capsule-validation.ts +416 -0
- package/src/core/context-capsule-verification.ts +218 -0
- package/src/core/context-capsule.ts +439 -0
- package/src/core/context-compiler.ts +513 -0
- package/src/core/context-evidence-metadata.ts +33 -0
- package/src/core/context-evidence.ts +495 -0
- package/src/core/context-facets.ts +163 -0
- package/src/core/context-guidance.ts +69 -0
- package/src/core/context-scope.ts +32 -0
- package/src/core/context-verifier-canonical.ts +90 -0
- package/src/core/context-verifier-input.ts +66 -0
- package/src/core/context-verifier.ts +447 -0
- package/src/core/job-manager.ts +19 -0
- package/src/core/mutation-generations.ts +33 -0
- package/src/core/sections.ts +63 -0
- package/src/llm/cache.ts +13 -3
- package/src/llm/nodeLlamaCpp/adapter.ts +10 -1
- package/src/llm/nodeLlamaCpp/lifecycle.ts +71 -0
- package/src/mcp/context.ts +161 -0
- package/src/mcp/http-security.ts +477 -0
- package/src/mcp/http-session.ts +272 -0
- package/src/mcp/http-transport.ts +370 -0
- package/src/mcp/resources/index.ts +141 -134
- package/src/mcp/server.ts +28 -82
- package/src/mcp/tools/add-collection.ts +3 -1
- package/src/mcp/tools/capture.ts +3 -0
- package/src/mcp/tools/clear-collection-embeddings.ts +2 -0
- package/src/mcp/tools/context.ts +230 -0
- package/src/mcp/tools/embed.ts +62 -52
- package/src/mcp/tools/index-cmd.ts +88 -74
- package/src/mcp/tools/index.ts +49 -2
- package/src/mcp/tools/remove-collection.ts +2 -0
- package/src/mcp/tools/status.ts +11 -0
- package/src/mcp/tools/sync.ts +16 -14
- package/src/mcp/tools/workspace-write.ts +7 -3
- package/src/pipeline/chunk-lookup.ts +33 -0
- package/src/pipeline/hybrid.ts +79 -57
- package/src/pipeline/types.ts +14 -0
- package/src/sdk/client.ts +68 -6
- package/src/sdk/index.ts +21 -0
- package/src/sdk/types.ts +24 -0
- package/src/serve/background-runtime.ts +12 -211
- package/src/serve/context-capsule.ts +136 -0
- package/src/serve/context.ts +10 -1
- package/src/serve/embed-scheduler.ts +74 -43
- package/src/serve/index.ts +9 -0
- package/src/serve/jobs.ts +78 -80
- package/src/serve/public/components/HealthCenter.tsx +74 -1
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/pages/Dashboard.tsx +1 -0
- package/src/serve/resident-admission.ts +159 -0
- package/src/serve/resident-background-work.ts +39 -0
- package/src/serve/resident-request.ts +55 -0
- package/src/serve/resident-runtime.ts +490 -0
- package/src/serve/resident-status.ts +96 -0
- package/src/serve/routes/api.ts +265 -167
- package/src/serve/routes/mcp.ts +69 -0
- package/src/serve/server.ts +212 -35
- package/src/serve/status-model.ts +51 -0
- package/src/serve/status.ts +5 -0
- package/src/store/sqlite/adapter.ts +64 -29
package/src/cli/options.ts
CHANGED
|
@@ -33,6 +33,8 @@ export const CMD = {
|
|
|
33
33
|
collectionList: "collection.list",
|
|
34
34
|
contextList: "context.list",
|
|
35
35
|
contextCheck: "context.check",
|
|
36
|
+
contextBuild: "context.build",
|
|
37
|
+
contextVerify: "context.verify",
|
|
36
38
|
modelsList: "models.list",
|
|
37
39
|
tagsList: "tags.list",
|
|
38
40
|
linksList: "links.list",
|
|
@@ -60,6 +62,8 @@ const FORMAT_SUPPORT: Record<CommandId, OutputFormat[]> = {
|
|
|
60
62
|
[CMD.collectionList]: ["terminal", "json", "md"],
|
|
61
63
|
[CMD.contextList]: ["terminal", "json", "md"],
|
|
62
64
|
[CMD.contextCheck]: ["terminal", "json", "md"],
|
|
65
|
+
[CMD.contextBuild]: ["terminal", "json", "md"],
|
|
66
|
+
[CMD.contextVerify]: ["terminal", "json", "md"],
|
|
63
67
|
[CMD.modelsList]: ["terminal", "json"],
|
|
64
68
|
[CMD.tagsList]: ["terminal", "json", "md"],
|
|
65
69
|
[CMD.linksList]: ["terminal", "json", "md"],
|
package/src/cli/program.ts
CHANGED
|
@@ -210,6 +210,42 @@ function parseCsvValues(raw: unknown): string[] | undefined {
|
|
|
210
210
|
return values.length > 0 ? values : undefined;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
function parseContextInteger(
|
|
214
|
+
name: string,
|
|
215
|
+
value: unknown,
|
|
216
|
+
allowZero = false
|
|
217
|
+
): number {
|
|
218
|
+
const raw = typeof value === "string" ? value : String(value);
|
|
219
|
+
if (!/^\d+$/.test(raw)) {
|
|
220
|
+
throw new CliError("VALIDATION", `--${name} must be an integer`, {
|
|
221
|
+
details: { contextCode: "invalid_budget" },
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
const parsed = Number(raw);
|
|
225
|
+
if (!Number.isSafeInteger(parsed) || (allowZero ? parsed < 0 : parsed < 1)) {
|
|
226
|
+
throw new CliError(
|
|
227
|
+
"VALIDATION",
|
|
228
|
+
`--${name} must be ${allowZero ? "non-negative" : "positive"}`,
|
|
229
|
+
{ details: { contextCode: "invalid_budget" } }
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return parsed;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function validateContextOutputPath(value: unknown): string | undefined {
|
|
236
|
+
if (value === undefined) return undefined;
|
|
237
|
+
if (typeof value !== "string" || !value.trim() || value === "-") {
|
|
238
|
+
throw new CliError(
|
|
239
|
+
"VALIDATION",
|
|
240
|
+
"--output requires an explicit file path",
|
|
241
|
+
{
|
|
242
|
+
details: { contextCode: "invalid_filter" },
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
|
|
213
249
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
214
250
|
// Program Factory
|
|
215
251
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -1624,6 +1660,148 @@ function wireManagementCommands(program: Command): void {
|
|
|
1624
1660
|
await contextCheck(format as "terminal" | "json" | "md");
|
|
1625
1661
|
});
|
|
1626
1662
|
|
|
1663
|
+
contextCmd
|
|
1664
|
+
.command("build <goal...>")
|
|
1665
|
+
.description("Build a deterministic evidence Capsule")
|
|
1666
|
+
.requiredOption("--budget <tokens>", "global token budget")
|
|
1667
|
+
.option("--bytes <bytes>", "global byte budget")
|
|
1668
|
+
.option("--query <query>", "retrieval query (defaults to goal)")
|
|
1669
|
+
.option(
|
|
1670
|
+
"--query-mode <mode:text>",
|
|
1671
|
+
"structured mode entry (repeatable): term:<text>, intent:<text>, or hyde:<text>",
|
|
1672
|
+
(value: string, previous: string[] = []) => [...previous, value],
|
|
1673
|
+
[]
|
|
1674
|
+
)
|
|
1675
|
+
.option(
|
|
1676
|
+
"-c, --collection <name>",
|
|
1677
|
+
"collection filter (repeatable)",
|
|
1678
|
+
(value: string, previous: string[] = []) => [...previous, value],
|
|
1679
|
+
[]
|
|
1680
|
+
)
|
|
1681
|
+
.option("--uri-prefix <uri>", "canonical GNO URI prefix")
|
|
1682
|
+
.option("--tags-all <tags>", "require ALL tags (comma-separated)")
|
|
1683
|
+
.option("--tags-any <tags>", "require ANY tag (comma-separated)")
|
|
1684
|
+
.option("--category <values>", "require category match (comma-separated)")
|
|
1685
|
+
.option("--author <text>", "filter by author")
|
|
1686
|
+
.option("--lang <code>", "language hint (BCP-47)")
|
|
1687
|
+
.option("--since <date>", "modified-at lower bound")
|
|
1688
|
+
.option("--until <date>", "modified-at upper bound")
|
|
1689
|
+
.option("--graph", "enable graph neighbor expansion")
|
|
1690
|
+
.option("--fast", "use lexical-first fast retrieval")
|
|
1691
|
+
.option("--thorough", "use a wider retrieval pool")
|
|
1692
|
+
.option("-n, --limit <num>", "maximum retrieved results")
|
|
1693
|
+
.option("-C, --candidate-limit <num>", "maximum rerank candidates")
|
|
1694
|
+
.option("--safety-margin <tokens>", "reserved token margin", "0")
|
|
1695
|
+
.option("--safety-margin-bytes <bytes>", "reserved byte margin", "0")
|
|
1696
|
+
.option("--output <path>", "write only to this explicit file")
|
|
1697
|
+
.option("--json", "canonical JSON output")
|
|
1698
|
+
.option("--md", "readable Markdown output")
|
|
1699
|
+
.action(async (goalParts: string[], cmdOpts: Record<string, unknown>) => {
|
|
1700
|
+
const format = getFormat(cmdOpts);
|
|
1701
|
+
assertFormatSupported(CMD.contextBuild, format);
|
|
1702
|
+
const outputPath = validateContextOutputPath(cmdOpts.output);
|
|
1703
|
+
if (cmdOpts.fast && cmdOpts.thorough) {
|
|
1704
|
+
throw new CliError("VALIDATION", "Choose either --fast or --thorough");
|
|
1705
|
+
}
|
|
1706
|
+
const globals = getGlobals();
|
|
1707
|
+
const { contextBuild } = await import("./commands/context-build");
|
|
1708
|
+
let queryModes: import("../pipeline/types").QueryModeInput[] | undefined;
|
|
1709
|
+
if (Array.isArray(cmdOpts.queryMode) && cmdOpts.queryMode.length > 0) {
|
|
1710
|
+
const { parseQueryModeSpecs } = await import("../pipeline/query-modes");
|
|
1711
|
+
const parsed = parseQueryModeSpecs(cmdOpts.queryMode as string[]);
|
|
1712
|
+
if (!parsed.ok) {
|
|
1713
|
+
throw new CliError("VALIDATION", parsed.error.message, {
|
|
1714
|
+
details: { contextCode: "invalid_filter" },
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
queryModes = parsed.value;
|
|
1718
|
+
}
|
|
1719
|
+
const output = await contextBuild(goalParts.join(" "), {
|
|
1720
|
+
configPath: globals.config,
|
|
1721
|
+
indexName: globals.index,
|
|
1722
|
+
budgetTokens: parseContextInteger("budget", cmdOpts.budget),
|
|
1723
|
+
budgetBytes:
|
|
1724
|
+
cmdOpts.bytes === undefined
|
|
1725
|
+
? undefined
|
|
1726
|
+
: parseContextInteger("bytes", cmdOpts.bytes),
|
|
1727
|
+
safetyMarginTokens: parseContextInteger(
|
|
1728
|
+
"safety-margin",
|
|
1729
|
+
cmdOpts.safetyMargin ?? "0",
|
|
1730
|
+
true
|
|
1731
|
+
),
|
|
1732
|
+
safetyMarginBytes: parseContextInteger(
|
|
1733
|
+
"safety-margin-bytes",
|
|
1734
|
+
cmdOpts.safetyMarginBytes ?? "0",
|
|
1735
|
+
true
|
|
1736
|
+
),
|
|
1737
|
+
query: cmdOpts.query as string | undefined,
|
|
1738
|
+
queryModes,
|
|
1739
|
+
collections: cmdOpts.collection as string[],
|
|
1740
|
+
uriPrefix: cmdOpts.uriPrefix as string | undefined,
|
|
1741
|
+
tagsAll: parseCsvValues(cmdOpts.tagsAll),
|
|
1742
|
+
tagsAny: parseCsvValues(cmdOpts.tagsAny),
|
|
1743
|
+
categories: parseCsvValues(cmdOpts.category),
|
|
1744
|
+
author: cmdOpts.author as string | undefined,
|
|
1745
|
+
lang: cmdOpts.lang as string | undefined,
|
|
1746
|
+
since: cmdOpts.since as string | undefined,
|
|
1747
|
+
until: cmdOpts.until as string | undefined,
|
|
1748
|
+
graph: Boolean(cmdOpts.graph),
|
|
1749
|
+
limit:
|
|
1750
|
+
cmdOpts.limit === undefined
|
|
1751
|
+
? undefined
|
|
1752
|
+
: parseContextInteger("limit", cmdOpts.limit),
|
|
1753
|
+
candidateLimit:
|
|
1754
|
+
cmdOpts.candidateLimit === undefined
|
|
1755
|
+
? undefined
|
|
1756
|
+
: parseContextInteger("candidate-limit", cmdOpts.candidateLimit),
|
|
1757
|
+
depthPolicy: cmdOpts.fast
|
|
1758
|
+
? "fast"
|
|
1759
|
+
: cmdOpts.thorough
|
|
1760
|
+
? "thorough"
|
|
1761
|
+
: "balanced",
|
|
1762
|
+
format: format === "json" ? "json" : "md",
|
|
1763
|
+
});
|
|
1764
|
+
if (outputPath !== undefined) {
|
|
1765
|
+
await Bun.write(outputPath, output);
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
await writeOutput(output, format === "json" ? "json" : "md");
|
|
1769
|
+
});
|
|
1770
|
+
|
|
1771
|
+
contextCmd
|
|
1772
|
+
.command("verify <file>")
|
|
1773
|
+
.description("Verify a saved Context Capsule without rebuilding it")
|
|
1774
|
+
.option("--output <path>", "write only to this explicit file")
|
|
1775
|
+
.option("--json", "canonical JSON receipt")
|
|
1776
|
+
.option("--md", "readable Markdown receipt")
|
|
1777
|
+
.action(
|
|
1778
|
+
async (
|
|
1779
|
+
file: string,
|
|
1780
|
+
cmdOpts: Record<string, unknown>,
|
|
1781
|
+
command: Command
|
|
1782
|
+
) => {
|
|
1783
|
+
const format = getFormat(cmdOpts);
|
|
1784
|
+
assertFormatSupported(CMD.contextVerify, format);
|
|
1785
|
+
const outputPath = validateContextOutputPath(cmdOpts.output);
|
|
1786
|
+
const globals = getGlobals();
|
|
1787
|
+
const explicitIndexName =
|
|
1788
|
+
command.getOptionValueSourceWithGlobals("index") === "cli"
|
|
1789
|
+
? globals.index
|
|
1790
|
+
: undefined;
|
|
1791
|
+
const { contextVerify } = await import("./commands/context-verify");
|
|
1792
|
+
const output = await contextVerify(file, {
|
|
1793
|
+
configPath: globals.config,
|
|
1794
|
+
indexName: explicitIndexName,
|
|
1795
|
+
format: format === "json" ? "json" : "md",
|
|
1796
|
+
});
|
|
1797
|
+
if (outputPath !== undefined) {
|
|
1798
|
+
await Bun.write(outputPath, output);
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
await writeOutput(output, format === "json" ? "json" : "md");
|
|
1802
|
+
}
|
|
1803
|
+
);
|
|
1804
|
+
|
|
1627
1805
|
contextCmd
|
|
1628
1806
|
.command("rm <uri>")
|
|
1629
1807
|
.description("Remove context item")
|
|
@@ -2628,20 +2806,46 @@ function wireGraphCommand(program: Command): void {
|
|
|
2628
2806
|
// Serve Command (web UI)
|
|
2629
2807
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
2630
2808
|
|
|
2631
|
-
function
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2809
|
+
function addGatewayOptions(
|
|
2810
|
+
command: Command,
|
|
2811
|
+
hostDescription = "HTTP listen address (default: 127.0.0.1)"
|
|
2812
|
+
): Command {
|
|
2813
|
+
return command
|
|
2814
|
+
.option("--host <address>", hostDescription)
|
|
2815
|
+
.option("--mcp-token-file <path>", "restrictive bearer token file")
|
|
2816
|
+
.option(
|
|
2817
|
+
"--mcp-allowed-host <host>",
|
|
2818
|
+
"exact allowed Host header (repeatable)",
|
|
2819
|
+
(value: string, previous: string[] = []) => [...previous, value]
|
|
2820
|
+
)
|
|
2635
2821
|
.option(
|
|
2636
|
-
"--
|
|
2637
|
-
"
|
|
2822
|
+
"--mcp-allowed-origin <origin>",
|
|
2823
|
+
"exact allowed Origin (repeatable)",
|
|
2824
|
+
(value: string, previous: string[] = []) => [...previous, value]
|
|
2638
2825
|
)
|
|
2639
|
-
// Local `--json` so `gno daemon --status --json` parses cleanly in any
|
|
2640
|
-
// argv position. Mirrors the serve wiring: gated to --status only.
|
|
2641
2826
|
.option(
|
|
2642
|
-
"--
|
|
2643
|
-
"
|
|
2827
|
+
"--mcp-enable-write",
|
|
2828
|
+
"authorize HTTP MCP mutation tools (independent of authentication)"
|
|
2644
2829
|
);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
function wireDaemonCommand(program: Command): void {
|
|
2833
|
+
const daemonCmd = addGatewayOptions(
|
|
2834
|
+
program
|
|
2835
|
+
.command("daemon")
|
|
2836
|
+
.description("Start headless continuous indexing")
|
|
2837
|
+
.option("-p, --port <num>", "MCP gateway port", "3000")
|
|
2838
|
+
.option(
|
|
2839
|
+
"--no-sync-on-start",
|
|
2840
|
+
"skip initial sync and only watch future file changes"
|
|
2841
|
+
)
|
|
2842
|
+
// Local `--json` so `gno daemon --status --json` parses cleanly in any
|
|
2843
|
+
// argv position. Mirrors the serve wiring: gated to --status only.
|
|
2844
|
+
.option(
|
|
2845
|
+
"--json",
|
|
2846
|
+
"JSON output (applies to --status; see process-status schema)"
|
|
2847
|
+
)
|
|
2848
|
+
);
|
|
2645
2849
|
|
|
2646
2850
|
// --detach / --status / --stop are mutually exclusive. Use Commander's
|
|
2647
2851
|
// Option API so the conflict error is the native "option '--status'
|
|
@@ -2695,8 +2899,8 @@ function wireDaemonCommand(program: Command): void {
|
|
|
2695
2899
|
* the foreground + detached-child paths boot the runtime.
|
|
2696
2900
|
*
|
|
2697
2901
|
* Mirrors `handleServeAction` exactly — same helper imports, same JSON
|
|
2698
|
-
* gating, same detached-child verification + pid-file cleanup. Daemon
|
|
2699
|
-
*
|
|
2902
|
+
* gating, same detached-child verification + pid-file cleanup. Daemon's port
|
|
2903
|
+
* identifies its headless MCP gateway.
|
|
2700
2904
|
*/
|
|
2701
2905
|
async function handleDaemonAction(
|
|
2702
2906
|
cmdOpts: Record<string, unknown>,
|
|
@@ -2749,7 +2953,9 @@ async function handleDaemonAction(
|
|
|
2749
2953
|
}
|
|
2750
2954
|
|
|
2751
2955
|
if (cmdOpts.detach) {
|
|
2956
|
+
const port = parsePositiveInt("port", cmdOpts.port);
|
|
2752
2957
|
await runDaemonDetach({
|
|
2958
|
+
port,
|
|
2753
2959
|
paths,
|
|
2754
2960
|
spawnDetached,
|
|
2755
2961
|
argv: resolveCliArgv(cmd),
|
|
@@ -2776,6 +2982,8 @@ async function handleDaemonAction(
|
|
|
2776
2982
|
installPidFileCleanup(paths.pidFile);
|
|
2777
2983
|
}
|
|
2778
2984
|
|
|
2985
|
+
const port = parsePositiveInt("port", cmdOpts.port);
|
|
2986
|
+
|
|
2779
2987
|
const { daemon } = await import("./commands/daemon.js");
|
|
2780
2988
|
const result = await daemon({
|
|
2781
2989
|
configPath: globals.config,
|
|
@@ -2784,6 +2992,12 @@ async function handleDaemonAction(
|
|
|
2784
2992
|
verbose: globals.verbose,
|
|
2785
2993
|
quiet: globals.quiet,
|
|
2786
2994
|
noSyncOnStart: cmdOpts.syncOnStart === false,
|
|
2995
|
+
port,
|
|
2996
|
+
host: cmdOpts.host as string | undefined,
|
|
2997
|
+
tokenFile: cmdOpts.mcpTokenFile as string | undefined,
|
|
2998
|
+
allowedHosts: cmdOpts.mcpAllowedHost as string[] | undefined,
|
|
2999
|
+
allowedOrigins: cmdOpts.mcpAllowedOrigin as string[] | undefined,
|
|
3000
|
+
enableWrite: cmdOpts.mcpEnableWrite === true ? true : undefined,
|
|
2787
3001
|
});
|
|
2788
3002
|
if (!result.success) {
|
|
2789
3003
|
throw new CliError("RUNTIME", result.error);
|
|
@@ -2817,8 +3031,9 @@ async function runDaemonStatus(deps: DaemonStatusDeps): Promise<void> {
|
|
|
2817
3031
|
process.stdout.write("gno daemon status\n");
|
|
2818
3032
|
process.stdout.write(`${"─".repeat(50)}\n`);
|
|
2819
3033
|
if (status.running) {
|
|
2820
|
-
|
|
2821
|
-
|
|
3034
|
+
process.stdout.write(
|
|
3035
|
+
` running yes (pid ${status.pid}, port ${status.port})\n`
|
|
3036
|
+
);
|
|
2822
3037
|
if (status.version) {
|
|
2823
3038
|
process.stdout.write(` version ${status.version}\n`);
|
|
2824
3039
|
}
|
|
@@ -2913,6 +3128,7 @@ async function runDaemonStop(deps: DaemonStopDeps): Promise<void> {
|
|
|
2913
3128
|
}
|
|
2914
3129
|
|
|
2915
3130
|
interface DaemonDetachDeps {
|
|
3131
|
+
port: number;
|
|
2916
3132
|
paths: { pidFile: string; logFile: string };
|
|
2917
3133
|
spawnDetached: typeof import("./detach.js").spawnDetached;
|
|
2918
3134
|
/**
|
|
@@ -2933,24 +3149,28 @@ async function runDaemonDetach(deps: DaemonDetachDeps): Promise<void> {
|
|
|
2933
3149
|
argv: childArgv,
|
|
2934
3150
|
pidFile: deps.paths.pidFile,
|
|
2935
3151
|
logFile: deps.paths.logFile,
|
|
3152
|
+
port: deps.port,
|
|
2936
3153
|
});
|
|
2937
|
-
// Daemon
|
|
3154
|
+
// Daemon has no Web UI URL; its listener exists solely for MCP.
|
|
2938
3155
|
process.stdout.write(`PID ${result.pid}\n`);
|
|
2939
3156
|
}
|
|
2940
3157
|
|
|
2941
3158
|
function wireServeCommand(program: Command): void {
|
|
2942
|
-
const serveCmd =
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
3159
|
+
const serveCmd = addGatewayOptions(
|
|
3160
|
+
program
|
|
3161
|
+
.command("serve")
|
|
3162
|
+
.description("Start web UI server")
|
|
3163
|
+
.option("-p, --port <num>", "port to listen on", "3000")
|
|
3164
|
+
// Local `--json` so `gno serve --status --json` parses cleanly in any
|
|
3165
|
+
// argv position. Global `--json` already exists on the root program
|
|
3166
|
+
// (see line 208); resolution goes through `getFormat()` /
|
|
3167
|
+
// `getGlobals()` which precedence-merges local over global.
|
|
3168
|
+
.option(
|
|
3169
|
+
"--json",
|
|
3170
|
+
"JSON output (applies to --status; see process-status schema)"
|
|
3171
|
+
),
|
|
3172
|
+
"loopback HTTP listen address (default: 127.0.0.1)"
|
|
3173
|
+
);
|
|
2954
3174
|
|
|
2955
3175
|
// --detach / --status / --stop are mutually exclusive. Use Commander's
|
|
2956
3176
|
// Option API so the conflict error is the native "option '--status'
|
|
@@ -3097,6 +3317,11 @@ async function handleServeAction(
|
|
|
3097
3317
|
port,
|
|
3098
3318
|
configPath: globals.config,
|
|
3099
3319
|
index: globals.index,
|
|
3320
|
+
host: cmdOpts.host as string | undefined,
|
|
3321
|
+
tokenFile: cmdOpts.mcpTokenFile as string | undefined,
|
|
3322
|
+
allowedHosts: cmdOpts.mcpAllowedHost as string[] | undefined,
|
|
3323
|
+
allowedOrigins: cmdOpts.mcpAllowedOrigin as string[] | undefined,
|
|
3324
|
+
enableWrite: cmdOpts.mcpEnableWrite === true ? true : undefined,
|
|
3100
3325
|
});
|
|
3101
3326
|
|
|
3102
3327
|
if (!result.success) {
|
package/src/config/index.ts
CHANGED
|
@@ -50,6 +50,8 @@ export {
|
|
|
50
50
|
CollectionSchema,
|
|
51
51
|
type Config,
|
|
52
52
|
ConfigSchema,
|
|
53
|
+
HttpGatewayConfigSchema,
|
|
54
|
+
HttpGatewayLimitsSchema,
|
|
53
55
|
CONTENT_TYPE_GRAPH_HINTS,
|
|
54
56
|
type ContentTypeConfig,
|
|
55
57
|
type ContentTypeGraphHint,
|
|
@@ -67,3 +69,4 @@ export {
|
|
|
67
69
|
type ScopeType,
|
|
68
70
|
ScopeTypeSchema,
|
|
69
71
|
} from "./types";
|
|
72
|
+
export type { HttpGatewayConfig } from "./types";
|
package/src/config/types.ts
CHANGED
|
@@ -245,6 +245,40 @@ export const ModelConfigSchema = z.object({
|
|
|
245
245
|
|
|
246
246
|
export type ModelConfig = z.infer<typeof ModelConfigSchema>;
|
|
247
247
|
|
|
248
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
249
|
+
// Resident HTTP gateway schema
|
|
250
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
export const HttpGatewayLimitsSchema = z.object({
|
|
253
|
+
maxBodyBytes: z
|
|
254
|
+
.number()
|
|
255
|
+
.int()
|
|
256
|
+
.min(1)
|
|
257
|
+
.max(16 * 1024 * 1024)
|
|
258
|
+
.optional(),
|
|
259
|
+
maxRequestsPerMinute: z.number().int().min(1).max(100_000).optional(),
|
|
260
|
+
maxConcurrentRequests: z.number().int().min(1).max(10_000).optional(),
|
|
261
|
+
maxQueuedRequests: z.number().int().min(0).max(10_000).optional(),
|
|
262
|
+
maxSessions: z.number().int().min(1).max(10_000).optional(),
|
|
263
|
+
sessionIdleTimeoutMs: z.number().int().min(1_000).optional(),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
export const HttpGatewayConfigSchema = z.object({
|
|
267
|
+
/** Literal listen address. Defaults to IPv4 loopback. */
|
|
268
|
+
host: z.string().min(1).optional(),
|
|
269
|
+
/** Bearer token file. Required for wildcard/non-loopback binding. */
|
|
270
|
+
tokenFile: z.string().min(1).optional(),
|
|
271
|
+
/** Exact Host header allowlist. */
|
|
272
|
+
allowedHosts: z.array(z.string().min(1)).optional(),
|
|
273
|
+
/** Exact Origin allowlist. Origin-less non-browser clients remain supported. */
|
|
274
|
+
allowedOrigins: z.array(z.string().min(1)).optional(),
|
|
275
|
+
/** Separate mutation authorization; authentication alone never enables it. */
|
|
276
|
+
enableWrite: z.boolean().optional(),
|
|
277
|
+
limits: HttpGatewayLimitsSchema.optional(),
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
export type HttpGatewayConfig = z.infer<typeof HttpGatewayConfigSchema>;
|
|
281
|
+
|
|
248
282
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
249
283
|
// Content Type Schema
|
|
250
284
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -301,6 +335,9 @@ export const ConfigSchema = z.object({
|
|
|
301
335
|
|
|
302
336
|
/** Model configuration */
|
|
303
337
|
models: ModelConfigSchema.optional(),
|
|
338
|
+
|
|
339
|
+
/** Resident Streamable HTTP MCP gateway configuration */
|
|
340
|
+
gateway: HttpGatewayConfigSchema.optional(),
|
|
304
341
|
});
|
|
305
342
|
|
|
306
343
|
export type Config = Omit<z.infer<typeof ConfigSchema>, "contentTypes"> & {
|