@farming-labs/docs 0.2.53 → 0.2.55

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 (51) hide show
  1. package/dist/agent-BOOcHVDL.mjs +623 -0
  2. package/dist/{agent-BMxMpEa0.mjs → agent-DXkXi-pS.mjs} +590 -19
  3. package/dist/agent-evals-BD17jOqL.mjs +1166 -0
  4. package/dist/{agent-export--X9Go-wj.mjs → agent-export-BYznnYed.mjs} +9 -8
  5. package/dist/agent-scope-B8lptqCd.mjs +283 -0
  6. package/dist/agent-surface-drift-LS8zQgbq.mjs +1429 -0
  7. package/dist/{agents-CW-zTNKP.mjs → agents-BHzeeY45.mjs} +7 -6
  8. package/dist/{analytics-BZEwTK-8.mjs → analytics-Bx44lg6d.mjs} +1 -1
  9. package/dist/cli/index.mjs +29 -29
  10. package/dist/client/react.d.mts +1 -1
  11. package/dist/client/react.mjs +1 -1
  12. package/dist/{cloud-zs0qAnKJ.mjs → cloud-HWFlbfLJ.mjs} +4 -4
  13. package/dist/{cloud-ask-ai-BFlx8VMO.mjs → cloud-ask-ai-1k0q6OAH.mjs} +1 -1
  14. package/dist/{cloud-ask-ai-CMzYVzVX.d.mts → cloud-ask-ai-CvRVnwdK.d.mts} +1 -1
  15. package/dist/code-blocks-C9awWzEQ.mjs +871 -0
  16. package/dist/codeblocks-BzfkREEC.mjs +250 -0
  17. package/dist/{config-DhIyFsy9.mjs → config-CydaZ5PB.mjs} +52 -11
  18. package/dist/{dev-DFeVU2Ui.mjs → dev-BA2kRuCn.mjs} +3 -3
  19. package/dist/docs-cloud-server.d.mts +2 -2
  20. package/dist/docs-cloud-server.mjs +2 -2
  21. package/dist/{doctor-CkM5tKdD.mjs → doctor-CNIPJdxN.mjs} +150 -32
  22. package/dist/{downgrade-Dj_KXV11.mjs → downgrade-BStNhyCc.mjs} +2 -2
  23. package/dist/index.d.mts +40 -17
  24. package/dist/index.mjs +6 -7
  25. package/dist/{init-Cmz-oqnp.mjs → init-gnHQ_Pz9.mjs} +3 -3
  26. package/dist/{mcp-B4Q1PN93.mjs → mcp-F9zkBHrU.mjs} +6 -5
  27. package/dist/mcp.d.mts +22 -4
  28. package/dist/mcp.mjs +521 -43
  29. package/dist/{package-version-DxnTZH4m.mjs → package-version-DRIc08EU.mjs} +1 -1
  30. package/dist/{reading-time-BuK2YL23.mjs → reading-time-BrTd3DIh.mjs} +2 -3
  31. package/dist/{review-BEsbm0l2.mjs → review-Dlb8dRRx.mjs} +149 -14
  32. package/dist/{robots-BOwkq9du.mjs → robots-4BUJxlvV.mjs} +4 -5
  33. package/dist/{robots-C6uQG-fS.mjs → robots-DAptQnkx.mjs} +1 -1
  34. package/dist/{search-CtAXkk_J.mjs → search-B8cF77_i.mjs} +6 -5
  35. package/dist/{search-CTu6Bvq-.d.mts → search-BnQKgDbk.d.mts} +1 -1
  36. package/dist/{search-CJNLkdjX.mjs → search-CEg1omHv.mjs} +54 -11
  37. package/dist/server.d.mts +108 -5
  38. package/dist/server.mjs +9 -9
  39. package/dist/{sitemap-OPb8EdNe.mjs → sitemap-D9ivTLm8.mjs} +8 -7
  40. package/dist/{sitemap-server-DnilMEiZ.mjs → sitemap-server-wsNLyVkb.mjs} +1 -1
  41. package/dist/{types-CdKu8KmO.d.mts → types-R-DN23yw.d.mts} +79 -2
  42. package/dist/{upgrade-BNdBB9tQ.mjs → upgrade-CXYRNV0C.mjs} +2 -2
  43. package/package.json +2 -2
  44. package/dist/agent-2t4kZLGo.mjs +0 -10
  45. package/dist/agent-contract-b0akaPcg.mjs +0 -573
  46. package/dist/codeblocks-DWM4IJPL.mjs +0 -1710
  47. package/dist/review-BHdy75TE.mjs +0 -541
  48. /package/dist/{cloud-analytics-Dlk_byos.mjs → cloud-analytics-CSyFE6SS.mjs} +0 -0
  49. /package/dist/{errors-BxfYhp1L.mjs → errors-BFUtdZfC.mjs} +0 -0
  50. /package/dist/{templates-Bty9M6lA.mjs → templates-1Cod8KrJ.mjs} +0 -0
  51. /package/dist/{utils-CMZTjRQT.mjs → utils-DBCCkkJS.mjs} +0 -0
@@ -0,0 +1,250 @@
1
+ import { i as validateCodeBlocks, r as resolveDocsCodeBlocksValidateConfig } from "./code-blocks-C9awWzEQ.mjs";
2
+ import { d as readStringProperty, h as resolveDocsContentDir, i as loadDocsConfigModule, m as resolveDocsConfigPath, p as readTopLevelStringProperty, s as readBooleanProperty, t as extractNestedObjectLiteral, u as readNumberProperty } from "./config-CydaZ5PB.mjs";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import pc from "picocolors";
5
+
6
+ //#region src/cli/codeblocks.ts
7
+ function parseInlineFlag(arg) {
8
+ const [rawKey, value] = arg.slice(2).split("=", 2);
9
+ return {
10
+ key: rawKey.trim(),
11
+ value
12
+ };
13
+ }
14
+ function parseCodeBlocksValidateArgs(argv) {
15
+ const parsed = {};
16
+ for (let index = 0; index < argv.length; index += 1) {
17
+ const arg = argv[index];
18
+ if (arg === "--help" || arg === "-h") {
19
+ parsed.help = true;
20
+ continue;
21
+ }
22
+ if (arg === "--json") {
23
+ parsed.json = true;
24
+ continue;
25
+ }
26
+ if (arg === "--plan") {
27
+ parsed.plan = true;
28
+ continue;
29
+ }
30
+ if (arg === "--run") {
31
+ parsed.run = true;
32
+ continue;
33
+ }
34
+ if (arg.startsWith("--config=")) {
35
+ const value = parseInlineFlag(arg).value;
36
+ if (!value) throw new Error("Missing value for --config.");
37
+ parsed.configPath = value;
38
+ continue;
39
+ }
40
+ if (arg === "--config") {
41
+ const value = argv[index + 1];
42
+ if (!value || value.startsWith("--")) throw new Error("Missing value for --config.");
43
+ parsed.configPath = value;
44
+ index += 1;
45
+ continue;
46
+ }
47
+ throw new Error(`Unknown codeblocks validate flag: ${arg}.`);
48
+ }
49
+ return parsed;
50
+ }
51
+ function printCodeBlocksValidateHelp() {
52
+ console.log(`
53
+ ${pc.bold("@farming-labs/docs codeblocks validate")}
54
+
55
+ ${pc.dim("Usage:")}
56
+ pnpm exec docs codeblocks validate
57
+ pnpm exec docs codeblocks validate --plan
58
+ pnpm exec docs codeblocks validate --json
59
+ pnpm exec docs codeblocks validate --config docs.config.ts
60
+
61
+ ${pc.dim("Options:")}
62
+ ${pc.cyan("--plan")} Build the execution plan without running code
63
+ ${pc.cyan("--run")} Force execution even when config mode is ${pc.dim("\"plan\"")}
64
+ ${pc.cyan("--json")} Print machine-readable output
65
+ ${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
66
+ ${pc.cyan("-h, --help")} Show this help message
67
+ `);
68
+ }
69
+ async function runCodeBlocksValidate(options = {}) {
70
+ const rootDir = process.cwd();
71
+ const loaded = await loadDocsConfigModule(rootDir, options.configPath, { silent: options.json });
72
+ const configPath = loaded?.path ?? resolveDocsConfigPath(rootDir, options.configPath);
73
+ const configContent = existsSync(configPath) ? readFileSync(configPath, "utf-8") : "";
74
+ const entry = loaded?.config.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs";
75
+ const contentDir = loaded?.config.contentDir ?? resolveDocsContentDir(rootDir, configContent, entry);
76
+ const config = resolveDocsCodeBlocksValidateConfig(loaded?.config.codeBlocks?.validate ?? readStaticCodeBlocksValidateConfig(configContent));
77
+ if (!config.enabled) {
78
+ const disabledReport = {
79
+ summary: {
80
+ total: 0,
81
+ planned: 0,
82
+ pass: 0,
83
+ skip: 0,
84
+ fail: 0
85
+ },
86
+ config,
87
+ targets: [],
88
+ plans: [],
89
+ results: []
90
+ };
91
+ if (options.json) console.log(JSON.stringify(redactReport(disabledReport), null, 2));
92
+ else console.log(pc.yellow("codeBlocks.validate is disabled. Add `codeBlocks: { validate: true }` to docs.config.ts."));
93
+ return disabledReport;
94
+ }
95
+ const effectiveConfig = {
96
+ ...config,
97
+ mode: options.run ? "report" : config.mode
98
+ };
99
+ const planOnly = options.plan === true || effectiveConfig.mode === "plan";
100
+ const report = await validateCodeBlocks({
101
+ rootDir,
102
+ contentDir,
103
+ config: effectiveConfig,
104
+ planOnly: options.plan
105
+ });
106
+ if (options.json) console.log(JSON.stringify(redactReport(report), null, 2));
107
+ else printCodeBlocksReport(report, planOnly);
108
+ if (!options.plan && report.summary.fail > 0) process.exitCode = 1;
109
+ return report;
110
+ }
111
+ function readStaticCodeBlocksValidateConfig(content) {
112
+ const block = extractNestedObjectLiteral(content, ["codeBlocks"]);
113
+ if (!block) return void 0;
114
+ if (/\bvalidate\s*:\s*true\b/.test(block)) return true;
115
+ if (/\bvalidate\s*:\s*false\b/.test(block)) return false;
116
+ const validateBlock = extractNestedObjectLiteral(content, ["codeBlocks", "validate"]);
117
+ if (validateBlock) {
118
+ const config = {};
119
+ const enabled = readBooleanProperty(validateBlock, "enabled");
120
+ const mode = readStringProperty(validateBlock, "mode");
121
+ const missingEnv = readStringProperty(validateBlock, "missingEnv");
122
+ const unsupportedLanguage = readStringProperty(validateBlock, "unsupportedLanguage");
123
+ const envFile = readStringArrayProperty(validateBlock, "envFile");
124
+ if (enabled !== void 0) config.enabled = enabled;
125
+ if (mode === "plan" || mode === "report") config.mode = mode;
126
+ if (missingEnv === "skip" || missingEnv === "warn" || missingEnv === "error") config.missingEnv = missingEnv;
127
+ if (unsupportedLanguage === "skip" || unsupportedLanguage === "warn" || unsupportedLanguage === "error") config.unsupportedLanguage = unsupportedLanguage;
128
+ if (envFile) config.envFile = envFile;
129
+ const planner = readStaticPlannerConfig(extractNestedObjectLiteral(content, [
130
+ "codeBlocks",
131
+ "validate",
132
+ "planner"
133
+ ]));
134
+ if (planner) config.planner = planner;
135
+ const runner = readStaticRunnerConfig(extractNestedObjectLiteral(content, [
136
+ "codeBlocks",
137
+ "validate",
138
+ "runner"
139
+ ]));
140
+ if (runner) config.runner = runner;
141
+ const env = readStringRecord(extractNestedObjectLiteral(content, [
142
+ "codeBlocks",
143
+ "validate",
144
+ "env"
145
+ ]));
146
+ if (env && Object.keys(env).length > 0) config.env = env;
147
+ return config;
148
+ }
149
+ if (/\bvalidate\s*:\s*\{/.test(block)) return true;
150
+ }
151
+ function readStaticPlannerConfig(block) {
152
+ if (!block) return void 0;
153
+ const provider = readStringProperty(block, "provider");
154
+ const model = readStringProperty(block, "model");
155
+ const baseUrl = readStringProperty(block, "baseUrl");
156
+ const baseUrlEnv = readStringProperty(block, "baseUrlEnv");
157
+ const apiKeyEnv = readStringProperty(block, "apiKeyEnv");
158
+ if (provider !== "metadata" && provider !== "openai" && provider !== "openai-compatible" && provider !== "cloud") return;
159
+ return {
160
+ provider,
161
+ ...model ? { model } : {},
162
+ ...baseUrl ? { baseUrl } : {},
163
+ ...baseUrlEnv ? { baseUrlEnv } : {},
164
+ ...apiKeyEnv ? { apiKeyEnv } : {}
165
+ };
166
+ }
167
+ function readStaticRunnerConfig(block) {
168
+ if (!block) return void 0;
169
+ const provider = readStringProperty(block, "provider");
170
+ const tokenEnv = readStringProperty(block, "tokenEnv");
171
+ const projectIdEnv = readStringProperty(block, "projectIdEnv");
172
+ const teamIdEnv = readStringProperty(block, "teamIdEnv");
173
+ const projectJson = readStringProperty(block, "projectJson");
174
+ const runtime = readStringProperty(block, "runtime");
175
+ const apiUrlEnv = readStringProperty(block, "apiUrlEnv");
176
+ const targetEnv = readStringProperty(block, "targetEnv");
177
+ const timeoutMs = readNumberProperty(block, "timeoutMs");
178
+ if (provider !== "local" && provider !== "vercel-sandbox" && provider !== "e2b" && provider !== "daytona" && provider !== "cloud") return;
179
+ return {
180
+ provider,
181
+ ...tokenEnv ? { tokenEnv } : {},
182
+ ...projectIdEnv ? { projectIdEnv } : {},
183
+ ...teamIdEnv ? { teamIdEnv } : {},
184
+ ...projectJson ? { projectJson } : {},
185
+ ...runtime === "node24" || runtime === "node22" || runtime === "python3.13" ? { runtime } : {},
186
+ ...apiUrlEnv ? { apiUrlEnv } : {},
187
+ ...targetEnv ? { targetEnv } : {},
188
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
189
+ };
190
+ }
191
+ function readStringArrayProperty(content, key) {
192
+ const single = readStringProperty(content, key);
193
+ if (single) return [single];
194
+ const match = content.match(new RegExp(`\\b${key}\\b\\s*:\\s*\\[([\\s\\S]*?)\\]`));
195
+ if (!match) return void 0;
196
+ const values = [...match[1].matchAll(/["']([^"']+)["']/g)].map((item) => item[1]);
197
+ return values.length > 0 ? values : void 0;
198
+ }
199
+ function readStringRecord(block) {
200
+ if (!block) return void 0;
201
+ const record = {};
202
+ for (const pattern of [/(?:^|,)\s*([A-Za-z_$][\w$]*)\s*:\s*["']([^"']+)["']/g, /(?:^|,)\s*["']([^"']+)["']\s*:\s*["']([^"']+)["']/g]) {
203
+ let match;
204
+ while (match = pattern.exec(block)) record[match[1]] = match[2];
205
+ }
206
+ return record;
207
+ }
208
+ function printCodeBlocksReport(report, planOnly) {
209
+ const label = planOnly ? "Code block plan" : "Code block validation";
210
+ console.log(pc.bold(label));
211
+ console.log([
212
+ ...report.summary.planned > 0 ? [`${pc.cyan(`${report.summary.planned} planned`)}`] : [],
213
+ `${pc.green(`${report.summary.pass} pass`)}`,
214
+ `${pc.yellow(`${report.summary.skip} skip`)}`,
215
+ `${report.summary.fail > 0 ? pc.red(`${report.summary.fail} fail`) : pc.dim("0 fail")}`,
216
+ `${report.targets.length} code blocks`
217
+ ].join(pc.dim(" • ")));
218
+ if (report.results.length === 0) return;
219
+ console.log();
220
+ for (const result of report.results) {
221
+ const status = result.status === "PLAN" ? pc.cyan(result.status) : result.status === "PASS" ? pc.green(result.status) : result.status === "FAIL" ? pc.red(result.status) : pc.yellow(result.status);
222
+ const location = `${result.target.relativePath}:${result.target.lineStart}`;
223
+ const detail = result.reason ?? result.plan.reason ?? result.plan.template;
224
+ console.log(`${status} ${pc.cyan(location)} ${pc.dim(detail)}`);
225
+ }
226
+ }
227
+ function redactReport(report) {
228
+ return {
229
+ ...report,
230
+ config: {
231
+ ...report.config,
232
+ planner: {
233
+ ...report.config.planner,
234
+ apiKey: report.config.planner.apiKey ? "[REDACTED]" : void 0
235
+ }
236
+ },
237
+ results: report.results.map((result) => ({
238
+ ...result,
239
+ stdout: trimOutput(result.stdout),
240
+ stderr: trimOutput(result.stderr)
241
+ }))
242
+ };
243
+ }
244
+ function trimOutput(value) {
245
+ if (!value) return value;
246
+ return value.length > 4e3 ? `${value.slice(0, 4e3)}...` : value;
247
+ }
248
+
249
+ //#endregion
250
+ export { parseCodeBlocksValidateArgs, printCodeBlocksValidateHelp, runCodeBlocksValidate };
@@ -279,28 +279,69 @@ function loadProjectEnv(rootDir) {
279
279
  return env;
280
280
  }
281
281
  async function loadDocsConfigModule(rootDir, explicitPath, options = {}) {
282
+ const result = await loadDocsConfigModuleResult(rootDir, explicitPath, options);
283
+ return result.status === "evaluated" ? {
284
+ path: result.path,
285
+ config: result.config
286
+ } : null;
287
+ }
288
+ function resolveDocsConfigModuleExport(loaded) {
289
+ if (!loaded || typeof loaded !== "object" || Array.isArray(loaded)) return loaded;
290
+ const record = loaded;
291
+ return Object.prototype.hasOwnProperty.call(record, "default") ? record.default : loaded;
292
+ }
293
+ function isPlainDocsConfig(value) {
294
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
295
+ const prototype = Object.getPrototypeOf(value);
296
+ return prototype === Object.prototype || prototype === null;
297
+ }
298
+ /**
299
+ * Load docs.config with an explicit confidence signal for diagnostics.
300
+ * Existing callers can keep using `loadDocsConfigModule`, which preserves the
301
+ * previous evaluated-config-or-null contract.
302
+ */
303
+ async function loadDocsConfigModuleResult(rootDir, explicitPath, options = {}) {
282
304
  const configPath = resolveDocsConfigPath(rootDir, explicitPath);
283
305
  try {
284
306
  const { createJiti } = await import("jiti");
285
- const loaded = await createJiti(import.meta.url, {
307
+ const config = resolveDocsConfigModuleExport(await createJiti(import.meta.url, {
286
308
  moduleCache: false,
287
309
  fsCache: false,
288
- interopDefault: true
289
- }).import(configPath);
290
- const config = loaded.default ?? loaded;
291
- if (!config || typeof config !== "object") return null;
310
+ interopDefault: false
311
+ }).import(configPath));
312
+ if (!isPlainDocsConfig(config)) return {
313
+ status: "static-fallback",
314
+ path: configPath,
315
+ error: "The config module did not export a plain object."
316
+ };
292
317
  return {
318
+ status: "evaluated",
293
319
  path: configPath,
294
320
  config
295
321
  };
296
322
  } catch (error) {
297
- if (!options.silent && process.env.NODE_ENV !== "test") {
298
- const message = error instanceof Error ? error.message : String(error);
299
- console.warn(`[docs] Could not evaluate ${configPath} as a module; falling back to static parsing. ${message}`);
300
- }
301
- return null;
323
+ const message = error instanceof Error ? error.message : String(error);
324
+ if (!options.silent && process.env.NODE_ENV !== "test") console.warn(`[docs] Could not evaluate ${configPath} as a module; falling back to static parsing. ${message}`);
325
+ return {
326
+ status: "static-fallback",
327
+ path: configPath,
328
+ error: message
329
+ };
330
+ }
331
+ }
332
+ /** Evaluate docs.config after loading project-local env files, then restore process.env. */
333
+ async function loadDocsConfigModuleResultWithProjectEnv(rootDir, explicitPath, options = {}) {
334
+ const env = loadProjectEnv(rootDir);
335
+ const injectedKeys = Object.entries(env).filter(([key]) => process.env[key] === void 0).map(([key, value]) => {
336
+ process.env[key] = value;
337
+ return key;
338
+ });
339
+ try {
340
+ return await loadDocsConfigModuleResult(rootDir, explicitPath, options);
341
+ } finally {
342
+ for (const key of injectedKeys) delete process.env[key];
302
343
  }
303
344
  }
304
345
 
305
346
  //#endregion
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 };
347
+ export { loadDocsConfigModuleResultWithProjectEnv as a, readEnvReferenceProperty as c, readStringProperty as d, readTopLevelBooleanProperty as f, resolveDocsContentDir as h, loadDocsConfigModule as i, readNavTitle as l, resolveDocsConfigPath as m, extractObjectLiteral as n, loadProjectEnv as o, readTopLevelStringProperty as p, extractTopLevelConfigObject as r, readBooleanProperty as s, extractNestedObjectLiteral as t, readNumberProperty as u };
@@ -1,10 +1,10 @@
1
- import { i as detectPackageManagerFromProject } from "./utils-CMZTjRQT.mjs";
2
- import { K as rootLayoutTemplate, W as postcssConfigTemplate, g as docsLayoutTemplate, v as globalCssTemplate, yt as tsconfigTemplate } from "./templates-Bty9M6lA.mjs";
1
+ import { i as detectPackageManagerFromProject } from "./utils-DBCCkkJS.mjs";
2
+ import { K as rootLayoutTemplate, W as postcssConfigTemplate, g as docsLayoutTemplate, v as globalCssTemplate, yt as tsconfigTemplate } from "./templates-1Cod8KrJ.mjs";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
- import pc from "picocolors";
6
5
  import { spawn } from "node:child_process";
7
6
  import os from "node:os";
7
+ import pc from "picocolors";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { z } from "zod";
10
10
 
@@ -1,5 +1,5 @@
1
- import { b as DocsAnalyticsEventInput, v as DocsAnalyticsConfig, y as DocsAnalyticsEvent } from "./types-CdKu8KmO.mjs";
2
- import { n as DocsCloudAskAIResponseOptions, t as DocsCloudAskAIConfig } from "./cloud-ask-ai-CMzYVzVX.mjs";
1
+ import { D as DocsAnalyticsEventInput, E as DocsAnalyticsEvent, T as DocsAnalyticsConfig } from "./types-R-DN23yw.mjs";
2
+ import { n as DocsCloudAskAIResponseOptions, t as DocsCloudAskAIConfig } from "./cloud-ask-ai-CvRVnwdK.mjs";
3
3
 
4
4
  //#region src/docs-cloud-server.d.ts
5
5
  type DocsCloudRuntimeValue<T = string> = T | undefined | null | (() => T | undefined | null);
@@ -1,5 +1,5 @@
1
- import { n as sendDocsCloudAnalyticsEvent } from "./cloud-analytics-Dlk_byos.mjs";
2
- import { t as createDocsCloudAskAIResponse } from "./cloud-ask-ai-BFlx8VMO.mjs";
1
+ import { n as sendDocsCloudAnalyticsEvent } from "./cloud-analytics-CSyFE6SS.mjs";
2
+ import { t as createDocsCloudAskAIResponse } from "./cloud-ask-ai-1k0q6OAH.mjs";
3
3
 
4
4
  //#region src/docs-cloud-server.ts
5
5
  const DEFAULT_DOCS_CLOUD_API_BASE_URL = "https://api.farming-labs.dev";