screencast-axi 0.1.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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +555 -0
  3. package/dist/bin/screencast-axi.d.ts +2 -0
  4. package/dist/bin/screencast-axi.js +16 -0
  5. package/dist/bin/screencast-axi.js.map +1 -0
  6. package/dist/src/auth/strategies.d.ts +54 -0
  7. package/dist/src/auth/strategies.js +137 -0
  8. package/dist/src/auth/strategies.js.map +1 -0
  9. package/dist/src/auth/types.d.ts +65 -0
  10. package/dist/src/auth/types.js +2 -0
  11. package/dist/src/auth/types.js.map +1 -0
  12. package/dist/src/browser.d.ts +100 -0
  13. package/dist/src/browser.js +168 -0
  14. package/dist/src/browser.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +160 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/commands/auth.d.ts +4 -0
  19. package/dist/src/commands/auth.js +207 -0
  20. package/dist/src/commands/auth.js.map +1 -0
  21. package/dist/src/commands/check.d.ts +12 -0
  22. package/dist/src/commands/check.js +103 -0
  23. package/dist/src/commands/check.js.map +1 -0
  24. package/dist/src/commands/doctor.d.ts +15 -0
  25. package/dist/src/commands/doctor.js +131 -0
  26. package/dist/src/commands/doctor.js.map +1 -0
  27. package/dist/src/commands/guide.d.ts +6 -0
  28. package/dist/src/commands/guide.js +257 -0
  29. package/dist/src/commands/guide.js.map +1 -0
  30. package/dist/src/commands/home.d.ts +12 -0
  31. package/dist/src/commands/home.js +78 -0
  32. package/dist/src/commands/home.js.map +1 -0
  33. package/dist/src/commands/list.d.ts +6 -0
  34. package/dist/src/commands/list.js +128 -0
  35. package/dist/src/commands/list.js.map +1 -0
  36. package/dist/src/commands/record.d.ts +7 -0
  37. package/dist/src/commands/record.js +266 -0
  38. package/dist/src/commands/record.js.map +1 -0
  39. package/dist/src/commands/scaffold.d.ts +4 -0
  40. package/dist/src/commands/scaffold.js +122 -0
  41. package/dist/src/commands/scaffold.js.map +1 -0
  42. package/dist/src/commands/setup.d.ts +14 -0
  43. package/dist/src/commands/setup.js +137 -0
  44. package/dist/src/commands/setup.js.map +1 -0
  45. package/dist/src/config.d.ts +126 -0
  46. package/dist/src/config.js +275 -0
  47. package/dist/src/config.js.map +1 -0
  48. package/dist/src/director.d.ts +217 -0
  49. package/dist/src/director.js +415 -0
  50. package/dist/src/director.js.map +1 -0
  51. package/dist/src/duration.d.ts +33 -0
  52. package/dist/src/duration.js +68 -0
  53. package/dist/src/duration.js.map +1 -0
  54. package/dist/src/encode.d.ts +67 -0
  55. package/dist/src/encode.js +225 -0
  56. package/dist/src/encode.js.map +1 -0
  57. package/dist/src/errors.d.ts +19 -0
  58. package/dist/src/errors.js +27 -0
  59. package/dist/src/errors.js.map +1 -0
  60. package/dist/src/flags.d.ts +39 -0
  61. package/dist/src/flags.js +130 -0
  62. package/dist/src/flags.js.map +1 -0
  63. package/dist/src/forensics.d.ts +51 -0
  64. package/dist/src/forensics.js +107 -0
  65. package/dist/src/forensics.js.map +1 -0
  66. package/dist/src/hooks.d.ts +9 -0
  67. package/dist/src/hooks.js +20 -0
  68. package/dist/src/hooks.js.map +1 -0
  69. package/dist/src/index.d.ts +18 -0
  70. package/dist/src/index.js +18 -0
  71. package/dist/src/index.js.map +1 -0
  72. package/dist/src/inventory.d.ts +45 -0
  73. package/dist/src/inventory.js +89 -0
  74. package/dist/src/inventory.js.map +1 -0
  75. package/dist/src/manifest.d.ts +89 -0
  76. package/dist/src/manifest.js +137 -0
  77. package/dist/src/manifest.js.map +1 -0
  78. package/dist/src/output.d.ts +8 -0
  79. package/dist/src/output.js +2 -0
  80. package/dist/src/output.js.map +1 -0
  81. package/dist/src/overlay.d.ts +105 -0
  82. package/dist/src/overlay.js +302 -0
  83. package/dist/src/overlay.js.map +1 -0
  84. package/dist/src/reference.d.ts +1 -0
  85. package/dist/src/reference.js +108 -0
  86. package/dist/src/reference.js.map +1 -0
  87. package/dist/src/run.d.ts +82 -0
  88. package/dist/src/run.js +321 -0
  89. package/dist/src/run.js.map +1 -0
  90. package/dist/src/skill.d.ts +12 -0
  91. package/dist/src/skill.js +69 -0
  92. package/dist/src/skill.js.map +1 -0
  93. package/dist/src/toolchain.d.ts +62 -0
  94. package/dist/src/toolchain.js +127 -0
  95. package/dist/src/toolchain.js.map +1 -0
  96. package/dist/src/types.d.ts +88 -0
  97. package/dist/src/types.js +18 -0
  98. package/dist/src/types.js.map +1 -0
  99. package/dist/src/version.d.ts +10 -0
  100. package/dist/src/version.js +11 -0
  101. package/dist/src/version.js.map +1 -0
  102. package/package.json +93 -0
  103. package/skills/screencast-axi/SKILL.md +49 -0
@@ -0,0 +1,321 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { captureSize, openContext, resolveViewport } from "./browser.js";
3
+ import { selectStrategy } from "./config.js";
4
+ import { Director } from "./director.js";
5
+ import { encode } from "./encode.js";
6
+ import { ScreencastError } from "./errors.js";
7
+ import { captureFailure } from "./forensics.js";
8
+ import { hashSteps, hashText, upsertEntry } from "./manifest.js";
9
+ import { mergeThemes, overlayInitScript, resolveOverlayTheme } from "./overlay.js";
10
+ import { detectToolchain, missingFfmpegError } from "./toolchain.js";
11
+ import { VERSION } from "./version.js";
12
+ /**
13
+ * A take failed. Carries what was on screen when it did.
14
+ *
15
+ * The forensics are the point: without them a stale selector is a bare
16
+ * timeout, and fixing it means another forty-second run to see anything.
17
+ */
18
+ export class ScenarioFailure extends ScreencastError {
19
+ scenarioId;
20
+ phase;
21
+ forensics;
22
+ lastStep;
23
+ constructor(init) {
24
+ super(init.message, "SCENARIO_FAILED", init.suggestions);
25
+ this.name = "ScenarioFailure";
26
+ this.scenarioId = init.scenarioId;
27
+ this.phase = init.phase;
28
+ this.forensics = init.forensics;
29
+ this.lastStep = init.lastStep;
30
+ }
31
+ }
32
+ /**
33
+ * Every declared narration line must appear once, in order.
34
+ *
35
+ * This is what stops the written workflow drifting from the recorded one: the
36
+ * same array becomes the burnt-in caption, the manifest's step list and the
37
+ * text a site renders beside the clip, so a line that never went on screen
38
+ * would be a caption promising something the video does not show.
39
+ */
40
+ export function assertScriptComplete(steps, shown) {
41
+ const declared = steps?.length ?? 0;
42
+ if (declared === 0)
43
+ return;
44
+ const expected = Array.from({ length: declared }, (_, i) => i);
45
+ const sameLength = shown.length === expected.length;
46
+ const inOrder = sameLength && shown.every((value, index) => value === expected[index]);
47
+ if (inOrder)
48
+ return;
49
+ const missing = expected.filter((i) => !shown.includes(i));
50
+ throw new ScreencastError(`The take showed steps [${shown.join(", ")}] but the scenario declares ${declared}`, "SCRIPT_INCOMPLETE", [
51
+ missing.length > 0
52
+ ? `Never shown: ${missing.map((i) => `step ${i} ("${steps?.[i] ?? ""}")`).join(", ")}`
53
+ : "Every step ran, but not once each in order",
54
+ "Call `await d.step(i)` once per declared line, in order, inside run()",
55
+ ]);
56
+ }
57
+ export async function runScenario(options) {
58
+ const { scenario, config, mode } = options;
59
+ const log = options.log ?? (() => { });
60
+ const recording = mode === "record";
61
+ const outDir = options.outDir ?? config.outDir;
62
+ const baseUrl = options.baseUrl ?? scenario.baseUrl ?? config.baseUrl;
63
+ const pace = options.pace ?? scenario.pace ?? config.pace;
64
+ const viewport = await resolveViewport({
65
+ ...((options.device ?? scenario.device ?? config.device)
66
+ ? { device: options.device ?? scenario.device ?? config.device }
67
+ : {}),
68
+ ...((options.viewport ?? scenario.viewport)
69
+ ? { viewport: options.viewport ?? scenario.viewport }
70
+ : {}),
71
+ ...((options.orientation ?? scenario.orientation)
72
+ ? { orientation: options.orientation ?? scenario.orientation }
73
+ : {}),
74
+ ...(config.browser.deviceScaleFactor !== undefined
75
+ ? { deviceScaleFactor: config.browser.deviceScaleFactor }
76
+ : {}),
77
+ }, config.viewport);
78
+ // Fail before the browser opens, not forty seconds into a take that cannot
79
+ // possibly produce a file.
80
+ let toolchain = options.toolchain;
81
+ if (recording) {
82
+ toolchain = toolchain ?? (await detectToolchain());
83
+ if (!toolchain.ffmpeg)
84
+ throw missingFfmpegError();
85
+ }
86
+ const strategy = selectStrategy(config, scenario.auth, options.auth);
87
+ const authCtx = {
88
+ baseUrl,
89
+ rootDir: config.rootDir,
90
+ scenario: { id: scenario.id, title: scenario.title },
91
+ log: (message) => log(` ${message}`),
92
+ };
93
+ // Before the browser opens, so an unreachable auth server costs a second
94
+ // rather than a take that has already started recording.
95
+ await strategy?.preflight?.(authCtx);
96
+ const patch = strategy?.prepareContext?.(authCtx) ?? {};
97
+ const opened = await openContext({
98
+ resolved: viewport,
99
+ headless: options.headed ? false : config.browser.headless,
100
+ args: config.browser.args,
101
+ ...(config.browser.profileDir ? { profileDir: config.browser.profileDir } : {}),
102
+ ...(recording ? { recordVideoDir: config.rawDir } : {}),
103
+ ...(config.browser.colorScheme ? { colorScheme: config.browser.colorScheme } : {}),
104
+ ...(config.browser.locale ? { locale: config.browser.locale } : {}),
105
+ ...(config.browser.timezoneId ? { timezoneId: config.browser.timezoneId } : {}),
106
+ ...(patch.storageState ? { storageState: patch.storageState } : {}),
107
+ ...(patch.httpCredentials ? { httpCredentials: patch.httpCredentials } : {}),
108
+ });
109
+ await opened.context.addInitScript({
110
+ content: overlayInitScript(resolveOverlayTheme(mergeThemes(config.overlay, scenario.overlay))),
111
+ });
112
+ // A rehearsal exists to fail fast. Playwright's 30s default is right for a
113
+ // take - a real app can genuinely take that long to settle - but it makes a
114
+ // rehearsal cost as much as the recording it was meant to replace, which
115
+ // defeats the point. A take keeps the generous default.
116
+ if (!recording) {
117
+ opened.context.setDefaultTimeout(config.timeouts.rehearseMs);
118
+ }
119
+ const director = new Director(opened.page, {
120
+ baseUrl,
121
+ pace,
122
+ settleMs: config.timeouts.settleMs,
123
+ ...(scenario.steps ? { steps: scenario.steps } : {}),
124
+ }, opened.createdAt);
125
+ /**
126
+ * Every host the page actually reached.
127
+ *
128
+ * Taken from the page rather than from the Director's own actions, which is
129
+ * the difference between reporting where a scenario was *told* to go and
130
+ * where it *went*: a navigation inside `setup()` never touches the Director,
131
+ * and a redirect lands somewhere the scenario never named. For a summary
132
+ * whose whole job is "is this safe to run", the second is the honest one.
133
+ */
134
+ const hosts = [];
135
+ opened.page.on("framenavigated", (frame) => {
136
+ if (frame !== opened.page.mainFrame())
137
+ return;
138
+ const host = hostOf(frame.url());
139
+ if (host && !hosts.includes(host))
140
+ hosts.push(host);
141
+ });
142
+ const ctx = {
143
+ baseUrl,
144
+ outDir,
145
+ rawDir: config.rawDir,
146
+ log: (message) => log(` ${message}`),
147
+ };
148
+ let clipEndedAt = opened.createdAt;
149
+ let rawVideoPath = null;
150
+ let identity;
151
+ /**
152
+ * Turns a thrown error into a failure that carries the page's state.
153
+ *
154
+ * Gathered here, *before* the context closes. Closing first - which the
155
+ * original in-repo recorder had to do, since the capture is only finalised
156
+ * on close - threw the page away along with any chance of seeing why the
157
+ * take failed.
158
+ */
159
+ const fail = async (phase, error) => {
160
+ const forensics = await captureFailure({
161
+ page: opened.page,
162
+ rawDir: config.rawDir,
163
+ id: scenario.id,
164
+ error,
165
+ });
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ throw new ScenarioFailure({
168
+ scenarioId: scenario.id,
169
+ phase,
170
+ message: `${scenario.id}: ${message}`,
171
+ forensics,
172
+ lastStep: director.shownSteps.at(-1) ?? null,
173
+ suggestions: buildSuggestions(scenario.id, forensics, mode),
174
+ });
175
+ };
176
+ try {
177
+ log(`${scenario.id}: ${baseUrl} at ${viewport.viewport.width}x${viewport.viewport.height}`);
178
+ try {
179
+ if (strategy) {
180
+ identity = (await strategy.signIn?.(opened.page, authCtx)) ?? identity;
181
+ await strategy.assertSignedIn?.(opened.page, authCtx);
182
+ log(` signed in via ${strategy.name}${identity ? ` as ${identity.label}` : ""}`);
183
+ }
184
+ await scenario.setup?.(opened.page, ctx);
185
+ }
186
+ catch (error) {
187
+ await fail("setup", error);
188
+ }
189
+ director.markClipStart();
190
+ try {
191
+ await scenario.run(director, ctx);
192
+ assertScriptComplete(scenario.steps, director.shownSteps);
193
+ await director.caption(null);
194
+ await director.beat(600);
195
+ }
196
+ catch (error) {
197
+ await fail("run", error);
198
+ }
199
+ clipEndedAt = Date.now();
200
+ }
201
+ finally {
202
+ await scenario.teardown?.(opened.page, ctx).catch(() => undefined);
203
+ const video = recording ? opened.page.video() : null;
204
+ // The capture is only written out when the context closes, so read the
205
+ // path afterwards rather than before.
206
+ await opened.close();
207
+ if (video)
208
+ rawVideoPath = await video.path().catch(() => null);
209
+ }
210
+ const trimStart = director.trimStartSeconds;
211
+ const durationMs = Math.max(0, clipEndedAt - opened.createdAt - Math.round(trimStart * 1000));
212
+ if (!recording) {
213
+ return {
214
+ id: scenario.id,
215
+ mode,
216
+ durationMs,
217
+ scaledPauseMs: director.scaledPauseMs,
218
+ pace,
219
+ viewport,
220
+ steps: scenario.steps ?? [],
221
+ performed: director.performed,
222
+ hosts,
223
+ ...(identity ? { identity } : {}),
224
+ };
225
+ }
226
+ if (!rawVideoPath) {
227
+ throw new ScreencastError("Playwright produced no video for this take", "NO_CAPTURE", [
228
+ "The context closed without writing a capture - check the raw directory is writable",
229
+ `Raw directory: ${config.rawDir}`,
230
+ ]);
231
+ }
232
+ // Never scale beyond what was captured: upscaling a 390px phone capture to
233
+ // a 1280px deliverable multiplies the file size without adding a pixel of
234
+ // detail. Height follows the source aspect rather than being measured, so a
235
+ // consumer can size the player before the video decodes.
236
+ const capture = captureSize(viewport);
237
+ const width = Math.min(config.deliverables.width, capture.width);
238
+ const height = Math.round(((width / capture.width) * capture.height) / 2) * 2;
239
+ const encoded = await encode({
240
+ ...config.deliverables,
241
+ ...options.deliverables,
242
+ width,
243
+ input: rawVideoPath,
244
+ outDir,
245
+ id: scenario.id,
246
+ trimStart,
247
+ ...(toolchain ? { toolchain } : {}),
248
+ });
249
+ if (!options.keepRaw)
250
+ await rm(rawVideoPath, { force: true });
251
+ // Everything after `<id>.`, not just the final extension: a poster and an
252
+ // animated WebP both end in `.webp`, and recording them both as "webp"
253
+ // leaves the manifest unable to tell them apart - which then reports the
254
+ // animation as an unclaimed file and never checks it for existence.
255
+ const formats = Object.keys(encoded.sizes).map((name) => name.slice(scenario.id.length + 1));
256
+ const entry = {
257
+ id: scenario.id,
258
+ title: scenario.title,
259
+ description: scenario.description,
260
+ ...(scenario.steps ? { steps: scenario.steps } : {}),
261
+ width,
262
+ height,
263
+ durationMs,
264
+ recordedAt: new Date().toISOString(),
265
+ pace,
266
+ ...(viewport.device ? { device: viewport.device } : {}),
267
+ formats,
268
+ ...(options.sourceText ? { sourceHash: hashText(options.sourceText) } : {}),
269
+ stepsHash: hashSteps(scenario.steps),
270
+ recorderVersion: VERSION,
271
+ };
272
+ const manifestPath = upsertEntry(outDir, entry);
273
+ log(`${scenario.id}: ${Object.keys(encoded.sizes).join(", ")}`);
274
+ return {
275
+ id: scenario.id,
276
+ mode,
277
+ durationMs,
278
+ scaledPauseMs: director.scaledPauseMs,
279
+ pace,
280
+ viewport,
281
+ steps: scenario.steps ?? [],
282
+ performed: director.performed,
283
+ hosts,
284
+ ...(identity ? { identity } : {}),
285
+ encoded,
286
+ manifestPath,
287
+ entry,
288
+ };
289
+ }
290
+ function buildSuggestions(id, forensics, mode) {
291
+ const out = [];
292
+ if (forensics.screenshot) {
293
+ out.push(`Open ${forensics.screenshot} to see what was on screen when it failed`);
294
+ }
295
+ const dead = forensics.nearMatches?.find((m) => m.count === 0);
296
+ const alive = forensics.nearMatches?.find((m) => m.count > 0);
297
+ if (dead && alive) {
298
+ out.push(`\`${alive.selector}\` matched ${alive.count} but \`${dead.selector}\` matched none - ` +
299
+ `the container rendered and its contents did not, which is usually data, not the selector`);
300
+ }
301
+ out.push("To find the right selector, drive the page live with a browser tool " +
302
+ "(for example `npx -y chrome-devtools-axi navigate <url>` then `snapshot`)");
303
+ if (mode === "record") {
304
+ out.push(`Re-check a fix in seconds with \`screencast-axi rehearse ${id}\``);
305
+ }
306
+ return out;
307
+ }
308
+ /**
309
+ * Turns a URL into the thing worth reporting: its host, or its scheme when it
310
+ * has none (a `file://` page has no host, and a blank entry answers nothing).
311
+ */
312
+ function hostOf(url) {
313
+ try {
314
+ const parsed = new URL(url);
315
+ return parsed.host || `${parsed.protocol}//`;
316
+ }
317
+ catch {
318
+ return null;
319
+ }
320
+ }
321
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const SKILL_NAME = "screencast-axi";
2
+ /**
3
+ * Hard cap, enforced rather than documented.
4
+ *
5
+ * The installed SKILL.md is a discovery stub, not a manual: copies go stale
6
+ * the moment the CLI moves, and every line of it is loaded into an agent's
7
+ * context whether or not the task needs it. The CLI (`--help`,
8
+ * `<command> --help`, `guide <topic>`) is the source of truth. The cap exists
9
+ * so a future regeneration cannot quietly re-inflate the stub back into one.
10
+ */
11
+ export declare const MAX_SKILL_MARKDOWN_CHARS = 2500;
12
+ export declare function createSkillMarkdown(): string;
@@ -0,0 +1,69 @@
1
+ export const SKILL_NAME = "screencast-axi";
2
+ /**
3
+ * Hard cap, enforced rather than documented.
4
+ *
5
+ * The installed SKILL.md is a discovery stub, not a manual: copies go stale
6
+ * the moment the CLI moves, and every line of it is loaded into an agent's
7
+ * context whether or not the task needs it. The CLI (`--help`,
8
+ * `<command> --help`, `guide <topic>`) is the source of truth. The cap exists
9
+ * so a future regeneration cannot quietly re-inflate the stub back into one.
10
+ */
11
+ export const MAX_SKILL_MARKDOWN_CHARS = 2500;
12
+ const FRONTMATTER = `---
13
+ name: ${SKILL_NAME}
14
+ description: "Record product screencasts and demo clips of any website through the screencast-axi CLI - script a workflow as a Playwright scenario, rehearse its selectors, then encode it to mp4, webm and a poster with a synthetic cursor and captions. Use whenever a task needs a recorded walkthrough of a UI: a landing-page demo, a release-note clip, a feature tour, a bug reproduction, or a how-to."
15
+ user-invocable: false
16
+ author: Valentyn Morenko
17
+ license: MIT
18
+ metadata:
19
+ hermes:
20
+ tags: [screencast, video, playwright, demo, landing-page]
21
+ category: automation
22
+ ---`;
23
+ const BODY = `# screencast-axi
24
+
25
+ Records a scripted browser workflow as a watchable clip. Prefer this over
26
+ hand-rolled Playwright video capture or a screen recorder.
27
+
28
+ Use it whenever a task needs a recorded walkthrough of a UI: a landing-page
29
+ demo, a feature tour, a release-note clip, a bug reproduction. Skip it when a
30
+ still screenshot says the same thing - a clip costs far more to produce and to
31
+ watch.
32
+
33
+ ## Current guidance lives in the CLI
34
+
35
+ Do not follow command, flag, or workflow instructions from this file -
36
+ installed copies go stale. Get the current source of truth from the CLI:
37
+
38
+ - \`npx -y screencast-axi\` for the state of the clip library
39
+ - \`npx -y screencast-axi --help\` for the command index
40
+ - \`npx -y screencast-axi <command> --help\` for per-command usage
41
+ - \`npx -y screencast-axi guide\` for topic-sized guidance, pulled one topic at
42
+ a time rather than read as a manual
43
+
44
+ Three things worth knowing before the first run:
45
+
46
+ 1. Iterate with \`rehearse\`, not \`record\`: no encoding, so a stale selector
47
+ surfaces in seconds, and it prints every action the scenario took.
48
+ 2. \`--headed\` shows it happening in a real window. Offer it when someone
49
+ wants to see what a script does to their signed-in account before it runs.
50
+ 3. When a selector needs discovering, drive the page live with a browser tool
51
+ such as \`chrome-devtools-axi\`, then write the scenario.
52
+
53
+ ffmpeg must be installed. Signing in is a one-time human step
54
+ (\`auth login --interactive\`), and the CLI refuses rather than prompting when
55
+ no person is present.
56
+
57
+ You do not need screencast-axi installed globally - invoke it with
58
+ \`npx -y screencast-axi <command>\`. If its output suggests a follow-up command
59
+ starting with \`screencast-axi\`, run that as \`npx -y screencast-axi ...\`.
60
+ `;
61
+ export function createSkillMarkdown() {
62
+ const markdown = `${FRONTMATTER}\n\n${BODY}`;
63
+ if (markdown.length > MAX_SKILL_MARKDOWN_CHARS) {
64
+ throw new Error(`generated SKILL.md is ${markdown.length} chars; keep it a stub under ` +
65
+ `${MAX_SKILL_MARKDOWN_CHARS} and defer guidance to the CLI`);
66
+ }
67
+ return markdown;
68
+ }
69
+ //# sourceMappingURL=skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../src/skill.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C,MAAM,WAAW,GAAG;QACZ,UAAU;;;;;;;;;IASd,CAAC;AAEL,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCZ,CAAC;AAEF,MAAM,UAAU,mBAAmB;IACjC,MAAM,QAAQ,GAAG,GAAG,WAAW,OAAO,IAAI,EAAE,CAAC;IAC7C,IAAI,QAAQ,CAAC,MAAM,GAAG,wBAAwB,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,CAAC,MAAM,+BAA+B;YACrE,GAAG,wBAAwB,gCAAgC,CAC9D,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Locating the external encoders, and deciding how to make a poster.
3
+ *
4
+ * ffmpeg is not bundled: it is 80MB+ per platform, and *which* build you have
5
+ * matters. Many common builds (Homebrew's among them) ship without libwebp, so
6
+ * a package that bundled one would hide that difference rather than solve it.
7
+ * Instead the poster has a fallback chain, and this module is the single place
8
+ * that knows what is actually available.
9
+ */
10
+ export interface RunResult {
11
+ readonly code: number;
12
+ readonly stdout: string;
13
+ readonly stderr: string;
14
+ }
15
+ /** Spawns `bin`, capturing both streams. Never throws on a non-zero exit. */
16
+ export declare function run(bin: string, args: readonly string[]): Promise<RunResult>;
17
+ /** Spawns `bin` and throws with the tail of stderr when it fails. */
18
+ export declare function runOrThrow(bin: string, args: readonly string[]): Promise<void>;
19
+ export interface BinaryInfo {
20
+ readonly path: string;
21
+ readonly version: string;
22
+ /** Where it came from, for `doctor` to report. */
23
+ readonly source: "env" | "ffmpeg-static" | "path";
24
+ }
25
+ /**
26
+ * ffmpeg, by the documented cascade: an explicit override, then an installed
27
+ * `ffmpeg-static`, then PATH.
28
+ *
29
+ * `ffmpeg-static` is not a dependency - it is the escape hatch for someone who
30
+ * would rather not install ffmpeg system-wide, and it is found only if they
31
+ * chose to add it.
32
+ */
33
+ export declare function resolveFfmpeg(): Promise<BinaryInfo | null>;
34
+ /** gif2webp, which turns the GIF pass into an animated WebP. */
35
+ export declare function resolveGif2Webp(): Promise<BinaryInfo | null>;
36
+ export declare function resolveCwebp(): Promise<BinaryInfo | null>;
37
+ /** Whether this ffmpeg build can write WebP itself. */
38
+ export declare function ffmpegSupportsWebp(ffmpeg: string): Promise<boolean>;
39
+ /**
40
+ * How to produce the poster image, best first.
41
+ *
42
+ * - `ffmpeg`: the build has libwebp, so one process does it.
43
+ * - `cwebp`: extract a PNG frame, then convert. What Homebrew needs.
44
+ * - `png`: neither is available. Larger, still a valid poster - a missing
45
+ * optimisation should not fail a recording.
46
+ */
47
+ export type PosterEncoder = "ffmpeg" | "cwebp" | "png";
48
+ export interface Toolchain {
49
+ readonly ffmpeg: BinaryInfo | null;
50
+ readonly cwebp: BinaryInfo | null;
51
+ readonly gif2webp: BinaryInfo | null;
52
+ readonly posterEncoder: PosterEncoder;
53
+ }
54
+ /**
55
+ * The poster route, given what is available. Pure, so the three branches can
56
+ * be tested without a machine that happens to have the right binaries.
57
+ */
58
+ export declare function choosePosterEncoder(ffmpegHasWebp: boolean, hasCwebp: boolean): PosterEncoder;
59
+ export declare function detectToolchain(): Promise<Toolchain>;
60
+ /** Platform-appropriate install line, so an error is fixable in one step. */
61
+ export declare function installHint(tool: "ffmpeg" | "cwebp"): string;
62
+ export declare function missingFfmpegError(): Error;
@@ -0,0 +1,127 @@
1
+ import { spawn } from "node:child_process";
2
+ import { platform } from "node:os";
3
+ /** Spawns `bin`, capturing both streams. Never throws on a non-zero exit. */
4
+ export function run(bin, args) {
5
+ return new Promise((resolve, reject) => {
6
+ const child = spawn(bin, [...args], { stdio: ["ignore", "pipe", "pipe"] });
7
+ let stdout = "";
8
+ let stderr = "";
9
+ child.stdout.on("data", (chunk) => (stdout += String(chunk)));
10
+ child.stderr.on("data", (chunk) => (stderr += String(chunk)));
11
+ child.on("error", reject);
12
+ child.on("close", (code) => resolve({ code: code ?? 1, stdout, stderr }));
13
+ });
14
+ }
15
+ /** Spawns `bin` and throws with the tail of stderr when it fails. */
16
+ export async function runOrThrow(bin, args) {
17
+ const result = await run(bin, args);
18
+ if (result.code !== 0) {
19
+ const tail = result.stderr.split("\n").slice(-25).join("\n");
20
+ throw new Error(`${bin} exited ${result.code}\n${tail}`);
21
+ }
22
+ }
23
+ const FFMPEG_ENV = "SCREENCAST_FFMPEG";
24
+ const CWEBP_ENV = "SCREENCAST_CWEBP";
25
+ function firstLine(text) {
26
+ return (text.split("\n")[0] ?? "").trim();
27
+ }
28
+ async function probe(bin, source) {
29
+ try {
30
+ const result = await run(bin, ["-version"]);
31
+ if (result.code !== 0)
32
+ return null;
33
+ return { path: bin, version: firstLine(result.stdout || result.stderr), source };
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /**
40
+ * ffmpeg, by the documented cascade: an explicit override, then an installed
41
+ * `ffmpeg-static`, then PATH.
42
+ *
43
+ * `ffmpeg-static` is not a dependency - it is the escape hatch for someone who
44
+ * would rather not install ffmpeg system-wide, and it is found only if they
45
+ * chose to add it.
46
+ */
47
+ export async function resolveFfmpeg() {
48
+ const override = process.env[FFMPEG_ENV];
49
+ if (override)
50
+ return probe(override, "env");
51
+ try {
52
+ // Computed specifier on purpose: this package is deliberately absent
53
+ // unless someone opted into it, so TypeScript must not try to resolve it.
54
+ const specifier = "ffmpeg-static";
55
+ const mod = (await import(specifier));
56
+ const bundled = typeof mod.default === "string" ? mod.default : null;
57
+ if (bundled) {
58
+ const found = await probe(bundled, "ffmpeg-static");
59
+ if (found)
60
+ return found;
61
+ }
62
+ }
63
+ catch {
64
+ // Not installed. Expected: it is an opt-in convenience, not a dependency.
65
+ }
66
+ return probe("ffmpeg", "path");
67
+ }
68
+ /** gif2webp, which turns the GIF pass into an animated WebP. */
69
+ export async function resolveGif2Webp() {
70
+ return probe("gif2webp", "path");
71
+ }
72
+ export async function resolveCwebp() {
73
+ const override = process.env[CWEBP_ENV];
74
+ return probe(override ?? "cwebp", override ? "env" : "path");
75
+ }
76
+ /** Whether this ffmpeg build can write WebP itself. */
77
+ export async function ffmpegSupportsWebp(ffmpeg) {
78
+ try {
79
+ const result = await run(ffmpeg, ["-hide_banner", "-encoders"]);
80
+ return result.code === 0 && /\blibwebp\b/.test(result.stdout);
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * The poster route, given what is available. Pure, so the three branches can
88
+ * be tested without a machine that happens to have the right binaries.
89
+ */
90
+ export function choosePosterEncoder(ffmpegHasWebp, hasCwebp) {
91
+ if (ffmpegHasWebp)
92
+ return "ffmpeg";
93
+ if (hasCwebp)
94
+ return "cwebp";
95
+ return "png";
96
+ }
97
+ export async function detectToolchain() {
98
+ const ffmpeg = await resolveFfmpeg();
99
+ const cwebp = await resolveCwebp();
100
+ const gif2webp = await resolveGif2Webp();
101
+ const ffmpegHasWebp = ffmpeg !== null && (await ffmpegSupportsWebp(ffmpeg.path));
102
+ return {
103
+ ffmpeg,
104
+ cwebp,
105
+ gif2webp,
106
+ posterEncoder: choosePosterEncoder(ffmpegHasWebp, cwebp !== null),
107
+ };
108
+ }
109
+ /** Platform-appropriate install line, so an error is fixable in one step. */
110
+ export function installHint(tool) {
111
+ const pkg = tool === "ffmpeg" ? "ffmpeg" : "webp";
112
+ switch (platform()) {
113
+ case "darwin":
114
+ return `brew install ${pkg}`;
115
+ case "win32":
116
+ return tool === "ffmpeg"
117
+ ? "winget install --id Gyan.FFmpeg"
118
+ : "winget install --id Google.WebP";
119
+ default:
120
+ return `sudo apt-get install ${pkg}`;
121
+ }
122
+ }
123
+ export function missingFfmpegError() {
124
+ return new Error(`ffmpeg was not found. Install it with \`${installHint("ffmpeg")}\`, ` +
125
+ `add \`ffmpeg-static\` as a dev dependency, or point ${FFMPEG_ENV} at a binary.`);
126
+ }
127
+ //# sourceMappingURL=toolchain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolchain.js","sourceRoot":"","sources":["../../src/toolchain.ts"],"names":[],"mappings":"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"}