@doxbrix/doxloop 0.1.5 → 0.2.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 (357) hide show
  1. package/CHANGELOG.md +1145 -0
  2. package/README.md +422 -227
  3. package/assets/doxbrix-preview.css +77 -15
  4. package/contracts/agent-events-v1.schema.json +1 -0
  5. package/contracts/coverage-v1.schema.json +59 -0
  6. package/contracts/drift-v1.schema.json +1 -0
  7. package/contracts/evaluation-v1.schema.json +62 -0
  8. package/contracts/quality-codes-v1.json +1 -0
  9. package/contracts/quality-report-v1.schema.json +1 -0
  10. package/contracts/validation-v1.schema.json +1 -0
  11. package/dist/agent-failure.d.ts +36 -0
  12. package/dist/agent-failure.js +98 -0
  13. package/dist/agent-isolation.d.ts +17 -0
  14. package/dist/agent-isolation.js +94 -0
  15. package/dist/agent-log.d.ts +197 -0
  16. package/dist/agent-log.js +1094 -0
  17. package/dist/agent-process.d.ts +63 -0
  18. package/dist/agent-process.js +128 -0
  19. package/dist/agent-reply.d.ts +47 -0
  20. package/dist/agent-reply.js +315 -0
  21. package/dist/agents.d.ts +24 -5
  22. package/dist/agents.js +60 -9
  23. package/dist/api-coverage.d.ts +11 -0
  24. package/dist/api-coverage.js +72 -0
  25. package/dist/application-probe.d.ts +28 -0
  26. package/dist/application-probe.js +93 -0
  27. package/dist/application-url.d.ts +14 -0
  28. package/dist/application-url.js +26 -0
  29. package/dist/args.js +15 -0
  30. package/dist/artifact-deploy.d.ts +9 -0
  31. package/dist/artifact-deploy.js +28 -8
  32. package/dist/assets.d.ts +72 -0
  33. package/dist/assets.js +382 -0
  34. package/dist/auth.d.ts +10 -0
  35. package/dist/auth.js +38 -9
  36. package/dist/author.d.ts +206 -4
  37. package/dist/author.js +1650 -52
  38. package/dist/authoring-batches.d.ts +190 -0
  39. package/dist/authoring-batches.js +489 -0
  40. package/dist/authoring-postpass.d.ts +79 -0
  41. package/dist/authoring-postpass.js +1289 -0
  42. package/dist/authoring-progress.d.ts +95 -0
  43. package/dist/authoring-progress.js +269 -0
  44. package/dist/autosync.d.ts +56 -0
  45. package/dist/autosync.js +442 -0
  46. package/dist/batch-artifacts.d.ts +81 -0
  47. package/dist/batch-artifacts.js +412 -0
  48. package/dist/batch-limits.d.ts +37 -0
  49. package/dist/batch-limits.js +80 -0
  50. package/dist/branding.d.ts +41 -0
  51. package/dist/branding.js +229 -0
  52. package/dist/bundle-upload.d.ts +26 -0
  53. package/dist/bundle-upload.js +99 -0
  54. package/dist/capture-auth.d.ts +95 -0
  55. package/dist/capture-auth.js +204 -0
  56. package/dist/capture.d.ts +48 -0
  57. package/dist/capture.js +20 -1
  58. package/dist/cli.js +839 -32
  59. package/dist/content-links.d.ts +10 -0
  60. package/dist/content-links.js +49 -0
  61. package/dist/contract-validation.d.ts +4 -0
  62. package/dist/contract-validation.js +25 -0
  63. package/dist/coverage-actions.d.ts +8 -0
  64. package/dist/coverage-actions.js +71 -0
  65. package/dist/coverage-resolutions.d.ts +16 -0
  66. package/dist/coverage-resolutions.js +58 -0
  67. package/dist/db.d.ts +37 -0
  68. package/dist/db.js +288 -0
  69. package/dist/demo.d.ts +12 -0
  70. package/dist/demo.js +122 -0
  71. package/dist/deploy-credentials.d.ts +7 -0
  72. package/dist/deploy-credentials.js +76 -0
  73. package/dist/deploy-targets/github-pages.d.ts +3 -0
  74. package/dist/deploy-targets/github-pages.js +100 -0
  75. package/dist/deploy-targets/index.d.ts +8 -0
  76. package/dist/deploy-targets/index.js +78 -0
  77. package/dist/deploy-targets/netlify.d.ts +3 -0
  78. package/dist/deploy-targets/netlify.js +49 -0
  79. package/dist/deploy-targets/types.d.ts +32 -0
  80. package/dist/deploy-targets/types.js +2 -0
  81. package/dist/deploy-targets/vercel.d.ts +3 -0
  82. package/dist/deploy-targets/vercel.js +95 -0
  83. package/dist/deploy.d.ts +9 -0
  84. package/dist/deploy.js +87 -13
  85. package/dist/deterministic-capture.d.ts +90 -0
  86. package/dist/deterministic-capture.js +435 -0
  87. package/dist/direct-edit.d.ts +40 -0
  88. package/dist/direct-edit.js +159 -0
  89. package/dist/docs-crawl.d.ts +86 -0
  90. package/dist/docs-crawl.js +536 -0
  91. package/dist/docs-site.d.ts +49 -0
  92. package/dist/docs-site.js +233 -0
  93. package/dist/doctor.js +8 -0
  94. package/dist/documentation-collections.d.ts +23 -0
  95. package/dist/documentation-collections.js +191 -0
  96. package/dist/documentation-plan.d.ts +253 -0
  97. package/dist/documentation-plan.js +2563 -0
  98. package/dist/doxbrix-build.d.ts +19 -0
  99. package/dist/doxbrix-build.js +167 -0
  100. package/dist/doxbrix-markdown.d.ts +9 -0
  101. package/dist/doxbrix-markdown.js +115 -14
  102. package/dist/drift.d.ts +10 -0
  103. package/dist/drift.js +164 -0
  104. package/dist/evaluation.d.ts +46 -0
  105. package/dist/evaluation.js +113 -0
  106. package/dist/evidence-pack.d.ts +47 -0
  107. package/dist/evidence-pack.js +358 -0
  108. package/dist/evidence.d.ts +25 -0
  109. package/dist/evidence.js +175 -0
  110. package/dist/fs.d.ts +8 -2
  111. package/dist/fs.js +41 -11
  112. package/dist/generator-api.d.ts +89 -0
  113. package/dist/generator-preflight.d.ts +27 -0
  114. package/dist/generator-preflight.js +105 -0
  115. package/dist/generator-runtime.d.ts +7 -0
  116. package/dist/generator-runtime.js +17 -1
  117. package/dist/generators.d.ts +24 -2
  118. package/dist/generators.js +54 -1
  119. package/dist/git-delivery.d.ts +17 -0
  120. package/dist/git-delivery.js +123 -0
  121. package/dist/globs.d.ts +16 -0
  122. package/dist/globs.js +65 -0
  123. package/dist/glossary.d.ts +26 -0
  124. package/dist/glossary.js +179 -0
  125. package/dist/history.d.ts +106 -0
  126. package/dist/history.js +600 -0
  127. package/dist/html-markdown.d.ts +46 -0
  128. package/dist/html-markdown.js +423 -0
  129. package/dist/interactive.js +3 -0
  130. package/dist/job-events.d.ts +74 -0
  131. package/dist/job-events.js +377 -0
  132. package/dist/keep-awake.d.ts +50 -0
  133. package/dist/keep-awake.js +123 -0
  134. package/dist/local-source-snapshot.d.ts +20 -0
  135. package/dist/local-source-snapshot.js +61 -0
  136. package/dist/mintlify-detect.d.ts +3 -0
  137. package/dist/mintlify-detect.js +18 -0
  138. package/dist/mintlify-import.d.ts +75 -0
  139. package/dist/mintlify-import.js +190 -0
  140. package/dist/navigation.d.ts +98 -0
  141. package/dist/navigation.js +310 -0
  142. package/dist/openapi.d.ts +60 -0
  143. package/dist/openapi.js +439 -0
  144. package/dist/page-editor-bridge.d.ts +3 -0
  145. package/dist/page-editor-bridge.js +109 -0
  146. package/dist/page-editor-preview.d.ts +10 -0
  147. package/dist/page-editor-preview.js +55 -0
  148. package/dist/page-extension.d.ts +9 -0
  149. package/dist/page-extension.js +15 -0
  150. package/dist/page-metadata.d.ts +28 -0
  151. package/dist/page-metadata.js +166 -0
  152. package/dist/page-operations.d.ts +34 -0
  153. package/dist/page-operations.js +215 -0
  154. package/dist/page-routes.d.ts +4 -0
  155. package/dist/page-routes.js +61 -0
  156. package/dist/pages.d.ts +20 -0
  157. package/dist/pages.js +184 -0
  158. package/dist/plan-generator.d.ts +3 -0
  159. package/dist/plan-generator.js +21 -0
  160. package/dist/plan-navigation.d.ts +11 -0
  161. package/dist/plan-navigation.js +30 -0
  162. package/dist/planning-captures.d.ts +20 -0
  163. package/dist/planning-captures.js +143 -0
  164. package/dist/planning-research.d.ts +135 -0
  165. package/dist/planning-research.js +472 -0
  166. package/dist/planning-triage.d.ts +23 -0
  167. package/dist/planning-triage.js +131 -0
  168. package/dist/preview.d.ts +24 -0
  169. package/dist/preview.js +280 -29
  170. package/dist/project-detect.d.ts +36 -0
  171. package/dist/project-detect.js +251 -0
  172. package/dist/project-import.d.ts +54 -0
  173. package/dist/project-import.js +157 -0
  174. package/dist/project-lock.d.ts +6 -0
  175. package/dist/project-lock.js +96 -0
  176. package/dist/project-registry.d.ts +25 -0
  177. package/dist/project-registry.js +79 -0
  178. package/dist/project.d.ts +35 -3
  179. package/dist/project.js +388 -38
  180. package/dist/prompts.d.ts +9 -0
  181. package/dist/prompts.js +25 -0
  182. package/dist/proposal-replay.d.ts +32 -0
  183. package/dist/proposal-replay.js +99 -0
  184. package/dist/quality-claims.d.ts +8 -0
  185. package/dist/quality-claims.js +168 -0
  186. package/dist/quality-config.d.ts +5 -0
  187. package/dist/quality-config.js +84 -0
  188. package/dist/quality-contract.d.ts +37 -0
  189. package/dist/quality-contract.js +46 -0
  190. package/dist/quality-examples.d.ts +4 -0
  191. package/dist/quality-examples.js +233 -0
  192. package/dist/quality-gates.d.ts +16 -0
  193. package/dist/quality-gates.js +192 -0
  194. package/dist/quality-links.d.ts +7 -0
  195. package/dist/quality-links.js +149 -0
  196. package/dist/quality-lint.d.ts +6 -0
  197. package/dist/quality-lint.js +124 -0
  198. package/dist/quality-rendered.d.ts +11 -0
  199. package/dist/quality-rendered.js +222 -0
  200. package/dist/quality-schema.d.ts +3 -0
  201. package/dist/quality-schema.js +71 -0
  202. package/dist/release-notes.d.ts +44 -0
  203. package/dist/release-notes.js +183 -0
  204. package/dist/remote-monitor.d.ts +16 -0
  205. package/dist/remote-monitor.js +74 -0
  206. package/dist/remote-source.d.ts +34 -0
  207. package/dist/remote-source.js +426 -0
  208. package/dist/review-diff.d.ts +82 -0
  209. package/dist/review-diff.js +400 -0
  210. package/dist/review-learning.d.ts +11 -0
  211. package/dist/review-learning.js +60 -0
  212. package/dist/review-render.d.ts +38 -0
  213. package/dist/review-render.js +224 -0
  214. package/dist/review-report.d.ts +9 -0
  215. package/dist/review-report.js +89 -0
  216. package/dist/review-ui.d.ts +14 -0
  217. package/dist/review-ui.js +1248 -0
  218. package/dist/schedule.d.ts +78 -0
  219. package/dist/schedule.js +480 -0
  220. package/dist/screen-capture-provider.d.ts +52 -0
  221. package/dist/screen-capture-provider.js +218 -0
  222. package/dist/screenshot-workflow.d.ts +167 -0
  223. package/dist/screenshot-workflow.js +1237 -0
  224. package/dist/settings.d.ts +1 -1
  225. package/dist/settings.js +95 -7
  226. package/dist/site-export.d.ts +18 -0
  227. package/dist/site-export.js +87 -0
  228. package/dist/source-connectors.d.ts +33 -0
  229. package/dist/source-connectors.js +268 -0
  230. package/dist/source-discovery.d.ts +132 -0
  231. package/dist/source-discovery.js +823 -0
  232. package/dist/source-intelligence.d.ts +9 -0
  233. package/dist/source-intelligence.js +306 -0
  234. package/dist/sync-review.d.ts +28 -0
  235. package/dist/sync-review.js +264 -0
  236. package/dist/sync-runs.d.ts +192 -0
  237. package/dist/sync-runs.js +2244 -0
  238. package/dist/sync.d.ts +35 -0
  239. package/dist/sync.js +298 -32
  240. package/dist/text-diff.d.ts +9 -0
  241. package/dist/text-diff.js +59 -0
  242. package/dist/types.d.ts +946 -1
  243. package/dist/ui/assets/doxloop-logo-light-De7Nx7j7.png +0 -0
  244. package/dist/ui/assets/index-BHBYU2aG.css +1 -0
  245. package/dist/ui/assets/index-Cq3RPQiC.js +33 -0
  246. package/dist/ui/index.html +18 -0
  247. package/dist/ui-server.d.ts +83 -0
  248. package/dist/ui-server.js +3532 -0
  249. package/dist/usage-budget.d.ts +28 -0
  250. package/dist/usage-budget.js +90 -0
  251. package/dist/validation.d.ts +25 -1
  252. package/dist/validation.js +312 -26
  253. package/dist/workspace-tools.d.ts +54 -0
  254. package/dist/workspace-tools.js +123 -0
  255. package/docs/agent-compatibility.md +49 -32
  256. package/docs/ci-and-automation.md +105 -66
  257. package/docs/doxbrix-http-api.md +8 -1
  258. package/docs/existing-documentation.md +80 -0
  259. package/docs/generation-performance.md +108 -0
  260. package/docs/generator-authoring.md +68 -5
  261. package/docs/generator-selection.md +50 -13
  262. package/docs/mintlify-import.md +71 -0
  263. package/docs/openapi-security.md +25 -0
  264. package/docs/project-format.md +331 -36
  265. package/docs/release-quality.md +158 -0
  266. package/docs/releasing.md +72 -0
  267. package/docs/review-workflows.md +51 -0
  268. package/docs/security-model.md +102 -39
  269. package/docs/troubleshooting.md +226 -93
  270. package/package.json +46 -15
  271. package/scripts/test-auto-screenshot.mjs +172 -0
  272. package/skills/doxloop-authoring/SKILL.md +242 -361
  273. package/skills/doxloop-authoring/references/existing-documentation.md +94 -0
  274. package/skills/doxloop-authoring/references/navigation-architecture.md +18 -7
  275. package/skills/doxloop-authoring/references/page-depth.md +169 -0
  276. package/skills/doxloop-authoring/references/project-format.md +106 -7
  277. package/skills/doxloop-authoring/references/quality.md +10 -0
  278. package/skills/doxloop-authoring/references/screenshot-manifest.md +113 -0
  279. package/skills/doxloop-authoring/references/screenshots.md +149 -235
  280. package/skills/doxloop-authoring/references/workflows.md +76 -0
  281. package/skills/doxloop-doxbrix/SKILL.md +38 -22
  282. package/skills/doxloop-doxbrix/references/api-endpoints.md +15 -14
  283. package/skills/doxloop-doxbrix/references/components.md +30 -3
  284. package/skills/doxloop-doxbrix/references/manifest.md +3 -2
  285. package/vendor/doxbrix-import/LICENSE +202 -0
  286. package/vendor/doxbrix-import/README.md +17 -0
  287. package/vendor/doxbrix-import/UPSTREAM.json +47 -0
  288. package/vendor/doxbrix-import/dist/docs/frontmatter.d.ts +13 -0
  289. package/vendor/doxbrix-import/dist/docs/frontmatter.js +83 -0
  290. package/vendor/doxbrix-import/dist/docs/import.d.ts +21 -0
  291. package/vendor/doxbrix-import/dist/docs/import.js +147 -0
  292. package/vendor/doxbrix-import/dist/docs/manifest.d.ts +163 -0
  293. package/vendor/doxbrix-import/dist/docs/manifest.js +64 -0
  294. package/vendor/doxbrix-import/dist/docs/project.d.ts +25 -0
  295. package/vendor/doxbrix-import/dist/docs/project.js +77 -0
  296. package/vendor/doxbrix-import/dist/docs/starter.d.ts +4 -0
  297. package/vendor/doxbrix-import/dist/docs/starter.js +11 -0
  298. package/vendor/doxbrix-import/dist/importer.d.ts +227 -0
  299. package/vendor/doxbrix-import/dist/importer.js +1567 -0
  300. package/vendor/doxbrix-import/dist/mintlify-openapi.d.ts +37 -0
  301. package/vendor/doxbrix-import/dist/mintlify-openapi.js +305 -0
  302. package/vendor/doxbrix-import/dist/safe-path.d.ts +9 -0
  303. package/vendor/doxbrix-import/dist/safe-path.js +47 -0
  304. package/dist/agents.d.ts.map +0 -1
  305. package/dist/agents.js.map +0 -1
  306. package/dist/args.d.ts.map +0 -1
  307. package/dist/args.js.map +0 -1
  308. package/dist/artifact-deploy.d.ts.map +0 -1
  309. package/dist/artifact-deploy.js.map +0 -1
  310. package/dist/auth.d.ts.map +0 -1
  311. package/dist/auth.js.map +0 -1
  312. package/dist/author.d.ts.map +0 -1
  313. package/dist/author.js.map +0 -1
  314. package/dist/capture.d.ts.map +0 -1
  315. package/dist/capture.js.map +0 -1
  316. package/dist/cli.d.ts.map +0 -1
  317. package/dist/cli.js.map +0 -1
  318. package/dist/deploy.d.ts.map +0 -1
  319. package/dist/deploy.js.map +0 -1
  320. package/dist/deployment-visibility.d.ts.map +0 -1
  321. package/dist/deployment-visibility.js.map +0 -1
  322. package/dist/doctor.d.ts.map +0 -1
  323. package/dist/doctor.js.map +0 -1
  324. package/dist/doxbrix-markdown.d.ts.map +0 -1
  325. package/dist/doxbrix-markdown.js.map +0 -1
  326. package/dist/errors.d.ts.map +0 -1
  327. package/dist/errors.js.map +0 -1
  328. package/dist/fs.d.ts.map +0 -1
  329. package/dist/fs.js.map +0 -1
  330. package/dist/generator-api.d.ts.map +0 -1
  331. package/dist/generator-api.js.map +0 -1
  332. package/dist/generator-manager.d.ts.map +0 -1
  333. package/dist/generator-manager.js.map +0 -1
  334. package/dist/generator-runtime.d.ts.map +0 -1
  335. package/dist/generator-runtime.js.map +0 -1
  336. package/dist/generators.d.ts.map +0 -1
  337. package/dist/generators.js.map +0 -1
  338. package/dist/interactive.d.ts.map +0 -1
  339. package/dist/interactive.js.map +0 -1
  340. package/dist/preview.d.ts.map +0 -1
  341. package/dist/preview.js.map +0 -1
  342. package/dist/progress.d.ts.map +0 -1
  343. package/dist/progress.js.map +0 -1
  344. package/dist/project.d.ts.map +0 -1
  345. package/dist/project.js.map +0 -1
  346. package/dist/prompts.d.ts.map +0 -1
  347. package/dist/prompts.js.map +0 -1
  348. package/dist/settings.d.ts.map +0 -1
  349. package/dist/settings.js.map +0 -1
  350. package/dist/sync.d.ts.map +0 -1
  351. package/dist/sync.js.map +0 -1
  352. package/dist/types.d.ts.map +0 -1
  353. package/dist/types.js.map +0 -1
  354. package/dist/validation.d.ts.map +0 -1
  355. package/dist/validation.js.map +0 -1
  356. package/dist/version.d.ts.map +0 -1
  357. package/dist/version.js.map +0 -1
@@ -2,5 +2,5 @@ import { type PromptIo } from './prompts.js';
2
2
  import type { DeploymentConfig, DoxloopProject } from './types.js';
3
3
  export declare function runSettingsWizard(root: string, cwd: string, io?: PromptIo): Promise<void>;
4
4
  export declare function formatProjectSettings(root: string, project: DoxloopProject): string;
5
- export declare function effectiveDeployment(project: DoxloopProject, fallbackApiUrl?: string): Required<DeploymentConfig>;
5
+ export declare function effectiveDeployment(project: DoxloopProject, fallbackApiUrl?: string): Required<Pick<DeploymentConfig, 'target' | 'name' | 'slug' | 'visibility' | 'apiUrl'>> & Omit<DeploymentConfig, 'target' | 'name' | 'slug' | 'visibility' | 'apiUrl'>;
6
6
  //# sourceMappingURL=settings.d.ts.map
package/dist/settings.js CHANGED
@@ -2,8 +2,10 @@ import { relative, resolve } from 'node:path';
2
2
  import { detectAgents } from './agents.js';
3
3
  import { apiUrl } from './auth.js';
4
4
  import { generatorCatalogEntry } from './generators.js';
5
- import { isSpecUrl, loadProject, parseDesignReference, parseSpec, saveProjectSettings, validateProjectSourceBoundaries, } from './project.js';
6
- import { heading, note, promptConfirm, promptSelect, promptText, } from './prompts.js';
5
+ import { isSpecUrl, loadProject, parseDesignReference, parseSpec, projectDefaultModel, saveProjectSettings, validateProjectSourceBoundaries, } from './project.js';
6
+ import { heading, note, promptConfirm, promptSecret, promptSelect, promptText, } from './prompts.js';
7
+ import { captureAuthStatus, removeCaptureCredentials, removeCaptureSession, saveCaptureCredentials, saveCaptureSession, } from './capture-auth.js';
8
+ import { startCaptureSignIn } from './screen-capture-provider.js';
7
9
  export async function runSettingsWizard(root, cwd, io = { input: process.stdin, output: process.stdout }) {
8
10
  heading(io, 'Doxloop — project settings');
9
11
  while (true) {
@@ -60,17 +62,23 @@ export function formatProjectSettings(root, project) {
60
62
  const evidence = project.sources.length === 0
61
63
  ? 'None configured'
62
64
  : project.sources
63
- .map((source) => `${source.name}: ${(source.kind ?? 'directory') === 'openapi' ? 'OpenAPI ' : ''}${source.path}`)
65
+ .map((source) => `${source.name}: ${(source.kind ?? 'directory') === 'openapi' ? 'OpenAPI ' : (source.kind ?? 'directory') === 'docs-site' ? 'Docs site ' : ''}${source.site?.url ?? source.path}`)
64
66
  .join('\n ');
65
67
  const deployment = effectiveDeployment(project);
66
- return `Project\n Root: ${root}\n Title: ${project.title}\n Generator: ${generator}\n Agent: ${project.defaultAgent ?? 'Choose at authoring time'}\n\nEvidence\n ${evidence}\n\nDocumentation\n Audience: ${project.documentation.primaryAudience ?? 'Agent will determine'}\n Locale: ${project.documentation.locale}\n Tone: ${project.documentation.tone.join(', ')}\n\nApplication screenshots\n ${project.application ? `${project.application.screenshots?.policy ?? 'requested'} at ${project.application.baseUrl}` : 'Not configured'}\n\nDeployment\n Name: ${deployment.name}\n Slug: ${deployment.slug}\n Visibility: ${deployment.visibility}\n Destination: ${deployment.apiUrl}`;
68
+ return `Project\n Root: ${root}\n Title: ${project.title}\n Generator: ${generator}\n Agent: ${project.defaultAgent ?? 'Choose at authoring time'}\n Model: ${project.defaultModel ?? 'Agent default'}\n\nEvidence\n ${evidence}\n\nDocumentation\n Audience: ${project.documentation.primaryAudience ?? 'Agent will determine'}\n Locale: ${project.documentation.locale}\n Tone: ${project.documentation.tone.join(', ')}\n\nApplication screenshots\n ${project.application ? `${project.application.screenshots?.policy ?? 'requested'} at ${project.application.baseUrl}` : 'Not configured'}\n\nDeployment\n Target: ${deployment.target}\n Name: ${deployment.name}\n Slug: ${deployment.slug}\n Visibility: ${deployment.visibility}\n Destination: ${deployment.apiUrl}`;
67
69
  }
68
70
  export function effectiveDeployment(project, fallbackApiUrl) {
69
71
  return {
72
+ target: project.deployment?.target ?? 'doxbrix',
70
73
  name: project.deployment?.name?.trim() || project.title,
71
74
  slug: project.deployment?.slug?.trim() || slugify(project.title),
72
75
  visibility: project.deployment?.visibility ?? 'private',
73
76
  apiUrl: apiUrl(project.deployment?.apiUrl ?? fallbackApiUrl),
77
+ ...(project.deployment?.siteId ? { siteId: project.deployment.siteId } : {}),
78
+ ...(project.deployment?.projectId ? { projectId: project.deployment.projectId } : {}),
79
+ ...(project.deployment?.teamId ? { teamId: project.deployment.teamId } : {}),
80
+ ...(project.deployment?.branch ? { branch: project.deployment.branch } : {}),
81
+ ...(project.deployment?.basePath ? { basePath: project.deployment.basePath } : {}),
74
82
  };
75
83
  }
76
84
  async function editTitle(root, project, io) {
@@ -104,7 +112,7 @@ async function editEvidence(root, cwd, project, io) {
104
112
  note(io, project.sources.length === 0
105
113
  ? 'No product evidence is configured.'
106
114
  : project.sources
107
- .map((source) => `${source.name}: ${(source.kind ?? 'directory') === 'openapi' ? 'OpenAPI — ' : ''}${source.path}`)
115
+ .map((source) => `${source.name}: ${(source.kind ?? 'directory') === 'openapi' ? 'OpenAPI — ' : (source.kind ?? 'directory') === 'docs-site' ? 'Docs site — ' : ''}${source.site?.url ?? source.path}`)
108
116
  .join('\n'));
109
117
  return;
110
118
  }
@@ -210,8 +218,15 @@ async function editAgent(root, io) {
210
218
  })),
211
219
  io,
212
220
  });
213
- await saveProjectSettings(root, { defaultAgent: agent });
214
- note(io, `✓ Default agent changed to ${labels[agent]}.`);
221
+ const project = await loadProject(root);
222
+ const model = (await promptText({
223
+ message: `Default ${labels[agent]} model (leave blank for the agent default)`,
224
+ initial: projectDefaultModel(project, agent) ?? '',
225
+ allowEmpty: true,
226
+ io,
227
+ })).trim();
228
+ await saveProjectSettings(root, { defaultAgent: agent, defaultModel: model || undefined });
229
+ note(io, `✓ Default agent changed to ${labels[agent]}${model ? ` using ${model}` : ''}.`);
215
230
  }
216
231
  async function editDocumentation(root, documentation, io) {
217
232
  const field = await promptSelect({
@@ -328,6 +343,12 @@ async function editScreenshots(root, project, io) {
328
343
  label: 'Screenshot policy',
329
344
  ...(!project.application ? { disabled: 'configure an application first' } : {}),
330
345
  },
346
+ {
347
+ value: 'signin',
348
+ label: 'Application sign-in',
349
+ hint: 'browser session or test-account credentials for login pages',
350
+ ...(!project.application ? { disabled: 'configure an application first' } : {}),
351
+ },
331
352
  {
332
353
  value: 'remove',
333
354
  label: 'Remove application configuration',
@@ -339,6 +360,10 @@ async function editScreenshots(root, project, io) {
339
360
  });
340
361
  if (action === 'back')
341
362
  return;
363
+ if (action === 'signin') {
364
+ await editCaptureSignIn(root, project, io);
365
+ return;
366
+ }
342
367
  if (action === 'remove') {
343
368
  await saveProjectSettings(root, { application: undefined });
344
369
  note(io, '✓ Application screenshot configuration removed.');
@@ -382,6 +407,69 @@ async function editScreenshots(root, project, io) {
382
407
  });
383
408
  note(io, `✓ Screenshot policy changed to ${policy}.`);
384
409
  }
410
+ /**
411
+ * Sign-in for the capture browser. Both options are stored in the user's
412
+ * Doxloop config home against this project, never in `.doxloop/project.json`.
413
+ */
414
+ async function editCaptureSignIn(root, project, io) {
415
+ const application = project.application;
416
+ const status = await captureAuthStatus(root);
417
+ note(io, `Browser session: ${status.session ? `saved ${status.session.savedAt} for ${status.session.origin} (${status.session.cookies} cookies)` : 'none'}`);
418
+ note(io, `Credentials: ${status.credentials ? `saved for ${status.credentials.username}` : 'none'}`);
419
+ const action = await promptSelect({
420
+ message: 'Application sign-in',
421
+ choices: [
422
+ { value: 'browser', label: 'Sign in with browser', hint: 'opens Chrome; sign in by hand, including MFA or SSO' },
423
+ { value: 'credentials', label: 'Save test-account credentials', hint: 'typed by the capture server, never shown to the agent' },
424
+ { value: 'login-path', label: 'Set the sign-in route', hint: application.authentication?.loginPath ?? 'not set' },
425
+ { value: 'forget-session', label: 'Forget the browser session', ...(!status.session ? { disabled: 'none saved' } : {}) },
426
+ { value: 'forget-credentials', label: 'Remove the credentials', ...(!status.credentials ? { disabled: 'none saved' } : {}) },
427
+ { value: 'back', label: 'Back' },
428
+ ],
429
+ io,
430
+ });
431
+ if (action === 'back')
432
+ return;
433
+ if (action === 'browser') {
434
+ const session = await startCaptureSignIn(application);
435
+ note(io, `Chrome opened at ${session.url}. Complete the sign-in there and wait for the signed-in screen.`);
436
+ const save = await promptConfirm({ message: 'Save the signed-in session now?', initial: true, io });
437
+ if (!save) {
438
+ await session.cancel();
439
+ note(io, 'Sign-in discarded.');
440
+ return;
441
+ }
442
+ const state = await session.finish();
443
+ const stored = await saveCaptureSession(root, new URL(application.baseUrl).origin, state);
444
+ note(io, `✓ Browser session saved with ${stored.state.cookies.length} cookies. Capture runs start signed in.`);
445
+ return;
446
+ }
447
+ if (action === 'credentials') {
448
+ const username = await promptText({ message: 'Test account username or email', ...(status.credentials ? { initial: status.credentials.username } : {}), io });
449
+ const password = await promptSecret({ message: 'Test account password', io });
450
+ await saveCaptureCredentials(root, { username, password });
451
+ note(io, `✓ Credentials saved for ${username}. The agent fills the sign-in form by secret name during capture.`);
452
+ return;
453
+ }
454
+ if (action === 'login-path') {
455
+ const loginPath = await promptText({
456
+ message: 'Sign-in route (relative to the application URL)',
457
+ initial: application.authentication?.loginPath ?? '/login',
458
+ validate: (value) => value.startsWith('/') && !value.startsWith('//') ? undefined : 'Start with one slash.',
459
+ io,
460
+ });
461
+ await saveProjectSettings(root, { application: { ...application, authentication: { ...application.authentication, loginPath } } });
462
+ note(io, `✓ Sign-in route set to ${loginPath}.`);
463
+ return;
464
+ }
465
+ if (action === 'forget-session') {
466
+ await removeCaptureSession(root);
467
+ note(io, '✓ Browser session removed.');
468
+ return;
469
+ }
470
+ await removeCaptureCredentials(root);
471
+ note(io, '✓ Credentials removed.');
472
+ }
385
473
  async function editDeployment(root, project, io) {
386
474
  const effective = effectiveDeployment(project);
387
475
  const field = await promptSelect({
@@ -0,0 +1,18 @@
1
+ export interface ExportSiteOptions {
2
+ root: string;
3
+ out: string;
4
+ zip?: boolean;
5
+ basePath?: string;
6
+ siteUrl?: string;
7
+ }
8
+ export interface ExportSiteResult {
9
+ outputDir: string;
10
+ zipPath?: string;
11
+ files: number;
12
+ bytes: number;
13
+ sha256: string;
14
+ generator: string;
15
+ }
16
+ /** Build and copy a generator's deployable static output to a user-selected folder. */
17
+ export declare function exportStaticSite(options: ExportSiteOptions): Promise<ExportSiteResult>;
18
+ //# sourceMappingURL=site-export.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { cp, mkdir, mkdtemp, readdir, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { dirname, relative, resolve, sep } from 'node:path';
4
+ import { containedOutput, packageStaticOutput, runLocalBuild, } from './artifact-deploy.js';
5
+ import { buildDoxbrixStaticSite } from './doxbrix-build.js';
6
+ import { DoxloopError } from './errors.js';
7
+ import { pathExists } from './fs.js';
8
+ import { loadGeneratorAdapter } from './generators.js';
9
+ import { loadProject } from './project.js';
10
+ import { validateProject } from './validation.js';
11
+ /** Build and copy a generator's deployable static output to a user-selected folder. */
12
+ export async function exportStaticSite(options) {
13
+ const root = resolve(options.root);
14
+ const outputDir = resolve(options.out);
15
+ if (outputDir === root)
16
+ throw new DoxloopError('Export output cannot replace the project directory.');
17
+ await assertEmptyDestination(outputDir);
18
+ const validation = await validateProject(root);
19
+ if (validation.errors > 0) {
20
+ throw new DoxloopError(`Export stopped because documentation has ${validation.errors} validation error${validation.errors === 1 ? '' : 's'}. Run \`doxloop test\`.`);
21
+ }
22
+ const project = await loadProject(root);
23
+ const temporary = await mkdtemp(`${tmpdir()}/doxloop-export-`);
24
+ try {
25
+ let builtOutput;
26
+ if (project.generator === 'doxbrix') {
27
+ builtOutput = resolve(temporary, 'site');
28
+ await buildDoxbrixStaticSite({
29
+ root,
30
+ outDir: builtOutput,
31
+ ...(options.basePath ? { basePath: options.basePath } : {}),
32
+ ...(options.siteUrl ? { siteUrl: options.siteUrl } : {}),
33
+ });
34
+ }
35
+ else {
36
+ const adapter = await loadGeneratorAdapter(root, project);
37
+ const siteUrl = options.siteUrl ?? process.env.DOXLOOP_SITE_URL ?? 'https://example.com';
38
+ await runLocalBuild(adapter.id, adapter.build.command, root, siteUrl);
39
+ builtOutput = containedOutput(root, adapter.build.outputDir);
40
+ }
41
+ if (overlaps(outputDir, builtOutput)) {
42
+ throw new DoxloopError('Export output must be different from the generator build directory.');
43
+ }
44
+ const packaged = await packageStaticOutput(builtOutput);
45
+ await mkdir(dirname(outputDir), { recursive: true });
46
+ await cp(builtOutput, outputDir, { recursive: true, errorOnExist: true, force: false });
47
+ const zipPath = options.zip ? `${outputDir}.zip` : undefined;
48
+ if (zipPath) {
49
+ if (await pathExists(zipPath))
50
+ throw new DoxloopError(`Export archive already exists: ${zipPath}`);
51
+ await writeFile(zipPath, packaged.archive, { mode: 0o600 });
52
+ }
53
+ return {
54
+ outputDir,
55
+ ...(zipPath ? { zipPath } : {}),
56
+ files: packaged.files,
57
+ bytes: packaged.archive.byteLength,
58
+ sha256: packaged.sha256,
59
+ generator: project.generator,
60
+ };
61
+ }
62
+ finally {
63
+ await rm(temporary, { recursive: true, force: true });
64
+ }
65
+ }
66
+ function overlaps(left, right) {
67
+ const relation = relative(left, right);
68
+ const reverse = relative(right, left);
69
+ const within = (value) => value === '' || (value !== '..' && !value.startsWith(`..${sep}`));
70
+ return within(relation) || within(reverse);
71
+ }
72
+ async function assertEmptyDestination(path) {
73
+ if (!(await pathExists(path)))
74
+ return;
75
+ let entries;
76
+ try {
77
+ entries = await readdir(path);
78
+ }
79
+ catch {
80
+ throw new DoxloopError(`Export output must be a directory: ${path}`);
81
+ }
82
+ if (entries.length > 0)
83
+ throw new DoxloopError(`Export output directory is not empty: ${path}`);
84
+ // cp with errorOnExist requires the destination not to exist.
85
+ await rm(path, { recursive: true });
86
+ }
87
+ //# sourceMappingURL=site-export.js.map
@@ -0,0 +1,33 @@
1
+ import type { OpenApiSnapshot, SourceBinding, SourceHealth } from './types.js';
2
+ export declare const SOURCE_CONNECTOR_API_VERSION: 1;
3
+ export interface SourceConnectorSnapshot {
4
+ revision: string;
5
+ capturedAt: string;
6
+ validators?: {
7
+ etag?: string;
8
+ lastModified?: string;
9
+ };
10
+ openapi?: OpenApiSnapshot;
11
+ }
12
+ export interface SourceConnectorInventory {
13
+ identifiers: string[];
14
+ metadata: Record<string, string | number | string[]>;
15
+ }
16
+ /** Versioned provider boundary used by source validation, inventory, drift, and health. */
17
+ export interface SourceConnector {
18
+ readonly id: string;
19
+ readonly version: typeof SOURCE_CONNECTOR_API_VERSION;
20
+ validate(root: string, source: SourceBinding): Promise<void>;
21
+ inventory(root: string, source: SourceBinding): Promise<SourceConnectorInventory>;
22
+ snapshot(root: string, source: SourceBinding): Promise<SourceConnectorSnapshot>;
23
+ detectChanges(previous: SourceConnectorSnapshot | undefined, current: SourceConnectorSnapshot): Promise<{
24
+ changed: boolean;
25
+ identifiers: string[];
26
+ }>;
27
+ evidenceIdentifiers(snapshot: SourceConnectorSnapshot): string[];
28
+ redact(source: SourceBinding): SourceBinding;
29
+ health(root: string, source: SourceBinding): Promise<SourceHealth>;
30
+ }
31
+ export declare function connectorForSource(source: SourceBinding): SourceConnector;
32
+ export declare function sourceHealth(root: string, sources: SourceBinding[]): Promise<SourceHealth[]>;
33
+ //# sourceMappingURL=source-connectors.d.ts.map
@@ -0,0 +1,268 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { lstat, readdir } from 'node:fs/promises';
3
+ import { basename, resolve } from 'node:path';
4
+ import { describeDocsSite, readDocsSiteManifest } from './docs-site.js';
5
+ import { DoxloopError } from './errors.js';
6
+ import { loadOpenApiSource } from './openapi.js';
7
+ import { isSpecUrl, sourceKind } from './project.js';
8
+ import { remoteHead } from './remote-source.js';
9
+ import { readSyncState } from './sync.js';
10
+ export const SOURCE_CONNECTOR_API_VERSION = 1;
11
+ export function connectorForSource(source) {
12
+ const kind = sourceKind(source);
13
+ return kind === 'openapi' ? openApiConnector : kind === 'docs-site' ? docsSiteConnector : directoryConnector;
14
+ }
15
+ export async function sourceHealth(root, sources) {
16
+ return Promise.all(sources.map(async (source) => {
17
+ try {
18
+ return await connectorForSource(source).health(root, source);
19
+ }
20
+ catch (error) {
21
+ const connector = connectorForSource(source);
22
+ const redacted = connector.redact(source);
23
+ return {
24
+ name: source.name,
25
+ connector: connector.id,
26
+ status: 'error',
27
+ checkedAt: new Date().toISOString(),
28
+ location: redacted.remote?.repository ?? redacted.path,
29
+ provider: source.remote?.provider ?? connector.id,
30
+ ...(source.remote?.branch ? { branch: source.remote.branch } : {}),
31
+ ...(source.remote?.subdirectory ? { subdirectory: source.remote.subdirectory } : {}),
32
+ monitored: Boolean(source.remote),
33
+ summary: safeHealthError(error, source, root),
34
+ details: ['Check that the location is reachable and that read-only credentials are available in the configured environment.'],
35
+ ...(source.scope ? { scope: source.scope } : {}),
36
+ };
37
+ }
38
+ }));
39
+ }
40
+ const openApiConnector = {
41
+ id: 'openapi',
42
+ version: SOURCE_CONNECTOR_API_VERSION,
43
+ async validate(root, source) { await loadOpenApiSource(root, source); },
44
+ async inventory(root, source) {
45
+ const loaded = await loadOpenApiSource(root, source);
46
+ return {
47
+ identifiers: [
48
+ ...Object.keys(loaded.snapshot.operations),
49
+ ...Object.keys(loaded.snapshot.schemas).map((item) => `schema:${item}`),
50
+ ],
51
+ metadata: {
52
+ title: loaded.summary.title,
53
+ version: loaded.summary.version,
54
+ operationCount: loaded.summary.operationCount,
55
+ servers: loaded.summary.servers,
56
+ securitySchemes: loaded.summary.securitySchemes,
57
+ schemas: loaded.summary.schemas,
58
+ },
59
+ };
60
+ },
61
+ async snapshot(root, source) {
62
+ const loaded = await loadOpenApiSource(root, source);
63
+ return {
64
+ revision: loaded.hash,
65
+ capturedAt: new Date().toISOString(),
66
+ ...(loaded.etag || loaded.lastModified ? { validators: { ...(loaded.etag ? { etag: loaded.etag } : {}), ...(loaded.lastModified ? { lastModified: loaded.lastModified } : {}) } } : {}),
67
+ openapi: loaded.snapshot,
68
+ };
69
+ },
70
+ async detectChanges(previous, current) {
71
+ return { changed: previous?.revision !== current.revision, identifiers: this.evidenceIdentifiers(current) };
72
+ },
73
+ evidenceIdentifiers(snapshot) {
74
+ return snapshot.openapi ? [
75
+ ...Object.keys(snapshot.openapi.operations),
76
+ ...Object.keys(snapshot.openapi.schemas).map((item) => `schema:${item}`),
77
+ ] : [];
78
+ },
79
+ redact(source) {
80
+ if (!isSpecUrl(source.path))
81
+ return source;
82
+ const url = new URL(source.path);
83
+ url.search = url.search ? '?…' : '';
84
+ return { ...source, path: url.toString() };
85
+ },
86
+ async health(root, source) {
87
+ const loaded = await loadOpenApiSource(root, source);
88
+ const baseline = (await readSyncState(root)).sources[source.name];
89
+ const redacted = this.redact(source);
90
+ const checkedAt = new Date().toISOString();
91
+ return {
92
+ name: source.name,
93
+ connector: this.id,
94
+ status: 'healthy',
95
+ checkedAt,
96
+ lastSuccessfulAt: checkedAt,
97
+ ...(baseline?.recordedAt ? { lastMonitoringAt: baseline.recordedAt } : {}),
98
+ location: redacted.path,
99
+ provider: isSpecUrl(source.path) ? 'https' : 'file',
100
+ monitored: isSpecUrl(source.path),
101
+ revision: loaded.hash,
102
+ summary: `${loaded.summary.title} ${loaded.summary.version} · ${loaded.summary.operationCount} operations`,
103
+ details: [
104
+ `OpenAPI ${loaded.summary.specificationVersion}`,
105
+ `${loaded.summary.schemas.length} schemas`,
106
+ `${loaded.summary.securitySchemes.length} security schemes`,
107
+ `${loaded.summary.servers.length} servers`,
108
+ ],
109
+ openapi: loaded.summary,
110
+ ...(source.scope ? { scope: source.scope } : {}),
111
+ };
112
+ },
113
+ };
114
+ const directoryConnector = {
115
+ id: 'directory',
116
+ version: SOURCE_CONNECTOR_API_VERSION,
117
+ async validate(root, source) {
118
+ const path = resolve(root, source.path);
119
+ let stats;
120
+ try {
121
+ stats = await lstat(path);
122
+ }
123
+ catch {
124
+ throw new DoxloopError(`Source directory does not exist: ${path}`);
125
+ }
126
+ if (!stats.isDirectory() || stats.isSymbolicLink())
127
+ throw new DoxloopError(`Source must be a non-symlinked directory: ${path}`);
128
+ },
129
+ async inventory(root, source) {
130
+ await this.validate(root, source);
131
+ const entries = await readdir(resolve(root, source.path), { withFileTypes: true });
132
+ return { identifiers: entries.map((entry) => entry.name).sort(), metadata: { entries: entries.length } };
133
+ },
134
+ async snapshot(root, source) {
135
+ const inventory = await this.inventory(root, source);
136
+ return { revision: createHash('sha256').update(JSON.stringify(inventory)).digest('hex'), capturedAt: new Date().toISOString() };
137
+ },
138
+ async detectChanges(previous, current) { return { changed: previous?.revision !== current.revision, identifiers: [] }; },
139
+ evidenceIdentifiers() { return []; },
140
+ redact(source) {
141
+ return {
142
+ ...source,
143
+ path: basename(source.path) || source.path,
144
+ ...(source.remote ? { remote: { ...source.remote, repository: redactedRepository(source.remote.repository) } } : {}),
145
+ };
146
+ },
147
+ async health(root, source) {
148
+ const [snapshot, baseline, head] = await Promise.all([
149
+ this.snapshot(root, source),
150
+ readSyncState(root).then((state) => state.sources[source.name]),
151
+ source.remote ? remoteHead(source.remote) : Promise.resolve(undefined),
152
+ ]);
153
+ const inventory = await this.inventory(root, source);
154
+ const redacted = this.redact(source);
155
+ const checkedAt = new Date().toISOString();
156
+ return {
157
+ name: source.name,
158
+ connector: this.id,
159
+ status: 'healthy',
160
+ checkedAt,
161
+ lastSuccessfulAt: checkedAt,
162
+ ...(baseline?.recordedAt ? { lastMonitoringAt: baseline.recordedAt } : {}),
163
+ location: redacted.remote?.repository ?? redacted.path,
164
+ provider: source.remote?.provider ?? 'directory',
165
+ ...(source.remote?.branch ? { branch: source.remote.branch } : {}),
166
+ ...(source.remote?.subdirectory ? { subdirectory: source.remote.subdirectory } : {}),
167
+ monitored: Boolean(source.remote),
168
+ revision: head ?? snapshot.revision,
169
+ summary: `${inventory.metadata.entries ?? 0} top-level entries available`,
170
+ details: source.remote ? [`Monitored from ${source.remote.provider}:${redacted.remote?.repository}@${source.remote.branch}`] : ['Local read-only directory'],
171
+ ...(source.scope ? { scope: source.scope } : {}),
172
+ };
173
+ },
174
+ };
175
+ /**
176
+ * An existing documentation website, crawled into a Markdown snapshot. The
177
+ * snapshot is frozen at crawl time, so health reports what was crawled and
178
+ * change detection compares snapshot contents; a re-crawl replaces the folder.
179
+ */
180
+ const docsSiteConnector = {
181
+ id: 'docs-site',
182
+ version: SOURCE_CONNECTOR_API_VERSION,
183
+ async validate(root, source) { await readDocsSiteManifest(root, source); },
184
+ async inventory(root, source) {
185
+ const manifest = await readDocsSiteManifest(root, source);
186
+ return {
187
+ identifiers: manifest.pages.map((page) => page.file).sort(),
188
+ metadata: {
189
+ url: manifest.url,
190
+ pages: manifest.totals.pages,
191
+ words: manifest.totals.words,
192
+ crawledAt: manifest.crawledAt,
193
+ ...(manifest.generator ? { generator: manifest.generator } : {}),
194
+ },
195
+ };
196
+ },
197
+ async snapshot(root, source) {
198
+ const manifest = await readDocsSiteManifest(root, source);
199
+ return { revision: manifest.hash, capturedAt: manifest.crawledAt };
200
+ },
201
+ async detectChanges(previous, current) { return { changed: previous?.revision !== current.revision, identifiers: [] }; },
202
+ evidenceIdentifiers() { return []; },
203
+ redact(source) {
204
+ if (!source.site)
205
+ return source;
206
+ try {
207
+ const url = new URL(source.site.url);
208
+ url.search = url.search ? '?…' : '';
209
+ return { ...source, site: { ...source.site, url: url.toString() } };
210
+ }
211
+ catch {
212
+ return source;
213
+ }
214
+ },
215
+ async health(root, source) {
216
+ const manifest = await readDocsSiteManifest(root, source);
217
+ const baseline = (await readSyncState(root)).sources[source.name];
218
+ const checkedAt = new Date().toISOString();
219
+ const site = source.site ?? { url: manifest.url, crawledAt: manifest.crawledAt, pages: manifest.totals.pages, words: manifest.totals.words, hash: manifest.hash };
220
+ const stale = source.site !== undefined && source.site.hash !== manifest.hash;
221
+ return {
222
+ name: source.name,
223
+ connector: this.id,
224
+ status: stale ? 'warning' : 'healthy',
225
+ checkedAt,
226
+ lastSuccessfulAt: checkedAt,
227
+ ...(baseline?.recordedAt ? { lastMonitoringAt: baseline.recordedAt } : {}),
228
+ location: this.redact(source).site?.url ?? manifest.url,
229
+ provider: 'https',
230
+ monitored: false,
231
+ revision: manifest.hash,
232
+ summary: stale ? `Snapshot on disk differs from the recorded crawl; re-crawl the site.` : describeDocsSite(site),
233
+ details: [
234
+ `Existing documentation site: ${manifest.url}`,
235
+ `${manifest.totals.pages} pages, ${manifest.totals.words} words${manifest.generator ? `, ${manifest.generator}` : ''}`,
236
+ `Discovered through ${manifest.discovery.length > 0 ? manifest.discovery.join(', ') : 'the entry page'}`,
237
+ ...(manifest.truncated ? [`Crawl stopped at the ${manifest.pageLimit}-page limit.`] : []),
238
+ ...(manifest.brokenLinks.length > 0 ? [`${manifest.brokenLinks.length} broken internal links`] : []),
239
+ 'Frozen at crawl time; use Re-crawl to refresh.',
240
+ ],
241
+ docsSite: { ...site, brokenLinks: manifest.brokenLinks.length },
242
+ ...(source.scope ? { scope: source.scope } : {}),
243
+ };
244
+ },
245
+ };
246
+ function redactedRepository(value) {
247
+ try {
248
+ const url = new URL(value);
249
+ if (url.username)
250
+ url.username = '…';
251
+ url.password = '';
252
+ url.search = url.search ? '?…' : '';
253
+ url.hash = '';
254
+ return url.toString();
255
+ }
256
+ catch {
257
+ return value;
258
+ }
259
+ }
260
+ function safeHealthError(error, source, root) {
261
+ let value = error instanceof Error ? error.message : String(error);
262
+ for (const secret of [resolve(root, source.path), source.path, source.remote?.repository]) {
263
+ if (secret)
264
+ value = value.split(secret).join(connectorForSource(source).redact(source).remote?.repository ?? connectorForSource(source).redact(source).path);
265
+ }
266
+ return value.replace(/([?&](?:token|key|secret|password|signature)=)[^&\s]+/gi, '$1…').slice(0, 500);
267
+ }
268
+ //# sourceMappingURL=source-connectors.js.map