egaki 0.9.0 → 0.11.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.
- package/CHANGELOG.md +72 -0
- package/dist/cli/chatgpt-auth.d.ts +30 -5
- package/dist/cli/chatgpt-auth.d.ts.map +1 -1
- package/dist/cli/chatgpt-auth.js +257 -79
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +132 -28
- package/dist/cli/dev-server.d.ts +30 -0
- package/dist/cli/dev-server.d.ts.map +1 -0
- package/dist/cli/dev-server.js +266 -0
- package/dist/cli/dev-server.test.d.ts +2 -0
- package/dist/cli/dev-server.test.d.ts.map +1 -0
- package/dist/cli/dev-server.test.js +95 -0
- package/dist/cli/generate.d.ts +8 -0
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +170 -58
- package/dist/cli/generate.test.js +50 -1
- package/dist/cli/login.d.ts +38 -3
- package/dist/cli/login.d.ts.map +1 -1
- package/dist/cli/login.js +44 -17
- package/dist/cli/login.test.d.ts +2 -0
- package/dist/cli/login.test.d.ts.map +1 -0
- package/dist/cli/login.test.js +158 -0
- package/dist/cli/open-browser.d.ts +3 -1
- package/dist/cli/open-browser.d.ts.map +1 -1
- package/dist/cli/open-browser.js +2 -2
- package/dist/cli/xai-auth.d.ts +3 -1
- package/dist/cli/xai-auth.d.ts.map +1 -1
- package/dist/cli/xai-auth.js +13 -3
- package/dist/vite/angled-screen-shader.d.ts +5 -0
- package/dist/vite/angled-screen-shader.d.ts.map +1 -1
- package/dist/vite/angled-screen-shader.js +77 -34
- package/dist/vite/generated-media-client.d.ts.map +1 -1
- package/dist/vite/generated-media-client.js +2 -2
- package/dist/vite/vite-plugin.d.ts.map +1 -1
- package/dist/vite/vite-plugin.js +128 -10
- package/package.json +6 -10
- package/skills/egaki/SKILL.md +14 -0
- package/src/cli/chatgpt-auth.ts +379 -89
- package/src/cli/cli.ts +161 -27
- package/src/cli/dev-server.test.ts +109 -0
- package/src/cli/dev-server.ts +303 -0
- package/src/cli/generate.test.ts +57 -0
- package/src/cli/generate.ts +191 -61
- package/src/cli/login.test.ts +185 -0
- package/src/cli/login.ts +68 -16
- package/src/cli/open-browser.ts +7 -6
- package/src/cli/xai-auth.ts +15 -3
- package/src/vite/angled-screen-shader.tsx +81 -34
- package/src/vite/generated-media-client.tsx +6 -1
- package/src/vite/vite-plugin.ts +137 -10
package/dist/vite/vite-plugin.js
CHANGED
|
@@ -18,6 +18,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
18
18
|
import { spiceflowPlugin } from 'spiceflow/vite';
|
|
19
19
|
import react from '@vitejs/plugin-react';
|
|
20
20
|
import tailwindcss from '@tailwindcss/vite';
|
|
21
|
+
import pc from 'picocolors';
|
|
21
22
|
import { mdxParse } from 'safe-mdx/parse';
|
|
22
23
|
import { collectServerImportSources } from "./server-mdx.js";
|
|
23
24
|
import { parseFrontmatter } from "./mdx-parse.js";
|
|
@@ -34,6 +35,23 @@ const RESOLVED_MDX = '\0' + VIRTUAL_MDX;
|
|
|
34
35
|
const VIRTUAL_MODULES = 'virtual:egaki-modules';
|
|
35
36
|
const RESOLVED_MODULES = '\0' + VIRTUAL_MODULES;
|
|
36
37
|
const PKG_NAME = 'egaki';
|
|
38
|
+
// Packages that must resolve to a single physical copy (same pattern as
|
|
39
|
+
// spiceflow:dedupe-singleton). Remotion Player + useVideoConfig share React
|
|
40
|
+
// context; two copies → "No video config found". Motion patches need one
|
|
41
|
+
// JSAnimation class. resolve.dedupe alone fails under pnpm when the package
|
|
42
|
+
// is only a transitive dep of egaki (not hoisted to the consumer root).
|
|
43
|
+
const dedupePackages = new Set([
|
|
44
|
+
'remotion',
|
|
45
|
+
'@remotion/player',
|
|
46
|
+
'@remotion/media',
|
|
47
|
+
'@remotion/web-renderer',
|
|
48
|
+
'motion',
|
|
49
|
+
'motion-dom',
|
|
50
|
+
'motion-utils',
|
|
51
|
+
]);
|
|
52
|
+
// Fake importer anchored inside egaki's package so this.resolve() finds the
|
|
53
|
+
// same remotion/motion copy egaki itself depends on.
|
|
54
|
+
const dedupeImporter = path.join(__pkgRoot, '_dedupe_importer_.js');
|
|
37
55
|
/** Resolve a relative MDX import source against the project root,
|
|
38
56
|
* probing common extensions. Returns the absolute path or undefined. */
|
|
39
57
|
function resolveSourceToFile(root, source) {
|
|
@@ -237,7 +255,7 @@ export function video(options) {
|
|
|
237
255
|
if (entry.isDirectory()) {
|
|
238
256
|
walkDir(fullPath);
|
|
239
257
|
}
|
|
240
|
-
else if (/\.(tsx?|jsx?|mdx?)$/.test(entry.name) && !/\.(test|spec|config)\./.test(entry.name)) {
|
|
258
|
+
else if (/\.(tsx?|jsx?|mdx?)$/.test(entry.name) && !/\.(test|spec|config)\./.test(entry.name) && !/\.d\.ts$/.test(entry.name)) {
|
|
241
259
|
// Entry MDX files are NOT skipped: they may be imported as
|
|
242
260
|
// partials by other MDX files (e.g. `import Intro from './intro.mdx'`).
|
|
243
261
|
// Self-import is a user error and harmless (safe-mdx handles it).
|
|
@@ -291,6 +309,50 @@ export function video(options) {
|
|
|
291
309
|
].join('\n');
|
|
292
310
|
}
|
|
293
311
|
},
|
|
312
|
+
// Print SDK usage instructions after Vite's startup banner.
|
|
313
|
+
// Agents read terminal output to learn how to interact with the
|
|
314
|
+
// running dev server via Playwriter + window.egakiSDK.
|
|
315
|
+
configureServer(server) {
|
|
316
|
+
server.httpServer?.on('listening', () => {
|
|
317
|
+
const log = server.config.logger;
|
|
318
|
+
const p = (s) => log.info(s);
|
|
319
|
+
// Small delay so our block prints after Vite's "ready in" + URL lines
|
|
320
|
+
setTimeout(() => {
|
|
321
|
+
const title = ` ${pc.cyan(pc.bold('egaki SDK'))} ${pc.dim('—')} ${pc.white('programmatic control via')} ${pc.yellow('window.egakiSDK')}`;
|
|
322
|
+
const sep = pc.dim(' ─'.padEnd(72, '─'));
|
|
323
|
+
p('');
|
|
324
|
+
p(title);
|
|
325
|
+
p(sep);
|
|
326
|
+
p('');
|
|
327
|
+
p(` ${pc.green('●')} ${pc.bold('Get info')} ${pc.dim('composition metadata, sections, fps')}`);
|
|
328
|
+
p(` ${pc.cyan(`playwriter -s 1 -e 'console.log(await state.page.evaluate(() => window.egakiSDK.getInfo()))'`)}`);
|
|
329
|
+
p('');
|
|
330
|
+
p(` ${pc.green('●')} ${pc.bold('Screenshot')} ${pc.dim('capture a single frame as PNG')}`);
|
|
331
|
+
p(` ${pc.cyan("playwriter -s 1 -e \"$(cat <<'JS'")}`);
|
|
332
|
+
p(` ${pc.cyan('const dataUrl = await state.page.evaluate(() => window.egakiSDK.screenshot({ frame: 60 }))')}`);
|
|
333
|
+
p(` ${pc.cyan('const buf = Buffer.from(await (await fetch(dataUrl)).arrayBuffer())')}`);
|
|
334
|
+
p(` ${pc.cyan('require("node:fs").writeFileSync("/tmp/frame-60.png", buf)')}`);
|
|
335
|
+
p(` ${pc.cyan('JS')}`);
|
|
336
|
+
p(` ${pc.cyan(')"')}`);
|
|
337
|
+
p('');
|
|
338
|
+
p(` ${pc.green('●')} ${pc.bold('Filmstrip')} ${pc.dim('grid of frames across scenes')}`);
|
|
339
|
+
p(` ${pc.cyan("playwriter -s 1 -e \"$(cat <<'JS'")}`);
|
|
340
|
+
p(` ${pc.cyan('const dataUrl = await state.page.evaluate(() =>')}`);
|
|
341
|
+
p(` ${pc.cyan(' window.egakiSDK.filmstrip({ scenes: [0, 1, 2], framesPerScene: 3 }))')}`);
|
|
342
|
+
p(` ${pc.cyan('const buf = Buffer.from(await (await fetch(dataUrl)).arrayBuffer())')}`);
|
|
343
|
+
p(` ${pc.cyan('require("node:fs").writeFileSync("/tmp/filmstrip.png", buf)')}`);
|
|
344
|
+
p(` ${pc.cyan('JS')}`);
|
|
345
|
+
p(` ${pc.cyan(')"')}`);
|
|
346
|
+
p('');
|
|
347
|
+
p(` ${pc.green('●')} ${pc.bold('Export video')} ${pc.dim('render MP4 via WebCodecs')}`);
|
|
348
|
+
p(` ${pc.cyan(`playwriter -s 1 -e 'await state.page.evaluate(() => window.egakiSDK.export({ path: "output.mp4" }))'`)}`);
|
|
349
|
+
p('');
|
|
350
|
+
p(` ${pc.green('●')} ${pc.bold('Seek')} ${pc.dim('jump to a specific frame')}`);
|
|
351
|
+
p(` ${pc.cyan(`playwriter -s 1 -e 'await state.page.evaluate(() => window.egakiSDK.seekTo(120))'`)}`);
|
|
352
|
+
p('');
|
|
353
|
+
}, 50);
|
|
354
|
+
});
|
|
355
|
+
},
|
|
294
356
|
// HMR for file changes in the project.
|
|
295
357
|
//
|
|
296
358
|
// Entry MDX: the source string flows server → client through the RSC
|
|
@@ -352,13 +414,32 @@ export function video(options) {
|
|
|
352
414
|
return [];
|
|
353
415
|
}
|
|
354
416
|
if (isEntryMdx) {
|
|
355
|
-
|
|
417
|
+
// Every root .mdx is both a route entry (virtual:egaki-mdx / RSC
|
|
418
|
+
// flight) and a ?raw partial in virtual:egaki-modules (importable
|
|
419
|
+
// via `import X from './foo.mdx'`). Must refresh both paths:
|
|
420
|
+
// rsc:update for the entry source, and modules dep-accept for
|
|
421
|
+
// partials. Invalidating only RESOLVED_MODULES is not enough —
|
|
422
|
+
// Vite keeps the cached ?raw binding, so also invalidate every
|
|
423
|
+
// module graph node for this file (including `?raw`).
|
|
424
|
+
invalidateVirtual([RESOLVED_APP, RESOLVED_MDX, RESOLVED_MODULES]);
|
|
356
425
|
if (this.environment.name === 'client') {
|
|
426
|
+
const fileMods = this.environment.moduleGraph.getModulesByFile(ctx.file);
|
|
427
|
+
if (fileMods) {
|
|
428
|
+
for (const mod of fileMods) {
|
|
429
|
+
this.environment.moduleGraph.invalidateModule(mod);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
357
432
|
ctx.server.environments.client?.hot.send({
|
|
358
433
|
type: 'custom',
|
|
359
434
|
event: 'rsc:update',
|
|
360
435
|
data: { file: ctx.file },
|
|
361
436
|
});
|
|
437
|
+
const modulesMod = this.environment.moduleGraph.getModuleById(RESOLVED_MODULES);
|
|
438
|
+
const updates = [
|
|
439
|
+
...(fileMods ? [...fileMods] : []),
|
|
440
|
+
...(modulesMod ? [modulesMod] : []),
|
|
441
|
+
];
|
|
442
|
+
return updates;
|
|
362
443
|
}
|
|
363
444
|
return [];
|
|
364
445
|
}
|
|
@@ -408,22 +489,29 @@ export function video(options) {
|
|
|
408
489
|
arr.push(new RegExp(`^${PKG_NAME}`));
|
|
409
490
|
arr.push(/^tweakpane/);
|
|
410
491
|
config.resolve.noExternal = arr;
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
//
|
|
415
|
-
|
|
416
|
-
// runs before configResolved where hasMotion is set. Harmless when
|
|
417
|
-
// motion isn't installed — dedupe on missing packages is a no-op.
|
|
418
|
-
config.resolve.dedupe = mergeUnique(config.resolve.dedupe, ['motion', 'motion-dom', 'motion-utils']);
|
|
492
|
+
// Baseline hint for Vite's built-in resolver (same list as the
|
|
493
|
+
// resolveId singleton plugin below). Real enforcement is the
|
|
494
|
+
// egaki:dedupe-singleton plugin — resolve.dedupe alone is not enough
|
|
495
|
+
// under pnpm when remotion is only a transitive dep of egaki.
|
|
496
|
+
config.resolve.dedupe = mergeUnique(config.resolve.dedupe, [...dedupePackages]);
|
|
419
497
|
if (name === 'client') {
|
|
420
498
|
config.optimizeDeps ??= {};
|
|
421
499
|
config.optimizeDeps.exclude = mergeUnique(config.optimizeDeps.exclude, [PKG_NAME]);
|
|
500
|
+
// Prebundle safe-mdx as a single entry so its pure-CJS leaves
|
|
501
|
+
// (format via fault/micromark) stay internalized with CJS interop.
|
|
502
|
+
// Do NOT list format/fault as separate optimizeDeps entries — that
|
|
503
|
+
// splits them into named-only ESM chunks and the browser crashes with
|
|
504
|
+
// "does not provide an export named 'default'" (same bug Holocron hit
|
|
505
|
+
// when safe-mdx leaked to the client; see holocron vite CHANGELOG).
|
|
506
|
+
// Holocron's clean fix is keep safe-mdx server-only; egaki needs it on
|
|
507
|
+
// the client for MDX-in-browser, so correct prebundling is the fix.
|
|
422
508
|
config.optimizeDeps.include = mergeUnique(config.optimizeDeps.include, [
|
|
423
509
|
`${PKG_NAME} > spiceflow > @vitejs/plugin-rsc/vendor/react-server-dom/client.browser`,
|
|
424
510
|
`${PKG_NAME} > remotion`,
|
|
425
511
|
`${PKG_NAME} > @remotion/player`,
|
|
426
512
|
`${PKG_NAME} > safe-mdx`,
|
|
513
|
+
'safe-mdx',
|
|
514
|
+
'safe-mdx/parse',
|
|
427
515
|
]);
|
|
428
516
|
}
|
|
429
517
|
if (name === 'rsc' || name === 'ssr') {
|
|
@@ -432,9 +520,39 @@ export function video(options) {
|
|
|
432
520
|
}
|
|
433
521
|
},
|
|
434
522
|
};
|
|
523
|
+
// Force remotion / motion to a single copy across all importers, even when
|
|
524
|
+
// egaki is a transitive or file:-linked dep under pnpm. Mirrors
|
|
525
|
+
// spiceflow:dedupe-singleton (spiceflow/src/vite.tsx).
|
|
526
|
+
//
|
|
527
|
+
// Vite's resolve.dedupe sets basedir=root then walks node_modules/, but
|
|
528
|
+
// pnpm only symlinks direct deps at the root. Transitive deps stay buried
|
|
529
|
+
// in .pnpm/, so dedupe silently falls back to importer-based resolution
|
|
530
|
+
// and Player vs useVideoConfig load different remotion instances.
|
|
531
|
+
const dedupeSingletonPlugin = {
|
|
532
|
+
name: 'egaki:dedupe-singleton',
|
|
533
|
+
enforce: 'pre',
|
|
534
|
+
async resolveId(id, _importer, options) {
|
|
535
|
+
if (id.startsWith('.') ||
|
|
536
|
+
id.startsWith('/') ||
|
|
537
|
+
id.startsWith('\0') ||
|
|
538
|
+
id.includes('?')) {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
const pkgName = id.startsWith('@')
|
|
542
|
+
? id.split('/').slice(0, 2).join('/')
|
|
543
|
+
: (id.split('/')[0] ?? id);
|
|
544
|
+
if (!dedupePackages.has(pkgName))
|
|
545
|
+
return null;
|
|
546
|
+
return this.resolve(id, dedupeImporter, {
|
|
547
|
+
...options,
|
|
548
|
+
skipSelf: true,
|
|
549
|
+
});
|
|
550
|
+
},
|
|
551
|
+
};
|
|
435
552
|
return [
|
|
436
553
|
videoPlugin,
|
|
437
554
|
rscPackagePlugin,
|
|
555
|
+
dedupeSingletonPlugin,
|
|
438
556
|
tailwindcss(),
|
|
439
557
|
spiceflowPlugin({ entry: VIRTUAL_APP }),
|
|
440
558
|
react(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "egaki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI image, video, and speech generation CLI with MDX-to-video framework",
|
|
6
6
|
"main": "./dist/cli/main.js",
|
|
@@ -94,9 +94,10 @@
|
|
|
94
94
|
"@tailwindcss/vite": "^4.2.2",
|
|
95
95
|
"@tweakpane/core": "^2.0.5",
|
|
96
96
|
"@tweakpane/plugin-essentials": "^0.2.1",
|
|
97
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
97
98
|
"ai": "6.0.103",
|
|
98
99
|
"eventsource-parser": "^3.0.6",
|
|
99
|
-
"goke": "^6.
|
|
100
|
+
"goke": "^6.14.1",
|
|
100
101
|
"js-yaml": "^4.1.1",
|
|
101
102
|
"mediabunny": "^1.50.8",
|
|
102
103
|
"moveable": "^0.53.0",
|
|
@@ -104,27 +105,23 @@
|
|
|
104
105
|
"react": "19.2.7",
|
|
105
106
|
"react-dom": "19.2.7",
|
|
106
107
|
"remotion": "4.0.494",
|
|
107
|
-
"safe-mdx": "^1.
|
|
108
|
+
"safe-mdx": "^1.12.0",
|
|
108
109
|
"shiki": "^4.2.0",
|
|
110
|
+
"spiceflow": "1.26.0-rsc.7",
|
|
109
111
|
"string-dedent": "^3.0.2",
|
|
110
112
|
"tailwindcss": "^4.3.0",
|
|
111
113
|
"tweakpane": "^4.0.5",
|
|
114
|
+
"vite": "^8.0.10",
|
|
112
115
|
"yaml": "^2.9.0",
|
|
113
116
|
"zod": "^4.3.6",
|
|
114
|
-
"@vitejs/plugin-react": "^6.0.2",
|
|
115
|
-
"spiceflow": "1.26.0-rsc.7",
|
|
116
117
|
"zustand": "^5.0.14"
|
|
117
118
|
},
|
|
118
119
|
"peerDependencies": {
|
|
119
|
-
"vite": ">=6",
|
|
120
120
|
"motion": ">=12",
|
|
121
121
|
"motion-dom": ">=12",
|
|
122
122
|
"motion-utils": ">=12"
|
|
123
123
|
},
|
|
124
124
|
"peerDependenciesMeta": {
|
|
125
|
-
"vite": {
|
|
126
|
-
"optional": true
|
|
127
|
-
},
|
|
128
125
|
"motion": {
|
|
129
126
|
"optional": true
|
|
130
127
|
},
|
|
@@ -141,7 +138,6 @@
|
|
|
141
138
|
"devDependencies": {
|
|
142
139
|
"@types/js-yaml": "^4.0.9",
|
|
143
140
|
"@types/react": "^19.2.17",
|
|
144
|
-
"vite": "^8.0.10",
|
|
145
141
|
"vitest": "^4.1.8"
|
|
146
142
|
},
|
|
147
143
|
"scripts": {
|
package/skills/egaki/SKILL.md
CHANGED
|
@@ -66,6 +66,20 @@ Agents should always pass `-m` explicitly to avoid the interactive picker.
|
|
|
66
66
|
|
|
67
67
|
1. **`grok-imagine-video-1.5`** (xAI Grok)
|
|
68
68
|
|
|
69
|
+
## Extracting small assets from a reference
|
|
70
|
+
|
|
71
|
+
**Crop to the target object first** with an image tool, leaving a small margin,
|
|
72
|
+
then pass that crop to `egaki image --input`. This makes small details easier for
|
|
73
|
+
the model to see and reduces ambiguity about which object to preserve. In a
|
|
74
|
+
switch-extraction comparison, a tight crop preserved the design much better than
|
|
75
|
+
the full instrument image with the same prompt.
|
|
76
|
+
|
|
77
|
+
Keep the prompt short: "Extract this switch exactly as it is, with a transparent
|
|
78
|
+
background. Do not redesign it or change its shape, materials, or details."
|
|
79
|
+
|
|
80
|
+
**Inspect the result:** generation can still redraw details. Use a direct crop
|
|
81
|
+
and background masking when exact original pixels are required.
|
|
82
|
+
|
|
69
83
|
## Video generation note
|
|
70
84
|
|
|
71
85
|
Video generation can be very slow (1-3 minutes per request). Always use a
|