@micropage-sh/mcp 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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/annotations.d.ts +20 -0
  4. package/dist/annotations.js +36 -0
  5. package/dist/annotations.js.map +1 -0
  6. package/dist/client/assets.d.ts +85 -0
  7. package/dist/client/assets.js +473 -0
  8. package/dist/client/assets.js.map +1 -0
  9. package/dist/client/auth-provider.d.ts +24 -0
  10. package/dist/client/auth-provider.js +10 -0
  11. package/dist/client/auth-provider.js.map +1 -0
  12. package/dist/client/builds.d.ts +82 -0
  13. package/dist/client/builds.js +166 -0
  14. package/dist/client/builds.js.map +1 -0
  15. package/dist/client/config.d.ts +18 -0
  16. package/dist/client/config.js +29 -0
  17. package/dist/client/config.js.map +1 -0
  18. package/dist/client/deploy-events.d.ts +98 -0
  19. package/dist/client/deploy-events.js +170 -0
  20. package/dist/client/deploy-events.js.map +1 -0
  21. package/dist/client/deploy-token.d.ts +48 -0
  22. package/dist/client/deploy-token.js +130 -0
  23. package/dist/client/deploy-token.js.map +1 -0
  24. package/dist/client/errors.d.ts +18 -0
  25. package/dist/client/errors.js +21 -0
  26. package/dist/client/errors.js.map +1 -0
  27. package/dist/client/http.d.ts +68 -0
  28. package/dist/client/http.js +191 -0
  29. package/dist/client/http.js.map +1 -0
  30. package/dist/client/jwt.d.ts +13 -0
  31. package/dist/client/jwt.js +23 -0
  32. package/dist/client/jwt.js.map +1 -0
  33. package/dist/client/lock.d.ts +16 -0
  34. package/dist/client/lock.js +67 -0
  35. package/dist/client/lock.js.map +1 -0
  36. package/dist/client/pages.d.ts +47 -0
  37. package/dist/client/pages.js +131 -0
  38. package/dist/client/pages.js.map +1 -0
  39. package/dist/client/posts.d.ts +108 -0
  40. package/dist/client/posts.js +187 -0
  41. package/dist/client/posts.js.map +1 -0
  42. package/dist/client/project-ref.d.ts +68 -0
  43. package/dist/client/project-ref.js +118 -0
  44. package/dist/client/project-ref.js.map +1 -0
  45. package/dist/client/session-store.d.ts +81 -0
  46. package/dist/client/session-store.js +271 -0
  47. package/dist/client/session-store.js.map +1 -0
  48. package/dist/client/submissions.d.ts +59 -0
  49. package/dist/client/submissions.js +111 -0
  50. package/dist/client/submissions.js.map +1 -0
  51. package/dist/client/tier.d.ts +42 -0
  52. package/dist/client/tier.js +76 -0
  53. package/dist/client/tier.js.map +1 -0
  54. package/dist/content/index.d.ts +5 -0
  55. package/dist/content/index.js +28 -0
  56. package/dist/content/index.js.map +1 -0
  57. package/dist/context.d.ts +30 -0
  58. package/dist/context.js +2 -0
  59. package/dist/context.js.map +1 -0
  60. package/dist/guards.d.ts +55 -0
  61. package/dist/guards.js +168 -0
  62. package/dist/guards.js.map +1 -0
  63. package/dist/index.d.ts +2 -0
  64. package/dist/index.js +16 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/progress.d.ts +7 -0
  67. package/dist/progress.js +25 -0
  68. package/dist/progress.js.map +1 -0
  69. package/dist/reference/prompts.d.ts +6 -0
  70. package/dist/reference/prompts.js +110 -0
  71. package/dist/reference/prompts.js.map +1 -0
  72. package/dist/reference/resources.d.ts +2 -0
  73. package/dist/reference/resources.js +41 -0
  74. package/dist/reference/resources.js.map +1 -0
  75. package/dist/reference/tool.d.ts +21 -0
  76. package/dist/reference/tool.js +50 -0
  77. package/dist/reference/tool.js.map +1 -0
  78. package/dist/reference/topics.d.ts +29 -0
  79. package/dist/reference/topics.js +91 -0
  80. package/dist/reference/topics.js.map +1 -0
  81. package/dist/reference.d.ts +9 -0
  82. package/dist/reference.js +20 -0
  83. package/dist/reference.js.map +1 -0
  84. package/dist/server.d.ts +29 -0
  85. package/dist/server.js +75 -0
  86. package/dist/server.js.map +1 -0
  87. package/dist/tools/account.d.ts +34 -0
  88. package/dist/tools/account.js +128 -0
  89. package/dist/tools/account.js.map +1 -0
  90. package/dist/tools/builds.d.ts +95 -0
  91. package/dist/tools/builds.js +403 -0
  92. package/dist/tools/builds.js.map +1 -0
  93. package/dist/tools/files.d.ts +71 -0
  94. package/dist/tools/files.js +205 -0
  95. package/dist/tools/files.js.map +1 -0
  96. package/dist/tools/forms.d.ts +59 -0
  97. package/dist/tools/forms.js +177 -0
  98. package/dist/tools/forms.js.map +1 -0
  99. package/dist/tools/posts.d.ts +191 -0
  100. package/dist/tools/posts.js +600 -0
  101. package/dist/tools/posts.js.map +1 -0
  102. package/dist/tools/projects.d.ts +201 -0
  103. package/dist/tools/projects.js +553 -0
  104. package/dist/tools/projects.js.map +1 -0
  105. package/dist/tools/shared.d.ts +33 -0
  106. package/dist/tools/shared.js +41 -0
  107. package/dist/tools/shared.js.map +1 -0
  108. package/dist/version.d.ts +2 -0
  109. package/dist/version.js +8 -0
  110. package/dist/version.js.map +1 -0
  111. package/package.json +67 -0
@@ -0,0 +1,187 @@
1
+ import { createHash } from "node:crypto";
2
+ import { MicropageError } from "./errors.js";
3
+ import { eq, gt, inList, is } from "./http.js";
4
+ /** Same rule as supabase/functions/_shared/slug.ts and the CLI's slugify. */
5
+ export function slugify(s) {
6
+ return s
7
+ .toLowerCase()
8
+ .trim()
9
+ .replace(/[^a-z0-9]+/g, "-")
10
+ .replace(/^-+|-+$/g, "");
11
+ }
12
+ /** Columns for the list view (cli posts list, plus form_id to name the list). */
13
+ export const POST_LIST_COLUMNS = "id,slug,title,web_visibility,email_enabled,form_id,status,published_at,created_at,recipient_count";
14
+ /** Everything posts pull reads, plus the email fields upsert_post writes. */
15
+ export const POST_FULL_COLUMNS = "id,slug,title,description,body_markdown,web_visibility,email_enabled,form_id,hero_image,subject,preheader," +
16
+ "status,published_at,created_at,recipient_count,sent_count";
17
+ export async function listPosts(http, projectId) {
18
+ return http.select("posts", {
19
+ select: POST_LIST_COLUMNS,
20
+ filters: { project_id: eq(projectId) },
21
+ order: "created_at.desc",
22
+ });
23
+ }
24
+ export async function getPostBySlug(http, projectId, slug) {
25
+ return http.selectOne("posts", {
26
+ select: POST_FULL_COLUMNS,
27
+ filters: { project_id: eq(projectId), slug: eq(slug) },
28
+ });
29
+ }
30
+ export function postNotFound(slug) {
31
+ return new MicropageError("POST_NOT_FOUND", `No post with slug "${slug}" in this project. Call list_posts to see the project's posts, or upsert_post to create it.`);
32
+ }
33
+ /** publish-post's condition for sending (supabase/functions/publish-post/index.ts). */
34
+ export function postWillEmail(post) {
35
+ return post.email_enabled === true && post.form_id != null;
36
+ }
37
+ /**
38
+ * `list: <form name>` -> form_id, as cli/src/commands/posts.js resolveFormId:
39
+ * case-insensitive exact name among the project's newsletter forms.
40
+ */
41
+ export async function resolveListFormId(http, projectId, listName) {
42
+ const name = listName.trim();
43
+ const forms = await http.select("forms", {
44
+ select: "id,form_name,is_newsletter",
45
+ filters: { project_id: eq(projectId), is_newsletter: eq(true) },
46
+ });
47
+ const matches = forms.filter((f) => (f.form_name ?? "").toLowerCase() === name.toLowerCase());
48
+ if (matches.length === 0) {
49
+ const known = forms.map((f) => f.form_name).filter((n) => !!n);
50
+ throw new MicropageError("LIST_NOT_FOUND", `No newsletter form named "${name}" in this project.` +
51
+ (known.length > 0
52
+ ? ` Newsletter forms here: ${known.map((n) => `"${n}"`).join(", ")}.`
53
+ : " The project has no newsletter forms; one is created by publishing a page with a newsletter form.") +
54
+ " Nothing was saved.");
55
+ }
56
+ if (matches.length > 1) {
57
+ throw new MicropageError("LIST_AMBIGUOUS", `More than one newsletter form is named "${name}". Ask the user to rename one in the editor. Nothing was saved.`);
58
+ }
59
+ return matches[0].id;
60
+ }
61
+ export async function formNamesById(http, ids) {
62
+ const unique = [...new Set(ids)];
63
+ if (unique.length === 0)
64
+ return new Map();
65
+ const rows = await http.select("forms", {
66
+ select: "id,form_name",
67
+ filters: { id: inList(unique) },
68
+ });
69
+ return new Map(rows.map((r) => [r.id, r.form_name]));
70
+ }
71
+ /**
72
+ * Active subscribers on a list, with the filter publish-post sends to
73
+ * (form_id, unsubscribed_at is null). The owner can read the table under RLS
74
+ * ("Users can select newsletter_subscribers for own projects").
75
+ */
76
+ export async function countActiveSubscribers(http, formId) {
77
+ return http.count("newsletter_subscribers", { form_id: eq(formId), unsubscribed_at: is("null") });
78
+ }
79
+ export function isAbsoluteUrl(s) {
80
+ return /^https?:\/\//i.test(s.trim());
81
+ }
82
+ /**
83
+ * The hero as cli/src/posts-assets.js resolveHeroImage resolves it, minus the
84
+ * local-file upload branch: an absolute URL passes through, anything else must
85
+ * be the filename of an asset already uploaded to the project.
86
+ */
87
+ export async function resolveHeroUrl(http, projectId, hero) {
88
+ const value = hero?.trim() ?? "";
89
+ if (!value)
90
+ return null;
91
+ if (isAbsoluteUrl(value))
92
+ return value;
93
+ const data = await http.invokeGet("list-files", { project_id: String(projectId) });
94
+ const files = data?.files ?? [];
95
+ const base = value.split(/[\\/]/).pop() ?? value;
96
+ const file = files.find((f) => f.filename === value) ?? files.find((f) => f.filename === base);
97
+ if (!file) {
98
+ throw new MicropageError("HERO_NOT_FOUND", `hero "${value}" is not an https URL and no uploaded asset in this project has that filename. ` +
99
+ "Upload the image with upload_asset first and pass the filename it returns (list_files shows existing ones). Nothing was saved.");
100
+ }
101
+ const urlData = await http.invokeGet("get-file-url", { file_id: file.id });
102
+ if (!urlData?.url) {
103
+ throw new MicropageError("HTTP", `get-file-url returned no URL for "${file.filename}". Retry shortly. Nothing was saved.`);
104
+ }
105
+ return urlData.url;
106
+ }
107
+ // Same pattern as posts-assets.js: ![alt](path "title").
108
+ const MD_IMAGE_RE = /!\[([^\]]*)\]\(\s*([^)\s]+)(?:\s+"[^"]*")?\s*\)/g;
109
+ /** Body image refs that are not hosted URLs; they would 404 on the live page. */
110
+ export function unhostedBodyImages(markdown) {
111
+ const out = [];
112
+ for (const match of markdown.matchAll(MD_IMAGE_RE)) {
113
+ const ref = match[2];
114
+ if (isAbsoluteUrl(ref) || ref.startsWith("/") || ref.startsWith("#") || ref.startsWith("data:"))
115
+ continue;
116
+ if (!out.includes(ref))
117
+ out.push(ref);
118
+ }
119
+ return out;
120
+ }
121
+ // ---------------------------------------------------------------------------
122
+ // Fingerprint for preview tokens
123
+ // ---------------------------------------------------------------------------
124
+ /**
125
+ * posts has no updated_at, so a preview is bound to a hash of every field a
126
+ * save can change. Fixed field order keeps it stable without key sorting.
127
+ */
128
+ export function postContentFingerprint(post) {
129
+ const fields = [
130
+ post.title,
131
+ post.slug,
132
+ post.subject,
133
+ post.preheader,
134
+ post.description,
135
+ post.body_markdown,
136
+ post.hero_image,
137
+ post.web_visibility,
138
+ ];
139
+ return createHash("sha256").update(JSON.stringify(fields)).digest("base64url");
140
+ }
141
+ export async function upsertPost(http, payload) {
142
+ try {
143
+ return await http.invoke("upsert-post", payload);
144
+ }
145
+ catch (err) {
146
+ if (err instanceof MicropageError && err.status === 409) {
147
+ throw new MicropageError("SLUG_CONFLICT", `The slug "${payload.slug}" is already used by another post in this project. Pass a different \`slug\`, ` +
148
+ "or call list_posts to find the existing post and edit that one. Nothing was saved.", { status: 409, data: err.data, cause: err });
149
+ }
150
+ throw err;
151
+ }
152
+ }
153
+ export async function publishPost(http, projectId, slug) {
154
+ try {
155
+ return await http.invoke("publish-post", { project_id: projectId, slug });
156
+ }
157
+ catch (err) {
158
+ if (err instanceof MicropageError && err.status === 402) {
159
+ throw new MicropageError("PLAN_LIMIT", `micropage refused to send this post: ${serverMessage(err) ?? "the plan does not allow it"}. ` +
160
+ "Newsletter sends need a Pro plan and count against a monthly recipient limit; " +
161
+ "tell the user (upgrade at https://micropage.sh/pricing). No email went out and the site was not rebuilt, " +
162
+ "but the post may now be marked published; check with list_posts.", { status: 402, data: err.data, cause: err });
163
+ }
164
+ if (err instanceof MicropageError && err.status === 404)
165
+ throw postNotFound(slug);
166
+ throw err;
167
+ }
168
+ }
169
+ export async function unpublishPost(http, projectId, slug) {
170
+ try {
171
+ return await http.invoke("unpublish-post", { project_id: projectId, slug });
172
+ }
173
+ catch (err) {
174
+ if (err instanceof MicropageError && err.status === 404)
175
+ throw postNotFound(slug);
176
+ throw err;
177
+ }
178
+ }
179
+ export async function deletePost(http, projectId, slug) {
180
+ return http.invoke("delete-post", { project_id: projectId, slug });
181
+ }
182
+ function serverMessage(err) {
183
+ const data = err.data;
184
+ const msg = data?.error ?? data?.message;
185
+ return typeof msg === "string" && msg ? msg : null;
186
+ }
187
+ //# sourceMappingURL=posts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts.js","sourceRoot":"","sources":["../../src/client/posts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import * as z from "zod";
2
+ import type { AuthProvider } from "./auth-provider.js";
3
+ import { type MicropageConfig } from "./config.js";
4
+ import { type Http } from "./http.js";
5
+ export declare function isProjectUuid(s: string): boolean;
6
+ export declare function isNumericProjectId(s: string): boolean;
7
+ /** The `project` input every project-scoped tool takes. */
8
+ export declare const ProjectRef: z.ZodString;
9
+ /** Columns read for every resolved project. */
10
+ export declare const PROJECT_COLUMNS = "id,uuid,name,domain,custom_domain,active_build_id,status,created_at";
11
+ export interface ProjectRow {
12
+ id: number;
13
+ uuid: string;
14
+ name: string | null;
15
+ domain: string | null;
16
+ custom_domain: string | null;
17
+ active_build_id: number | null;
18
+ status: string | null;
19
+ created_at: string | null;
20
+ }
21
+ export interface Project extends ProjectRow {
22
+ /** Public URL: the custom domain when set, else https://<domain>.<baseDomain>. */
23
+ live_url: string | null;
24
+ /** The project in the web editor. */
25
+ editor_url: string;
26
+ }
27
+ /** What resolveProject needs; ToolContext satisfies it. */
28
+ export interface ProjectRefDeps {
29
+ http: Http;
30
+ auth: Pick<AuthProvider, "mode" | "pinnedProjectUuid">;
31
+ config: Pick<MicropageConfig, "baseDomain" | "appUrl">;
32
+ }
33
+ export declare function toProject(row: ProjectRow, config: ProjectRefDeps["config"]): Project;
34
+ export type ParsedProjectRef = {
35
+ kind: "uuid";
36
+ value: string;
37
+ } | {
38
+ kind: "id";
39
+ value: number;
40
+ }
41
+ /** `{slug}.<baseDomain>` or a bare slug: matches projects.domain. */
42
+ | {
43
+ kind: "slug";
44
+ value: string;
45
+ }
46
+ /** Any other hostname: a custom domain (or, as the CLI allows, a domain stored verbatim). */
47
+ | {
48
+ kind: "host";
49
+ value: string;
50
+ };
51
+ /**
52
+ * Classifies a user-supplied ref the way cli/src/project-ref.js does, plus
53
+ * URL/hostname handling. Throws PROJECT_NOT_FOUND for something that cannot
54
+ * be any of them.
55
+ */
56
+ export declare function parseProjectRef(ref: string, baseDomain: string): ParsedProjectRef;
57
+ /**
58
+ * The pin a deploy token carries. The minted JWT can reach every project the
59
+ * owner has, so this check is what keeps a deploy-token session on one project.
60
+ */
61
+ export declare function assertPinnedProject(auth: ProjectRefDeps["auth"], projectUuid: string): void;
62
+ /**
63
+ * Loads one of the user's projects from a uuid, numeric id, micropage
64
+ * hostname or custom domain. In deploy-token mode an omitted ref means the
65
+ * pinned project and any other project is refused. Throws PROJECT_NOT_FOUND
66
+ * (RLS hides other users' projects, so "not yours" reads the same).
67
+ */
68
+ export declare function resolveProject(deps: ProjectRefDeps, ref: string | undefined): Promise<Project>;
@@ -0,0 +1,118 @@
1
+ import * as z from "zod";
2
+ import { projectUrl } from "./config.js";
3
+ import { MicropageError } from "./errors.js";
4
+ import { eq } from "./http.js";
5
+ const PROJECT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6
+ export function isProjectUuid(s) {
7
+ return PROJECT_UUID_RE.test(s.trim());
8
+ }
9
+ export function isNumericProjectId(s) {
10
+ return /^\d+$/.test(s.trim());
11
+ }
12
+ /** The `project` input every project-scoped tool takes. */
13
+ export const ProjectRef = z
14
+ .string()
15
+ .trim()
16
+ .min(1)
17
+ .max(300)
18
+ .describe('Which project: its uuid, its numeric id, its micropage hostname ("acme" or "acme.micropage.sh") or its ' +
19
+ "custom domain (\"www.acme.com\"). A full URL also works. Get valid values from list_projects.");
20
+ /** Columns read for every resolved project. */
21
+ export const PROJECT_COLUMNS = "id,uuid,name,domain,custom_domain,active_build_id,status,created_at";
22
+ export function toProject(row, config) {
23
+ return {
24
+ ...row,
25
+ live_url: projectUrl(config, row.domain, row.custom_domain),
26
+ editor_url: `${config.appUrl.replace(/\/+$/, "")}/editor/${row.id}`,
27
+ };
28
+ }
29
+ /**
30
+ * Classifies a user-supplied ref the way cli/src/project-ref.js does, plus
31
+ * URL/hostname handling. Throws PROJECT_NOT_FOUND for something that cannot
32
+ * be any of them.
33
+ */
34
+ export function parseProjectRef(ref, baseDomain) {
35
+ const raw = ref.trim();
36
+ if (isProjectUuid(raw))
37
+ return { kind: "uuid", value: raw.toLowerCase() };
38
+ if (isNumericProjectId(raw))
39
+ return { kind: "id", value: Number(raw) };
40
+ const host = raw
41
+ .replace(/^[a-z][a-z0-9+.-]*:\/\//i, "")
42
+ .replace(/[/?#].*$/, "")
43
+ .replace(/:\d+$/, "")
44
+ .replace(/\.$/, "")
45
+ .toLowerCase();
46
+ // Also keeps the value safe to splice into the PostgREST `or=(...)` filter below.
47
+ if (!/^[a-z0-9-]+(\.[a-z0-9-]+)*$/.test(host)) {
48
+ throw new MicropageError("PROJECT_NOT_FOUND", `"${ref}" is not a project uuid, numeric id or hostname. Call list_projects to see valid projects.`);
49
+ }
50
+ const base = baseDomain.toLowerCase();
51
+ if (host.endsWith(`.${base}`)) {
52
+ const slug = host.slice(0, -(base.length + 1));
53
+ if (slug && !slug.includes("."))
54
+ return { kind: "slug", value: slug };
55
+ }
56
+ if (!host.includes("."))
57
+ return { kind: "slug", value: host };
58
+ return { kind: "host", value: host };
59
+ }
60
+ function filtersFor(parsed) {
61
+ switch (parsed.kind) {
62
+ case "uuid":
63
+ return { uuid: eq(parsed.value) };
64
+ case "id":
65
+ return { id: eq(parsed.value) };
66
+ case "slug":
67
+ return { domain: eq(parsed.value) };
68
+ case "host":
69
+ return { or: `(custom_domain.eq.${parsed.value},domain.eq.${parsed.value})` };
70
+ }
71
+ }
72
+ /**
73
+ * The pin a deploy token carries. The minted JWT can reach every project the
74
+ * owner has, so this check is what keeps a deploy-token session on one project.
75
+ */
76
+ export function assertPinnedProject(auth, projectUuid) {
77
+ if (auth.mode !== "deploy_token")
78
+ return;
79
+ if (projectUuid.toLowerCase() === auth.pinnedProjectUuid?.toLowerCase())
80
+ return;
81
+ throw new MicropageError("NOT_ALLOWED_IN_DEPLOY_TOKEN_MODE", `This deploy token is pinned to project ${auth.pinnedProjectUuid ?? "(unset)"}; ` +
82
+ `it cannot act on ${projectUuid}. Omit the project or pass the pinned one.`);
83
+ }
84
+ /**
85
+ * Loads one of the user's projects from a uuid, numeric id, micropage
86
+ * hostname or custom domain. In deploy-token mode an omitted ref means the
87
+ * pinned project and any other project is refused. Throws PROJECT_NOT_FOUND
88
+ * (RLS hides other users' projects, so "not yours" reads the same).
89
+ */
90
+ export async function resolveProject(deps, ref) {
91
+ const pinned = deps.auth.mode === "deploy_token" ? deps.auth.pinnedProjectUuid : undefined;
92
+ const given = ref?.trim() ?? "";
93
+ if (!given) {
94
+ if (!pinned) {
95
+ throw new MicropageError("PROJECT_REQUIRED", "Pass `project` (a uuid, numeric id or domain). Call list_projects to see the user's projects.");
96
+ }
97
+ }
98
+ const parsed = given
99
+ ? parseProjectRef(given, deps.config.baseDomain)
100
+ : { kind: "uuid", value: pinned };
101
+ // Refuse a foreign uuid before any network call.
102
+ if (parsed.kind === "uuid")
103
+ assertPinnedProject(deps.auth, parsed.value);
104
+ const rows = await deps.http.select("projects", {
105
+ select: PROJECT_COLUMNS,
106
+ filters: filtersFor(parsed),
107
+ order: "id.asc",
108
+ limit: 2,
109
+ });
110
+ // A host can match one project's custom_domain and another's domain; the custom domain wins.
111
+ const row = parsed.kind === "host" ? (rows.find((r) => r.custom_domain?.toLowerCase() === parsed.value) ?? rows[0]) : rows[0];
112
+ if (!row) {
113
+ throw new MicropageError("PROJECT_NOT_FOUND", `No project matching "${given || pinned}" in this micropage account. Call list_projects to see valid projects.`);
114
+ }
115
+ assertPinnedProject(deps.auth, row.uuid);
116
+ return toProject(row, deps.config);
117
+ }
118
+ //# sourceMappingURL=project-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-ref.js","sourceRoot":"","sources":["../../src/client/project-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB,OAAO,EAAE,UAAU,EAAwB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,EAAE,EAA2B,MAAM,WAAW,CAAC;AAExD,MAAM,eAAe,GAAG,iEAAiE,CAAC;AAE1F,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,CAAS;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC;KACxB,MAAM,EAAE;KACR,IAAI,EAAE;KACN,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,GAAG,CAAC;KACR,QAAQ,CACP,yGAAyG;IACvG,+FAA+F,CAClG,CAAC;AAEJ,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,qEAAqE,CAAC;AA2BrG,MAAM,UAAU,SAAS,CAAC,GAAe,EAAE,MAAgC;IACzE,OAAO;QACL,GAAG,GAAG;QACN,QAAQ,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC;QAC3D,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,GAAG,CAAC,EAAE,EAAE;KACpE,CAAC;AACJ,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,UAAkB;IAC7D,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;IAC1E,IAAI,kBAAkB,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAEvE,MAAM,IAAI,GAAG,GAAG;SACb,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC;SACvC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,WAAW,EAAE,CAAC;IACjB,kFAAkF;IAClF,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,cAAc,CACtB,mBAAmB,EACnB,IAAI,GAAG,4FAA4F,CACpG,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IACtC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,UAAU,CAAC,MAAwB;IAC1C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM;YACT,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,KAAK,IAAI;YACP,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,KAAK,MAAM;YACT,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,KAAK,MAAM;YACT,OAAO,EAAE,EAAE,EAAE,qBAAqB,MAAM,CAAC,KAAK,cAAc,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;IAClF,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAA4B,EAAE,WAAmB;IACnF,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc;QAAE,OAAO;IACzC,IAAI,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,iBAAiB,EAAE,WAAW,EAAE;QAAE,OAAO;IAChF,MAAM,IAAI,cAAc,CACtB,kCAAkC,EAClC,0CAA0C,IAAI,CAAC,iBAAiB,IAAI,SAAS,IAAI;QAC/E,oBAAoB,WAAW,4CAA4C,CAC9E,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAoB,EAAE,GAAuB;IAChF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3F,MAAM,KAAK,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,cAAc,CACtB,kBAAkB,EAClB,+FAA+F,CAChG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAqB,KAAK;QACpC,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAChD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAO,EAAE,CAAC;IACrC,iDAAiD;IACjD,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;QAAE,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAEzE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAa,UAAU,EAAE;QAC1D,MAAM,EAAE,eAAe;QACvB,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC;QAC3B,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,CAAC;KACT,CAAC,CAAC;IACH,6FAA6F;IAC7F,MAAM,GAAG,GACP,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,WAAW,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpH,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CACtB,mBAAmB,EACnB,wBAAwB,KAAK,IAAI,MAAM,wEAAwE,CAChH,CAAC;IACJ,CAAC;IACD,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,81 @@
1
+ import type { AuthProvider } from "./auth-provider.js";
2
+ import type { MicropageConfig } from "./config.js";
3
+ import type { FetchLike } from "./http.js";
4
+ import { type LockOptions } from "./lock.js";
5
+ export declare const LOGIN_HINT = "Run `micropage login` in a terminal, then retry.";
6
+ /** Same location as the CLI (cli/src/auth.js), plus a MICROPAGE_CONFIG_DIR override. */
7
+ export declare function configDir(env?: NodeJS.ProcessEnv): string;
8
+ export declare function sessionFilePath(env?: NodeJS.ProcessEnv): string;
9
+ export interface StoredSession {
10
+ access_token: string;
11
+ refresh_token: string;
12
+ user: unknown;
13
+ }
14
+ /**
15
+ * The CLI's ~/.micropage/config.json. Reads tolerate the CLI's non-atomic
16
+ * writes; our writes are atomic and only ever replace the token keys, so the
17
+ * session is never cleared from here.
18
+ */
19
+ export declare class SessionStore {
20
+ readonly path: string;
21
+ constructor(path: string);
22
+ get lockPath(): string;
23
+ /** The whole config object, or null when there is no file. */
24
+ readConfig(): Promise<Record<string, unknown> | null>;
25
+ readSession(): Promise<StoredSession | null>;
26
+ /**
27
+ * Replaces the token keys (and user, when given), keeping every other key.
28
+ * Re-reads the file first and writes nothing when it no longer holds a
29
+ * session: the user logged out while a refresh was in flight, and writing
30
+ * the tokens back would undo that. Returns whether it wrote.
31
+ */
32
+ writeTokens(tokens: {
33
+ access_token: string;
34
+ refresh_token: string;
35
+ user?: unknown;
36
+ }): Promise<boolean>;
37
+ }
38
+ export interface SessionAuthOptions {
39
+ config: Pick<MicropageConfig, "supabaseUrl" | "supabaseAnonKey">;
40
+ /** Path of the CLI config file (see sessionFilePath()). */
41
+ path: string;
42
+ fetch?: FetchLike;
43
+ now?: () => number;
44
+ lock?: LockOptions;
45
+ timeoutMs?: number;
46
+ /** Where the one-time "could not save the session" notice goes; stderr by default (stdout is the MCP stream). */
47
+ log?: (message: string) => void;
48
+ }
49
+ /**
50
+ * Access tokens from the CLI's login session. Refreshes proactively within
51
+ * 60 s of expiry; refreshes are single-flight in-process and serialised across
52
+ * MCP processes by a lockfile next to the config. The CLI does not take that
53
+ * lock, so a failed refresh re-reads the file and retries once if the CLI
54
+ * rotated the refresh token meanwhile.
55
+ */
56
+ export declare class SessionAuthProvider implements AuthProvider {
57
+ readonly mode: "session";
58
+ readonly store: SessionStore;
59
+ private readonly config;
60
+ private readonly fetchImpl;
61
+ private readonly now;
62
+ private readonly lockOptions;
63
+ private readonly timeoutMs;
64
+ private inflight;
65
+ private lastIssued;
66
+ private memory;
67
+ private warnedWriteFailure;
68
+ private readonly log;
69
+ constructor(options: SessionAuthOptions);
70
+ get path(): string;
71
+ hasSession(): Promise<boolean>;
72
+ /** The file's session, or the in-memory one while the file still holds the refresh token it replaced. */
73
+ private currentSession;
74
+ getAccessToken(options?: {
75
+ forceRefresh?: boolean;
76
+ }): Promise<string>;
77
+ private issue;
78
+ private requireSession;
79
+ private refreshUnderLock;
80
+ private exchange;
81
+ }