@farming-labs/docs 0.1.125 → 0.1.128

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 (29) hide show
  1. package/dist/{agent-CBhuGaVu.mjs → agent-CJNYhzu7.mjs} +4 -4
  2. package/dist/{agents-BPX4YRPs.mjs → agents-BuL01_U0.mjs} +2 -2
  3. package/dist/cli/index.mjs +52 -19
  4. package/dist/cloud-88xTLXn3.mjs +529 -0
  5. package/dist/{codeblocks-B4QzFqNC.mjs → codeblocks-DbJrclYa.mjs} +1 -1
  6. package/dist/{config-B8wA38OC.mjs → config-CBoixmrv.mjs} +9 -1
  7. package/dist/{dev-1VFeAWUi.mjs → dev-DdlhqXNU.mjs} +7 -1
  8. package/dist/{doctor-lV9lhqGA.mjs → doctor-D_c7vJCb.mjs} +4 -4
  9. package/dist/{downgrade-BGANzvQb.mjs → downgrade-BayU3nlg.mjs} +1 -1
  10. package/dist/index.d.mts +3 -3
  11. package/dist/index.mjs +1 -1
  12. package/dist/{init-DzQWiVN1.mjs → init-BJqy-LK2.mjs} +1 -1
  13. package/dist/{mcp-BvERlrP_.mjs → mcp-CUAR3XfW.mjs} +2 -2
  14. package/dist/mcp.d.mts +1 -1
  15. package/dist/mcp.mjs +36 -0
  16. package/dist/{reading-time-HpTq-0VB.mjs → reading-time-DPAazAGu.mjs} +1 -0
  17. package/dist/{review-ClNDYRnn.mjs → review-CjyI-bVg.mjs} +1 -1
  18. package/dist/{robots-CNLiZCay.mjs → robots-Dx2uMF3g.mjs} +1 -1
  19. package/dist/{search-G0otDMfP.d.mts → search-CNoiftmI.d.mts} +1 -1
  20. package/dist/{search-BjyDm30M.mjs → search-Cekr2OyP.mjs} +2 -2
  21. package/dist/server.d.mts +13 -3
  22. package/dist/server.mjs +2 -2
  23. package/dist/{sitemap-BdSdcAmS.mjs → sitemap-BBfGr3tJ.mjs} +2 -2
  24. package/dist/{sitemap-server-BZHoJHqC.mjs → sitemap-server-idLUrmmU.mjs} +57 -1
  25. package/dist/{types-Dg82p0Fm.d.mts → types-C-C-9SMJ.d.mts} +65 -1
  26. package/dist/{upgrade-C7DvvfTR.mjs → upgrade-DDWolmHe.mjs} +1 -1
  27. package/package.json +1 -1
  28. /package/dist/{package-version-OrjpmqoR.mjs → package-version-CQm0KO-H.mjs} +0 -0
  29. /package/dist/{templates-CGaORZOY.mjs → templates-DomB3eeS.mjs} +0 -0
@@ -0,0 +1,529 @@
1
+ import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelBooleanProperty, f as readTopLevelStringProperty, i as loadDocsConfigModule, m as resolveDocsContentDir, p as resolveDocsConfigPath, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-CBoixmrv.mjs";
2
+ import { t as detectFramework } from "./utils-x5EtYWjC.mjs";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import pc from "picocolors";
6
+ import { execFileSync } from "node:child_process";
7
+
8
+ //#region src/cli/cloud.ts
9
+ const DOCS_JSON_FILE = "docs.json";
10
+ const DOCS_CLOUD_SCHEMA_URL = "https://docs.farming-labs.dev/schema/docs.json";
11
+ const DOCS_CLOUD_DEFAULT_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
12
+ const DEFAULT_DOCS_CLOUD_API_BASE_URL = "https://docs-app.farming-labs.dev";
13
+ const DEFAULT_PREVIEW_TIMEOUT_MS = 300 * 1e3;
14
+ const DEFAULT_PREVIEW_POLL_INTERVAL_MS = 2e3;
15
+ function isRecord(value) {
16
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
17
+ }
18
+ function isJsonRecord(value) {
19
+ return isRecord(value);
20
+ }
21
+ function toJsonRecord(value) {
22
+ if (!isRecord(value)) return void 0;
23
+ return JSON.parse(JSON.stringify(value));
24
+ }
25
+ function readPackageName(rootDir) {
26
+ const packagePath = path.join(rootDir, "package.json");
27
+ if (!fs.existsSync(packagePath)) return void 0;
28
+ try {
29
+ const parsed = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
30
+ return typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : void 0;
31
+ } catch {
32
+ return;
33
+ }
34
+ }
35
+ function runGit(rootDir, args) {
36
+ try {
37
+ return execFileSync("git", [
38
+ "-C",
39
+ rootDir,
40
+ ...args
41
+ ], {
42
+ encoding: "utf-8",
43
+ stdio: [
44
+ "ignore",
45
+ "pipe",
46
+ "ignore"
47
+ ]
48
+ }).trim();
49
+ } catch {
50
+ return;
51
+ }
52
+ }
53
+ function parseGitHubRemote(remoteUrl) {
54
+ const trimmed = remoteUrl.trim();
55
+ const match = trimmed.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/) ?? trimmed.match(/^https:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?(?:\/)?$/);
56
+ if (!match?.[1] || !match[2]) return null;
57
+ return {
58
+ owner: match[1],
59
+ name: match[2].replace(/\.git$/i, "")
60
+ };
61
+ }
62
+ function resolveGitRepositoryMetadata(rootDir) {
63
+ const gitRoot = runGit(rootDir, ["rev-parse", "--show-toplevel"]);
64
+ const remoteUrl = runGit(rootDir, [
65
+ "remote",
66
+ "get-url",
67
+ "origin"
68
+ ]);
69
+ if (!gitRoot || !remoteUrl) return;
70
+ const parsedRemote = parseGitHubRemote(remoteUrl);
71
+ if (!parsedRemote) return;
72
+ const branch = runGit(rootDir, ["branch", "--show-current"]) ?? runGit(rootDir, [
73
+ "rev-parse",
74
+ "--abbrev-ref",
75
+ "HEAD"
76
+ ]);
77
+ const resolvedGitRoot = fs.realpathSync(gitRoot);
78
+ const resolvedRootDir = fs.realpathSync(rootDir);
79
+ const rootDirectory = path.relative(resolvedGitRoot, resolvedRootDir).split(path.sep).filter(Boolean).join("/");
80
+ return {
81
+ ...parsedRemote,
82
+ branch: branch && branch !== "HEAD" ? branch : void 0,
83
+ rootDirectory: rootDirectory || ".",
84
+ remoteUrl
85
+ };
86
+ }
87
+ function titleFromPackageName(name) {
88
+ if (!name) return void 0;
89
+ const normalized = name.replace(/^@[^/]+\//, "").replace(/[-_]+/g, " ").trim();
90
+ if (!normalized) return void 0;
91
+ return normalized.replace(/\b\w/g, (match) => match.toUpperCase());
92
+ }
93
+ function tryResolveDocsConfigPath(rootDir, explicitPath) {
94
+ if (explicitPath) return resolveDocsConfigPath(rootDir, explicitPath);
95
+ try {
96
+ return resolveDocsConfigPath(rootDir);
97
+ } catch {
98
+ return;
99
+ }
100
+ }
101
+ async function loadDocsConfigSnapshot(rootDir, explicitPath) {
102
+ const configPath = tryResolveDocsConfigPath(rootDir, explicitPath);
103
+ if (!configPath) return {};
104
+ return {
105
+ path: configPath,
106
+ content: fs.readFileSync(configPath, "utf-8"),
107
+ config: (await loadDocsConfigModule(rootDir, configPath, { silent: true }))?.config
108
+ };
109
+ }
110
+ function readExistingDocsJson(docsJsonPath) {
111
+ if (!fs.existsSync(docsJsonPath)) return void 0;
112
+ try {
113
+ const parsed = JSON.parse(fs.readFileSync(docsJsonPath, "utf-8"));
114
+ if (!isJsonRecord(parsed)) throw new Error(`${DOCS_JSON_FILE} must contain a JSON object.`);
115
+ return parsed;
116
+ } catch (error) {
117
+ const message = error instanceof Error ? error.message : String(error);
118
+ throw new Error(`Could not parse ${DOCS_JSON_FILE}: ${message}`);
119
+ }
120
+ }
121
+ function normalizeApiKeyConfig(apiKey) {
122
+ return { env: apiKey?.env?.trim() || DOCS_CLOUD_DEFAULT_API_KEY_ENV };
123
+ }
124
+ function normalizePreviewConfig(preview) {
125
+ return { enabled: preview?.enabled ?? true };
126
+ }
127
+ function normalizePublishConfig(publish) {
128
+ return {
129
+ mode: publish?.mode === "direct-commit" ? "direct-commit" : "draft-pr",
130
+ baseBranch: publish?.baseBranch?.trim() || "main"
131
+ };
132
+ }
133
+ function normalizeFeatureConfig(feature) {
134
+ if (!feature) return void 0;
135
+ return { enabled: feature.enabled ?? true };
136
+ }
137
+ function normalizeAnalyticsConfig(analytics) {
138
+ if (typeof analytics === "undefined") return void 0;
139
+ if (typeof analytics === "boolean") return analytics;
140
+ return {
141
+ enabled: analytics.enabled ?? true,
142
+ ...typeof analytics.console !== "undefined" ? { console: analytics.console } : {},
143
+ includeInputs: analytics.includeInputs ?? false
144
+ };
145
+ }
146
+ function normalizeCloudConfig(cloud) {
147
+ const normalized = {
148
+ apiKey: normalizeApiKeyConfig(cloud?.apiKey),
149
+ preview: normalizePreviewConfig(cloud?.preview),
150
+ publish: normalizePublishConfig(cloud?.publish)
151
+ };
152
+ if (cloud?.enabled === false) normalized.enabled = false;
153
+ const analytics = normalizeAnalyticsConfig(cloud?.analytics);
154
+ if (typeof analytics !== "undefined") normalized.analytics = analytics;
155
+ const ai = normalizeFeatureConfig(cloud?.ai);
156
+ if (ai) normalized.ai = ai;
157
+ const deploy = normalizeFeatureConfig(cloud?.deploy);
158
+ if (deploy) normalized.deploy = deploy;
159
+ return normalized;
160
+ }
161
+ function readStaticCloudConfig(content) {
162
+ if (!content || !extractNestedObjectLiteral(content, ["cloud"])) return void 0;
163
+ const cloudBlock = extractNestedObjectLiteral(content, ["cloud"]);
164
+ const apiKeyBlock = extractNestedObjectLiteral(content, ["cloud", "apiKey"]);
165
+ const previewBlock = extractNestedObjectLiteral(content, ["cloud", "preview"]);
166
+ const publishBlock = extractNestedObjectLiteral(content, ["cloud", "publish"]);
167
+ const analyticsBlock = extractNestedObjectLiteral(content, ["cloud", "analytics"]);
168
+ const aiBlock = extractNestedObjectLiteral(content, ["cloud", "ai"]);
169
+ const deployBlock = extractNestedObjectLiteral(content, ["cloud", "deploy"]);
170
+ const cloud = {};
171
+ const enabled = cloudBlock ? readTopLevelBooleanProperty(cloudBlock, "enabled") : void 0;
172
+ if (typeof enabled === "boolean") cloud.enabled = enabled;
173
+ const apiKeyEnv = apiKeyBlock ? readStringProperty(apiKeyBlock, "env") : void 0;
174
+ if (apiKeyEnv) cloud.apiKey = { env: apiKeyEnv };
175
+ const previewEnabled = previewBlock ? readTopLevelBooleanProperty(previewBlock, "enabled") : void 0;
176
+ if (typeof previewEnabled === "boolean") cloud.preview = { enabled: previewEnabled };
177
+ const publishMode = publishBlock ? readStringProperty(publishBlock, "mode") : void 0;
178
+ const baseBranch = publishBlock ? readStringProperty(publishBlock, "baseBranch") : void 0;
179
+ if (publishMode || baseBranch) cloud.publish = {
180
+ ...publishMode === "direct-commit" || publishMode === "draft-pr" ? { mode: publishMode } : {},
181
+ ...baseBranch ? { baseBranch } : {}
182
+ };
183
+ const analyticsEnabled = cloudBlock ? readTopLevelBooleanProperty(cloudBlock, "analytics") : void 0;
184
+ if (typeof analyticsEnabled === "boolean") cloud.analytics = analyticsEnabled;
185
+ else if (analyticsBlock) {
186
+ const analytics = {};
187
+ const enabledValue = readTopLevelBooleanProperty(analyticsBlock, "enabled");
188
+ const consoleBoolean = readTopLevelBooleanProperty(analyticsBlock, "console");
189
+ const consoleMode = readStringProperty(analyticsBlock, "console");
190
+ const includeInputs = readTopLevelBooleanProperty(analyticsBlock, "includeInputs");
191
+ if (typeof enabledValue === "boolean") analytics.enabled = enabledValue;
192
+ if (typeof consoleBoolean === "boolean") analytics.console = consoleBoolean;
193
+ else if (consoleMode === "log" || consoleMode === "info" || consoleMode === "debug") analytics.console = consoleMode;
194
+ if (typeof includeInputs === "boolean") analytics.includeInputs = includeInputs;
195
+ cloud.analytics = analytics;
196
+ }
197
+ const aiEnabled = aiBlock ? readTopLevelBooleanProperty(aiBlock, "enabled") : void 0;
198
+ if (typeof aiEnabled === "boolean") cloud.ai = { enabled: aiEnabled };
199
+ const deployEnabled = deployBlock ? readTopLevelBooleanProperty(deployBlock, "enabled") : void 0;
200
+ if (typeof deployEnabled === "boolean") cloud.deploy = { enabled: deployEnabled };
201
+ return cloud;
202
+ }
203
+ function resolveCloudConfig(snapshot, existing) {
204
+ const moduleCloud = snapshot.config?.cloud;
205
+ const staticCloud = readStaticCloudConfig(snapshot.content);
206
+ const existingCloud = existing?.cloud;
207
+ return normalizeCloudConfig(moduleCloud ?? staticCloud ?? existingCloud);
208
+ }
209
+ function resolveApiReferenceRoot(snapshot) {
210
+ const apiReference = snapshot.config?.apiReference;
211
+ if (isRecord(apiReference) && typeof apiReference.path === "string" && apiReference.path.trim()) return apiReference.path.trim();
212
+ const apiReferenceBlock = extractNestedObjectLiteral(snapshot.content ?? "", ["apiReference"]);
213
+ if (!apiReferenceBlock) return void 0;
214
+ return readStringProperty(apiReferenceBlock, "path");
215
+ }
216
+ function resolveSiteConfig(rootDir, snapshot, existing) {
217
+ const existingSite = toJsonRecord(existing?.site);
218
+ const navTitle = isRecord(snapshot.config?.nav) && typeof snapshot.config.nav.title === "string" ? snapshot.config.nav.title : readNavTitle(snapshot.content ?? "");
219
+ const metadata = isRecord(snapshot.config?.metadata) ? snapshot.config.metadata : void 0;
220
+ const metadataDescription = typeof metadata?.description === "string" && metadata.description.trim() ? metadata.description.trim() : void 0;
221
+ const packageTitle = titleFromPackageName(readPackageName(rootDir));
222
+ const name = navTitle?.trim() || existingSite?.name || packageTitle;
223
+ const description = metadataDescription || existingSite?.description;
224
+ if (!name && !description && !existingSite) return void 0;
225
+ return {
226
+ ...existingSite ?? {},
227
+ ...typeof name === "string" ? { name } : {},
228
+ ...typeof description === "string" ? { description } : {}
229
+ };
230
+ }
231
+ function resolveDocsRoot(rootDir, snapshot, existing) {
232
+ const entry = snapshot.config?.entry ?? readTopLevelStringProperty(snapshot.content ?? "", "entry") ?? "docs";
233
+ if (snapshot.config?.contentDir) return snapshot.config.contentDir;
234
+ if (snapshot.content) return resolveDocsContentDir(rootDir, snapshot.content, entry);
235
+ if (typeof existing?.content?.docsRoot === "string") return existing.content.docsRoot;
236
+ return fs.existsSync(path.join(rootDir, "app", entry)) ? path.join("app", entry) : entry;
237
+ }
238
+ function resolveDocsBlock(rootDir, snapshot, existing) {
239
+ const detectedFramework = detectFramework(rootDir);
240
+ const existingDocs = existing?.docs;
241
+ const runtime = detectedFramework ?? existingDocs?.runtime ?? "nextjs";
242
+ if (!Boolean(snapshot.path || detectedFramework) && existingDocs) return existingDocs;
243
+ return {
244
+ mode: "framework",
245
+ runtime,
246
+ root: existingDocs?.root || "."
247
+ };
248
+ }
249
+ function materializeDocsJsonObject(params) {
250
+ const cloud = resolveCloudConfig(params.snapshot, params.existing);
251
+ const docsRoot = resolveDocsRoot(params.rootDir, params.snapshot, params.existing);
252
+ const apiReferenceRoot = resolveApiReferenceRoot(params.snapshot);
253
+ const existingContent = toJsonRecord(params.existing?.content);
254
+ const site = resolveSiteConfig(params.rootDir, params.snapshot, params.existing);
255
+ const content = {
256
+ ...existingContent ?? {},
257
+ docsRoot,
258
+ ...apiReferenceRoot ? { apiReferenceRoot } : {}
259
+ };
260
+ return {
261
+ ...params.existing ?? {},
262
+ $schema: params.existing?.$schema ?? DOCS_CLOUD_SCHEMA_URL,
263
+ version: 1,
264
+ docs: resolveDocsBlock(params.rootDir, params.snapshot, params.existing),
265
+ content,
266
+ ...site ? { site } : {},
267
+ cloud
268
+ };
269
+ }
270
+ function serializeMaterializedDocsJson(config) {
271
+ return `${JSON.stringify(config, null, 2)}\n`;
272
+ }
273
+ async function materializeCloudConfig(options = {}) {
274
+ const rootDir = options.rootDir ?? process.cwd();
275
+ const docsJsonPath = path.join(rootDir, DOCS_JSON_FILE);
276
+ const existing = readExistingDocsJson(docsJsonPath);
277
+ const snapshot = await loadDocsConfigSnapshot(rootDir, options.configPath);
278
+ const config = materializeDocsJsonObject({
279
+ rootDir,
280
+ snapshot,
281
+ existing
282
+ });
283
+ const serialized = serializeMaterializedDocsJson(config);
284
+ const updated = (existing ? fs.readFileSync(docsJsonPath, "utf-8") : void 0) !== serialized;
285
+ if (updated) fs.writeFileSync(docsJsonPath, serialized, "utf-8");
286
+ return {
287
+ configPath: snapshot.path ?? docsJsonPath,
288
+ docsJsonPath,
289
+ config,
290
+ apiKeyEnv: config.cloud?.apiKey?.env ?? DOCS_CLOUD_DEFAULT_API_KEY_ENV,
291
+ created: !existing,
292
+ updated
293
+ };
294
+ }
295
+ function resolveApiBaseUrl(options) {
296
+ return (options.apiBaseUrl ?? process.env.DOCS_CLOUD_API_URL ?? process.env.NEXT_PUBLIC_DOCS_CLOUD_URL ?? DEFAULT_DOCS_CLOUD_API_BASE_URL).replace(/\/+$/, "");
297
+ }
298
+ function resolveApiKey(options, rootDir, envName) {
299
+ if (options.apiKey?.trim()) return options.apiKey.trim();
300
+ const token = {
301
+ ...loadProjectEnv(rootDir),
302
+ ...process.env
303
+ }[envName]?.trim();
304
+ if (token) return token;
305
+ throw new Error(`Missing Docs Cloud API key. Set ${envName} in your shell or .env.local, or configure cloud.apiKey.env in docs.config.ts.`);
306
+ }
307
+ async function readJsonResponse(response) {
308
+ const text = await response.text();
309
+ if (!text.trim()) return {};
310
+ try {
311
+ return JSON.parse(text);
312
+ } catch {
313
+ return { message: text };
314
+ }
315
+ }
316
+ function readResponseMessage(body, fallback) {
317
+ if (isRecord(body)) for (const key of [
318
+ "error",
319
+ "message",
320
+ "detail"
321
+ ]) {
322
+ const value = body[key];
323
+ if (typeof value === "string" && value.trim()) return value.trim();
324
+ }
325
+ return fallback;
326
+ }
327
+ async function fetchCloudJson(params) {
328
+ const headers = new Headers(params.init?.headers);
329
+ headers.set("authorization", `Bearer ${params.apiKey}`);
330
+ headers.set("accept", "application/json");
331
+ if (params.init?.body && !headers.has("content-type")) headers.set("content-type", "application/json");
332
+ const response = await fetch(params.url, {
333
+ ...params.init,
334
+ headers
335
+ });
336
+ const body = await readJsonResponse(response);
337
+ if (!response.ok) {
338
+ const requestPath = new URL(params.url).pathname;
339
+ if (response.status === 404 && requestPath === "/api/cloud/preview") throw new Error("Docs Cloud preview API is not available on this cloud host yet. The API key was validated, but the host did not expose /api/cloud/preview.");
340
+ const message = readResponseMessage(body, `Docs Cloud request failed with HTTP ${response.status}.`);
341
+ throw new Error(message);
342
+ }
343
+ return body;
344
+ }
345
+ function readPreviewUrl(body) {
346
+ if (!isRecord(body)) return void 0;
347
+ const direct = body.url ?? body.previewUrl;
348
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
349
+ const preview = body.preview;
350
+ if (isRecord(preview) && typeof preview.url === "string" && preview.url.trim()) return preview.url.trim();
351
+ }
352
+ function readStatusUrl(body, apiBaseUrl) {
353
+ if (!isRecord(body)) return void 0;
354
+ const statusUrl = body.statusUrl ?? body.pollUrl;
355
+ if (typeof statusUrl !== "string" || !statusUrl.trim()) return void 0;
356
+ return new URL(statusUrl, apiBaseUrl).toString();
357
+ }
358
+ function readPreviewStatus(body) {
359
+ if (!isRecord(body)) return void 0;
360
+ const status = body.status;
361
+ return typeof status === "string" ? status : void 0;
362
+ }
363
+ async function requestPreview(params) {
364
+ const initial = await fetchCloudJson({
365
+ url: `${params.apiBaseUrl}/api/cloud/preview`,
366
+ apiKey: params.apiKey,
367
+ init: {
368
+ method: "POST",
369
+ body: JSON.stringify({
370
+ config: params.config,
371
+ configPath: path.relative(params.rootDir, params.configPath) || DOCS_JSON_FILE,
372
+ repository: resolveGitRepositoryMetadata(params.rootDir)
373
+ })
374
+ }
375
+ });
376
+ const initialUrl = readPreviewUrl(initial);
377
+ if (initialUrl) return {
378
+ url: initialUrl,
379
+ response: initial
380
+ };
381
+ const statusUrl = readStatusUrl(initial, params.apiBaseUrl);
382
+ if (!statusUrl) throw new Error("Docs Cloud accepted the preview request but did not return a preview URL or status URL.");
383
+ const startedAt = Date.now();
384
+ while (Date.now() - startedAt < params.timeoutMs) {
385
+ await new Promise((resolve) => setTimeout(resolve, params.pollIntervalMs));
386
+ params.spinner.update("Waiting for Docs Cloud preview deployment");
387
+ const statusBody = await fetchCloudJson({
388
+ url: statusUrl,
389
+ apiKey: params.apiKey
390
+ });
391
+ const url = readPreviewUrl(statusBody);
392
+ if (url) return {
393
+ url,
394
+ response: statusBody
395
+ };
396
+ const status = readPreviewStatus(statusBody);
397
+ if (status === "failed" || status === "error") throw new Error(readResponseMessage(statusBody, "Docs Cloud preview failed."));
398
+ }
399
+ throw new Error("Docs Cloud preview timed out before a URL was ready.");
400
+ }
401
+ function createSpinner(initialMessage, options = {}) {
402
+ const interactive = Boolean(process.stdout.isTTY && !options.json && process.env.NODE_ENV !== "test");
403
+ const frames = [
404
+ "-",
405
+ "\\",
406
+ "|",
407
+ "/"
408
+ ];
409
+ let frame = 0;
410
+ let message = initialMessage;
411
+ let timer;
412
+ const render = () => {
413
+ process.stdout.write(`\r${pc.cyan(frames[frame % frames.length])} ${message}`);
414
+ frame += 1;
415
+ };
416
+ if (interactive) {
417
+ render();
418
+ timer = setInterval(render, 90);
419
+ } else if (!options.json) console.log(`${pc.dim("-")} ${initialMessage}`);
420
+ const clear = () => {
421
+ if (timer) {
422
+ clearInterval(timer);
423
+ timer = void 0;
424
+ }
425
+ if (interactive) process.stdout.write("\r\x1B[K");
426
+ };
427
+ return {
428
+ update(nextMessage) {
429
+ message = nextMessage;
430
+ if (!interactive && !options.json) console.log(`${pc.dim("-")} ${nextMessage}`);
431
+ },
432
+ succeed(doneMessage) {
433
+ clear();
434
+ if (!options.json) console.log(`${pc.green("ok")} ${doneMessage}`);
435
+ },
436
+ fail(doneMessage) {
437
+ clear();
438
+ if (!options.json) console.log(`${pc.red("error")} ${doneMessage}`);
439
+ },
440
+ stop() {
441
+ clear();
442
+ }
443
+ };
444
+ }
445
+ async function syncCloudConfig(options = {}) {
446
+ const result = await materializeCloudConfig(options);
447
+ if (options.json) {
448
+ console.log(JSON.stringify(result, null, 2));
449
+ return result;
450
+ }
451
+ const action = result.created ? "Created" : result.updated ? "Synced" : "Checked";
452
+ console.log(`${pc.green("ok")} ${action} ${pc.cyan(path.relative(process.cwd(), result.docsJsonPath) || DOCS_JSON_FILE)}`);
453
+ console.log(`${pc.dim("api key env")} ${result.apiKeyEnv}`);
454
+ return result;
455
+ }
456
+ async function runCloudPreview(options = {}) {
457
+ const rootDir = options.rootDir ?? process.cwd();
458
+ const spinner = createSpinner("Preparing Docs Cloud preview", options);
459
+ try {
460
+ const materialized = await materializeCloudConfig({
461
+ ...options,
462
+ rootDir
463
+ });
464
+ spinner.update(`${materialized.created ? "Created" : "Synced"} ${DOCS_JSON_FILE}`);
465
+ if (materialized.config.cloud?.enabled === false) throw new Error("Docs Cloud is disabled in cloud.enabled. Remove that override or set cloud.enabled: true before requesting a preview.");
466
+ if (materialized.config.cloud?.preview?.enabled === false) throw new Error("Docs Cloud preview is disabled in cloud.preview.enabled. Set it to true before requesting a preview.");
467
+ const apiKey = resolveApiKey(options, rootDir, materialized.apiKeyEnv);
468
+ const apiBaseUrl = resolveApiBaseUrl(options);
469
+ spinner.update("Validating Docs Cloud API key");
470
+ const identity = await fetchCloudJson({
471
+ url: `${apiBaseUrl}/api/cloud/me`,
472
+ apiKey
473
+ });
474
+ spinner.update("Requesting Docs Cloud preview deployment");
475
+ const preview = await requestPreview({
476
+ apiBaseUrl,
477
+ apiKey,
478
+ config: materialized.config,
479
+ configPath: materialized.docsJsonPath,
480
+ rootDir,
481
+ spinner,
482
+ timeoutMs: options.timeoutMs ?? DEFAULT_PREVIEW_TIMEOUT_MS,
483
+ pollIntervalMs: options.pollIntervalMs ?? DEFAULT_PREVIEW_POLL_INTERVAL_MS
484
+ });
485
+ spinner.succeed("Docs Cloud preview is ready");
486
+ const result = {
487
+ url: preview.url,
488
+ docsJsonPath: materialized.docsJsonPath,
489
+ apiBaseUrl,
490
+ identity,
491
+ response: preview.response
492
+ };
493
+ if (options.json) console.log(JSON.stringify(result, null, 2));
494
+ else console.log(`${pc.bold("Preview")} ${pc.cyan(preview.url)}`);
495
+ return result;
496
+ } catch (error) {
497
+ const message = error instanceof Error ? error.message : String(error);
498
+ spinner.fail(message);
499
+ throw error;
500
+ } finally {
501
+ spinner.stop();
502
+ }
503
+ }
504
+ function printCloudHelp() {
505
+ console.log(`
506
+ ${pc.bold("@farming-labs/docs cloud")}
507
+
508
+ ${pc.dim("Usage:")}
509
+ ${pc.cyan("docs preview")} Sync ${pc.dim("docs.config.ts")} to ${pc.dim("docs.json")} and request a cloud preview
510
+ ${pc.cyan("docs cloud preview")} Same as ${pc.cyan("docs preview")}
511
+ ${pc.cyan("docs cloud sync")} Only materialize cloud settings into ${pc.dim("docs.json")}
512
+
513
+ ${pc.dim("Options:")}
514
+ ${pc.cyan("--config <path>")} Use a custom docs config path
515
+ ${pc.cyan("--api-base-url <url>")} Override Docs Cloud API base URL
516
+ ${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
517
+ ${pc.cyan("--json")} Print machine-readable output
518
+
519
+ ${pc.dim("Config example:")}
520
+ cloud: {
521
+ apiKey: { env: "DOCS_CLOUD_API_KEY" },
522
+ preview: { enabled: true },
523
+ publish: { mode: "draft-pr", baseBranch: "main" },
524
+ }
525
+ `);
526
+ }
527
+
528
+ //#endregion
529
+ export { printCloudHelp, runCloudPreview, syncCloudConfig };
@@ -3,7 +3,7 @@ import { E as findDocsMarkdownPage, U as renderDocsMarkdownDocument } from "./ag
3
3
  import "./index.mjs";
4
4
  import { createFilesystemDocsMcpSource } from "./mcp.mjs";
5
5
  import "./server.mjs";
6
- import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelStringProperty, f as resolveDocsConfigPath, i as loadDocsConfigModule, l as readNumberProperty, o as readBooleanProperty, p as resolveDocsContentDir, s as readEnvReferenceProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-B8wA38OC.mjs";
6
+ import { a as loadProjectEnv, c as readNavTitle, f as readTopLevelStringProperty, i as loadDocsConfigModule, l as readNumberProperty, m as resolveDocsContentDir, o as readBooleanProperty, p as resolveDocsConfigPath, s as readEnvReferenceProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-CBoixmrv.mjs";
7
7
  import matter from "gray-matter";
8
8
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
9
9
  import path from "node:path";
@@ -230,6 +230,14 @@ function readTopLevelStringProperty(content, key) {
230
230
  if (match) return match[1];
231
231
  }
232
232
  }
233
+ function readTopLevelBooleanProperty(content, key) {
234
+ const source = extractTopLevelConfigObject(content) ?? content;
235
+ const propertyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*:\\s*(true|false)\\b`);
236
+ for (const property of splitTopLevelProperties(source)) {
237
+ const match = property.trim().match(propertyPattern);
238
+ if (match) return match[1] === "true";
239
+ }
240
+ }
233
241
  function readNavTitle(content) {
234
242
  const block = extractObjectLiteral(extractTopLevelConfigObject(content) ?? content, "nav");
235
243
  if (!block) return void 0;
@@ -295,4 +303,4 @@ async function loadDocsConfigModule(rootDir, explicitPath, options = {}) {
295
303
  }
296
304
 
297
305
  //#endregion
298
- export { loadProjectEnv as a, readNavTitle as c, readTopLevelStringProperty as d, resolveDocsConfigPath as f, loadDocsConfigModule as i, readNumberProperty as l, extractObjectLiteral as n, readBooleanProperty as o, resolveDocsContentDir as p, extractTopLevelConfigObject as r, readEnvReferenceProperty as s, extractNestedObjectLiteral as t, readStringProperty as u };
306
+ export { loadProjectEnv as a, readNavTitle as c, readTopLevelBooleanProperty as d, readTopLevelStringProperty as f, loadDocsConfigModule as i, readNumberProperty as l, resolveDocsContentDir as m, extractObjectLiteral as n, readBooleanProperty as o, resolveDocsConfigPath as p, extractTopLevelConfigObject as r, readEnvReferenceProperty as s, extractNestedObjectLiteral as t, readStringProperty as u };
@@ -1,5 +1,5 @@
1
1
  import { i as detectPackageManagerFromLockfile } from "./utils-x5EtYWjC.mjs";
2
- import { K as rootLayoutTemplate, W as postcssConfigTemplate, g as docsLayoutTemplate, v as globalCssTemplate, yt as tsconfigTemplate } from "./templates-CGaORZOY.mjs";
2
+ import { K as rootLayoutTemplate, W as postcssConfigTemplate, g as docsLayoutTemplate, v as globalCssTemplate, yt as tsconfigTemplate } from "./templates-DomB3eeS.mjs";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import pc from "picocolors";
@@ -203,6 +203,12 @@ const managedConfigSchema = z.object({
203
203
  theme: z.object({ preset: z.string().optional() }).passthrough().optional(),
204
204
  cloud: z.object({
205
205
  enabled: z.boolean().optional(),
206
+ apiKey: z.object({ env: z.string().min(1).optional() }).passthrough().optional(),
207
+ preview: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
208
+ publish: z.object({
209
+ mode: z.enum(["draft-pr", "direct-commit"]).optional(),
210
+ baseBranch: z.string().min(1).optional()
211
+ }).passthrough().optional(),
206
212
  analytics: z.union([z.boolean(), z.object({
207
213
  enabled: z.boolean().optional(),
208
214
  console: z.union([z.boolean(), z.enum([
@@ -1,14 +1,14 @@
1
- import "./reading-time-HpTq-0VB.mjs";
1
+ import "./reading-time-DPAazAGu.mjs";
2
2
  import "./search-BL7o2rXk.mjs";
3
3
  import { a as DEFAULT_SITEMAP_XML_ROUTE, d as resolveDocsSitemapConfig, i as DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE, r as DEFAULT_SITEMAP_MD_ROUTE } from "./sitemap-CMNj0Pt3.mjs";
4
4
  import { T as buildDocsMcpEndpointCandidates, b as DEFAULT_SKILL_MD_ROUTE, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, d as DEFAULT_LLMS_FULL_TXT_ROUTE, g as DEFAULT_MCP_PUBLIC_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, l as DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, m as DEFAULT_LLMS_TXT_ROUTE, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_MCP_WELL_KNOWN_ROUTE, x as DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE } from "./agent-BS39vnhS.mjs";
5
5
  import { a as analyzeDocsRobotsTxt, n as DEFAULT_ROBOTS_TXT_ROUTE, u as resolveDocsRobotsConfig } from "./robots-B2BfoyVM.mjs";
6
- import "./sitemap-server-BZHoJHqC.mjs";
6
+ import "./sitemap-server-idLUrmmU.mjs";
7
7
  import { createFilesystemDocsMcpSource, resolveDocsMcpConfig } from "./mcp.mjs";
8
8
  import "./server.mjs";
9
- import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelStringProperty, f as resolveDocsConfigPath, i as loadDocsConfigModule, o as readBooleanProperty, p as resolveDocsContentDir, r as extractTopLevelConfigObject, t as extractNestedObjectLiteral } from "./config-B8wA38OC.mjs";
9
+ import { a as loadProjectEnv, c as readNavTitle, f as readTopLevelStringProperty, i as loadDocsConfigModule, m as resolveDocsContentDir, o as readBooleanProperty, p as resolveDocsConfigPath, r as extractTopLevelConfigObject, t as extractNestedObjectLiteral } from "./config-CBoixmrv.mjs";
10
10
  import { t as detectFramework } from "./utils-x5EtYWjC.mjs";
11
- import { n as inspectAgentCompactionState, o as scanDocsPageTargets } from "./codeblocks-B4QzFqNC.mjs";
11
+ import { n as inspectAgentCompactionState, o as scanDocsPageTargets } from "./codeblocks-DbJrclYa.mjs";
12
12
  import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
13
13
  import path from "node:path";
14
14
  import { LATEST_PROTOCOL_VERSION } from "@modelcontextprotocol/sdk/types.js";
@@ -1,5 +1,5 @@
1
1
  import { c as fileExists, o as exec, s as execOutput } from "./utils-x5EtYWjC.mjs";
2
- import { a as getPackagesForFramework, c as resolveDocsPackageManager, l as validateUpgradeVersion, r as buildDocsPackageInstallCommand, s as resolveDocsPackageFramework } from "./package-version-OrjpmqoR.mjs";
2
+ import { a as getPackagesForFramework, c as resolveDocsPackageManager, l as validateUpgradeVersion, r as buildDocsPackageInstallCommand, s as resolveDocsPackageFramework } from "./package-version-CQm0KO-H.mjs";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import pc from "picocolors";