@argo-video/cli 0.1.0 → 0.1.1

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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/asset-server.d.ts +7 -0
  4. package/dist/asset-server.d.ts.map +1 -0
  5. package/dist/asset-server.js +66 -0
  6. package/dist/asset-server.js.map +1 -0
  7. package/dist/captions.d.ts +17 -0
  8. package/dist/captions.d.ts.map +1 -0
  9. package/dist/captions.js +23 -0
  10. package/dist/captions.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +87 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +44 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +74 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/export.d.ts +18 -0
  20. package/dist/export.d.ts.map +1 -0
  21. package/dist/export.js +64 -0
  22. package/dist/export.js.map +1 -0
  23. package/dist/fixtures.d.ts +13 -0
  24. package/dist/fixtures.d.ts.map +1 -0
  25. package/dist/fixtures.js +36 -0
  26. package/dist/fixtures.js.map +1 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +14 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/init.d.ts +2 -0
  32. package/dist/init.d.ts.map +1 -0
  33. package/{src/init.ts → dist/init.js} +39 -54
  34. package/dist/init.js.map +1 -0
  35. package/dist/narration.d.ts +9 -0
  36. package/dist/narration.d.ts.map +1 -0
  37. package/dist/narration.js +27 -0
  38. package/dist/narration.js.map +1 -0
  39. package/dist/overlays/index.d.ts +8 -0
  40. package/dist/overlays/index.d.ts.map +1 -0
  41. package/dist/overlays/index.js +34 -0
  42. package/dist/overlays/index.js.map +1 -0
  43. package/dist/overlays/manifest.d.ts +5 -0
  44. package/dist/overlays/manifest.d.ts.map +1 -0
  45. package/dist/overlays/manifest.js +52 -0
  46. package/dist/overlays/manifest.js.map +1 -0
  47. package/dist/overlays/motion.d.ts +4 -0
  48. package/dist/overlays/motion.d.ts.map +1 -0
  49. package/dist/overlays/motion.js +25 -0
  50. package/dist/overlays/motion.js.map +1 -0
  51. package/dist/overlays/templates.d.ts +7 -0
  52. package/dist/overlays/templates.d.ts.map +1 -0
  53. package/dist/overlays/templates.js +98 -0
  54. package/dist/overlays/templates.js.map +1 -0
  55. package/dist/overlays/types.d.ts +42 -0
  56. package/dist/overlays/types.d.ts.map +1 -0
  57. package/dist/overlays/types.js +25 -0
  58. package/dist/overlays/types.js.map +1 -0
  59. package/dist/overlays/zones.d.ts +15 -0
  60. package/dist/overlays/zones.d.ts.map +1 -0
  61. package/dist/overlays/zones.js +69 -0
  62. package/dist/overlays/zones.js.map +1 -0
  63. package/dist/pipeline.d.ts +3 -0
  64. package/dist/pipeline.d.ts.map +1 -0
  65. package/dist/pipeline.js +93 -0
  66. package/dist/pipeline.js.map +1 -0
  67. package/dist/record.d.ts +14 -0
  68. package/dist/record.d.ts.map +1 -0
  69. package/dist/record.js +100 -0
  70. package/dist/record.js.map +1 -0
  71. package/dist/tts/align.d.ts +17 -0
  72. package/dist/tts/align.d.ts.map +1 -0
  73. package/dist/tts/align.js +40 -0
  74. package/dist/tts/align.js.map +1 -0
  75. package/dist/tts/cache.d.ts +31 -0
  76. package/dist/tts/cache.d.ts.map +1 -0
  77. package/dist/tts/cache.js +51 -0
  78. package/dist/tts/cache.js.map +1 -0
  79. package/dist/tts/engine.d.ts +41 -0
  80. package/dist/tts/engine.d.ts.map +1 -0
  81. package/dist/tts/engine.js +108 -0
  82. package/dist/tts/engine.js.map +1 -0
  83. package/dist/tts/generate.d.ts +20 -0
  84. package/dist/tts/generate.d.ts.map +1 -0
  85. package/dist/tts/generate.js +58 -0
  86. package/dist/tts/generate.js.map +1 -0
  87. package/dist/tts/kokoro.d.ts +13 -0
  88. package/dist/tts/kokoro.d.ts.map +1 -0
  89. package/dist/tts/kokoro.js +46 -0
  90. package/dist/tts/kokoro.js.map +1 -0
  91. package/package.json +13 -1
  92. package/.claude/settings.local.json +0 -34
  93. package/DESIGN.md +0 -261
  94. package/docs/enhancement-proposal.md +0 -262
  95. package/docs/superpowers/plans/2026-03-12-argo.md +0 -208
  96. package/docs/superpowers/plans/2026-03-12-editorial-overlay-system.md +0 -1560
  97. package/docs/superpowers/plans/2026-03-13-npm-rename-skill-showcase.md +0 -499
  98. package/docs/superpowers/specs/2026-03-13-npm-rename-skill-showcase-design.md +0 -109
  99. package/skills/argo-demo-creator.md +0 -355
  100. package/src/asset-server.ts +0 -81
  101. package/src/captions.ts +0 -36
  102. package/src/cli.ts +0 -97
  103. package/src/config.ts +0 -125
  104. package/src/export.ts +0 -93
  105. package/src/fixtures.ts +0 -50
  106. package/src/index.ts +0 -41
  107. package/src/narration.ts +0 -31
  108. package/src/overlays/index.ts +0 -54
  109. package/src/overlays/manifest.ts +0 -68
  110. package/src/overlays/motion.ts +0 -27
  111. package/src/overlays/templates.ts +0 -121
  112. package/src/overlays/types.ts +0 -73
  113. package/src/overlays/zones.ts +0 -82
  114. package/src/pipeline.ts +0 -120
  115. package/src/record.ts +0 -123
  116. package/src/tts/align.ts +0 -75
  117. package/src/tts/cache.ts +0 -65
  118. package/src/tts/engine.ts +0 -147
  119. package/src/tts/generate.ts +0 -83
  120. package/src/tts/kokoro.ts +0 -51
  121. package/tests/asset-server.test.ts +0 -67
  122. package/tests/captions.test.ts +0 -76
  123. package/tests/cli.test.ts +0 -131
  124. package/tests/config.test.ts +0 -150
  125. package/tests/e2e/fake-server.ts +0 -45
  126. package/tests/e2e/record.e2e.test.ts +0 -131
  127. package/tests/export.test.ts +0 -155
  128. package/tests/fixtures.test.ts +0 -74
  129. package/tests/init.test.ts +0 -77
  130. package/tests/narration.test.ts +0 -120
  131. package/tests/overlays/index.test.ts +0 -73
  132. package/tests/overlays/manifest.test.ts +0 -120
  133. package/tests/overlays/motion.test.ts +0 -34
  134. package/tests/overlays/templates.test.ts +0 -69
  135. package/tests/overlays/types.test.ts +0 -36
  136. package/tests/overlays/zones.test.ts +0 -49
  137. package/tests/pipeline.test.ts +0 -177
  138. package/tests/record.test.ts +0 -87
  139. package/tests/tts/align.test.ts +0 -118
  140. package/tests/tts/cache.test.ts +0 -110
  141. package/tests/tts/engine.test.ts +0 -204
  142. package/tests/tts/generate.test.ts +0 -177
  143. package/tests/tts/kokoro.test.ts +0 -25
  144. package/tsconfig.json +0 -19
@@ -0,0 +1,58 @@
1
+ /**
2
+ * TTS clip generation with manifest parsing and cache integration.
3
+ */
4
+ import fs from 'node:fs';
5
+ import { ClipCache } from './cache.js';
6
+ export async function generateClips(options) {
7
+ const { manifestPath, demoName, engine, projectRoot, defaults } = options;
8
+ // 1. Check manifest exists
9
+ if (!fs.existsSync(manifestPath)) {
10
+ throw new Error(`Manifest file not found: ${manifestPath}`);
11
+ }
12
+ // 2. Read and parse JSON
13
+ let rawEntries;
14
+ try {
15
+ const content = fs.readFileSync(manifestPath, 'utf-8');
16
+ rawEntries = JSON.parse(content);
17
+ }
18
+ catch (err) {
19
+ if (err instanceof SyntaxError) {
20
+ throw new Error(`Failed to parse manifest ${manifestPath}: ${err.message}`);
21
+ }
22
+ throw err;
23
+ }
24
+ if (!Array.isArray(rawEntries)) {
25
+ throw new Error(`Manifest ${manifestPath} must contain a JSON array`);
26
+ }
27
+ // 3. Validate entries
28
+ for (const entry of rawEntries) {
29
+ const e = entry;
30
+ if (typeof e.scene !== 'string' || typeof e.text !== 'string') {
31
+ throw new Error('Manifest entry missing required field: scene and text are required');
32
+ }
33
+ }
34
+ const cache = new ClipCache(projectRoot);
35
+ const results = [];
36
+ for (const raw of rawEntries) {
37
+ const r = raw;
38
+ // 4. Build ManifestEntry with defaults
39
+ const manifestEntry = {
40
+ scene: r.scene,
41
+ text: r.text,
42
+ voice: r.voice ?? defaults?.voice,
43
+ speed: r.speed ?? defaults?.speed,
44
+ };
45
+ const clipPath = cache.getClipPath(demoName, manifestEntry);
46
+ // 5/6. Check cache or generate
47
+ if (!cache.isCached(demoName, manifestEntry)) {
48
+ const wavBuffer = await engine.generate(manifestEntry.text, {
49
+ voice: manifestEntry.voice,
50
+ speed: manifestEntry.speed,
51
+ });
52
+ cache.cacheClip(demoName, manifestEntry, wavBuffer);
53
+ }
54
+ results.push({ scene: manifestEntry.scene, clipPath });
55
+ }
56
+ return results;
57
+ }
58
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/tts/generate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAsB,MAAM,YAAY,CAAC;AAe3D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA6B;IAC/D,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAE1E,2BAA2B;IAC3B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,4BAA4B,YAAY,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,yBAAyB;IACzB,IAAI,UAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACvD,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,4BAA4B,YAAY,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,YAAY,YAAY,4BAA4B,CAAC,CAAC;IACxE,CAAC;IAED,sBAAsB;IACtB,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,KAAgC,CAAC;QAC3C,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,GAA8B,CAAC;QAEzC,uCAAuC;QACvC,MAAM,aAAa,GAAkB;YACnC,KAAK,EAAE,CAAC,CAAC,KAAe;YACxB,IAAI,EAAE,CAAC,CAAC,IAAc;YACtB,KAAK,EAAG,CAAC,CAAC,KAA4B,IAAI,QAAQ,EAAE,KAAK;YACzD,KAAK,EAAG,CAAC,CAAC,KAA4B,IAAI,QAAQ,EAAE,KAAK;SAC1D,CAAC;QAEF,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAE5D,+BAA+B;QAC/B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC;YAC7C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE;gBAC1D,KAAK,EAAE,aAAa,CAAC,KAAK;gBAC1B,KAAK,EAAE,aAAa,CAAC,KAAK;aAC3B,CAAC,CAAC;YACH,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { TTSEngine, TTSEngineOptions } from './engine.js';
2
+ export declare class KokoroEngine implements TTSEngine {
3
+ private tts;
4
+ private modelId;
5
+ private dtype;
6
+ constructor(options?: {
7
+ modelId?: string;
8
+ dtype?: string;
9
+ });
10
+ private getTTS;
11
+ generate(text: string, options: TTSEngineOptions): Promise<Buffer>;
12
+ }
13
+ //# sourceMappingURL=kokoro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kokoro.d.ts","sourceRoot":"","sources":["../../src/tts/kokoro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/D,qBAAa,YAAa,YAAW,SAAS;IAC5C,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;YAK5C,MAAM;IAiBd,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;CAqBzE"}
@@ -0,0 +1,46 @@
1
+ export class KokoroEngine {
2
+ tts = null;
3
+ modelId;
4
+ dtype;
5
+ constructor(options) {
6
+ this.modelId = options?.modelId ?? 'onnx-community/Kokoro-82M-ONNX';
7
+ this.dtype = options?.dtype ?? 'fp32';
8
+ }
9
+ async getTTS() {
10
+ if (this.tts)
11
+ return this.tts;
12
+ try {
13
+ const { KokoroTTS } = await import('kokoro-js');
14
+ this.tts = await KokoroTTS.from_pretrained(this.modelId, {
15
+ dtype: this.dtype,
16
+ });
17
+ }
18
+ catch (err) {
19
+ throw new Error(`Failed to initialize Kokoro TTS (model: ${this.modelId}, dtype: ${this.dtype}). ` +
20
+ `This may require an internet connection for first-time model download. ` +
21
+ `Original error: ${err.message}`);
22
+ }
23
+ return this.tts;
24
+ }
25
+ async generate(text, options) {
26
+ if (!text?.trim())
27
+ throw new Error('TTS text must not be empty');
28
+ const tts = await this.getTTS();
29
+ const audio = await tts.generate(text, {
30
+ voice: options.voice ?? 'af_heart',
31
+ speed: options.speed ?? 1.0,
32
+ });
33
+ const samples = audio.data ?? audio.audio;
34
+ if (!samples || !(samples instanceof Float32Array)) {
35
+ throw new Error('kokoro-js returned unexpected audio format: neither .data nor .audio contains Float32Array samples. ' +
36
+ 'Check that your kokoro-js version is compatible.');
37
+ }
38
+ const sampleRate = audio.sampling_rate;
39
+ if (typeof sampleRate !== 'number' || sampleRate <= 0) {
40
+ throw new Error(`kokoro-js returned invalid sample rate: ${sampleRate}.`);
41
+ }
42
+ const { createWavBuffer } = await import('./engine.js');
43
+ return createWavBuffer(samples, sampleRate);
44
+ }
45
+ }
46
+ //# sourceMappingURL=kokoro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kokoro.js","sourceRoot":"","sources":["../../src/tts/kokoro.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACf,GAAG,GAAQ,IAAI,CAAC;IAChB,OAAO,CAAS;IAChB,KAAK,CAAS;IAEtB,YAAY,OAA8C;QACxD,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,gCAAgC,CAAC;QACpE,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,MAAM,CAAC;IACxC,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YAChD,IAAI,CAAC,GAAG,GAAG,MAAM,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE;gBACvD,KAAK,EAAE,IAAI,CAAC,KAAgD;aAC7D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,CAAC,OAAO,YAAY,IAAI,CAAC,KAAK,KAAK;gBAClF,yEAAyE;gBACzE,mBAAoB,GAAa,CAAC,OAAO,EAAE,CAC5C,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,OAAyB;QACpD,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE;YACrC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,UAAU;YAClC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG;SAC5B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC;QAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,YAAY,YAAY,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CACb,sGAAsG;gBACtG,kDAAkD,CACnD,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC;QACvC,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,2CAA2C,UAAU,GAAG,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QACxD,OAAO,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9C,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@argo-video/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Turn Playwright demo scripts into polished product demo videos with AI voiceover",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,6 +28,18 @@
28
28
  "commander": "^12.0.0",
29
29
  "kokoro-js": "^1.2.1"
30
30
  },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/shreyaskarnik/argo.git"
37
+ },
38
+ "license": "MIT",
39
+ "files": [
40
+ "dist",
41
+ "bin"
42
+ ],
31
43
  "devDependencies": {
32
44
  "@playwright/test": "^1.50.0",
33
45
  "@types/node": "^25.5.0",
@@ -1,34 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(git add:*)",
5
- "Bash(git commit:*)",
6
- "Bash(gh repo:*)",
7
- "Bash(gh api:*)",
8
- "Bash(/Users/shreyas/work/rnd/argo/bin/argo.js:*)",
9
- "Bash(chmod +x /Users/shreyas/work/rnd/argo/bin/argo.js)",
10
- "Bash(npm install:*)",
11
- "Bash(npx tsc:*)",
12
- "Bash(ls /Users/shreyas/work/rnd/argo/src/ /Users/shreyas/work/rnd/argo/tests/ 2>/dev/null; ls /Users/shreyas/work/rnd/argo/vitest.config.* 2>/dev/null)",
13
- "Bash(npx vitest:*)",
14
- "Bash(CI=true npx vitest run tests/tts/kokoro.test.ts 2>&1)",
15
- "mcp__plugin_context7_context7__resolve-library-id",
16
- "mcp__plugin_context7_context7__query-docs",
17
- "Bash(node -e \"\nimport\\('@huggingface/transformers'\\).then\\(async \\(m\\) => {\n // Check what task maps to what\n console.log\\('SUPPORTED_TASKS keys that include audio or speech:'\\);\n const keys = Object.keys\\(m\\).filter\\(k => k.toLowerCase\\(\\).includes\\('task'\\) || k.toLowerCase\\(\\).includes\\('mapping'\\)\\);\n console.log\\(keys.slice\\(0, 20\\)\\);\n}\\).catch\\(e => console.error\\(e.message\\)\\);\n\" 2>&1)",
18
- "Bash(node -e \"\nimport\\('@huggingface/transformers'\\).then\\(async \\(m\\) => {\n try {\n const p = await m.pipeline\\('text-to-audio', 'onnx-community/Kokoro-82M-v1.0-ONNX', { dtype: 'fp32' }\\);\n console.log\\('SUCCESS with text-to-audio'\\);\n } catch\\(e\\) {\n console.log\\('text-to-audio error:', e.message\\);\n }\n}\\).catch\\(e => console.error\\(e.message\\)\\);\n\" 2>&1)",
19
- "Bash(npm ls:*)",
20
- "Bash(npm run:*)",
21
- "Bash(node bin/argo.js init 2>&1)",
22
- "Bash(node bin/argo.js --help 2>&1)",
23
- "Bash(rm -rf demos argo.config.ts && node bin/argo.js init 2>&1)",
24
- "Bash(git rm:*)",
25
- "Bash(rm -f argo.config.ts argo.config.js && rm -rf demos/ && npm run build 2>&1 && node bin/argo.js init 2>&1)",
26
- "Bash(node -e \"import\\('./dist/config.js'\\).then\\(m => m.loadConfig\\(process.cwd\\(\\)\\)\\).then\\(c => console.log\\(JSON.stringify\\(c, null, 2\\)\\)\\)\" 2>&1)",
27
- "Bash(rm -rf demos argo.config.* playwright.config.ts && npm run build 2>&1 && node bin/argo.js init 2>&1)",
28
- "Bash(node bin/argo.js record example 2>&1)",
29
- "Bash(npx playwright:*)",
30
- "Bash(npm link:*)",
31
- "Bash(node bin/argo.js pipeline example 2>&1)"
32
- ]
33
- }
34
- }
package/DESIGN.md DELETED
@@ -1,261 +0,0 @@
1
- # Argo — Playwright Demo Recording with Voiceover
2
-
3
- **Date:** 2026-03-12
4
- **Status:** Draft
5
-
6
- ## Overview
7
-
8
- Argo is a standalone, open-source tool that turns Playwright scripts into polished product demo videos with AI-generated voiceover. Developers author demo scripts using Playwright fixtures; anyone can regenerate the final video with a single CLI command.
9
-
10
- The pipeline: **TTS** (Kokoro generates voiceover clips via Transformers.js, cached) → **record** (Playwright captures video + scene timestamps) → **align** (clips placed at recorded timestamps) → **export** (ffmpeg merges video + audio into MP4).
11
-
12
- ## Target Users
13
-
14
- - **Developers** author demo scripts using Playwright's familiar test API
15
- - **Non-developers** (marketing, DevRel) regenerate videos via CLI without touching code
16
-
17
- ## Architecture
18
-
19
- ### Library + CLI
20
-
21
- Argo ships as a single package with two interfaces:
22
-
23
- **Library** — Playwright fixtures and helpers for authoring demos:
24
- - `test`, `expect` — re-exported Playwright fixtures with `narration` auto-injected
25
- - `NarrationTimeline` — low-level control for manual usage
26
- - `showCaption`, `hideCaption`, `withCaption` — DOM caption helpers
27
- - `defineConfig` — creates a full Argo config with sensible defaults
28
- - `demosProject` — creates a Playwright project entry for integration into existing configs
29
-
30
- **CLI** — pipeline commands anyone can run:
31
- - `argo record <demo>` — run Playwright for a specific demo
32
- - `argo tts generate <manifest>` — generate TTS clips from `.voiceover.json`
33
- - `argo tts align <demo>` — align clips to recording timestamps
34
- - `argo export <demo>` — merge video + audio via ffmpeg
35
- - `argo pipeline <demo>` — all steps end-to-end
36
- - `argo init` — scaffold demo files + config into a project
37
-
38
- ### Project Layout
39
-
40
- ```
41
- argo/
42
- ├── src/
43
- │ ├── index.ts # Library exports
44
- │ ├── cli.ts # CLI entry point
45
- │ ├── fixtures.ts # Playwright test fixture (injects narration)
46
- │ ├── narration.ts # NarrationTimeline class
47
- │ ├── captions.ts # DOM caption overlay helpers
48
- │ ├── tts/
49
- │ │ ├── engine.ts # TTSEngine interface
50
- │ │ ├── kokoro.ts # Default Kokoro via @huggingface/transformers
51
- │ │ └── align.ts # Timestamp alignment logic
52
- │ ├── export.ts # ffmpeg video+audio merge
53
- │ ├── pipeline.ts # Orchestrates record→tts→align→export
54
- │ └── config.ts # defineConfig helper + defaults
55
- ├── bin/
56
- │ └── argo.js # CLI bin entry
57
- ├── package.json
58
- └── tsconfig.json
59
- ```
60
-
61
- ### Dependencies
62
-
63
- - `playwright` — recording engine (peer dependency)
64
- - `@huggingface/transformers@next` — Kokoro TTS via ONNX in Node.js
65
- - `ffmpeg` — system dependency for video export (not bundled)
66
-
67
- No Python required. The entire pipeline runs in Node.js.
68
-
69
- **ffmpeg detection:** All CLI commands that need ffmpeg (`export`, `pipeline`) check for it on startup and exit with a clear error message + install instructions if missing.
70
-
71
- ## Authoring API
72
-
73
- A demo consists of two files:
74
-
75
- ### Demo Script (`demos/onboarding.demo.ts`)
76
-
77
- ```ts
78
- import { test, demoType } from 'argo';
79
-
80
- test('onboarding', async ({ page, narration }) => {
81
- // narration.start() is called automatically by the fixture before the test runs
82
- await page.goto('/');
83
-
84
- // Show caption + record scene timestamp
85
- await narration.showCaption(page, 'welcome', 'Welcome to our app', 3000);
86
-
87
- // Caption around an action
88
- await narration.withCaption(page, 'signup', 'Sign up in seconds', async () => {
89
- await page.fill('[name=email]', 'demo@example.com');
90
- await demoType(page, '[name=password]', 'supersecure');
91
- });
92
-
93
- // Timestamp-only scene (no visible caption, used for voiceover alignment)
94
- narration.mark('dashboard-loaded');
95
- });
96
- ```
97
-
98
- **Constraints:** Each demo file must contain exactly one `test()` block (one video per demo). Use separate demo files for separate videos.
99
-
100
- **Key API:**
101
- - `narration.showCaption(page, scene, text, durationMs)` — show overlay + record timestamp
102
- - `narration.withCaption(page, scene, text, action)` — wrap action with caption
103
- - `narration.mark(scene)` — record timestamp without visual
104
- - `demoType(page, selector, text)` — standalone helper, slow-types for demo effect (60ms/char)
105
- - `narration.start()` — sets timestamp zero; called automatically by fixture before each test
106
- - `narration.flush()` — writes `.timing.json`; called automatically by fixture after each test
107
-
108
- ### Voiceover Manifest (`demos/onboarding.voiceover.json`)
109
-
110
- ```json
111
- [
112
- {
113
- "scene": "welcome",
114
- "text": "Welcome to Acme — get started in under a minute."
115
- },
116
- {
117
- "scene": "signup",
118
- "text": "Just enter your email and choose a password.",
119
- "speed": 0.9
120
- },
121
- {
122
- "scene": "dashboard-loaded",
123
- "text": "And you're in.",
124
- "voice": "af_heart"
125
- }
126
- ]
127
- ```
128
-
129
- Scene names link the manifest to the demo script. `voice` and `speed` are optional (defaults from config).
130
-
131
- ### Config (`argo.config.ts`, optional)
132
-
133
- The CLI looks for `argo.config.ts` (or `.js`, `.mjs`) in the current working directory. Override with `--config <path>` on any CLI command.
134
-
135
- ```ts
136
- import { defineConfig } from 'argo';
137
-
138
- export default defineConfig({
139
- baseURL: 'http://localhost:3000',
140
- demosDir: 'demos/',
141
- outputDir: 'videos/',
142
- tts: {
143
- defaultVoice: 'af_heart',
144
- defaultSpeed: 1.0,
145
- // engine: myCustomTTSEngine
146
- },
147
- video: {
148
- width: 2560,
149
- height: 1440,
150
- fps: 30,
151
- },
152
- export: {
153
- preset: 'slow',
154
- crf: 16,
155
- },
156
- });
157
- ```
158
-
159
- ## TTS System
160
-
161
- ### Default Engine: Kokoro via Transformers.js
162
-
163
- - Uses `@huggingface/transformers@next` with ONNX Kokoro model
164
- - Runs in Node.js — no Python, no GPU
165
- - Model downloaded on first run (~80MB ONNX), cached locally
166
- - Generates one WAV clip per scene
167
-
168
- ### Alignment
169
-
170
- 1. Recording produces `.timing.json` (scene name → millisecond timestamp)
171
- 2. Aligner reads `.timing.json` + clip durations
172
- 3. Places each clip at its scene's recorded timestamp
173
- 4. Prevents overlap — clips pushed forward with 100ms minimum gap
174
- 5. Outputs single `narration-aligned.wav` matching video duration
175
-
176
- ### Plugin Interface
177
-
178
- ```ts
179
- interface TTSEngine {
180
- generate(text: string, options: {
181
- voice?: string;
182
- speed?: number;
183
- lang?: string;
184
- }): Promise<Buffer>;
185
- // Must return a complete WAV file (with headers).
186
- // Required format: mono, 24kHz, 32-bit float.
187
- // Argo will resample if needed, but matching this format avoids overhead.
188
- }
189
- ```
190
-
191
- Custom engines (ElevenLabs, OpenAI TTS, Piper, etc.) implement this single method. Argo validates the returned WAV headers and resamples to 24kHz mono if the format doesn't match.
192
-
193
- ### Clip Caching
194
-
195
- Clips stored in `.argo/<demoName>/clips/`. Each clip is keyed by a hash of its manifest entry (`scene` + `text` + `voice` + `speed`). When a clip's entry hasn't changed, the cached WAV is reused. When an entry changes (different text, voice, or speed), only that clip is regenerated. This is per-entry, not per-file — reordering entries or changing one scene doesn't invalidate others.
196
-
197
- ## Recording
198
-
199
- - `argo record` runs Playwright with the demos project config
200
- - Uses Playwright's default VP8 encoder (no patching)
201
- - Higher quality achieved during export via ffmpeg re-encoding
202
- - Output: `video.webm` + `.timing.json` in `.argo/<demoName>/`
203
-
204
- ## Export
205
-
206
- - ffmpeg merges video + aligned audio
207
- - Re-encodes to `libx264` (compensates for VP8's lower quality)
208
- - Default: preset slow, crf 16, AAC audio @ 192k
209
- - Configurable via `argo.config.ts` or CLI flags (`--crf`, `--preset`, `--fps`, `--width`, `--height`)
210
- - Uses `-shortest` so audio trims to video length
211
- - Output: `<outputDir>/<demoName>.mp4`
212
-
213
- ## Pipeline
214
-
215
- `argo pipeline <demo>` chains all steps:
216
-
217
- ```
218
- 1. Generate TTS clips (skip if cached & manifest unchanged)
219
- 2. Record demo via Playwright → .webm + .timing.json
220
- 3. Align clips to timestamps → narration-aligned.wav
221
- 4. Export → final .mp4
222
- ```
223
-
224
- Each step is independently runnable.
225
-
226
- ## `argo init`
227
-
228
- Scaffolds into an existing project:
229
- - Creates `demos/` directory
230
- - Generates sample `example.demo.ts` and `example.voiceover.json`
231
- - Creates starter `argo.config.ts`
232
- - Adds `demos` project to `playwright.config.ts` (or creates one) using `defineConfig`
233
- - Prints next steps
234
-
235
- ## Playwright Integration
236
-
237
- Two modes:
238
-
239
- **Standalone** — Argo provides its own Playwright config via `defineConfig`. Users just install and point at their app URL. `argo init` sets this up.
240
-
241
- **Integrated** — Power users add a `demos` project to their existing `playwright.config.ts` using the exported config helper:
242
-
243
- ```ts
244
- import { demosProject } from 'argo';
245
-
246
- export default defineConfig({
247
- projects: [
248
- // ... existing projects
249
- demosProject({ baseURL: 'http://localhost:3000' }),
250
- ],
251
- });
252
- ```
253
-
254
- ## Out of Scope (v1)
255
-
256
- - Post-render subtitle burn-in via ffmpeg (future enhancement)
257
- - npm registry publishing
258
- - Non-Playwright backends (Puppeteer, Cypress)
259
- - Video editing (cuts, transitions, zooms)
260
- - Background music / audio mixing
261
- - GUI / visual editor