cinatra 0.1.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/LICENSE +202 -0
- package/README.md +77 -0
- package/bin/cinatra.mjs +8 -0
- package/package.json +32 -0
- package/src/agents-install.mjs +801 -0
- package/src/checkout-resolve.mjs +236 -0
- package/src/cinatra-dev-extensions.mjs +338 -0
- package/src/clone-registry.mjs +623 -0
- package/src/clone-runtime.mjs +543 -0
- package/src/command-table.mjs +390 -0
- package/src/dev-apps.mjs +79 -0
- package/src/dev-cli-modules.mjs +91 -0
- package/src/dev-refresh.mjs +117 -0
- package/src/dev-repo-sync.mjs +297 -0
- package/src/extensions-dependency-gate.mjs +258 -0
- package/src/extensions-submit.mjs +137 -0
- package/src/index.mjs +9203 -0
- package/src/install.mjs +815 -0
- package/src/login.mjs +508 -0
- package/src/marketplace-mcp.mjs +100 -0
- package/src/mcp-public-base-url-shape.mjs +134 -0
- package/src/prod-extension-acquisition.mjs +679 -0
- package/src/seed-local-registry.mjs +538 -0
- package/src/tailscale-provision.mjs +219 -0
- package/src/teardown-config.mjs +113 -0
- package/src/worktree-collision-guard.mjs +157 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Pure URL-shape helper for the MCP server's `publicBaseUrl`.
|
|
3
|
+
//
|
|
4
|
+
// Plain ESM `.mjs`, NO imports, NO `server-only`, NO `@/` aliases. Importable
|
|
5
|
+
// from anywhere:
|
|
6
|
+
// - TS in-process callers (`packages/mcp-server/src/llm-credentials.ts`)
|
|
7
|
+
// via `import { normaliseMcpPublicBaseUrl } from "./mcp-public-base-url-shape.mjs"`.
|
|
8
|
+
// - The plain-`.mjs` Cinatra CLI (`packages/cli/src/index.mjs`) directly.
|
|
9
|
+
//
|
|
10
|
+
// Why a separate file: the CLI must write `mcp_server.publicBaseUrl` into a
|
|
11
|
+
// clone's Postgres DB, but the existing TS
|
|
12
|
+
// `setMcpPublicBaseUrl` lives behind `server-only` + Next path aliases and
|
|
13
|
+
// `@cinatra-ai/mcp-server` ships no compiled `dist/`. Extracting only the pure
|
|
14
|
+
// URL-shape rules (validation, normalisation, origin-only contract) into this
|
|
15
|
+
// module gives both writers a single source of truth without forcing either
|
|
16
|
+
// to bend toward the other's import constraints.
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The metadata row key under which the MCP server's settings live. Stable
|
|
21
|
+
* across the writers; matches `MCP_SETTINGS_KEY` in
|
|
22
|
+
* `packages/mcp-server/src/llm-credentials.ts` and `packages/cli/src/index.mjs`.
|
|
23
|
+
*/
|
|
24
|
+
export const MCP_PUBLIC_BASE_URL_METADATA_KEY = "connector_config:mcp_server";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The set of `publicBaseUrlSource` values callers can request when writing.
|
|
28
|
+
* `"unknown"` is reserved for the null-URL path (cannot be passed in).
|
|
29
|
+
*
|
|
30
|
+
* - `"manual"` (default): operator pasted the URL into the dev tab form.
|
|
31
|
+
* - `"tailscale-auto"`: `cinatra clone start` minted a
|
|
32
|
+
* Tailscale Funnel URL via the Nango-stored OAuth client.
|
|
33
|
+
* - `"tailscale-funnel"`: Tailscale sidecar path that read `TS_AUTHKEY`
|
|
34
|
+
* from env.
|
|
35
|
+
*
|
|
36
|
+
* @typedef {"manual" | "tailscale-auto" | "tailscale-funnel"} McpPublicBaseUrlSource
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Normalise an operator-supplied (or env-derived) MCP public base URL.
|
|
41
|
+
* Returns the canonical origin-only form plus the matching
|
|
42
|
+
* `publicBaseUrlSource`.
|
|
43
|
+
*
|
|
44
|
+
* Rules:
|
|
45
|
+
* - `null` / `undefined` / empty / whitespace → `{ url: null, source: "unknown" }`.
|
|
46
|
+
* - Trailing slashes stripped before parsing.
|
|
47
|
+
* - Scheme MUST be http(s); other schemes throw.
|
|
48
|
+
* - URL MUST be origin-only — no path, no query, no fragment. Throws otherwise.
|
|
49
|
+
* - Successful normalisation: `{ url, source: options.source ?? "manual" }`.
|
|
50
|
+
*
|
|
51
|
+
* Error messages mirror the prior in-place validation in
|
|
52
|
+
* `setMcpPublicBaseUrl()` so callers who relied on the wording continue to see
|
|
53
|
+
* an equivalent rejection.
|
|
54
|
+
*
|
|
55
|
+
* @param {string | null | undefined} input
|
|
56
|
+
* @param {{ source?: McpPublicBaseUrlSource }} [options]
|
|
57
|
+
* @returns {{ url: string | null, source: McpPublicBaseUrlSource | "unknown" }}
|
|
58
|
+
*/
|
|
59
|
+
export function normaliseMcpPublicBaseUrl(input, options) {
|
|
60
|
+
if (input == null) return { url: null, source: "unknown" };
|
|
61
|
+
if (typeof input !== "string") {
|
|
62
|
+
return { url: null, source: "unknown" };
|
|
63
|
+
}
|
|
64
|
+
// Strip trailing slashes via a LINEAR char-index trim. The previous
|
|
65
|
+
// `/\/+$/` is an anchored greedy slash-repetition — polynomial-ReDoS on
|
|
66
|
+
// input with many trailing slashes (CodeQL js/polynomial-redos, high).
|
|
67
|
+
const trimmedInput = input.trim();
|
|
68
|
+
let trimEnd = trimmedInput.length;
|
|
69
|
+
while (trimEnd > 0 && trimmedInput.charCodeAt(trimEnd - 1) === 47) trimEnd--; // 47 = "/"
|
|
70
|
+
const trimmed = trimmedInput.slice(0, trimEnd);
|
|
71
|
+
if (trimmed.length === 0) return { url: null, source: "unknown" };
|
|
72
|
+
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = new URL(trimmed);
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`MCP publicBaseUrl: URL must be a valid http(s)://… origin, got ${JSON.stringify(input)}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`MCP publicBaseUrl: URL must use http(s) scheme, got ${JSON.stringify(input)}`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (parsed.pathname !== "/" && parsed.pathname !== "") {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`MCP publicBaseUrl: URL must be an origin without a path (got ${JSON.stringify(input)}). ` +
|
|
89
|
+
`Save just the host, e.g. https://my-tunnel.example.ts.net`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (parsed.search.length > 0 || parsed.hash.length > 0) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`MCP publicBaseUrl: URL must not have a query string or fragment (got ${JSON.stringify(input)}).`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const requestedSource = options?.source;
|
|
98
|
+
const source =
|
|
99
|
+
requestedSource === "tailscale-auto" || requestedSource === "tailscale-funnel"
|
|
100
|
+
? requestedSource
|
|
101
|
+
: "manual";
|
|
102
|
+
return { url: `${parsed.protocol}//${parsed.host}`, source };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Compute the next metadata-row body for a publicBaseUrl write, preserving
|
|
107
|
+
* any sibling fields and dropping retired columns. Mirrors the in-place
|
|
108
|
+
* merge in `setMcpPublicBaseUrl()` so the CLI writer + the in-process writer
|
|
109
|
+
* produce byte-equivalent rows for the same input.
|
|
110
|
+
*
|
|
111
|
+
* @param {Record<string, unknown>} current Existing row contents (may be empty).
|
|
112
|
+
* @param {string | null | undefined} url Operator-supplied URL.
|
|
113
|
+
* @param {{ source?: McpPublicBaseUrlSource }} [options] Tag the write with a
|
|
114
|
+
* non-default `publicBaseUrlSource` (e.g. `"tailscale-auto"` for the
|
|
115
|
+
* auto-tunnel path). Defaults to `"manual"`.
|
|
116
|
+
* @returns {Record<string, unknown>}
|
|
117
|
+
*/
|
|
118
|
+
export function buildMcpPublicBaseUrlRow(current, url, options) {
|
|
119
|
+
const { url: nextUrl, source: nextSource } = normaliseMcpPublicBaseUrl(
|
|
120
|
+
url,
|
|
121
|
+
options,
|
|
122
|
+
);
|
|
123
|
+
const next = {
|
|
124
|
+
...current,
|
|
125
|
+
publicBaseUrl: nextUrl,
|
|
126
|
+
publicBaseUrlSource: nextSource,
|
|
127
|
+
updatedAt: new Date().toISOString(),
|
|
128
|
+
};
|
|
129
|
+
// Drop fields no longer used by the public base URL row shape.
|
|
130
|
+
delete next.tunnelMode;
|
|
131
|
+
delete next.externalUrl;
|
|
132
|
+
delete next.cloudflaredMissing;
|
|
133
|
+
return next;
|
|
134
|
+
}
|