@supportpages.io/wtfm 0.0.0-stage → 0.3.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 (275) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +23 -0
  3. package/README.md +392 -2
  4. package/dist/actions.d.ts +98 -0
  5. package/dist/actions.js +86 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/agent-settings.d.ts +22 -0
  8. package/dist/agent-settings.js +35 -0
  9. package/dist/agent-settings.js.map +1 -0
  10. package/dist/api.d.ts +14 -0
  11. package/dist/api.js +159 -0
  12. package/dist/api.js.map +1 -0
  13. package/dist/article-link.d.ts +16 -0
  14. package/dist/article-link.js +25 -0
  15. package/dist/article-link.js.map +1 -0
  16. package/dist/artifacts.d.ts +45 -0
  17. package/dist/artifacts.js +144 -0
  18. package/dist/artifacts.js.map +1 -0
  19. package/dist/brand.d.ts +4 -0
  20. package/dist/brand.js +9 -0
  21. package/dist/brand.js.map +1 -0
  22. package/dist/bridge.d.ts +2964 -0
  23. package/dist/bridge.js +1036 -0
  24. package/dist/bridge.js.map +1 -0
  25. package/dist/capacity.d.ts +5 -0
  26. package/dist/capacity.js +11 -0
  27. package/dist/capacity.js.map +1 -0
  28. package/dist/credentials.d.ts +13 -0
  29. package/dist/credentials.js +88 -0
  30. package/dist/credentials.js.map +1 -0
  31. package/dist/development-tls.d.ts +8 -0
  32. package/dist/development-tls.js +38 -0
  33. package/dist/development-tls.js.map +1 -0
  34. package/dist/errors.d.ts +15 -0
  35. package/dist/errors.js +18 -0
  36. package/dist/errors.js.map +1 -0
  37. package/dist/export.d.ts +11 -0
  38. package/dist/export.js +91 -0
  39. package/dist/export.js.map +1 -0
  40. package/dist/hosted-operations.d.ts +108 -0
  41. package/dist/hosted-operations.js +120 -0
  42. package/dist/hosted-operations.js.map +1 -0
  43. package/dist/hosting-benefits.d.ts +66 -0
  44. package/dist/hosting-benefits.js +67 -0
  45. package/dist/hosting-benefits.js.map +1 -0
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +49 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/local-inventory.d.ts +22 -0
  50. package/dist/local-inventory.js +59 -0
  51. package/dist/local-inventory.js.map +1 -0
  52. package/dist/local-setup.d.ts +208 -0
  53. package/dist/local-setup.js +140 -0
  54. package/dist/local-setup.js.map +1 -0
  55. package/dist/pairing.d.ts +71 -0
  56. package/dist/pairing.js +235 -0
  57. package/dist/pairing.js.map +1 -0
  58. package/dist/preferences.d.ts +18 -0
  59. package/dist/preferences.js +44 -0
  60. package/dist/preferences.js.map +1 -0
  61. package/dist/progress.d.ts +205 -0
  62. package/dist/progress.js +224 -0
  63. package/dist/progress.js.map +1 -0
  64. package/dist/reminders.d.ts +31 -0
  65. package/dist/reminders.js +73 -0
  66. package/dist/reminders.js.map +1 -0
  67. package/dist/replace-connection.d.ts +6 -0
  68. package/dist/replace-connection.js +77 -0
  69. package/dist/replace-connection.js.map +1 -0
  70. package/dist/repository-actions.d.ts +18 -0
  71. package/dist/repository-actions.js +9 -0
  72. package/dist/repository-actions.js.map +1 -0
  73. package/dist/repository-benefits.d.ts +96 -0
  74. package/dist/repository-benefits.js +64 -0
  75. package/dist/repository-benefits.js.map +1 -0
  76. package/dist/run-update.d.ts +17 -0
  77. package/dist/run-update.js +27 -0
  78. package/dist/run-update.js.map +1 -0
  79. package/dist/runs.d.ts +541 -0
  80. package/dist/runs.js +146 -0
  81. package/dist/runs.js.map +1 -0
  82. package/dist/runtime.d.ts +12 -0
  83. package/dist/runtime.js +58 -0
  84. package/dist/runtime.js.map +1 -0
  85. package/dist/schema.d.ts +277 -0
  86. package/dist/schema.js +66 -0
  87. package/dist/schema.js.map +1 -0
  88. package/dist/server.d.ts +4 -0
  89. package/dist/server.js +298 -0
  90. package/dist/server.js.map +1 -0
  91. package/dist/session.d.ts +1714 -0
  92. package/dist/session.js +619 -0
  93. package/dist/session.js.map +1 -0
  94. package/dist/settings.d.ts +9 -0
  95. package/dist/settings.js +21 -0
  96. package/dist/settings.js.map +1 -0
  97. package/dist/sync.d.ts +495 -0
  98. package/dist/sync.js +191 -0
  99. package/dist/sync.js.map +1 -0
  100. package/dist/telemetry-scrub.d.ts +13 -0
  101. package/dist/telemetry-scrub.js +51 -0
  102. package/dist/telemetry-scrub.js.map +1 -0
  103. package/dist/telemetry.d.ts +73 -0
  104. package/dist/telemetry.js +173 -0
  105. package/dist/telemetry.js.map +1 -0
  106. package/dist/walkthroughs.d.ts +224 -0
  107. package/dist/walkthroughs.js +109 -0
  108. package/dist/walkthroughs.js.map +1 -0
  109. package/dist/workspace.d.ts +14 -0
  110. package/dist/workspace.js +127 -0
  111. package/dist/workspace.js.map +1 -0
  112. package/dist/writer-agent.d.ts +21 -0
  113. package/dist/writer-agent.js +27 -0
  114. package/dist/writer-agent.js.map +1 -0
  115. package/dist/writer-entry.d.ts +171 -0
  116. package/dist/writer-entry.js +233 -0
  117. package/dist/writer-entry.js.map +1 -0
  118. package/dist/writing-style.d.ts +7 -0
  119. package/dist/writing-style.js +52 -0
  120. package/dist/writing-style.js.map +1 -0
  121. package/engine/SYNC.json +4 -0
  122. package/engine/VERSION +1 -0
  123. package/engine/detect-project/README.md +141 -0
  124. package/engine/detect-project/SKILL.md +1421 -0
  125. package/engine/detect-project/assets/desktop/desktop-frame.css +428 -0
  126. package/engine/detect-project/assets/game/game-frame.css +132 -0
  127. package/engine/detect-project/assets/macosui/LICENSE-puppertino.txt +21 -0
  128. package/engine/detect-project/assets/macosui/VERSIONS.txt +1 -0
  129. package/engine/detect-project/assets/macosui/fonts.css +15 -0
  130. package/engine/detect-project/assets/macosui/macos-frame.css +481 -0
  131. package/engine/detect-project/assets/macosui/puppertino.css +2153 -0
  132. package/engine/detect-project/assets/mobileui/LICENSE-fonts.txt +13 -0
  133. package/engine/detect-project/assets/mobileui/LICENSE-framework7.txt +52 -0
  134. package/engine/detect-project/assets/mobileui/VERSIONS.txt +6 -0
  135. package/engine/detect-project/assets/mobileui/device-frame.css +316 -0
  136. package/engine/detect-project/assets/mobileui/f7-color-theme.mjs +1345 -0
  137. package/engine/detect-project/assets/mobileui/f7-icons-names.json +1254 -0
  138. package/engine/detect-project/assets/mobileui/fonts.css +16 -0
  139. package/engine/detect-project/assets/mobileui/framework7-components.css +39 -0
  140. package/engine/detect-project/assets/mobileui/framework7-core.css +5245 -0
  141. package/engine/detect-project/assets/mobileui/icons.css +31 -0
  142. package/engine/detect-project/assets/mobileui/md3-defaults.css +89 -0
  143. package/engine/detect-project/assets/mobileui/platforms.json +46 -0
  144. package/engine/detect-project/assets/tailwind-fallback.css +1729 -0
  145. package/engine/detect-project/assets/webtui/LICENSE-webtui.txt +28 -0
  146. package/engine/detect-project/assets/webtui/VERSIONS.txt +7 -0
  147. package/engine/detect-project/assets/webtui/terminal-frame.css +195 -0
  148. package/engine/detect-project/assets/webtui/theme-catppuccin.css +1 -0
  149. package/engine/detect-project/assets/webtui/theme-everforest.css +1 -0
  150. package/engine/detect-project/assets/webtui/theme-gruvbox.css +1 -0
  151. package/engine/detect-project/assets/webtui/theme-nord.css +1 -0
  152. package/engine/detect-project/assets/webtui/theme-vitesse.css +1 -0
  153. package/engine/detect-project/assets/webtui/themes.json +37 -0
  154. package/engine/detect-project/assets/webtui/webtui-core.css +1 -0
  155. package/engine/detect-project/assets/win32ui/7css.css +2 -0
  156. package/engine/detect-project/assets/win32ui/LICENSE-7css.txt +21 -0
  157. package/engine/detect-project/assets/win32ui/VERSIONS.txt +1 -0
  158. package/engine/detect-project/assets/win32ui/win32-frame.css +278 -0
  159. package/engine/detect-project/package-lock.json +12 -0
  160. package/engine/detect-project/package.json +10 -0
  161. package/engine/detect-project/scripts/apply_runtime_profiles.js +313 -0
  162. package/engine/detect-project/scripts/check_css_health.js +412 -0
  163. package/engine/detect-project/scripts/check_project_map.js +150 -0
  164. package/engine/detect-project/scripts/check_runtime_coverage.js +311 -0
  165. package/engine/detect-project/scripts/check_runtime_recipe_quality.js +184 -0
  166. package/engine/detect-project/scripts/classify_app_type.sh +246 -0
  167. package/engine/detect-project/scripts/classify_surface.sh +95 -0
  168. package/engine/detect-project/scripts/classify_workspace.js +39 -0
  169. package/engine/detect-project/scripts/compile_css.sh +447 -0
  170. package/engine/detect-project/scripts/detect_static.js +645 -0
  171. package/engine/detect-project/scripts/detect_structure.js +187 -0
  172. package/engine/detect-project/scripts/include_census.js +451 -0
  173. package/engine/detect-project/scripts/json_get.js +142 -0
  174. package/engine/detect-project/scripts/merge_json.js +52 -0
  175. package/engine/detect-project/scripts/recommend_model_tier.js +252 -0
  176. package/engine/detect-project/scripts/resolve_route_chains.js +135 -0
  177. package/engine/detect-project/scripts/run_css_build.sh +40 -0
  178. package/engine/detect-project/scripts/sanitize_css.js +83 -0
  179. package/engine/detect-project/scripts/test_classify_surface.js +108 -0
  180. package/engine/detect-project/scripts/test_node_helpers.js +142 -0
  181. package/engine/detect-project/scripts/test_recommend_model_tier.js +119 -0
  182. package/engine/detect-project/scripts/test_resolve_route_chains.js +182 -0
  183. package/engine/detect-project/scripts/test_runtime_coverage.js +323 -0
  184. package/engine/detect-project/scripts/test_runtime_recipe_quality.js +187 -0
  185. package/engine/detect-project/scripts/theme_overrides.js +169 -0
  186. package/engine/detect-project/scripts/write_branding.js +129 -0
  187. package/engine/generate-illustrated-article/SKILL.md +461 -0
  188. package/engine/generate-illustrated-article/contracts/desktop.md +90 -0
  189. package/engine/generate-illustrated-article/contracts/game.md +38 -0
  190. package/engine/generate-illustrated-article/contracts/label-evidence.md +38 -0
  191. package/engine/generate-illustrated-article/contracts/macos.md +79 -0
  192. package/engine/generate-illustrated-article/contracts/mobile.md +40 -0
  193. package/engine/generate-illustrated-article/contracts/terminal.md +29 -0
  194. package/engine/generate-illustrated-article/contracts/win32.md +43 -0
  195. package/engine/generate-illustrated-article/package-lock.json +366 -0
  196. package/engine/generate-illustrated-article/package.json +15 -0
  197. package/engine/generate-illustrated-article/scripts/article_blocks.js +34 -0
  198. package/engine/generate-illustrated-article/scripts/check_article_json.js +105 -0
  199. package/engine/generate-illustrated-article/scripts/emit_walkthrough_signals.js +100 -0
  200. package/engine/generate-illustrated-article/scripts/extract_images.js +187 -0
  201. package/engine/generate-illustrated-article/scripts/generate_content_images.js +376 -0
  202. package/engine/generate-illustrated-article/scripts/include_census.js +451 -0
  203. package/engine/generate-illustrated-article/scripts/inject_assets.js +1691 -0
  204. package/engine/generate-illustrated-article/scripts/jit_mockup_css.js +191 -0
  205. package/engine/generate-illustrated-article/scripts/label_evidence.js +87 -0
  206. package/engine/generate-illustrated-article/scripts/lint_article_copy.js +252 -0
  207. package/engine/generate-illustrated-article/scripts/lint_mockup_fidelity.js +2403 -0
  208. package/engine/generate-illustrated-article/scripts/polish_tickets.js +1148 -0
  209. package/engine/generate-illustrated-article/scripts/related_repos.sh +52 -0
  210. package/engine/generate-illustrated-article/scripts/render_all.js +177 -0
  211. package/engine/generate-illustrated-article/scripts/render_mockup.js +665 -0
  212. package/engine/generate-illustrated-article/scripts/render_ready.js +164 -0
  213. package/engine/generate-illustrated-article/scripts/resolve_workspace.sh +86 -0
  214. package/engine/generate-illustrated-article/scripts/runtime_region_geometry.js +55 -0
  215. package/engine/generate-illustrated-article/scripts/source_paths.js +49 -0
  216. package/engine/generate-illustrated-article/scripts/test_control_visibility.js +30 -0
  217. package/engine/generate-illustrated-article/scripts/test_emit_walkthrough_signals.js +157 -0
  218. package/engine/generate-illustrated-article/scripts/test_generate_content_images.js +168 -0
  219. package/engine/generate-illustrated-article/scripts/test_include_census.js +126 -0
  220. package/engine/generate-illustrated-article/scripts/test_label_evidence.js +49 -0
  221. package/engine/generate-illustrated-article/scripts/test_lint_article_copy.js +138 -0
  222. package/engine/generate-illustrated-article/scripts/test_lint_mockup_fidelity.js +653 -0
  223. package/engine/generate-illustrated-article/scripts/test_node_ports.js +152 -0
  224. package/engine/generate-illustrated-article/scripts/test_polish_tickets.js +481 -0
  225. package/engine/generate-illustrated-article/scripts/test_related_repos.js +64 -0
  226. package/engine/generate-illustrated-article/scripts/test_render_ready.js +85 -0
  227. package/engine/generate-illustrated-article/scripts/test_source_paths.js +68 -0
  228. package/engine/generate-illustrated-article/scripts/trace_hook.js +79 -0
  229. package/engine/generate-illustrated-article/scripts/trace_hook.sh +4 -0
  230. package/engine/generate-illustrated-article/scripts/validate_html.js +114 -0
  231. package/engine/generate-illustrated-article/scripts/watermark.js +69 -0
  232. package/install.sh +14 -0
  233. package/package.json +57 -4
  234. package/scripts/auth.mjs +33 -0
  235. package/scripts/auto-update.mjs +7 -0
  236. package/scripts/build-plugin.mjs +58 -0
  237. package/scripts/build-release.mjs +94 -0
  238. package/scripts/check-release.mjs +40 -0
  239. package/scripts/check-runtime.mjs +10 -0
  240. package/scripts/cli.mjs +8 -0
  241. package/scripts/install.mjs +64 -0
  242. package/scripts/lib/agent-runner.mjs +181 -0
  243. package/scripts/lib/agent-settings.mjs +215 -0
  244. package/scripts/lib/article-skills.mjs +82 -0
  245. package/scripts/lib/auto-update.mjs +50 -0
  246. package/scripts/lib/brand.mjs +9 -0
  247. package/scripts/lib/browser.mjs +12 -0
  248. package/scripts/lib/claude-connection.mjs +33 -0
  249. package/scripts/lib/claude-permissions.mjs +47 -0
  250. package/scripts/lib/claude-plugin.mjs +15 -0
  251. package/scripts/lib/claude-writer.mjs +10 -0
  252. package/scripts/lib/cli-main.mjs +85 -0
  253. package/scripts/lib/cli.mjs +888 -0
  254. package/scripts/lib/codex-config.mjs +51 -0
  255. package/scripts/lib/codex-integration.mjs +61 -0
  256. package/scripts/lib/codex-skill.mjs +34 -0
  257. package/scripts/lib/harness-models.mjs +109 -0
  258. package/scripts/lib/install.mjs +269 -0
  259. package/scripts/lib/managed-writer.mjs +38 -0
  260. package/scripts/lib/planning.mjs +263 -0
  261. package/scripts/lib/prepare-update.mjs +85 -0
  262. package/scripts/lib/refresh-writers.mjs +12 -0
  263. package/scripts/lib/remove.mjs +167 -0
  264. package/scripts/lib/renderer.mjs +34 -0
  265. package/scripts/lib/terminal.mjs +252 -0
  266. package/scripts/lib/uninit.mjs +72 -0
  267. package/scripts/lib/update.mjs +55 -0
  268. package/scripts/lib/writer-recovery.mjs +35 -0
  269. package/scripts/lib/yolo.mjs +362 -0
  270. package/scripts/plugin-session.mjs +65 -0
  271. package/scripts/prepare-update.mjs +12 -0
  272. package/scripts/publish-release.mjs +92 -0
  273. package/scripts/skills/supportpages/SKILL.md +120 -0
  274. package/scripts/smoke-release.mjs +101 -0
  275. package/server.json +27 -0
@@ -0,0 +1,224 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { z } from 'zod';
3
+ import { articleSchema, manifestSchema, remoteArticleSchema } from './schema.js';
4
+ import { createManifest, parse, sha256, progressSnapshot } from './artifacts.js';
5
+ import { exportArticle } from './export.js';
6
+ import path from 'node:path';
7
+ import { fail, publicError } from './errors.js';
8
+ export const progressResponse = z.object({ run_id: z.uuid(), attempt: z.uuid(), state: z.enum(['running', 'failed', 'interrupted', 'cancelled', 'complete', 'kept', 'discarded']), sequence: z.number().int().nonnegative(), article: remoteArticleSchema });
9
+ export async function openPreview(url) {
10
+ const env = { ...process.env };
11
+ delete env.SUPPORTPAGES_API_TOKEN;
12
+ delete env.SUPPORTPAGES_API_TOKEN_FILE;
13
+ if (!['darwin', 'linux'].includes(process.platform))
14
+ return false;
15
+ return new Promise(resolve => execFile(process.platform === 'darwin' ? 'open' : 'xdg-open', [url], { env, timeout: 5000 }, error => resolve(!error)));
16
+ }
17
+ /** Session-owned relay. The host writer only writes artifacts, never credentials. */
18
+ export class ProgressRelay {
19
+ bridge;
20
+ open;
21
+ timer;
22
+ running = false;
23
+ failures = 0;
24
+ generation = 0;
25
+ constructor(bridge, open = openPreview) {
26
+ this.bridge = bridge;
27
+ this.open = open;
28
+ }
29
+ stop() { this.running = false; this.generation++; clearTimeout(this.timer); }
30
+ start(id) {
31
+ this.stop();
32
+ this.running = true;
33
+ this.failures = 0;
34
+ this.schedule(id, 0);
35
+ }
36
+ schedule(id, delay) {
37
+ if (!this.running)
38
+ return;
39
+ const generation = this.generation;
40
+ this.timer = setTimeout(async () => {
41
+ try {
42
+ await this.tick(id, true, generation);
43
+ if (generation === this.generation)
44
+ this.failures = 0;
45
+ }
46
+ catch (error) {
47
+ if (generation !== this.generation)
48
+ return;
49
+ this.failures++;
50
+ const safe = publicError(error);
51
+ await this.bridge.lock(async () => {
52
+ if (generation !== this.generation)
53
+ return;
54
+ const run = await this.bridge.runs.read(id);
55
+ if (run.progress)
56
+ await this.bridge.runs.save({ ...run, progress: { ...run.progress, sync_error: safe.message, sync_failures: (run.progress.sync_failures ?? 0) + 1 } });
57
+ }).catch(() => { });
58
+ if (generation === this.generation && !['network_error', 'remote_error', 'rate_limited', 'workspace_busy'].includes(safe.code))
59
+ this.stop();
60
+ }
61
+ if (generation !== this.generation)
62
+ return;
63
+ this.schedule(id, this.failures ? Math.min(60_000, 2000 * 2 ** this.failures) : 2000);
64
+ }, delay);
65
+ this.timer.unref();
66
+ }
67
+ async ensureStartedUnlocked(id) {
68
+ let run = await this.bridge.runs.read(id);
69
+ if (!run.progress || run.progress.article)
70
+ return run;
71
+ let result;
72
+ try {
73
+ result = parse(progressResponse, await this.bridge.api.request('POST', `/projects/${run.project_id}/generation_runs`, {
74
+ run_id: run.id, local_article_id: run.local_article_id, attempt: run.progress.attempt,
75
+ title: run.title, article_type: run.article_type ?? 'how-to', section_id: run.section_id,
76
+ }), 'invalid_response');
77
+ }
78
+ catch (error) {
79
+ await this.bridge.runs.save({ ...run, progress: { ...run.progress, sync_error: publicError(error).message } });
80
+ throw error;
81
+ }
82
+ run = { ...run, progress: { ...run.progress, article: result.article, last_heartbeat: Date.now(), sync_error: undefined } };
83
+ await this.bridge.runs.save(run);
84
+ if (result.state !== 'running')
85
+ fail('run_closed', 'This preview is closed. Use retry_article after stopping the writer.');
86
+ return run;
87
+ }
88
+ async tick(id, scheduled = false, generation = this.generation) {
89
+ if (scheduled) {
90
+ if (!this.running || generation !== this.generation)
91
+ return;
92
+ const observed = await this.bridge.runs.read(id);
93
+ // Shutdown may finish while this read is in flight. Do not enqueue a new
94
+ // workspace operation after close() has drained the existing queue.
95
+ if (!this.running || generation !== this.generation)
96
+ return;
97
+ // Watching for an explicit finish must not compete with unrelated
98
+ // workspace operations while no preview work or finish request exists.
99
+ // Recheck every condition under the lock when there is work to perform.
100
+ if (!observed.writer_completed_at && ((!observed.progress && observed.project_id) || observed.phase === 'prepared'))
101
+ return;
102
+ }
103
+ const complete = await this.bridge.lock(async () => {
104
+ if (scheduled && (!this.running || generation !== this.generation))
105
+ return;
106
+ let run = await this.bridge.runs.read(id);
107
+ if (run.writer_completed_at && !run.remote && !run.error && run.phase !== 'saved' && ['prepared', 'finalized'].includes(run.status))
108
+ return true;
109
+ if (run.status !== 'prepared') {
110
+ this.stop();
111
+ return;
112
+ }
113
+ // Preparation arms observation; only actual writer entry (or the host's
114
+ // explicit started event) enables heartbeats and preview uploads.
115
+ if (run.phase === 'prepared')
116
+ return;
117
+ if (run.phase !== 'writing') {
118
+ this.stop();
119
+ return;
120
+ }
121
+ if (!run.project_id) {
122
+ await this.exportLocalUnlocked(run);
123
+ return;
124
+ }
125
+ if (!run.progress)
126
+ return;
127
+ run = await this.ensureStartedUnlocked(id);
128
+ let progress = run.progress;
129
+ const route = `/projects/${run.project_id}/generation_runs/${run.id}`;
130
+ if (Date.now() - (progress.last_heartbeat ?? 0) >= 30_000) {
131
+ const response = parse(progressResponse, await this.bridge.api.request('POST', `${route}/event`, { event: 'heartbeat', attempt: progress.attempt }), 'invalid_response');
132
+ progress = { ...progress, article: response.article, last_heartbeat: Date.now(), sync_error: undefined };
133
+ run = { ...run, progress };
134
+ await this.bridge.runs.save(run);
135
+ }
136
+ if (progress.pending) {
137
+ await this.sendPendingUnlocked(id);
138
+ return;
139
+ }
140
+ const snap = await progressSnapshot(this.bridge.ws, run.artifact_dir, { imageHashes: progress.image_hashes });
141
+ if (!snap)
142
+ return;
143
+ const articleHash = sha256(snap.articleBytes);
144
+ if (articleHash === progress.article_hash && !snap.images.length)
145
+ return;
146
+ const manifest = createManifest(snap, { local_article_id: run.local_article_id, run_id: run.id, skills_version: run.skills_version, source_commit: null, source_dirty: true, section_id: run.section_id });
147
+ // Persist the exact request before sending: a lost response must retry the
148
+ // same sequence and bytes, even when the writer has produced newer files.
149
+ const pending = progress.pending ?? { sequence: progress.sequence + 1, directory: `${this.bridge.stateRoot}/runs/${run.id}/progress-pending` };
150
+ if (!progress.pending) {
151
+ await this.bridge.ws.writeJson(`${pending.directory}/manifest.json`, manifest);
152
+ await this.bridge.ws.write(`${pending.directory}/article.json`, snap.articleBytes);
153
+ for (const image of snap.images)
154
+ await this.bridge.ws.write(`${pending.directory}/${image.filename}`, image.bytes);
155
+ progress = { ...progress, pending };
156
+ await this.bridge.runs.save({ ...run, progress });
157
+ }
158
+ await this.sendPendingUnlocked(id);
159
+ });
160
+ // Reuse final validation/import outside the operation lock. Only an explicit
161
+ // successful writer finish can reach here; files or silence alone never do.
162
+ // Delivery copy is left for the parent's complete_article call, so the relay
163
+ // does not consume the one-time invitation or publication question.
164
+ if (complete && (!scheduled || (this.running && generation === this.generation))) {
165
+ await this.bridge.complete({ run_id: id, completed: true }, { deliver: false });
166
+ }
167
+ }
168
+ async exportLocalUnlocked(run) {
169
+ if (await this.bridge.destination() !== 'local')
170
+ fail('destination_mismatch', 'This local writer no longer belongs to the current destination.');
171
+ const local = await this.bridge.local();
172
+ if (!local)
173
+ fail('local_required', 'Local article settings are missing.');
174
+ const directory = run.local_export?.directory ?? local.export_dir;
175
+ const slug = run.artifact_dir.split('/').at(-1);
176
+ try {
177
+ const snap = await progressSnapshot(this.bridge.ws, run.artifact_dir, {
178
+ fallbackDir: path.posix.join(directory, slug),
179
+ });
180
+ if (!snap)
181
+ return;
182
+ const exported = await exportArticle(this.bridge.ws, snap, run.artifact_dir, directory, slug);
183
+ if (run.local_export?.markdown_path !== exported.markdown_path || run.local_export?.sync_error) {
184
+ await this.bridge.runs.save({ ...run, local_export: { directory, markdown_path: exported.markdown_path } });
185
+ }
186
+ }
187
+ catch (error) {
188
+ // Keep writing and retry the export on the next observation. Preview
189
+ // delivery errors do not mean that generation itself has failed.
190
+ await this.bridge.runs.save({ ...run, local_export: { ...run.local_export, directory, sync_error: publicError(error).message } });
191
+ }
192
+ }
193
+ async sendPendingUnlocked(id) {
194
+ const run = await this.bridge.runs.read(id);
195
+ const progress = run.progress;
196
+ if (!progress.pending)
197
+ return;
198
+ const { directory, sequence } = progress.pending;
199
+ const manifest = parse(manifestSchema, await this.bridge.ws.json(`${directory}/manifest.json`));
200
+ const form = new FormData();
201
+ form.set('attempt', progress.attempt);
202
+ form.set('sequence', String(sequence));
203
+ form.set('manifest', JSON.stringify(manifest));
204
+ form.set('article', new Blob([new Uint8Array(await this.bridge.ws.read(`${directory}/article.json`))]), 'article.json');
205
+ for (const image of manifest.images)
206
+ form.set(`images[${image.block_id}]`, new Blob([new Uint8Array(await this.bridge.ws.read(`${directory}/${image.filename}`, 10 * 1024 * 1024))], { type: 'image/png' }), image.filename);
207
+ const response = parse(progressResponse, await this.bridge.api.request('POST', `/projects/${run.project_id}/generation_runs/${id}/milestones`, form, manifest.bundle_hash), 'invalid_response');
208
+ const article = parse(articleSchema, JSON.parse((await this.bridge.ws.read(`${directory}/article.json`)).toString()));
209
+ const ids = new Set(article.blocks.filter(b => b.type === 'section' && b.has_image).map(b => b.id));
210
+ const hashes = Object.fromEntries(Object.entries(progress.image_hashes ?? {}).filter(([key]) => ids.has(key)));
211
+ for (const image of manifest.images)
212
+ hashes[image.block_id] = image.sha256;
213
+ const shouldOpen = run.open_when_ready && !progress.open_attempted;
214
+ await this.bridge.runs.save({ ...run, progress: { ...progress, article: response.article, sequence: response.sequence,
215
+ pending: undefined, article_hash: manifest.article_sha256, image_hashes: hashes, last_heartbeat: Date.now(), sync_error: undefined,
216
+ open_attempted: progress.open_attempted || Boolean(shouldOpen) } });
217
+ if (shouldOpen) {
218
+ const opened = await this.open(response.article.editor_url).catch(() => false);
219
+ const current = await this.bridge.runs.read(id);
220
+ await this.bridge.runs.save({ ...current, progress: { ...current.progress, browser_opened: opened } });
221
+ }
222
+ }
223
+ }
224
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ export declare const defaultConfigDir: () => string;
3
+ export type Benefit = {
4
+ readonly id: string;
5
+ readonly category: string;
6
+ readonly message: string;
7
+ };
8
+ export type ReminderState<Invitation> = {
9
+ version: 1;
10
+ enabled: boolean;
11
+ count: number;
12
+ articles: Record<string, Invitation | null>;
13
+ history: string[];
14
+ last_category?: string;
15
+ };
16
+ export declare function reminderStateSchema<Invitation>(invitation: z.ZodType<Invitation>): z.ZodType<ReminderState<Invitation>>;
17
+ /** Device-local reminder journal: one private state file per scope, serialized
18
+ * across processes and checkouts. Shared by the repository and hosting reminders. */
19
+ export declare class ReminderJournal<Invitation> {
20
+ private directory;
21
+ private schema;
22
+ constructor(directory: string, schema: z.ZodType<ReminderState<Invitation>>);
23
+ transaction<T>(scope: unknown[], operation: (state: ReminderState<Invitation>) => T): Promise<T>;
24
+ }
25
+ /** Claim one delivery per article before returning: a replay cannot emit twice,
26
+ * even if a process exits before saving its workspace journal or delivering the
27
+ * response to the host. Returns false when the article was already counted. */
28
+ export declare function claimDelivery<Invitation>(state: ReminderState<Invitation>, articleId: string): boolean;
29
+ /** Rotate through a catalogue: unused copy first, preferring a different category
30
+ * from the previous invitation; a new cycle starts only once everything eligible was used. */
31
+ export declare function selectBenefit<B extends Benefit>(state: ReminderState<unknown>, eligible: readonly B[]): B;
@@ -0,0 +1,73 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { setTimeout as delay } from 'node:timers/promises';
6
+ import { z } from 'zod';
7
+ import { Workspace } from './workspace.js';
8
+ export const defaultConfigDir = () => path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'supportpages');
9
+ export function reminderStateSchema(invitation) {
10
+ return z.object({
11
+ version: z.literal(1), enabled: z.boolean(), count: z.number().int().nonnegative(),
12
+ articles: z.record(z.string(), invitation.nullable()),
13
+ history: z.array(z.string()), last_category: z.string().optional(),
14
+ });
15
+ }
16
+ const initial = () => ({ version: 1, enabled: true, count: 0, articles: {}, history: [] });
17
+ /** Device-local reminder journal: one private state file per scope, serialized
18
+ * across processes and checkouts. Shared by the repository and hosting reminders. */
19
+ export class ReminderJournal {
20
+ directory;
21
+ schema;
22
+ constructor(directory, schema) {
23
+ this.directory = directory;
24
+ this.schema = schema;
25
+ }
26
+ async transaction(scope, operation) {
27
+ const key = createHash('sha256').update(JSON.stringify(scope)).digest('hex');
28
+ const root = path.join(this.directory, key);
29
+ await mkdir(root, { recursive: true, mode: 0o700 });
30
+ const ws = await Workspace.create(root);
31
+ // Separate processes/checkouts can complete together. Retry lock contention,
32
+ // but never remove a lock owned by another process (including after a crash).
33
+ for (let attempt = 0;; attempt++) {
34
+ try {
35
+ return await ws.lock(async () => {
36
+ const state = await ws.exists('state.json') ? this.schema.parse(await ws.json('state.json')) : initial();
37
+ const result = operation(state);
38
+ await ws.writeJson('state.json', state);
39
+ return result;
40
+ });
41
+ }
42
+ catch (error) {
43
+ if (error.code !== 'workspace_busy' || attempt >= 100)
44
+ throw error;
45
+ await delay(20);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ /** Claim one delivery per article before returning: a replay cannot emit twice,
51
+ * even if a process exits before saving its workspace journal or delivering the
52
+ * response to the host. Returns false when the article was already counted. */
53
+ export function claimDelivery(state, articleId) {
54
+ if (Object.hasOwn(state.articles, articleId))
55
+ return false;
56
+ state.articles[articleId] = null;
57
+ state.count++;
58
+ return true;
59
+ }
60
+ /** Rotate through a catalogue: unused copy first, preferring a different category
61
+ * from the previous invitation; a new cycle starts only once everything eligible was used. */
62
+ export function selectBenefit(state, eligible) {
63
+ let unused = eligible.filter(benefit => !state.history.includes(benefit.id));
64
+ if (!unused.length) {
65
+ state.history = [];
66
+ unused = [...eligible];
67
+ }
68
+ const benefit = unused.find(benefit => benefit.category !== state.last_category) ?? unused[0];
69
+ state.history.push(benefit.id);
70
+ state.last_category = benefit.category;
71
+ return benefit;
72
+ }
73
+ //# sourceMappingURL=reminders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reminders.js","sourceRoot":"","sources":["../src/reminders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;AASnI,MAAM,UAAU,mBAAmB,CAAa,UAAiC;IAC/E,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAClF,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;QACrD,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACnE,CAAoD,CAAC;AACxD,CAAC;AACD,MAAM,OAAO,GAAG,GAA0C,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AAElI;qFACqF;AACrF,MAAM,OAAO,eAAe;IACN;IAA2B;IAA/C,YAAoB,SAAiB,EAAU,MAA4C;QAAvE,cAAS,GAAT,SAAS,CAAQ;QAAU,WAAM,GAAN,MAAM,CAAsC;IAAG,CAAC;IAC/F,KAAK,CAAC,WAAW,CAAI,KAAgB,EAAE,SAAkD;QACvF,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACpD,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxC,6EAA6E;QAC7E,8EAA8E;QAC9E,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;oBAC9B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAc,CAAC;oBACrH,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;oBAChC,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;oBACxC,OAAO,MAAM,CAAC;gBAChB,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA2B,CAAC,IAAI,KAAK,gBAAgB,IAAI,OAAO,IAAI,GAAG;oBAAE,MAAM,KAAK,CAAC;gBAC1F,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED;;+EAE+E;AAC/E,MAAM,UAAU,aAAa,CAAa,KAAgC,EAAE,SAAiB;IAC3F,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3D,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACjC,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,IAAI,CAAC;AACd,CAAC;AAED;8FAC8F;AAC9F,MAAM,UAAU,aAAa,CAAoB,KAA6B,EAAE,QAAsB;IACpG,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;QAAC,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;IAAC,CAAC;IACnE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,KAAK,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,CAAC,CAAE,CAAC;IAC/F,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/B,KAAK,CAAC,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;IACvC,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Bridge } from './bridge.js';
2
+ /** CLI-only, after the user confirms moving this folder to another help centre. The device credential is untouched. */
3
+ export declare function replaceConnection(bridge: Bridge, expectedProjectId: string, projectId: string): Promise<{
4
+ archive: string;
5
+ restored_history: boolean;
6
+ }>;
@@ -0,0 +1,77 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, rename, rm, stat } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { parse } from './artifacts.js';
5
+ import { contextSchema, remoteId, bindingSchema } from './schema.js';
6
+ import { fail } from './errors.js';
7
+ import { CLI_NAME } from './brand.js';
8
+ /** CLI-only, after the user confirms moving this folder to another help centre. The device credential is untouched. */
9
+ export async function replaceConnection(bridge, expectedProjectId, projectId) {
10
+ parse(remoteId, projectId);
11
+ parse(remoteId, expectedProjectId);
12
+ const context = parse(contextSchema, await bridge.api.request('GET', `/projects/${projectId}/context`), 'invalid_response');
13
+ if (context.project.id !== projectId)
14
+ fail('invalid_response', 'Project identity mismatch.');
15
+ return bridge.lock(async () => {
16
+ const previous = await bridge.binding();
17
+ if (previous.project_id !== expectedProjectId)
18
+ fail('destination_mismatch', `The workspace connection changed during setup. Run ${CLI_NAME} init again to review it.`);
19
+ await bridge.runs.assertAvailable();
20
+ if ((await bridge.setup.progress())?.status === 'running')
21
+ fail('workspace_busy', 'Finish or stop project analysis before changing the connected help centre.');
22
+ const root = bridge.stateRoot;
23
+ const archive = `${root}/archives/project-${expectedProjectId}-${randomUUID()}`;
24
+ const candidates = [];
25
+ for (const entry of await bridge.ws.list(`${root}/archives`)) {
26
+ if (!entry.isDirectory() || !entry.name.startsWith(`project-${projectId}-`))
27
+ continue;
28
+ const saved = `${root}/archives/${entry.name}`;
29
+ if (!await bridge.ws.exists(`${saved}/binding.json`))
30
+ continue;
31
+ const identity = bindingSchema.safeParse(await bridge.ws.json(`${saved}/binding.json`));
32
+ if (identity.success && identity.data.project_id === projectId && identity.data.api_origin === bridge.api.origin) {
33
+ candidates.push({ root: saved, time: (await stat(await bridge.ws.resolve(saved))).mtimeMs });
34
+ }
35
+ }
36
+ const restore = candidates.sort((a, b) => b.time - a.time)[0]?.root;
37
+ // Detection and agent settings belong to the checkout. Its task directories
38
+ // contain the summary/overview referenced by the accepted analysis receipt.
39
+ // Working files already have project-specific paths; keep those paths stable
40
+ // so archived runs can be restored and resumed without moving their artifacts.
41
+ const entries = (await bridge.ws.list(root))
42
+ .filter(entry => !['operation.lock', 'dev', 'archives', 'setup', 'work'].includes(entry.name)).map(entry => entry.name);
43
+ entries.push(...(await bridge.ws.list(`${root}/setup`))
44
+ .filter(entry => !['analysis.json', 'settings.json', 'tasks'].includes(entry.name)).map(entry => `setup/${entry.name}`));
45
+ const moved = [];
46
+ const restored = [];
47
+ try {
48
+ for (const entry of entries) {
49
+ const destination = await bridge.ws.resolve(`${archive}/${entry}`);
50
+ await mkdir(path.dirname(destination), { recursive: true, mode: 0o700 });
51
+ await rename(await bridge.ws.resolve(`${root}/${entry}`), destination);
52
+ moved.push(entry);
53
+ }
54
+ if (restore)
55
+ for (const entry of ['runs', 'articles', 'bundles', 'previews', 'active-run.json']) {
56
+ if (!await bridge.ws.exists(`${restore}/${entry}`))
57
+ continue;
58
+ await rename(await bridge.ws.resolve(`${restore}/${entry}`), await bridge.ws.resolve(`${root}/${entry}`));
59
+ restored.push(entry);
60
+ }
61
+ await bridge.ws.writeJson(`${root}/binding.json`, { version: 1, api_origin: bridge.api.origin, project_id: projectId });
62
+ return { archive, restored_history: restored.length > 0 };
63
+ }
64
+ catch (error) {
65
+ for (const entry of restored.reverse())
66
+ await rename(await bridge.ws.resolve(`${root}/${entry}`), await bridge.ws.resolve(`${restore}/${entry}`));
67
+ // Restore only entries actually moved, including the previous binding.
68
+ for (const entry of moved.reverse()) {
69
+ await rm(await bridge.ws.resolve(`${root}/${entry}`), { recursive: true, force: true });
70
+ await rename(await bridge.ws.resolve(`${archive}/${entry}`), await bridge.ws.resolve(`${root}/${entry}`));
71
+ }
72
+ await rm(await bridge.ws.resolve(archive), { recursive: true, force: true });
73
+ throw error;
74
+ }
75
+ });
76
+ }
77
+ //# sourceMappingURL=replace-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-connection.js","sourceRoot":"","sources":["../src/replace-connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Bridge } from './bridge.js';
2
+ export type RepositoryFeature = 'article_gaps' | 'video_walkthrough';
3
+ /** Preserve the requested action through account, permission and repository setup. */
4
+ export declare function repositoryAction(bridge: Bridge, action: 'find_article_gaps' | 'create_video_walkthrough' | 'connect_repository', feature?: RepositoryFeature): Promise<{
5
+ instructions: string;
6
+ action: "read_articles" | "create_article" | "update_article" | "edit_article" | "publish_article" | "unpublish_article" | "delete_article" | "find_article_gaps" | "suggest_sections" | "recommend_articles" | "review_sections" | "review_recommendations" | "create_video_walkthrough" | "get_operation" | "generate_help_centre";
7
+ allowed: boolean;
8
+ execution: "local" | "remote" | "hosted";
9
+ required_scopes: string[];
10
+ next_step: {
11
+ code: string;
12
+ message: string;
13
+ requested_action: "read_articles" | "create_article" | "update_article" | "edit_article" | "publish_article" | "unpublish_article" | "delete_article" | "find_article_gaps" | "suggest_sections" | "recommend_articles" | "review_sections" | "review_recommendations" | "create_video_walkthrough" | "get_operation" | "generate_help_centre";
14
+ missing_scopes?: ("read" | "import" | "publish" | "manage" | "generate")[] | undefined;
15
+ url?: string | undefined;
16
+ } | null;
17
+ status: string;
18
+ }>;
@@ -0,0 +1,9 @@
1
+ import { resolveAction } from './actions.js';
2
+ /** Preserve the requested action through account, permission and repository setup. */
3
+ export async function repositoryAction(bridge, action, feature = 'article_gaps') {
4
+ const requested = action === 'connect_repository' ? feature === 'video_walkthrough' ? 'create_video_walkthrough' : 'find_article_gaps' : action;
5
+ const decision = await resolveAction(bridge, requested);
6
+ return { status: decision.allowed ? 'available' : 'action_required', ...decision,
7
+ instructions: 'Show the action-specific next step and preserve the requested action. The user must approve browser consent. Retry the original action after setup; do not start local analysis or generation.' };
8
+ }
9
+ //# sourceMappingURL=repository-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-actions.js","sourceRoot":"","sources":["../src/repository-actions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,MAA+E,EAAE,UAA6B,cAAc;IACjL,MAAM,SAAS,GAAG,MAAM,KAAK,oBAAoB,CAAC,CAAC,CAAC,OAAO,KAAK,mBAAmB,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC;IAChJ,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,EAAE,GAAG,QAAQ;QAC9E,YAAY,EAAE,gMAAgM,EAAE,CAAC;AACrN,CAAC"}
@@ -0,0 +1,96 @@
1
+ import { z } from 'zod';
2
+ import { repositoryConnectionSchema } from './schema.js';
3
+ export declare const repositoryInvitationSchema: z.ZodObject<{
4
+ id: z.ZodString;
5
+ message: z.ZodString;
6
+ connect_url: z.ZodURL;
7
+ }, z.core.$strip>;
8
+ export type RepositoryInvitation = z.infer<typeof repositoryInvitationSchema>;
9
+ export declare const repositoryBenefits: readonly [{
10
+ readonly id: "agent_allowance";
11
+ readonly category: "usage";
12
+ readonly message: "Keep your coding-agent allowance for building your product. Generate future articles using your SupportPages.io plan’s AI allowance.";
13
+ }, {
14
+ readonly id: "documentation_allowance";
15
+ readonly category: "usage";
16
+ readonly message: "Let SupportPages.io spend its AI allowance on your documentation, while you keep your coding-agent usage for your product.";
17
+ }, {
18
+ readonly id: "next_article_allowance";
19
+ readonly category: "usage";
20
+ readonly message: "Your next article can use your SupportPages.io plan’s AI allowance. Connect your repository to generate it in the web app.";
21
+ }, {
22
+ readonly id: "elapsed_generation";
23
+ readonly category: "hosted";
24
+ readonly message: "This draft was ready in {duration}. Next time, let SupportPages.io handle generation without keeping your local coding agent running.";
25
+ }, {
26
+ readonly id: "web_generation";
27
+ readonly category: "hosted";
28
+ readonly message: "Create your next article directly in SupportPages.io, without opening a local coding-agent session.";
29
+ }, {
30
+ readonly id: "background_generation";
31
+ readonly category: "hosted";
32
+ readonly message: "Let SupportPages.io generate articles in the background while you carry on building your product.";
33
+ }, {
34
+ readonly id: "topic_discovery";
35
+ readonly category: "discovery";
36
+ readonly message: "Not sure what to document next? SupportPages.io can suggest articles based on your product’s features.";
37
+ }, {
38
+ readonly id: "coverage_gaps";
39
+ readonly category: "discovery";
40
+ readonly message: "Discover features your help centre doesn’t cover yet, with suggestions based on your repository.";
41
+ }, {
42
+ readonly id: "section_suggestions";
43
+ readonly category: "organisation";
44
+ readonly message: "Give your help centre a clearer structure with section suggestions based on your product.";
45
+ }, {
46
+ readonly id: "organisation";
47
+ readonly category: "organisation";
48
+ readonly message: "Help customers find the right answer by organising your articles into help-centre sections.";
49
+ }, {
50
+ readonly id: "change_tracking";
51
+ readonly category: "maintenance";
52
+ readonly message: "Turn merged pull requests into suggestions for documentation your customers might need.";
53
+ }, {
54
+ readonly id: "weekly_analysis";
55
+ readonly category: "maintenance";
56
+ readonly message: "Prefer a regular catch-up? Connect your repository and choose weekly analysis of merged pull requests.";
57
+ }, {
58
+ readonly id: "maintenance";
59
+ readonly category: "maintenance";
60
+ readonly message: "Spot articles that need updating by comparing your documentation with your code.";
61
+ }, {
62
+ readonly id: "automatic_drafts";
63
+ readonly category: "automation";
64
+ readonly message: "Choose automatic draft generation to turn article suggestions into drafts you can review before publishing.";
65
+ }, {
66
+ readonly id: "batch_generation";
67
+ readonly category: "automation";
68
+ readonly message: "Work through your documentation backlog by generating multiple suggested articles from the web app.";
69
+ }];
70
+ /** The exact paragraph the agent shows after a draft review link: the benefit, then the link. */
71
+ export declare const repositoryShowText: (invitation: RepositoryInvitation) => string;
72
+ export declare function articleDuration(start?: string, ready?: string): string | undefined;
73
+ /** Device-local preferences and deduplication, shared by all checkouts of a help centre. */
74
+ export declare class RepositoryReminders {
75
+ private journal;
76
+ constructor(configDir?: string);
77
+ private transaction;
78
+ preference(origin: string, projectId: string, enabled?: boolean): Promise<{
79
+ enabled: boolean;
80
+ api_origin: string;
81
+ project_id: string;
82
+ scope: string;
83
+ }>;
84
+ complete(input: {
85
+ origin: string;
86
+ projectId: string;
87
+ articleId: string;
88
+ connection?: z.infer<typeof repositoryConnectionSchema>;
89
+ firstWriterStartedAt?: string;
90
+ readyAt?: string;
91
+ }): Promise<{
92
+ id: "maintenance" | "agent_allowance" | "documentation_allowance" | "next_article_allowance" | "elapsed_generation" | "web_generation" | "background_generation" | "topic_discovery" | "coverage_gaps" | "section_suggestions" | "organisation" | "change_tracking" | "weekly_analysis" | "automatic_drafts" | "batch_generation";
93
+ message: string;
94
+ connect_url: string;
95
+ } | undefined>;
96
+ }