@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,98 @@
1
+ import { z } from 'zod';
2
+ import type { Bridge } from './bridge.js';
3
+ export declare const writerAction: z.ZodEnum<{
4
+ read_articles: "read_articles";
5
+ create_article: "create_article";
6
+ update_article: "update_article";
7
+ edit_article: "edit_article";
8
+ publish_article: "publish_article";
9
+ unpublish_article: "unpublish_article";
10
+ delete_article: "delete_article";
11
+ find_article_gaps: "find_article_gaps";
12
+ suggest_sections: "suggest_sections";
13
+ recommend_articles: "recommend_articles";
14
+ review_sections: "review_sections";
15
+ review_recommendations: "review_recommendations";
16
+ create_video_walkthrough: "create_video_walkthrough";
17
+ get_operation: "get_operation";
18
+ generate_help_centre: "generate_help_centre";
19
+ }>;
20
+ export type WriterAction = z.infer<typeof writerAction>;
21
+ export declare const actionDecisionSchema: z.ZodObject<{
22
+ action: z.ZodEnum<{
23
+ read_articles: "read_articles";
24
+ create_article: "create_article";
25
+ update_article: "update_article";
26
+ edit_article: "edit_article";
27
+ publish_article: "publish_article";
28
+ unpublish_article: "unpublish_article";
29
+ delete_article: "delete_article";
30
+ find_article_gaps: "find_article_gaps";
31
+ suggest_sections: "suggest_sections";
32
+ recommend_articles: "recommend_articles";
33
+ review_sections: "review_sections";
34
+ review_recommendations: "review_recommendations";
35
+ create_video_walkthrough: "create_video_walkthrough";
36
+ get_operation: "get_operation";
37
+ generate_help_centre: "generate_help_centre";
38
+ }>;
39
+ allowed: z.ZodBoolean;
40
+ execution: z.ZodEnum<{
41
+ local: "local";
42
+ remote: "remote";
43
+ hosted: "hosted";
44
+ }>;
45
+ required_scopes: z.ZodArray<z.ZodString>;
46
+ next_step: z.ZodNullable<z.ZodObject<{
47
+ code: z.ZodString;
48
+ message: z.ZodString;
49
+ requested_action: z.ZodEnum<{
50
+ read_articles: "read_articles";
51
+ create_article: "create_article";
52
+ update_article: "update_article";
53
+ edit_article: "edit_article";
54
+ publish_article: "publish_article";
55
+ unpublish_article: "unpublish_article";
56
+ delete_article: "delete_article";
57
+ find_article_gaps: "find_article_gaps";
58
+ suggest_sections: "suggest_sections";
59
+ recommend_articles: "recommend_articles";
60
+ review_sections: "review_sections";
61
+ review_recommendations: "review_recommendations";
62
+ create_video_walkthrough: "create_video_walkthrough";
63
+ get_operation: "get_operation";
64
+ generate_help_centre: "generate_help_centre";
65
+ }>;
66
+ missing_scopes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
67
+ read: "read";
68
+ import: "import";
69
+ publish: "publish";
70
+ manage: "manage";
71
+ generate: "generate";
72
+ }>>>;
73
+ url: z.ZodOptional<z.ZodURL>;
74
+ }, z.core.$strip>>;
75
+ }, z.core.$strip>;
76
+ /** Actions this Writer does not know are ignored and missing ones read as unadvertised,
77
+ * so a server that adds an action never breaks an older Writer (and vice versa). */
78
+ export declare const writerCapabilitiesSchema: z.ZodObject<{
79
+ version: z.ZodLiteral<1>;
80
+ actions: z.ZodPipe<z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodTransform<Partial<Record<"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", {
81
+ 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";
82
+ allowed: boolean;
83
+ execution: "local" | "remote" | "hosted";
84
+ required_scopes: string[];
85
+ next_step: {
86
+ code: string;
87
+ message: string;
88
+ 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";
89
+ missing_scopes?: ("read" | "import" | "publish" | "manage" | "generate")[] | undefined;
90
+ url?: string | undefined;
91
+ } | null;
92
+ }>>, Record<string, unknown>>>;
93
+ }, z.core.$strip>;
94
+ export type ActionDecision = z.infer<typeof actionDecisionSchema>;
95
+ /** Both terminal and MCP entrypoints use this read-only dispatcher. It never
96
+ * starts authentication or replaces hosted work with a local writer. */
97
+ export declare function resolveAction(bridge: Bridge, action: WriterAction, articleId?: string): Promise<ActionDecision>;
98
+ export declare function requireExecution(decision: ActionDecision, execution: ActionDecision['execution']): void;
@@ -0,0 +1,86 @@
1
+ import { z } from 'zod';
2
+ import { fail, publicError } from './errors.js';
3
+ import { CLI_NAME } from './brand.js';
4
+ export const writerAction = z.enum(['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']);
5
+ export const actionDecisionSchema = z.object({ action: writerAction, allowed: z.boolean(), execution: z.enum(['local', 'remote', 'hosted']), required_scopes: z.array(z.string()),
6
+ next_step: z.object({ code: z.string().max(100), message: z.string().max(1000), requested_action: writerAction, missing_scopes: z.array(z.enum(['read', 'import', 'publish', 'manage', 'generate'])).optional(), url: z.url().optional() }).nullable(),
7
+ }).refine(value => value.allowed === (value.next_step === null));
8
+ /** Actions this Writer does not know are ignored and missing ones read as unadvertised,
9
+ * so a server that adds an action never breaks an older Writer (and vice versa). */
10
+ export const writerCapabilitiesSchema = z.object({ version: z.literal(1), actions: z.record(z.string(), z.unknown()).transform((value, ctx) => {
11
+ const actions = {};
12
+ for (const [name, raw] of Object.entries(value)) {
13
+ if (!writerAction.safeParse(name).success)
14
+ continue;
15
+ const decision = actionDecisionSchema.safeParse(raw);
16
+ if (!decision.success) {
17
+ ctx.addIssue({ code: 'custom', message: `Invalid decision for ${name}`, path: [name] });
18
+ return z.NEVER;
19
+ }
20
+ actions[name] = decision.data;
21
+ }
22
+ return actions;
23
+ }) });
24
+ const blocked = (action, code, message) => ({ action, allowed: false, execution: 'remote', required_scopes: [], next_step: { code, message, requested_action: action } });
25
+ /** Both terminal and MCP entrypoints use this read-only dispatcher. It never
26
+ * starts authentication or replaces hosted work with a local writer. */
27
+ export async function resolveAction(bridge, action, articleId) {
28
+ writerAction.parse(action);
29
+ const destination = await bridge.destination();
30
+ if (action === 'create_article' && (destination === 'local' || destination === 'none' && !bridge.api.configured())) {
31
+ return { action, allowed: true, execution: 'local', required_scopes: [], next_step: null };
32
+ }
33
+ if (!bridge.api.configured())
34
+ return blocked(action, 'authentication_required', `Sign in or create an account with ${CLI_NAME} login, then select a help centre and retry this action.`);
35
+ try {
36
+ if (destination !== 'hosted') {
37
+ await bridge.listProjects(); // Validate credentials before asking for a project.
38
+ return blocked(action, 'project_required', 'Select or create a help centre with supportpages_init, then retry this action.');
39
+ }
40
+ const context = await bridge.context();
41
+ let decision = context.repository_connection?.writer?.actions[action];
42
+ if (articleId) {
43
+ if (!/^[1-9][0-9]*$/.test(articleId))
44
+ fail('invalid_article', 'Choose an article ID from this project.');
45
+ const response = z.object({ version: z.literal(1), decision: actionDecisionSchema }).safeParse(await bridge.api.request('GET', `/projects/${context.project.id}/writer/capabilities?action_name=${action}&article_id=${articleId}`));
46
+ if (!response.success)
47
+ fail('invalid_response', 'The server returned an invalid action decision.');
48
+ decision = response.data.decision;
49
+ }
50
+ // Preserve legacy local authoring only when the server explicitly confirms
51
+ // there is no repository. Unknown or broken servers never imply local work.
52
+ if (!decision && action === 'create_article' && context.repository_connection?.state === 'not_connected') {
53
+ return { action, allowed: true, execution: 'local', required_scopes: ['read', 'import'], next_step: null };
54
+ }
55
+ if (!decision)
56
+ return blocked(action, 'server_update_required', 'Update the SupportPages server and Writer to use this action. The server did not advertise a supported action contract.');
57
+ if (decision.action !== action || decision.next_step && decision.next_step.requested_action !== action)
58
+ fail('invalid_response', 'The server returned a decision for another action.');
59
+ if (decision.next_step?.url) {
60
+ const url = new URL(decision.next_step.url);
61
+ const transport = new URL(bridge.api.origin);
62
+ const developmentUpgrade = bridge.api.dev && transport.hostname === url.hostname && url.protocol === 'https:';
63
+ if ((url.origin !== bridge.api.origin && !developmentUpgrade) || url.username || url.password)
64
+ fail('invalid_response', 'The server returned an invalid recovery link.');
65
+ }
66
+ return decision;
67
+ }
68
+ catch (error) {
69
+ const safe = publicError(error);
70
+ if (safe.code === 'invalid_credentials')
71
+ return blocked(action, 'authentication_required', 'Device sign-in has expired or was revoked. Sign in again, then retry this action.');
72
+ if (safe.code === 'not_found')
73
+ return blocked(action, 'resource_unavailable', 'This project or article is unavailable. Restore access or select an accessible resource before retrying.');
74
+ throw error;
75
+ }
76
+ }
77
+ export function requireExecution(decision, execution) {
78
+ if (!decision.allowed) {
79
+ const next = decision.next_step;
80
+ const guidance = next.url ? ` Open: ${next.url}` : next.missing_scopes?.length ? ` Run ${CLI_NAME} login --scopes ${next.missing_scopes.join(',')}, or use supportpages_request_permissions.` : '';
81
+ fail(next.code, next.message + guidance, { writer_action: decision });
82
+ }
83
+ if (decision.execution !== execution)
84
+ fail('hosted_action_required', 'This action runs in SupportPages. Use the hosted action tool; do not launch a local writer.', { writer_action: decision });
85
+ }
86
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,eAAe,EAAE,sBAAsB,CAAC,CAAC,CAAC;AAErV,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/K,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE;CACvP,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC;AACjE;oFACoF;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5I,MAAM,OAAO,GAAkD,EAAE,CAAC;QAClE,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO;gBAAE,SAAS;YACpD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACrD,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wBAAwB,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAAC,OAAO,CAAC,CAAC,KAAK,CAAC;YAAC,CAAC;YACnI,OAAO,CAAC,IAAoB,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;QAChD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,EAAE,CAAC,CAAC;AAGN,MAAM,OAAO,GAAG,CAAC,MAAoB,EAAE,IAAY,EAAE,OAAe,EAAkB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AAExN;wEACwE;AACxE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,MAAoB,EAAE,SAAkB;IAC1F,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/C,IAAI,MAAM,KAAK,gBAAgB,IAAI,CAAC,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;QACnH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC7F,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE;QAAE,OAAO,OAAO,CAAC,MAAM,EAAE,yBAAyB,EAAE,qCAAqC,QAAQ,0DAA0D,CAAC,CAAC;IACzL,IAAI,CAAC;QACH,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,oDAAoD;YACjF,OAAO,OAAO,CAAC,MAAM,EAAE,kBAAkB,EAAE,gFAAgF,CAAC,CAAC;QAC/H,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvC,IAAI,QAAQ,GAAG,OAAO,CAAC,qBAAqB,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACtE,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,IAAI,CAAC,iBAAiB,EAAE,yCAAyC,CAAC,CAAC;YACzG,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,OAAO,CAAC,OAAO,CAAC,EAAE,oCAAoC,MAAM,eAAe,SAAS,EAAE,CAAC,CAAC,CAAC;YACrO,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,IAAI,CAAC,kBAAkB,EAAE,iDAAiD,CAAC,CAAC;YACnG,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QACpC,CAAC;QACD,2EAA2E;QAC3E,4EAA4E;QAC5E,IAAI,CAAC,QAAQ,IAAI,MAAM,KAAK,gBAAgB,IAAI,OAAO,CAAC,qBAAqB,EAAE,KAAK,KAAK,eAAe,EAAE,CAAC;YACzG,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC7G,CAAC;QACD,IAAI,CAAC,QAAQ;YAAE,OAAO,OAAO,CAAC,MAAM,EAAE,wBAAwB,EAAE,yHAAyH,CAAC,CAAC;QAC3L,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,gBAAgB,KAAK,MAAM;YAAE,IAAI,CAAC,kBAAkB,EAAE,oDAAoD,CAAC,CAAC;QACvL,IAAI,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC5C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,SAAS,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;YAC9G,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;gBAAE,IAAI,CAAC,kBAAkB,EAAE,+CAA+C,CAAC,CAAC;QAC3K,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,qBAAqB;YAAE,OAAO,OAAO,CAAC,MAAM,EAAE,yBAAyB,EAAE,mFAAmF,CAAC,CAAC;QAChL,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;YAAE,OAAO,OAAO,CAAC,MAAM,EAAE,sBAAsB,EAAE,0GAA0G,CAAC,CAAC;QAC1L,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAwB,EAAE,SAAsC;IAC/F,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAU,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,QAAQ,mBAAmB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC,CAAC,EAAE,CAAC;QACnM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,GAAG,QAAQ,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS;QAAE,IAAI,CAAC,wBAAwB,EAAE,6FAA6F,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;AACnM,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { Workspace } from './workspace.js';
2
+ type SettingsWorkspace = {
3
+ ws: Workspace;
4
+ stateRoot: string;
5
+ };
6
+ export declare const efforts: Record<string, string[]>;
7
+ export declare const modelValid: (value: unknown) => value is string;
8
+ export declare const settingsFile: (bridge: SettingsWorkspace) => string;
9
+ export declare function modelSettings(agent: string, saved?: unknown): {
10
+ model: string | null;
11
+ effort: string | null;
12
+ };
13
+ export declare function readAgentSettings(bridge: SettingsWorkspace): Promise<{
14
+ [x: string]: unknown;
15
+ models?: Record<string, unknown> | undefined;
16
+ agents?: ("claude" | "codex")[] | undefined;
17
+ }>;
18
+ export declare function executionSettings(bridge: SettingsWorkspace, agent: string): Promise<{
19
+ model: string | null;
20
+ effort: string | null;
21
+ }>;
22
+ export {};
@@ -0,0 +1,35 @@
1
+ import { fail } from './errors.js';
2
+ import { z } from 'zod';
3
+ import { CLI_NAME } from './brand.js';
4
+ export const efforts = { claude: ['low', 'medium', 'high', 'xhigh', 'max'], codex: ['minimal', 'low', 'medium', 'high', 'xhigh'] };
5
+ export const modelValid = (value) => typeof value === 'string' && value.length <= 200 && /^[a-zA-Z0-9][a-zA-Z0-9._:/\[\]-]*$/.test(value);
6
+ export const settingsFile = (bridge) => `${bridge.stateRoot}/setup/settings.json`;
7
+ // Undefined means SupportPages.io defaults; null explicitly inherits the agent setting.
8
+ export function modelSettings(agent, saved = {}) {
9
+ if (!efforts[agent])
10
+ fail('invalid_agent', 'Choose Claude Code or Codex.');
11
+ const values = z.object({ model: z.unknown().optional(), effort: z.unknown().optional() }).safeParse(saved);
12
+ if (!values.success)
13
+ fail('invalid_configuration', `Invalid coding-agent settings. Run ${CLI_NAME} configure to change them.`);
14
+ const model = values.data.model === undefined ? (agent === 'claude' ? 'sonnet' : null) : values.data.model;
15
+ const effort = values.data.effort === undefined ? (agent === 'claude' ? 'low' : null) : values.data.effort;
16
+ if (model !== null && !modelValid(model) || effort !== null && (typeof effort !== 'string' || !efforts[agent].includes(effort))) {
17
+ fail('invalid_configuration', `Invalid coding-agent model or effort. Run ${CLI_NAME} configure to change it.`);
18
+ }
19
+ return { model, effort };
20
+ }
21
+ export async function readAgentSettings(bridge) {
22
+ const value = await bridge.ws.exists(settingsFile(bridge)) ? await bridge.ws.json(settingsFile(bridge)) : {};
23
+ const result = z.object({
24
+ models: z.record(z.string(), z.unknown()).optional(),
25
+ agents: z.array(z.enum(['claude', 'codex'])).min(1).max(2).refine(agents => new Set(agents).size === agents.length).optional(),
26
+ }).passthrough().safeParse(value);
27
+ if (!result.success) {
28
+ fail('invalid_configuration', 'The saved coding-agent settings are invalid.');
29
+ }
30
+ return result.data;
31
+ }
32
+ export async function executionSettings(bridge, agent) {
33
+ return modelSettings(agent, (await readAgentSettings(bridge)).models?.[agent] ?? {});
34
+ }
35
+ //# sourceMappingURL=agent-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-settings.js","sourceRoot":"","sources":["../src/agent-settings.ts"],"names":[],"mappings":"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"}
package/dist/api.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare function developmentMode(value?: string): boolean;
2
+ export declare function defaultOrigin(dev: boolean): string;
3
+ export declare function connectionStateRoot(origin: string, dev: boolean): string;
4
+ export declare function apiOrigin(value: string, dev?: boolean): string;
5
+ export declare const WRITER_FEATURES: readonly ["generate_help_centre"];
6
+ export declare class ApiClient {
7
+ private token?;
8
+ private fetcher;
9
+ dev: boolean;
10
+ origin: string;
11
+ constructor(origin: string, token?: string | undefined, fetcher?: typeof fetch, dev?: boolean);
12
+ configured(): boolean;
13
+ request(method: string, route: string, body?: FormData | object, idempotencyKey?: string, timeoutMs?: number): Promise<unknown>;
14
+ }
package/dist/api.js ADDED
@@ -0,0 +1,159 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { fail } from './errors.js';
3
+ import { articleCapacitySchema } from './schema.js';
4
+ import { capacityMessage } from './capacity.js';
5
+ import { connectionFailure } from './development-tls.js';
6
+ import { CLI_NAME } from './brand.js';
7
+ export function developmentMode(value) {
8
+ if (value === undefined || value === 'false' || value === '0')
9
+ return false;
10
+ if (value === 'true' || value === '1')
11
+ return true;
12
+ fail('invalid_configuration', 'SUPPORTPAGES_DEV must be true, false, 1 or 0.');
13
+ }
14
+ export function defaultOrigin(dev) {
15
+ return dev ? 'https://app.lvh.me:3443' : 'https://app.supportpages.io';
16
+ }
17
+ export function connectionStateRoot(origin, dev) {
18
+ return dev ? `.rtfm/supportpages/dev/${createHash('sha256').update(origin).digest('hex').slice(0, 16)}` : '.rtfm/supportpages';
19
+ }
20
+ export function apiOrigin(value, dev = false) {
21
+ let url;
22
+ try {
23
+ url = new URL(value);
24
+ }
25
+ catch {
26
+ fail('invalid_configuration', 'SUPPORTPAGES_API_URL must be an HTTP(S) origin.');
27
+ }
28
+ const loopback = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
29
+ const local = loopback || url.hostname === 'app.lvh.me';
30
+ if (dev && !local)
31
+ fail('invalid_configuration', 'Dev mode requires localhost, loopback, or app.lvh.me.');
32
+ if ((url.protocol !== 'https:' && !(url.protocol === 'http:' && (loopback || (dev && local)))) || url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
33
+ fail('invalid_configuration', 'Use an HTTPS API origin, or HTTP on loopback (app.lvh.me requires --dev), without credentials or a path.');
34
+ }
35
+ return url.origin;
36
+ }
37
+ export const WRITER_FEATURES = ['generate_help_centre'];
38
+ export class ApiClient {
39
+ token;
40
+ fetcher;
41
+ dev;
42
+ origin;
43
+ constructor(origin, token, fetcher = fetch, dev = false) {
44
+ this.token = token;
45
+ this.fetcher = fetcher;
46
+ this.dev = dev;
47
+ this.origin = apiOrigin(origin, dev);
48
+ }
49
+ configured() { return Boolean(this.token); }
50
+ async request(method, route, body, idempotencyKey, timeoutMs = 60_000) {
51
+ if (!this.token)
52
+ fail('missing_credentials', `This device is not signed in to SupportPages.io. In the terminal, run ${CLI_NAME} publish to host locally saved articles, or ${CLI_NAME} login; then call supportpages_init.`);
53
+ // Name the optional actions this Writer understands, so the server lists
54
+ // them in capability maps only for clients that can parse them.
55
+ const headers = { Authorization: `Bearer ${this.token}`, Accept: 'application/json', 'X-SupportPages-Writer-Features': WRITER_FEATURES.join(',') };
56
+ if (idempotencyKey)
57
+ headers['Idempotency-Key'] = idempotencyKey;
58
+ let payload;
59
+ if (body instanceof FormData)
60
+ payload = body;
61
+ else if (body !== undefined) {
62
+ headers['Content-Type'] = 'application/json';
63
+ payload = JSON.stringify(body);
64
+ }
65
+ let response;
66
+ try {
67
+ response = await this.fetcher(`${this.origin}/api/v1${route}`, { method, headers, body: payload, redirect: 'error', signal: AbortSignal.timeout(timeoutMs) });
68
+ }
69
+ catch (error) {
70
+ connectionFailure(error, this.dev, 'SupportPages.io could not be reached. Check the connection and retry.');
71
+ }
72
+ if (!response.ok) {
73
+ const code = { 401: 'invalid_credentials', 403: 'permission_denied', 404: 'not_found', 409: 'revision_conflict', 413: 'bundle_too_large', 422: 'invalid_artifact', 429: 'rate_limited' }[response.status] ?? 'remote_error';
74
+ // Decode only bounded, recognised error codes. Never echo arbitrary server
75
+ // messages, proxy diagnostics or user-controlled response fields.
76
+ const known = {
77
+ invalid_content: { status: 422, message: 'The supplied article content is invalid. Read the article and preserve its content format before retrying.' },
78
+ unsupported_article_format: { status: 422, message: 'Preserve this article’s content format: structured content for illustrated articles, Markdown for API-authored articles.' },
79
+ generation_running: { status: 409, message: 'Wait for the current generation to finish, or keep the local preview as an editable draft in SupportPages.' },
80
+ article_deleted: { status: 409, message: 'This article was deleted remotely. Restore it in SupportPages.io before updating it; it will not be recreated automatically.' },
81
+ plan_limit: { status: 403, message: 'Your account’s article hosting limit has been reached. Free up article capacity or update your plan, then retry the saved upload. Reconnecting will not resolve this limit.' },
82
+ permission_denied: { status: 403, message: `This token does not have permission for the requested operation. Check its scopes and project access; reconnect with ${CLI_NAME} init if needed.` },
83
+ repository_connection_required: { status: 403, message: 'Connect a repository in the web app before assigning sections. Individual unsectioned articles do not require a repository connection.' },
84
+ run_closed: { status: 409, message: 'This generation attempt is closed. The saved draft and your local files are preserved; do not overwrite browser edits.' },
85
+ published_article: { status: 409, message: 'This article is already published. Local uploads cannot overwrite it; review it in the web app.' },
86
+ };
87
+ const reader = response.body?.getReader();
88
+ let recognised;
89
+ let capacity;
90
+ try {
91
+ const chunks = [];
92
+ let size = 0;
93
+ if (reader)
94
+ while (size <= 8192) {
95
+ const item = await reader.read();
96
+ if (item.done)
97
+ break;
98
+ size += item.value.length;
99
+ if (size <= 8192)
100
+ chunks.push(item.value);
101
+ }
102
+ const value = size <= 8192 ? JSON.parse(Buffer.concat(chunks).toString()) : null;
103
+ const candidate = value?.error?.code;
104
+ if (typeof candidate === 'string' && Object.hasOwn(known, candidate) && known[candidate]?.status === response.status)
105
+ recognised = candidate;
106
+ if (recognised === 'plan_limit') {
107
+ const parsed = articleCapacitySchema.safeParse(value?.error?.details?.article_capacity);
108
+ if (parsed.success && !parsed.data.can_create)
109
+ capacity = parsed.data;
110
+ }
111
+ }
112
+ catch { /* Older servers and HTML errors retain their HTTP fallback. */ }
113
+ finally {
114
+ await reader?.cancel().catch(() => { });
115
+ reader?.releaseLock();
116
+ }
117
+ if (recognised) {
118
+ if (capacity)
119
+ fail(recognised, `${capacityMessage(capacity)} Then retry the saved upload; no regeneration is needed.`, { status: response.status, article_capacity: capacity });
120
+ const message = recognised === 'plan_limit' && !/\/(article_imports|generation_runs)(\/|$)/.test(route)
121
+ ? 'Your account’s plan limit for this operation has been reached. Check your plan and available capacity before retrying.'
122
+ : known[recognised].message;
123
+ fail(recognised, message, { status: response.status });
124
+ }
125
+ fail(code, `SupportPages.io returned HTTP ${response.status}.`, { status: response.status });
126
+ }
127
+ if (!response.headers.get('content-type')?.includes('application/json'))
128
+ fail('invalid_response', 'The API did not return JSON. Verify the RTFM integration is installed on the app host.');
129
+ const reader = response.body?.getReader();
130
+ if (!reader)
131
+ fail('invalid_response', 'The API returned an empty response.');
132
+ const chunks = [];
133
+ let size = 0;
134
+ try {
135
+ while (true) {
136
+ const chunk = await reader.read();
137
+ if (chunk.done)
138
+ break;
139
+ size += chunk.value.byteLength;
140
+ if (size > 4 * 1024 * 1024) {
141
+ await reader.cancel();
142
+ fail('invalid_response', 'The API response exceeded the size limit.');
143
+ }
144
+ chunks.push(chunk.value);
145
+ }
146
+ }
147
+ finally {
148
+ reader.releaseLock();
149
+ }
150
+ const data = Buffer.concat(chunks).toString('utf8');
151
+ try {
152
+ return JSON.parse(data);
153
+ }
154
+ catch {
155
+ fail('invalid_response', 'The API returned malformed JSON.');
156
+ }
157
+ }
158
+ }
159
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Run } from './runs.js';
2
+ export declare const articleChatInstruction = "Keep user-facing chat focused on the SupportPages.io editor link, article progress, review and publication. Do not mention local assets, saved files, filesystem paths, output directories, filenames, rendering scripts or validation reports in routine progress, completion or failure messages. These are internal execution details, not deliverables to list or download. Explain failures in terms of their effect on the article and the next action, with the editor link when available. If delivery fails, say that the latest changes could not be confirmed in the editor; do not claim the editor is up to date or reassure the user that assets are saved locally. Share technical file details only when the user explicitly asks for them.";
3
+ export declare const showArticleLinkInstruction: string;
4
+ /** Local workspaces have no editor: the saved Markdown path is the deliverable. */
5
+ export declare const localChatInstruction = "This folder saves articles locally; there is no SupportPages.io editor or help centre for it. The saved Markdown path is the deliverable: show it in your completion message, with the number of screenshots. Keep progress messages concise and free of intermediate filenames, rendering scripts or validation reports. Never ask whether to publish automatically; hosting needs an account. If the user explicitly asks to host or publish saved local articles, ask whether to sign in or create a free SupportPages.io account, then use supportpages_publish. Any hosting offer comes from complete_article as hosting_invitation; do not add your own.";
6
+ export declare const localLinkInstruction: string;
7
+ export declare function articleLink(run: Run): {
8
+ editor_url: string | null;
9
+ link_message: string;
10
+ link_instructions: string;
11
+ };
12
+ /** Keep the URL visible as a separate MCP text item as well as structured data. */
13
+ export declare function articleLinkContent(value: unknown): {
14
+ type: "text";
15
+ text: string;
16
+ }[];
@@ -0,0 +1,25 @@
1
+ export const articleChatInstruction = 'Keep user-facing chat focused on the SupportPages.io editor link, article progress, review and publication. Do not mention local assets, saved files, filesystem paths, output directories, filenames, rendering scripts or validation reports in routine progress, completion or failure messages. These are internal execution details, not deliverables to list or download. Explain failures in terms of their effect on the article and the next action, with the editor link when available. If delivery fails, say that the latest changes could not be confirmed in the editor; do not claim the editor is up to date or reassure the user that assets are saved locally. Share technical file details only when the user explicitly asks for them.';
2
+ export const showArticleLinkInstruction = articleChatInstruction + ' Always show the returned article link in your next user-facing message, before launching or retrying the writer. Repeat it even if this article already existed, the link was shown earlier, or the operation failed. Do not wait for article text, images or completion. If no confirmed link is available, say so and explain the reported problem; never invent a URL. Showing a link does not mean opening the browser or publishing.';
3
+ /** Local workspaces have no editor: the saved Markdown path is the deliverable. */
4
+ export const localChatInstruction = 'This folder saves articles locally; there is no SupportPages.io editor or help centre for it. The saved Markdown path is the deliverable: show it in your completion message, with the number of screenshots. Keep progress messages concise and free of intermediate filenames, rendering scripts or validation reports. Never ask whether to publish automatically; hosting needs an account. If the user explicitly asks to host or publish saved local articles, ask whether to sign in or create a free SupportPages.io account, then use supportpages_publish. Any hosting offer comes from complete_article as hosting_invitation; do not add your own.';
5
+ export const localLinkInstruction = localChatInstruction + ' Do not invent an editor URL; there is none for this article.';
6
+ export function articleLink(run) {
7
+ if (run.export_path)
8
+ return { editor_url: null, link_message: `Saved to ${run.export_path}`, link_instructions: localLinkInstruction };
9
+ const editor_url = run.remote?.editor_url ?? run.progress?.article?.editor_url ?? null;
10
+ return { editor_url, link_message: editor_url ? `View article: ${editor_url}` : 'No confirmed article link is available yet.',
11
+ link_instructions: showArticleLinkInstruction };
12
+ }
13
+ /** Keep the URL visible as a separate MCP text item as well as structured data. */
14
+ export function articleLinkContent(value) {
15
+ if (!value || typeof value !== 'object')
16
+ return [];
17
+ const data = value;
18
+ const url = data.editor_url ?? data.article_run?.editor_url ?? data.details?.editor_url ?? data.article?.editor_url;
19
+ if (typeof url !== 'string')
20
+ return [];
21
+ // Guidance is already in server instructions and structured responses. Keep
22
+ // this visible text item useful without repeating that policy on every call.
23
+ return [{ type: 'text', text: `View article: ${url}` }];
24
+ }
25
+ //# sourceMappingURL=article-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-link.js","sourceRoot":"","sources":["../src/article-link.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,sBAAsB,GAAG,6tBAA6tB,CAAC;AAEpwB,MAAM,CAAC,MAAM,0BAA0B,GAAG,sBAAsB,GAAG,4aAA4a,CAAC;AAEhf,mFAAmF;AACnF,MAAM,CAAC,MAAM,oBAAoB,GAAG,goBAAgoB,CAAC;AAErqB,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,GAAG,+DAA+D,CAAC;AAE3H,MAAM,UAAU,WAAW,CAAC,GAAQ;IAClC,IAAI,GAAG,CAAC,WAAW;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,GAAG,CAAC,WAAW,EAAE,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,CAAC;IACvI,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC;IACvF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC,CAAC,6CAA6C;QAC3H,iBAAiB,EAAE,0BAA0B,EAAE,CAAC;AACpD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACnD,MAAM,IAAI,GAAG,KAA4B,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;IACpH,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACvC,4EAA4E;IAC5E,6EAA6E;IAC7E,OAAO,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,GAAG,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ import { type Article, type Manifest } from './schema.js';
3
+ import { Workspace } from './workspace.js';
4
+ export declare const sha256: (data: string | Buffer) => string;
5
+ export declare function canonical(value: unknown): string;
6
+ export declare function parse<T>(schema: z.ZodType<T>, value: unknown, code?: string): T;
7
+ export type Snapshot = {
8
+ article: Article;
9
+ articleBytes: Buffer;
10
+ images: {
11
+ block_id: string;
12
+ filename: string;
13
+ bytes: Buffer;
14
+ }[];
15
+ warnings: string[];
16
+ };
17
+ /** Read the latest usable preview without requiring finished screenshots or lint. */
18
+ export declare function progressSnapshot(ws: Workspace, dir: string, options?: {
19
+ imageHashes?: Record<string, string>;
20
+ fallbackDir?: string;
21
+ }): Promise<Snapshot | undefined>;
22
+ export declare function snapshot(ws: Workspace, dir: string, startedAt?: string): Promise<Snapshot>;
23
+ export declare function createManifest(snap: Snapshot, metadata: Pick<Manifest, 'local_article_id' | 'run_id' | 'skills_version' | 'source_commit' | 'source_dirty' | 'section_id'>): Manifest;
24
+ export declare function verifyBundle(ws: Workspace, dir: string): Promise<{
25
+ manifest: {
26
+ bundle_version: 1;
27
+ local_article_id: string;
28
+ run_id: string;
29
+ skills_version: string;
30
+ source_commit: string | null;
31
+ source_dirty: boolean;
32
+ section_id: string | null;
33
+ article_sha256: string;
34
+ images: {
35
+ block_id: string;
36
+ filename: string;
37
+ sha256: string;
38
+ size: number;
39
+ mime_type: "image/png";
40
+ }[];
41
+ bundle_hash: string;
42
+ };
43
+ snap: Snapshot;
44
+ }>;
45
+ export declare function preview(snap: Snapshot): string;