@cotal-ai/linear 0.0.0-bootstrap.0 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/oauth.js ADDED
@@ -0,0 +1,240 @@
1
+ /**
2
+ * OAuth for a Linear MCP account, through the MCP SDK's client: dynamic client registration and an
3
+ * authorization-code grant with PKCE, against the authorization server the pinned Linear MCP origin
4
+ * advertises. `cotal linear account login` runs it once on a loopback redirect and prints only the
5
+ * authorize URL. Refresh happens before a request is sent, never by re-sending a refused request.
6
+ *
7
+ * Every OAuth request goes to `https://mcp.linear.app` (any path), with no redirects followed and a
8
+ * bounded body. If Linear moves its authorization server to another origin, login refuses and
9
+ * names that origin rather than following it.
10
+ *
11
+ * State (client registration, tokens, the PKCE verifier) lives in one 0600 file per account.
12
+ */
13
+ import { randomBytes } from "node:crypto";
14
+ import { existsSync, readFileSync } from "node:fs";
15
+ import { createServer } from "node:http";
16
+ import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
17
+ import { mkSecretDir, writeSecretFileAtomic } from "@cotal-ai/core";
18
+ import { assertPrivateFile, linearDir, oauthStatePath, readAccountToken, saveAccount } from "./account.js";
19
+ import { LINEAR_MCP_ORIGIN, linearMcpUrl, OriginRefusedError, ResponseTooLargeError } from "./origin.js";
20
+ const MAX_OAUTH_BODY = 64 * 1024;
21
+ const LOGIN_TIMEOUT_MS = 5 * 60_000;
22
+ /** Refresh this long before the access token expires. */
23
+ const REFRESH_MARGIN_MS = 60_000;
24
+ export function scopeFor(mode) {
25
+ return mode === "readonly" ? "read" : "read write";
26
+ }
27
+ /** OAuth traffic only reaches the Linear MCP origin, never follows a redirect, and is bounded. */
28
+ const oauthFetch = async (url, init) => {
29
+ const target = new URL(typeof url === "string" ? url : url.href);
30
+ if (target.origin !== LINEAR_MCP_ORIGIN)
31
+ throw new OriginRefusedError(`refusing an OAuth request to ${target.origin}: only ${LINEAR_MCP_ORIGIN} is allowed`);
32
+ const res = await fetch(target, { ...init, redirect: "manual" });
33
+ if (res.type === "opaqueredirect" || (res.status >= 300 && res.status < 400)) {
34
+ await res.body?.cancel().catch(() => { });
35
+ throw new OriginRefusedError(`refusing a ${res.status} redirect from ${target.origin}${target.pathname}`);
36
+ }
37
+ return new Response(await boundedBody(res), { status: res.status, statusText: res.statusText, headers: res.headers });
38
+ };
39
+ async function boundedBody(res) {
40
+ const declared = res.headers.get("content-length");
41
+ if (declared !== null && Number(declared) > MAX_OAUTH_BODY) {
42
+ await res.body?.cancel().catch(() => { });
43
+ throw new ResponseTooLargeError(MAX_OAUTH_BODY);
44
+ }
45
+ const out = new Uint8Array(MAX_OAUTH_BODY);
46
+ if (!res.body)
47
+ return out.subarray(0, 0);
48
+ const reader = res.body.getReader();
49
+ let total = 0;
50
+ for (;;) {
51
+ const { done, value } = await reader.read();
52
+ if (done)
53
+ break;
54
+ if (total + value.byteLength > MAX_OAUTH_BODY) {
55
+ await reader.cancel().catch(() => { });
56
+ throw new ResponseTooLargeError(MAX_OAUTH_BODY);
57
+ }
58
+ out.set(value, total);
59
+ total += value.byteLength;
60
+ }
61
+ return out.subarray(0, total);
62
+ }
63
+ class FileOAuthProvider {
64
+ name;
65
+ redirect;
66
+ scope;
67
+ expectedState;
68
+ constructor(name, redirect, scope) {
69
+ this.name = name;
70
+ this.redirect = redirect;
71
+ this.scope = scope;
72
+ }
73
+ load() {
74
+ const path = oauthStatePath(this.name);
75
+ if (!existsSync(path))
76
+ return {};
77
+ assertPrivateFile(path);
78
+ return JSON.parse(readFileSync(path, "utf8"));
79
+ }
80
+ store(patch) {
81
+ mkSecretDir(linearDir());
82
+ writeSecretFileAtomic(oauthStatePath(this.name), `${JSON.stringify({ ...this.load(), ...patch }, null, 2)}\n`);
83
+ }
84
+ /** Start a login from nothing: a new redirect port means a new client registration. */
85
+ reset() {
86
+ mkSecretDir(linearDir());
87
+ writeSecretFileAtomic(oauthStatePath(this.name), "{}\n");
88
+ }
89
+ get redirectUrl() {
90
+ return this.redirect;
91
+ }
92
+ get clientMetadata() {
93
+ return {
94
+ client_name: "Cotal Linear",
95
+ redirect_uris: this.redirect ? [this.redirect] : [],
96
+ grant_types: ["authorization_code", "refresh_token"],
97
+ response_types: ["code"],
98
+ token_endpoint_auth_method: "none",
99
+ scope: this.scope,
100
+ };
101
+ }
102
+ state() {
103
+ this.expectedState = randomBytes(16).toString("hex");
104
+ return this.expectedState;
105
+ }
106
+ stateFor() {
107
+ return this.expectedState;
108
+ }
109
+ clientInformation() {
110
+ return this.load().client;
111
+ }
112
+ saveClientInformation(client) {
113
+ this.store({ client });
114
+ }
115
+ tokens() {
116
+ return this.load().tokens;
117
+ }
118
+ saveTokens(tokens) {
119
+ this.store({ tokens, obtainedAt: Date.now() });
120
+ }
121
+ prepareTokenRequest() {
122
+ if (this.redirect !== undefined)
123
+ return undefined;
124
+ const refreshToken = this.tokens()?.refresh_token;
125
+ if (!refreshToken)
126
+ return undefined;
127
+ return new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken });
128
+ }
129
+ redirectToAuthorization(url) {
130
+ if (!this.redirect)
131
+ throw new Error(`the Linear OAuth session for ${this.name} has ended; run \`cotal linear account login ${this.name}\` again`);
132
+ if (url.protocol !== "https:")
133
+ throw new OriginRefusedError(`refusing a non-https authorization URL (${url.origin})`);
134
+ console.log(`Open this URL to authorize Cotal with Linear:\n${url.href}`);
135
+ }
136
+ saveCodeVerifier(codeVerifier) {
137
+ this.store({ codeVerifier });
138
+ }
139
+ codeVerifier() {
140
+ const v = this.load().codeVerifier;
141
+ if (!v)
142
+ throw new Error("no PKCE verifier is stored for this login");
143
+ return v;
144
+ }
145
+ }
146
+ /** Interactive login: loopback redirect, PKCE, dynamic registration. Saves an `oauth` account. */
147
+ export async function loginAccount(name, mode) {
148
+ const serverUrl = linearMcpUrl(mode);
149
+ const scope = scopeFor(mode);
150
+ const http = createServer();
151
+ await new Promise((ok, bad) => {
152
+ http.once("error", bad);
153
+ http.listen(0, "127.0.0.1", () => ok());
154
+ });
155
+ try {
156
+ const port = http.address().port;
157
+ const provider = new FileOAuthProvider(name, `http://127.0.0.1:${port}/callback`, scope);
158
+ provider.reset();
159
+ let timer;
160
+ const code = new Promise((ok, bad) => {
161
+ timer = setTimeout(() => bad(new Error("no authorization arrived within 5 minutes")), LOGIN_TIMEOUT_MS);
162
+ http.on("request", (req, res) => {
163
+ const u = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
164
+ if (req.method !== "GET" || u.pathname !== "/callback") {
165
+ res.writeHead(404).end();
166
+ return;
167
+ }
168
+ const expected = provider.stateFor();
169
+ if (!expected || u.searchParams.get("state") !== expected) {
170
+ res.writeHead(400).end("state mismatch");
171
+ bad(new Error("the authorization callback carried the wrong state; nothing was saved"));
172
+ return;
173
+ }
174
+ const error = u.searchParams.get("error");
175
+ const got = u.searchParams.get("code");
176
+ if (error || !got) {
177
+ res.writeHead(400).end("authorization failed");
178
+ bad(new Error(`Linear refused the authorization: ${error ?? "no code"}`));
179
+ return;
180
+ }
181
+ res.writeHead(200, { "content-type": "text/plain" }).end("Linear authorization received. You can close this tab.");
182
+ ok(got);
183
+ });
184
+ });
185
+ code.catch(() => { });
186
+ try {
187
+ if ((await auth(provider, { serverUrl, scope, fetchFn: oauthFetch })) !== "AUTHORIZED") {
188
+ const authorizationCode = await code;
189
+ if ((await auth(provider, { serverUrl, scope, authorizationCode, fetchFn: oauthFetch })) !== "AUTHORIZED")
190
+ throw new Error("Linear did not issue tokens for this authorization");
191
+ }
192
+ }
193
+ finally {
194
+ clearTimeout(timer);
195
+ }
196
+ if (!provider.tokens()?.access_token)
197
+ throw new Error("Linear issued no access token");
198
+ saveAccount({ name, mode, auth: "oauth" });
199
+ }
200
+ finally {
201
+ http.close();
202
+ }
203
+ }
204
+ function oauthCredential(account) {
205
+ const provider = new FileOAuthProvider(account.name, undefined, scopeFor(account.mode));
206
+ let refreshing;
207
+ const relogin = `run \`cotal linear account login ${account.name} --mode ${account.mode}\``;
208
+ return {
209
+ bearer() {
210
+ const token = provider.tokens()?.access_token;
211
+ if (!token)
212
+ throw new Error(`the Linear account ${account.name} has no OAuth token; ${relogin}`);
213
+ return token;
214
+ },
215
+ async prepare() {
216
+ const s = provider.load();
217
+ if (!s.tokens)
218
+ throw new Error(`the Linear account ${account.name} has no OAuth token; ${relogin}`);
219
+ if (s.tokens.expires_in === undefined || s.obtainedAt === undefined)
220
+ return;
221
+ if (s.obtainedAt + s.tokens.expires_in * 1000 - REFRESH_MARGIN_MS > Date.now())
222
+ return;
223
+ if (!s.tokens.refresh_token)
224
+ throw new Error(`the OAuth token for ${account.name} expired and cannot be refreshed; ${relogin}`);
225
+ // One refresh at a time: Linear rotates refresh tokens, so a second concurrent refresh fails.
226
+ refreshing ??= (async () => {
227
+ const r = await auth(provider, { serverUrl: linearMcpUrl(account.mode), scope: scopeFor(account.mode), fetchFn: oauthFetch });
228
+ if (r !== "AUTHORIZED")
229
+ throw new Error(`the OAuth refresh for ${account.name} failed; ${relogin}`);
230
+ })().finally(() => (refreshing = undefined));
231
+ await refreshing;
232
+ },
233
+ };
234
+ }
235
+ export function credentialFor(account) {
236
+ if (account.auth === "oauth")
237
+ return oauthCredential(account);
238
+ return { bearer: () => readAccountToken(account), prepare: async () => { } };
239
+ }
240
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The only network surface of this package: the official Linear MCP server, hard-pinned. There is
3
+ * no caller-supplied URL or header anywhere; a mode picks one of two fixed paths.
4
+ *
5
+ * Every request goes through {@link pinnedFetch}, which refuses any other origin or path, sets the
6
+ * bearer itself, never follows a redirect (a redirect would carry the credential somewhere else),
7
+ * and bounds each response body BEFORE it is accumulated: a declared `content-length` over the cap
8
+ * is refused unread, and an undeclared or streamed body errors at the first byte past the cap.
9
+ */
10
+ import type { FetchLike } from "@modelcontextprotocol/sdk/shared/transport.js";
11
+ export declare const LINEAR_MCP_ORIGIN = "https://mcp.linear.app";
12
+ /** `write` is Linear's default read-write server; `readonly` only exposes read tools. */
13
+ export declare const LINEAR_MCP_PATHS: {
14
+ readonly write: "/mcp";
15
+ readonly readonly: "/mcp/readonly";
16
+ };
17
+ export type LinearMode = keyof typeof LINEAR_MCP_PATHS;
18
+ export declare function isLinearMode(v: unknown): v is LinearMode;
19
+ export declare function linearMcpUrl(mode: LinearMode): URL;
20
+ /** A response body crossed the per-response byte cap. The payload is discarded, never truncated. */
21
+ export declare class ResponseTooLargeError extends Error {
22
+ readonly maxBytes: number;
23
+ constructor(maxBytes: number);
24
+ }
25
+ /** Refused before any byte left this process. */
26
+ export declare class OriginRefusedError extends Error {
27
+ constructor(message: string);
28
+ }
29
+ /** The request reached the upstream, but its redirect is not followed. Its effect is unknown. */
30
+ export declare class RedirectRefusedError extends Error {
31
+ constructor(message: string);
32
+ }
33
+ export declare function pinnedFetch(pinned: URL, readToken: () => string, maxBytes: number): FetchLike;
34
+ //# sourceMappingURL=origin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../src/origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAE/E,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D,yFAAyF;AACzF,eAAO,MAAM,gBAAgB;;;CAAwD,CAAC;AAEtF,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,gBAAgB,CAAC;AAEvD,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,UAAU,CAExD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,GAAG,CAElD;AAED,oGAAoG;AACpG,qBAAa,qBAAsB,SAAQ,KAAK;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAItC;AAED,iDAAiD;AACjD,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,iGAAiG;AACjG,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAc7F"}
package/dist/origin.js ADDED
@@ -0,0 +1,69 @@
1
+ export const LINEAR_MCP_ORIGIN = "https://mcp.linear.app";
2
+ /** `write` is Linear's default read-write server; `readonly` only exposes read tools. */
3
+ export const LINEAR_MCP_PATHS = { write: "/mcp", readonly: "/mcp/readonly" };
4
+ export function isLinearMode(v) {
5
+ return v === "write" || v === "readonly";
6
+ }
7
+ export function linearMcpUrl(mode) {
8
+ return new URL(LINEAR_MCP_PATHS[mode], LINEAR_MCP_ORIGIN);
9
+ }
10
+ /** A response body crossed the per-response byte cap. The payload is discarded, never truncated. */
11
+ export class ResponseTooLargeError extends Error {
12
+ maxBytes;
13
+ constructor(maxBytes) {
14
+ super(`the Linear MCP response exceeded ${maxBytes} bytes and was discarded`);
15
+ this.maxBytes = maxBytes;
16
+ this.name = "ResponseTooLargeError";
17
+ }
18
+ }
19
+ /** Refused before any byte left this process. */
20
+ export class OriginRefusedError extends Error {
21
+ constructor(message) {
22
+ super(message);
23
+ this.name = "OriginRefusedError";
24
+ }
25
+ }
26
+ /** The request reached the upstream, but its redirect is not followed. Its effect is unknown. */
27
+ export class RedirectRefusedError extends Error {
28
+ constructor(message) {
29
+ super(message);
30
+ this.name = "RedirectRefusedError";
31
+ }
32
+ }
33
+ export function pinnedFetch(pinned, readToken, maxBytes) {
34
+ return async (url, init) => {
35
+ const target = new URL(typeof url === "string" ? url : url.href);
36
+ if (target.origin !== pinned.origin || target.pathname !== pinned.pathname || target.search !== "")
37
+ throw new OriginRefusedError(`refusing a request to ${target.origin}${target.pathname}: only ${pinned.href} is allowed`);
38
+ const headers = new Headers(init?.headers);
39
+ headers.set("authorization", `Bearer ${readToken()}`);
40
+ const res = await fetch(target, { ...init, headers, redirect: "manual" });
41
+ if (res.type === "opaqueredirect" || (res.status >= 300 && res.status < 400)) {
42
+ await res.body?.cancel().catch(() => { });
43
+ throw new RedirectRefusedError(`refusing a ${res.status} redirect from ${pinned.href}: the credential is never forwarded`);
44
+ }
45
+ return boundResponse(res, maxBytes);
46
+ };
47
+ }
48
+ function boundResponse(res, maxBytes) {
49
+ const declared = res.headers.get("content-length");
50
+ if (declared !== null && Number(declared) > maxBytes) {
51
+ void res.body?.cancel().catch(() => { });
52
+ throw new ResponseTooLargeError(maxBytes);
53
+ }
54
+ if (!res.body)
55
+ return res;
56
+ let seen = 0;
57
+ const counted = res.body.pipeThrough(new TransformStream({
58
+ transform(chunk, ctl) {
59
+ seen += chunk.byteLength;
60
+ if (seen > maxBytes) {
61
+ ctl.error(new ResponseTooLargeError(maxBytes));
62
+ }
63
+ else
64
+ ctl.enqueue(chunk);
65
+ },
66
+ }));
67
+ return new Response(counted, { status: res.status, statusText: res.statusText, headers: res.headers });
68
+ }
69
+ //# sourceMappingURL=origin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin.js","sourceRoot":"","sources":["../src/origin.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAE1D,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAW,CAAC;AAItF,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,UAAU,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,OAAO,IAAI,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC,CAAC;AAC5D,CAAC;AAED,oGAAoG;AACpG,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACzB;IAArB,YAAqB,QAAgB;QACnC,KAAK,CAAC,oCAAoC,QAAQ,0BAA0B,CAAC,CAAC;QAD3D,aAAQ,GAAR,QAAQ,CAAQ;QAEnC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,iDAAiD;AACjD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,iGAAiG;AACjG,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,MAAW,EAAE,SAAuB,EAAE,QAAgB;IAChF,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjE,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE;YAChG,MAAM,IAAI,kBAAkB,CAAC,yBAAyB,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,UAAU,MAAM,CAAC,IAAI,aAAa,CAAC,CAAC;QAC3H,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,SAAS,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1E,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACzC,MAAM,IAAI,oBAAoB,CAAC,cAAc,GAAG,CAAC,MAAM,kBAAkB,MAAM,CAAC,IAAI,qCAAqC,CAAC,CAAC;QAC7H,CAAC;QACD,OAAO,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAa,EAAE,QAAgB;IACpD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACnD,IAAI,QAAQ,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,QAAQ,EAAE,CAAC;QACrD,KAAK,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACxC,MAAM,IAAI,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IAC1B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,eAAe,CAAyB;QAC/E,SAAS,CAAC,KAAK,EAAE,GAAG;YAClB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;YACzB,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACpB,GAAG,CAAC,KAAK,CAAC,IAAI,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;YACjD,CAAC;;gBAAM,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;KACF,CAAC,CAAC,CAAC;IACJ,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;AACzG,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { type EpCapability, type EpServeGrant, type Identity, type SpaceAuth } from "@cotal-ai/core";
2
+ import type { LinearUpstream } from "./upstream.js";
3
+ /** A reverse-DNS endpoint name: at least two lowercase labels, as the operator configured it. */
4
+ export declare function assertLinearEndpointName(name: string): string;
5
+ /** An authorized, scoped serve bundle. `creds` returns the CURRENT serve credential and is read on
6
+ * every (re)connect, so a renewed credential is adopted without re-registering. */
7
+ export interface LinearServeBundle {
8
+ servers: string;
9
+ space: string;
10
+ tls: boolean;
11
+ identity: Identity;
12
+ grant: EpServeGrant;
13
+ creds: () => string;
14
+ }
15
+ export interface LinearEndpointHandle {
16
+ readonly endpoint: string;
17
+ readonly instanceId: string;
18
+ readonly epoch: number;
19
+ /** Stop serving, then close the upstream session, each within `graceMs`. */
20
+ stop(graceMs?: number): Promise<void>;
21
+ /** Resolves when the serve connection closes for good. */
22
+ closed: Promise<Error | undefined>;
23
+ }
24
+ /** Serve the fixed Linear contract on an authorized bundle. Command names, schemas and the
25
+ * `linear.mcp` capability are fixed; the upstream result is returned as data and never parsed. */
26
+ export declare function runLinearEndpoint(bundle: LinearServeBundle, upstream: LinearUpstream): Promise<LinearEndpointHandle>;
27
+ export interface LinearRegistration {
28
+ bundle: LinearServeBundle;
29
+ /** Re-mint the serve credential through the same fenced issuance. */
30
+ renew(): Promise<string>;
31
+ /** Remove this instance's service record. The issuance gate stays, as for every endpoint. */
32
+ deregister(): Promise<void>;
33
+ }
34
+ /**
35
+ * Register `endpoint` as a fresh instance on a static-auth mesh and mint its serve credential.
36
+ * `auth` is the operator's own space authority; it stays in this closure for renewal and
37
+ * deregistration and is never placed in the returned bundle.
38
+ */
39
+ export declare function registerLinearEndpoint(opts: {
40
+ auth: SpaceAuth;
41
+ servers: string;
42
+ space: string;
43
+ tls: boolean;
44
+ endpoint: string;
45
+ }): Promise<LinearRegistration>;
46
+ /** Milliseconds until a credential reaches 75% of its remaining lifetime, or undefined when it is
47
+ * unbounded. */
48
+ export declare function renewalDelayMs(creds: string, nowMs?: number): number | undefined;
49
+ /** The caller rows for every Linear command on `endpoint`: what a least-privilege caller
50
+ * credential carries, and nothing more. */
51
+ export declare function linearCallerCapabilities(endpoint: string): EpCapability[];
52
+ /**
53
+ * Provision and mint a standalone caller for a static-auth mesh: the ordinary `agent` profile, its
54
+ * lifecycle-keyed mailboxes, and the exact request rows for the Linear commands on `endpoint`. It
55
+ * carries no spawn, run, admin or provisioner capability. The caller is an isolated, hand-launched
56
+ * seat the operator starts with this credential and lifecycle UID; managed spawns still refuse
57
+ * endpoint capabilities in static mode, which this does not change. The provisioner connection is
58
+ * minted for this call and stopped before it returns.
59
+ */
60
+ export declare function provisionLinearCaller(auth: SpaceAuth, target: {
61
+ servers: string;
62
+ space: string;
63
+ tls: boolean;
64
+ }, endpoint: string, opts?: {
65
+ channels?: string[];
66
+ expiresInSeconds?: number;
67
+ }): Promise<{
68
+ creds: string;
69
+ identity: string;
70
+ lifecycleUid: string;
71
+ }>;
72
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAmBA,OAAO,EA0BL,KAAK,YAAY,EACjB,KAAK,YAAY,EAEjB,KAAK,QAAQ,EAEb,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,iGAAiG;AACjG,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED;oFACoF;AACpF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CACpC;AAMD;mGACmG;AACnG,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA4B1H;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,qEAAqE;IACrE,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,6FAA6F;IAC7F,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoF9B;AAED;iBACiB;AACjB,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,MAAM,GAAG,SAAS,CAIpF;AAED;4CAC4C;AAC5C,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAGzE;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,EACxD,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAgCpE"}