makefx 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,62 +1,34 @@
1
- # Make Effects
1
+ # makefx
2
2
 
3
- Make Effects is an AI media production workspace at
4
- [makefx.app](https://makefx.app) where people and agents can generate, refine,
5
- track, and hand off production assets across images, audio, and video.
3
+ Command-line interface for [Make Effects](https://makefx.app), an AI media
4
+ production workspace for game assets, production references, and handoff files.
6
5
 
7
- The hosted app is the source of truth for spaces, assets, variants, prompts,
8
- recipes, lineage, collaboration state, and stored media. The `makefx` CLI lets
9
- local agents, scripts, and developers drive that same production graph from the
10
- command line: start generation jobs, watch progress, download outputs, inspect
11
- results, curate variants, and export production handoff data.
6
+ `makefx` lets local developers, scripts, and coding agents create and manage
7
+ website-backed assets from the terminal: generate images, audio, and video;
8
+ upload local media; inspect variants; download outputs; watch real-time space
9
+ events; and export production records.
12
10
 
13
- ## What You Can Build With It
14
-
15
- - Generate images, audio, and video through website-backed jobs.
16
- - Track every result as an asset variant with prompt, provider metadata, and
17
- lineage.
18
- - Refine an existing variant or derive new assets from references.
19
- - Upload local media into a collaborative space so generated and hand-made
20
- assets live together.
21
- - Let automation agents use the CLI as a stable control surface instead of
22
- touching database or storage internals.
23
- - Export production records for downstream render, game, or editorial tools.
24
-
25
- ## Features
26
-
27
- - **Authentication**: Google OAuth with JWT tokens, OIDC-compliant
28
- - **Dual-Worker Architecture**: Separate workers for HTTP/frontend and media generation
29
- - **React 19 Frontend**: Vite, TanStack Router, Zustand, CSS Modules
30
- - **D1 Database**: SQLite with migrations and Kysely query builder
31
- - **R2 Media Storage**: Durable media artifacts for generated and uploaded files
32
- - **Dependency Injection**: InversifyJS for clean architecture
33
- - **Make Effects CLI**: Command-line access through the `makefx` package and command
34
- - **TypeScript**: End-to-end type safety
35
-
36
- ## CLI Quick Start
37
-
38
- Install the CLI from npm:
11
+ ## Install
39
12
 
40
13
  ```bash
41
14
  npm install -g makefx
15
+ makefx --version
42
16
  ```
43
17
 
44
- Authenticate with the hosted app:
18
+ Requirements:
45
19
 
46
- ```bash
47
- makefx login
48
- ```
20
+ - Node.js 20 or newer
21
+ - A Make Effects account at https://makefx.app
22
+
23
+ The npm package installs a bundled executable and does not declare runtime
24
+ dependencies.
49
25
 
50
- Create a space or bind an existing website space to the current directory:
26
+ ## Quick Start
51
27
 
52
28
  ```bash
29
+ makefx login
53
30
  makefx spaces create "My Game Assets" --init
54
- makefx init --space YOUR_SPACE_ID
55
- ```
56
-
57
- Generate media and download completed outputs:
58
31
 
59
- ```bash
60
32
  makefx generate "A cozy pixel-art market background" \
61
33
  --name "Market Background" \
62
34
  --type scene \
@@ -73,113 +45,133 @@ makefx video generate "A looping idle animation for a tiny robot" \
73
45
  -o video/robot-idle.mp4
74
46
  ```
75
47
 
76
- Inspect and reuse tracked results:
48
+ ## Authentication
77
49
 
78
50
  ```bash
79
- makefx assets
80
- makefx assets show ASSET_ID --json
81
- makefx assets download VARIANT_ID -o references/variant.png
82
- makefx listen --space YOUR_SPACE_ID
83
- makefx rotation --variant VARIANT_ID --config 8-directional
84
- makefx tileset "cohesive grass, path, and stone terrain tiles" --type terrain --grid 3x3
51
+ makefx login
52
+ makefx logout
85
53
  ```
86
54
 
87
- Use run manifests for debugging agent workflows:
55
+ `login` opens a browser-based sign-in flow and stores CLI credentials in the
56
+ user config directory. Credentials are separate from project bindings and are
57
+ not written into your repository.
58
+
59
+ ## Project Binding
60
+
61
+ Bind the current directory to a Make Effects space:
88
62
 
89
63
  ```bash
90
- makefx runs --debug
91
- makefx runs show --latest --debug --json
64
+ makefx init --space YOUR_SPACE_ID
92
65
  ```
93
66
 
67
+ This writes `.inventory/config.json` with the target environment and space ID.
68
+ It does not store assets, prompts, media files, provider keys, or auth tokens.
69
+ Inside an initialized project, commands can omit `--space` and `--env`.
70
+
94
71
  The CLI defaults to production at `https://makefx.app`. Use `--env stage` for
95
- staging or `--local` for a local development server.
96
-
97
- ## CLI Commands
98
-
99
- | Command | Description |
100
- |---------|-------------|
101
- | `makefx login` | Authenticate with makefx.app |
102
- | `makefx spaces` | List, view, or create collaborative spaces |
103
- | `makefx init` | Bind the current directory to a website space |
104
- | `makefx generate` | Generate a new image asset |
105
- | `makefx refine` | Refine an existing image variant |
106
- | `makefx derive` | Create a new image asset from variant IDs or local refs |
107
- | `makefx batch` | Generate multiple images and write a debug run manifest |
108
- | `makefx audio` | Generate speech, dialogue, music, or sound effects |
109
- | `makefx video` | Generate, refine, or derive video assets |
110
- | `makefx upload` | Upload local image, audio, or video files |
111
- | `makefx assets` | List, show, download, rename, delete, and set active assets |
112
- | `makefx variants` | Retry, star, rate, or delete variants |
113
- | `makefx rotation` | Generate and monitor rotation views from a completed image variant |
114
- | `makefx tileset` | Generate and monitor consistent tile sets |
115
- | `makefx listen` | Stream real-time space events |
116
- | `makefx productions` | Place, list, and export production records |
117
- | `makefx runs` | Inspect debug-only local generation manifests |
118
-
119
- See [docs/cli.md](./docs/cli.md) and
120
- [docs/cli-generation.md](./docs/cli-generation.md) for the full command guide.
121
-
122
- ## Local Development
72
+ staging, or `--local` for a local development server.
123
73
 
124
- ```bash
125
- # Install dependencies
126
- pnpm install
74
+ ## Common Workflows
75
+
76
+ Inspect and download tracked assets:
127
77
 
128
- # Set up environment
129
- cp .env.example .env
130
- # Edit .env with your Google OAuth credentials
78
+ ```bash
79
+ makefx assets
80
+ makefx assets show ASSET_ID --json
81
+ makefx assets download VARIANT_ID -o references/variant.png
82
+ ```
131
83
 
132
- # Initialize local database
133
- pnpm run db:migrate
84
+ Upload local media:
134
85
 
135
- # Start development
136
- pnpm run dev
86
+ ```bash
87
+ makefx upload hero.png --name "Hero Character" --type character
88
+ makefx upload theme.mp3 --name "Theme Music" --type audio
89
+ makefx upload cutscene.mp4 --name "Cutscene" --type video
137
90
  ```
138
91
 
139
- Access at http://localhost:3001/
92
+ Refine or derive from references:
140
93
 
141
- ## Developer Commands
94
+ ```bash
95
+ makefx refine --variant VARIANT_ID "make it evening, with warmer lights" \
96
+ -o art/market-evening.png
142
97
 
143
- | Command | Description |
144
- |---------|-------------|
145
- | `pnpm run dev` | Start local development (frontend + worker) |
146
- | `pnpm run build:cli` | Build `dist/cli/makefx.mjs` |
147
- | `pnpm run cli:dev -- --help` | Run the CLI from TypeScript sources |
148
- | `pnpm run db:migrate` | Apply database migrations locally |
149
- | `pnpm test` | Run tests |
150
- | `pnpm run typecheck` | TypeScript type checking |
151
- | `pnpm run lint` | ESLint |
152
- | `pnpm run deploy:stage` | Deploy to stage environment |
153
- | `pnpm run deploy:production` | Deploy to production |
98
+ makefx derive \
99
+ --refs CHARACTER_VARIANT_ID,BACKGROUND_VARIANT_ID \
100
+ --name "Hero In Market" \
101
+ --type scene \
102
+ "Place the hero naturally in the market" \
103
+ -o art/hero-market.png
104
+ ```
154
105
 
155
- ## Project Structure
106
+ Create production helpers:
156
107
 
108
+ ```bash
109
+ makefx rotation --variant VARIANT_ID --config 8-directional
110
+ makefx tileset "grass and stone path tiles" --type terrain --grid 3x3
111
+ makefx productions export --production-id trailer-01 -o handoff/scenes.args
157
112
  ```
158
- src/
159
- ├── backend/ # API routes, services, middleware
160
- ├── frontend/ # React application
161
- ├── dao/ # Data access layer
162
- ├── db/migrations/ # SQL migrations
163
- ├── cli/ # CLI tool
164
- └── worker/ # Cloudflare Worker entry points
113
+
114
+ Watch real-time space activity:
115
+
116
+ ```bash
117
+ makefx listen --space YOUR_SPACE_ID
118
+ makefx listen --space YOUR_SPACE_ID --json
165
119
  ```
166
120
 
167
- ## Documentation
121
+ ## Agent And Script Usage
168
122
 
169
- - [docs/cli.md](./docs/cli.md) - complete CLI reference
170
- - [docs/cli-generation.md](./docs/cli-generation.md) - agent-oriented media generation flows
171
- - [docs/architecture.md](./docs/architecture.md) - system architecture
172
- - [PRD.md](./PRD.md) - product requirements and core concepts
123
+ Many commands support `--json` for stable machine-readable output. Use JSON
124
+ outputs for automation instead of parsing human-readable tables.
173
125
 
174
- ## Tech Stack
126
+ ```bash
127
+ makefx spaces create "Agent Test Space" --init --json
128
+ makefx assets --json
129
+ makefx runs show --latest --debug --json
130
+ ```
175
131
 
176
- - **Runtime**: Cloudflare Workers
177
- - **Database**: Cloudflare D1 (SQLite)
178
- - **Frontend**: React 19, Vite, Zustand
179
- - **Backend**: Hono, InversifyJS, Kysely
180
- - **Auth**: Google OAuth, JWT (jose)
181
- - **Testing**: Node.js test runner
132
+ Debug run manifests are local troubleshooting traces for generation commands:
182
133
 
183
- ## License
134
+ ```bash
135
+ makefx runs --debug
136
+ makefx runs show --latest --debug
137
+ ```
184
138
 
185
- MIT
139
+ The website remains the source of truth for spaces, assets, variants, recipes,
140
+ lineage, and stored media.
141
+
142
+ ## Command Groups
143
+
144
+ | Command | Purpose |
145
+ |---------|---------|
146
+ | `login`, `logout` | Manage CLI authentication |
147
+ | `spaces`, `init` | Create spaces and bind local projects |
148
+ | `generate`, `refine`, `derive`, `batch` | Generate image assets |
149
+ | `audio` | Generate speech, dialogue, music, and sound effects |
150
+ | `video` | Generate, refine, and derive video assets |
151
+ | `upload` | Upload local image, audio, or video files |
152
+ | `assets`, `variants` | Inspect, download, curate, retry, or delete outputs |
153
+ | `rotation`, `tileset` | Generate game-ready reference sets |
154
+ | `productions` | Place and export production handoff records |
155
+ | `listen` | Stream real-time space events |
156
+ | `runs` | Inspect local debug manifests |
157
+ | `billing` | Inspect billing sync and operational status |
158
+
159
+ Run `makefx --help` or `makefx help <command>` for command-specific options.
160
+
161
+ ## Troubleshooting
162
+
163
+ - If a command says you are not authenticated, run `makefx login`.
164
+ - If a command cannot find a space, run `makefx init --space YOUR_SPACE_ID` or
165
+ pass `--space YOUR_SPACE_ID`.
166
+ - If a generation command times out after creating a variant, resume with
167
+ `--follow VARIANT_ID` and the same output path.
168
+ - If automation needs stable output, add `--json` where supported.
169
+
170
+ ## Links
171
+
172
+ - App: https://makefx.app
173
+ - CLI docs: https://makefx.app/docs/cli
174
+ - Media generation guide: https://makefx.app/docs/media-playbooks
175
+ - Source: https://github.com/krasnoperov/inventory
176
+
177
+ Version: 0.1.2
@@ -488,6 +488,9 @@ async function handleBilling(parsed) {
488
488
  case "ops-check":
489
489
  await handleBillingOpsCheck(env);
490
490
  break;
491
+ case "reconcile":
492
+ await handleBillingReconcile(env, parsed);
493
+ break;
491
494
  case "retry":
492
495
  case "retry-failed":
493
496
  await handleBillingRetry(env);
@@ -510,6 +513,7 @@ Usage:
510
513
  Subcommands:
511
514
  status Show sync status (pending, failed, synced events)
512
515
  check Run operational checks for workers, Polar meters, and sync health
516
+ reconcile Compare local billable usage with Polar usage for one user
513
517
  retry-failed Reset failed events for retry (next cron will sync them)
514
518
 
515
519
  Options:
@@ -520,6 +524,7 @@ Examples:
520
524
  makefx billing status Show production sync status
521
525
  makefx billing status --env stage Show stage sync status
522
526
  makefx billing check Run production operational checks
527
+ makefx billing reconcile --user-id 42 Reconcile one customer usage period
523
528
  makefx billing retry-failed Reset failed events for retry
524
529
 
525
530
  Note: Requires login first. Run 'makefx login' to authenticate.
@@ -656,15 +661,35 @@ function secondsSummary(seconds) {
656
661
  }
657
662
  async function handleBillingOpsCheck(env) {
658
663
  console.log(`Running billing operational checks for ${env}...\n`);
659
- const [workerChecks, billingChecks] = await Promise.all([checkWorkerHealth(env), callBillingApi(env, "/api/billing/operational-checks")]);
664
+ const workerChecks = await checkWorkerHealth(env);
660
665
  console.log("=== Billing Operational Checks ===\n");
661
666
  console.log("Workers:");
662
667
  for (const check of workerChecks) console.log(` ${statusLabel(check.status)} ${check.name.padEnd(12)} ${check.message}`);
668
+ let billingChecks;
669
+ try {
670
+ billingChecks = await callBillingApi(env, "/api/billing/operational-checks");
671
+ } catch (error) {
672
+ console.log("\nAuthenticated billing checks:");
673
+ console.log(` ${statusLabel("critical")} ${error instanceof Error ? error.message : String(error)}`);
674
+ console.log("\nResult: CRITICAL - billing checks need an authenticated admin session.");
675
+ process$1.exitCode = 1;
676
+ return;
677
+ }
663
678
  const meterCheck = billingChecks.checks.polarMeters;
664
679
  console.log("\nPolar meters:");
665
680
  console.log(` ${statusLabel(meterCheck.status)} configured=${meterCheck.configured} active=${meterCheck.active.length} expected=${meterCheck.expected.length}`);
666
681
  if (meterCheck.error) console.log(` Error: ${meterCheck.error}`);
667
682
  if (meterCheck.missing.length > 0) console.log(` Missing: ${meterCheck.missing.join(", ")}`);
683
+ if (meterCheck.invalid.length > 0) console.log(` Invalid: ${meterCheck.invalid.map((meter) => meter.name).join(", ")}`);
684
+ const productCheck = billingChecks.checks.paidGenerationProduct;
685
+ if (productCheck) {
686
+ console.log("\nPaid generation product:");
687
+ const product = productCheck.product;
688
+ console.log(` ${statusLabel(productCheck.status)} configured=${product?.configured ?? false} recurring=${product?.isRecurring ?? "n/a"} archived=${product?.isArchived ?? "n/a"}`);
689
+ if (product?.productId) console.log(` Product: ${product.productId}${product.name ? ` (${product.name})` : ""}`);
690
+ console.log(` Metered prices: ${product?.meteredPriceMeters.length ?? 0}/${productCheck.expectedMeteredPriceMeters.length}`);
691
+ if (productCheck.missingMeteredPriceMeters.length > 0) console.log(` Missing metered prices: ${productCheck.missingMeteredPriceMeters.join(", ")}`);
692
+ }
668
693
  const syncCheck = billingChecks.checks.syncHealth;
669
694
  console.log("\nSync health:");
670
695
  console.log(` ${statusLabel(syncCheck.status)} pending=${syncCheck.events.pending} failed=${syncCheck.events.failed} synced=${syncCheck.events.synced}`);
@@ -677,11 +702,38 @@ async function handleBillingOpsCheck(env) {
677
702
  const hasCriticalBilling = billingChecks.status === "critical";
678
703
  const hasWarning = workerChecks.some((check) => check.status === "warning") || billingChecks.status === "warning";
679
704
  if (hasCriticalWorker || hasCriticalBilling) {
680
- console.log("\nResult: CRITICAL - production billing operations need attention.");
705
+ console.log("\nResult: CRITICAL - billing operations need attention.");
681
706
  process$1.exitCode = 1;
682
707
  } else if (hasWarning) console.log("\nResult: WARNING - billing is operational but has lag or cleanup work.");
683
708
  else console.log("\nResult: OK - billing workers, meters, and sync health are ready.");
684
709
  }
710
+ async function handleBillingReconcile(env, parsed) {
711
+ const userId = parsed.options["user-id"] || parsed.options.userId || parsed.positionals[1];
712
+ if (!userId) {
713
+ console.error("Missing user id. Use: makefx billing reconcile --user-id <id>");
714
+ process$1.exitCode = 1;
715
+ return;
716
+ }
717
+ console.log(`Reconciling billing usage for user ${userId} in ${env}...\n`);
718
+ try {
719
+ const data = await callBillingApi(env, `/api/billing/reconcile?user_id=${encodeURIComponent(userId)}`);
720
+ console.log("=== Billing Reconciliation ===\n");
721
+ console.log(`Period: ${data.period.start} → ${data.period.end}`);
722
+ console.log(`Status: ${data.status.toUpperCase()}`);
723
+ console.log("\nMeters:");
724
+ for (const meter of data.meters) {
725
+ const marker = meter.matched ? "OK " : "MISMATCH";
726
+ console.log(` ${marker} ${meter.name.padEnd(22)} local=${meter.local} polar=${meter.polar} delta=${meter.delta}`);
727
+ }
728
+ if (data.mismatches.length > 0) {
729
+ console.log("\nMismatched meters need sync/retry investigation before billing is considered reconciled.");
730
+ process$1.exitCode = 1;
731
+ }
732
+ } catch (error) {
733
+ console.error("Reconciliation failed:", error instanceof Error ? error.message : error);
734
+ process$1.exitCode = 1;
735
+ }
736
+ }
685
737
  async function handleBillingRetry(env) {
686
738
  console.log(`Resetting failed events for ${env}...\n`);
687
739
  try {
@@ -756,65 +808,91 @@ function validateProjectConfig(config, configPath) {
756
808
  * makefx spaces --id <space_id> Show details for a specific space
757
809
  * makefx spaces create <name> Create a new space
758
810
  */
811
+ var defaultDeps$9 = {
812
+ loadConfig: loadStoredConfig,
813
+ resolveBaseUrl,
814
+ saveProjectConfig,
815
+ fetch,
816
+ print: console.log
817
+ };
759
818
  async function handleSpaces(parsed) {
819
+ try {
820
+ await executeSpaces(parsed);
821
+ } catch (error) {
822
+ console.error("Error:", error instanceof Error ? error.message : error);
823
+ process$1.exitCode = 1;
824
+ }
825
+ }
826
+ async function executeSpaces(parsed, deps = defaultDeps$9) {
760
827
  const env = resolveCommandEnvironment(parsed);
761
828
  const showDetails = parsed.options.details === "true";
762
829
  const spaceId = parsed.options.id;
763
830
  const subcommand = parsed.positionals[0];
764
831
  const jsonOutput = parsed.options.json === "true";
765
- const config = await loadStoredConfig(env);
766
- if (!config) {
767
- console.error(`Not logged in to ${env} environment.`);
768
- console.error(`Run: ${loginCommandForEnvironment(env)}`);
769
- process$1.exitCode = 1;
770
- return;
771
- }
772
- if (config.token.expiresAt < Date.now()) {
773
- console.error(`Token expired for ${env} environment.`);
774
- console.error(`Run: ${loginCommandForEnvironment(env)}`);
775
- process$1.exitCode = 1;
776
- return;
777
- }
778
- const baseUrl = resolveBaseUrl(env);
779
- const accessToken = config.token.accessToken;
832
+ const config = await deps.loadConfig(env);
833
+ if (!config) throw new Error(`Not logged in to ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
834
+ if (config.token.expiresAt < Date.now()) throw new Error(`Token expired for ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
780
835
  if (env === "local") process$1.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
781
- try {
782
- if (subcommand === "create") {
783
- const spaceName = parsed.positionals.slice(1).join(" ") || parsed.options.name;
784
- if (!spaceName) {
785
- console.error("Error: Space name is required");
786
- console.error("Usage: makefx spaces create <name>");
787
- console.error(" makefx spaces create --name \"My Space\"");
788
- process$1.exitCode = 1;
789
- return;
790
- }
791
- const space = await createSpace(baseUrl, accessToken, spaceName);
792
- const project = parsed.options.init === "true" ? {
793
- configPath: await saveProjectConfig({
794
- environment: env,
795
- spaceId: space.id
796
- }),
836
+ const ctx = {
837
+ env,
838
+ baseUrl: deps.resolveBaseUrl(env),
839
+ accessToken: config.token.accessToken
840
+ };
841
+ if (subcommand === "create") {
842
+ const spaceName = parsed.positionals.slice(1).join(" ") || parsed.options.name;
843
+ if (!spaceName) throw new Error("Space name is required. Usage: makefx spaces create <name>");
844
+ const space = await createSpace(ctx, deps, spaceName);
845
+ const project = parsed.options.init === "true" ? {
846
+ configPath: await deps.saveProjectConfig({
797
847
  environment: env,
798
848
  spaceId: space.id
799
- } : void 0;
800
- if (jsonOutput) console.log(JSON.stringify({
801
- space,
802
- project
803
- }, null, 2));
804
- else printCreatedSpace(space, env, project?.configPath);
805
- } else if (spaceId) await showSpaceDetails(baseUrl, accessToken, spaceId);
806
- else if (showDetails) await listSpacesWithDetails(baseUrl, accessToken);
807
- else await listSpaces(baseUrl, accessToken);
808
- } catch (error) {
809
- console.error("Error:", error instanceof Error ? error.message : error);
810
- process$1.exitCode = 1;
849
+ }),
850
+ environment: env,
851
+ spaceId: space.id
852
+ } : void 0;
853
+ if (jsonOutput) deps.print(JSON.stringify({
854
+ space,
855
+ project
856
+ }, null, 2));
857
+ else printCreatedSpace(space, ctx.env, project?.configPath, deps.print);
858
+ return {
859
+ type: "create",
860
+ space,
861
+ project
862
+ };
811
863
  }
864
+ if (spaceId) {
865
+ const details = await getSpaceDetails(ctx, deps, spaceId);
866
+ if (jsonOutput) deps.print(JSON.stringify(details, null, 2));
867
+ else printSpaceDetails(details, deps.print);
868
+ return {
869
+ type: "show",
870
+ space: details.space,
871
+ assets: details.assets
872
+ };
873
+ }
874
+ if (showDetails) {
875
+ const spaces = await listSpaceSummaries(ctx, deps);
876
+ if (jsonOutput) deps.print(JSON.stringify(spaces, null, 2));
877
+ else printSpaceSummaries(spaces, deps.print);
878
+ return {
879
+ type: "details",
880
+ spaces
881
+ };
882
+ }
883
+ const spaces = await fetchSpaces(ctx, deps);
884
+ if (jsonOutput) deps.print(JSON.stringify(spaces, null, 2));
885
+ else printSpaces(spaces, deps.print);
886
+ return {
887
+ type: "list",
888
+ spaces
889
+ };
812
890
  }
813
- async function createSpace(baseUrl, accessToken, name) {
814
- const response = await fetch(`${baseUrl}/api/spaces`, {
891
+ async function createSpace(ctx, deps, name) {
892
+ const response = await deps.fetch(`${ctx.baseUrl}/api/spaces`, {
815
893
  method: "POST",
816
894
  headers: {
817
- "Authorization": `Bearer ${accessToken}`,
895
+ "Authorization": `Bearer ${ctx.accessToken}`,
818
896
  "Content-Type": "application/json"
819
897
  },
820
898
  body: JSON.stringify({ name })
@@ -825,83 +903,82 @@ async function createSpace(baseUrl, accessToken, name) {
825
903
  }
826
904
  return (await response.json()).space;
827
905
  }
828
- function printCreatedSpace(space, env, configPath) {
906
+ function printCreatedSpace(space, env, configPath, print) {
829
907
  const envFlag = env === "production" ? "" : env === "local" ? " --local" : ` --env ${env}`;
830
- console.log(`\nSpace created successfully!\n`);
831
- console.log(` ID: ${space.id}`);
832
- console.log(` Name: ${space.name}`);
833
- console.log(` Role: ${space.role}`);
834
- if (configPath) console.log(` Config: ${configPath}`);
835
- console.log(`\nTo bind this directory:`);
836
- console.log(` makefx init --space ${space.id}${envFlag}`);
837
- console.log(`\nTo listen for events:`);
838
- console.log(` makefx listen --space ${space.id}${envFlag}`);
839
- }
840
- async function listSpaces(baseUrl, accessToken) {
841
- const response = await fetch(`${baseUrl}/api/spaces`, { headers: {
842
- "Authorization": `Bearer ${accessToken}`,
908
+ print(`\nSpace created successfully!\n`);
909
+ print(` ID: ${space.id}`);
910
+ print(` Name: ${space.name}`);
911
+ print(` Role: ${space.role}`);
912
+ if (configPath) print(` Config: ${configPath}`);
913
+ print(`\nTo bind this directory:`);
914
+ print(` makefx init --space ${space.id}${envFlag}`);
915
+ print(`\nTo listen for events:`);
916
+ print(` makefx listen --space ${space.id}${envFlag}`);
917
+ }
918
+ async function fetchSpaces(ctx, deps) {
919
+ const response = await deps.fetch(`${ctx.baseUrl}/api/spaces`, { headers: {
920
+ "Authorization": `Bearer ${ctx.accessToken}`,
843
921
  "Content-Type": "application/json"
844
922
  } });
845
923
  if (!response.ok) throw new Error(`Failed to fetch spaces: ${response.status}`);
846
- const spaces = (await response.json()).spaces;
924
+ return (await response.json()).spaces;
925
+ }
926
+ function printSpaces(spaces, print) {
847
927
  if (spaces.length === 0) {
848
- console.log("No spaces found.");
928
+ print("No spaces found.");
849
929
  return;
850
930
  }
851
- console.log(`\nFound ${spaces.length} space(s):\n`);
852
- console.log("ID".padEnd(38) + "Name".padEnd(30) + "Role");
853
- console.log("-".repeat(80));
854
- for (const space of spaces) console.log(space.id.padEnd(38) + truncatePad(space.name, 30) + space.role);
855
- console.log(`\nFor details: makefx spaces --details`);
856
- console.log(`For a specific space: makefx spaces --id <space_id>`);
931
+ print(`\nFound ${spaces.length} space(s):\n`);
932
+ print("ID".padEnd(38) + "Name".padEnd(30) + "Role");
933
+ print("-".repeat(80));
934
+ for (const space of spaces) print(space.id.padEnd(38) + truncatePad(space.name, 30) + space.role);
935
+ print(`\nFor details: makefx spaces --details`);
936
+ print(`For a specific space: makefx spaces --id <space_id>`);
857
937
  }
858
- async function listSpacesWithDetails(baseUrl, accessToken) {
859
- const response = await fetch(`${baseUrl}/api/spaces`, { headers: {
860
- "Authorization": `Bearer ${accessToken}`,
861
- "Content-Type": "application/json"
862
- } });
863
- if (!response.ok) throw new Error(`Failed to fetch spaces: ${response.status}`);
864
- const spaces = (await response.json()).spaces;
865
- if (spaces.length === 0) {
866
- console.log("No spaces found.");
938
+ async function listSpaceSummaries(ctx, deps) {
939
+ const spaces = await fetchSpaces(ctx, deps);
940
+ const summaries = [];
941
+ for (const space of spaces) {
942
+ const assets = await fetchSpaceAssets(ctx, deps, space.id);
943
+ summaries.push({
944
+ space,
945
+ assetCount: assets.length,
946
+ assetSummary: summarizeAssets(assets)
947
+ });
948
+ }
949
+ return summaries;
950
+ }
951
+ function printSpaceSummaries(summaries, print) {
952
+ if (summaries.length === 0) {
953
+ print("No spaces found.");
867
954
  return;
868
955
  }
869
- console.log(`\nFetching details for ${spaces.length} space(s)...\n`);
870
- for (const space of spaces) {
871
- const assetsResponse = await fetch(`${baseUrl}/api/spaces/${space.id}/assets`, { headers: {
872
- "Authorization": `Bearer ${accessToken}`,
873
- "Content-Type": "application/json"
874
- } });
875
- let assetCount = 0;
876
- let assetSummary = "";
877
- if (assetsResponse.ok) {
878
- const assets = (await assetsResponse.json()).assets;
879
- assetCount = assets.length;
880
- if (assets.length > 0) {
881
- const byType = /* @__PURE__ */ new Map();
882
- for (const asset of assets) {
883
- const type = asset.type || "unknown";
884
- byType.set(type, (byType.get(type) || 0) + 1);
885
- }
886
- const typeParts = [];
887
- for (const [type, count] of byType) typeParts.push(`${count} ${type}`);
888
- assetSummary = typeParts.join(", ");
889
- const firstNames = assets.slice(0, 3).map((a) => a.name);
890
- if (assets.length > 3) firstNames.push(`+${assets.length - 3} more`);
891
- assetSummary += ` (${firstNames.join(", ")})`;
892
- }
893
- }
894
- console.log(`━━━ ${space.name} ━━━`);
895
- console.log(` ID: ${space.id}`);
896
- console.log(` Role: ${space.role}`);
897
- console.log(` Assets: ${assetCount}`);
898
- if (assetSummary) console.log(` Content: ${assetSummary}`);
899
- console.log("");
956
+ print(`\nFetching details for ${summaries.length} space(s)...\n`);
957
+ for (const summary of summaries) {
958
+ print(`━━━ ${summary.space.name} ━━━`);
959
+ print(` ID: ${summary.space.id}`);
960
+ print(` Role: ${summary.space.role}`);
961
+ print(` Assets: ${summary.assetCount}`);
962
+ if (summary.assetSummary) print(` Content: ${summary.assetSummary}`);
963
+ print("");
900
964
  }
901
965
  }
902
- async function showSpaceDetails(baseUrl, accessToken, spaceId) {
903
- const spaceResponse = await fetch(`${baseUrl}/api/spaces/${spaceId}`, { headers: {
904
- "Authorization": `Bearer ${accessToken}`,
966
+ function summarizeAssets(assets) {
967
+ if (assets.length === 0) return "";
968
+ const byType = /* @__PURE__ */ new Map();
969
+ for (const asset of assets) {
970
+ const type = asset.type || "unknown";
971
+ byType.set(type, (byType.get(type) || 0) + 1);
972
+ }
973
+ const typeParts = [];
974
+ for (const [type, count] of byType) typeParts.push(`${count} ${type}`);
975
+ const firstNames = assets.slice(0, 3).map((a) => a.name);
976
+ if (assets.length > 3) firstNames.push(`+${assets.length - 3} more`);
977
+ return `${typeParts.join(", ")} (${firstNames.join(", ")})`;
978
+ }
979
+ async function getSpaceDetails(ctx, deps, spaceId) {
980
+ const spaceResponse = await deps.fetch(`${ctx.baseUrl}/api/spaces/${spaceId}`, { headers: {
981
+ "Authorization": `Bearer ${ctx.accessToken}`,
905
982
  "Content-Type": "application/json"
906
983
  } });
907
984
  if (!spaceResponse.ok) {
@@ -909,18 +986,28 @@ async function showSpaceDetails(baseUrl, accessToken, spaceId) {
909
986
  if (spaceResponse.status === 404) throw new Error(`Space not found: ${spaceId}`);
910
987
  throw new Error(`Failed to fetch space: ${spaceResponse.status}`);
911
988
  }
912
- const space = (await spaceResponse.json()).space;
913
- const assetsResponse = await fetch(`${baseUrl}/api/spaces/${spaceId}/assets`, { headers: {
914
- "Authorization": `Bearer ${accessToken}`,
989
+ const spaceData = await spaceResponse.json();
990
+ const assets = await fetchSpaceAssets(ctx, deps, spaceId);
991
+ return {
992
+ space: spaceData.space,
993
+ assets
994
+ };
995
+ }
996
+ async function fetchSpaceAssets(ctx, deps, spaceId) {
997
+ const assetsResponse = await deps.fetch(`${ctx.baseUrl}/api/spaces/${spaceId}/assets`, { headers: {
998
+ "Authorization": `Bearer ${ctx.accessToken}`,
915
999
  "Content-Type": "application/json"
916
1000
  } });
917
- let assets = [];
918
- if (assetsResponse.ok) assets = (await assetsResponse.json()).assets;
919
- console.log(`\n━━━ ${space.name} ━━━`);
920
- console.log(`ID: ${space.id}`);
921
- console.log(`Assets: ${assets.length}\n`);
1001
+ if (assetsResponse.ok) return (await assetsResponse.json()).assets;
1002
+ throw new Error(`Failed to fetch assets for space ${spaceId}: ${assetsResponse.status}`);
1003
+ }
1004
+ function printSpaceDetails(details, print) {
1005
+ const { space, assets } = details;
1006
+ print(`\n━━━ ${space.name} ━━━`);
1007
+ print(`ID: ${space.id}`);
1008
+ print(`Assets: ${assets.length}\n`);
922
1009
  if (assets.length === 0) {
923
- console.log("(No assets in this space)");
1010
+ print("(No assets in this space)");
924
1011
  return;
925
1012
  }
926
1013
  const byType = /* @__PURE__ */ new Map();
@@ -930,16 +1017,15 @@ async function showSpaceDetails(baseUrl, accessToken, spaceId) {
930
1017
  byType.get(type).push(asset);
931
1018
  }
932
1019
  for (const [type, typeAssets] of byType) {
933
- console.log(`${type.charAt(0).toUpperCase() + type.slice(1)}s (${typeAssets.length}):`);
1020
+ print(`${type.charAt(0).toUpperCase() + type.slice(1)}s (${typeAssets.length}):`);
934
1021
  for (const asset of typeAssets) {
935
- const hasImage = asset.active_variant_id ? "✓" : "○";
936
- console.log(` ${hasImage} ${asset.name}`);
937
- console.log(` ID: ${asset.id}`);
1022
+ print(` ${asset.active_variant_id ? "✓" : "○"} ${asset.name}`);
1023
+ print(` ID: ${asset.id}`);
938
1024
  }
939
- console.log("");
1025
+ print("");
940
1026
  }
941
- console.log(`\nTo start a chat session with this space:`);
942
- console.log(` makefx chat send "What's in this space?" --space ${spaceId} --state ./test/${space.name.toLowerCase().replace(/\s+/g, "-")}.json`);
1027
+ print(`\nTo start a chat session with this space:`);
1028
+ print(` makefx chat send "What's in this space?" --space ${space.id} --state ./test/${space.name.toLowerCase().replace(/\s+/g, "-")}.json`);
943
1029
  }
944
1030
  function truncatePad(str, width) {
945
1031
  if (str.length > width - 2) return str.slice(0, width - 3) + ".. ";
@@ -21542,9 +21628,9 @@ async function executeForgeCommand(command, parsed, deps = defaultDeps$6, option
21542
21628
  try {
21543
21629
  await client.connect();
21544
21630
  switch (command) {
21545
- case "generate": return await executeGenerate(parsed, ctx, client, deps, mediaKind);
21631
+ case "generate": return await executeGenerate(parsed, ctx, client, deps, mediaKind, { audioMode: options.audioMode });
21546
21632
  case "refine": return await executeRefine(parsed, ctx, client, deps, mediaKind);
21547
- case "derive": return await executeDerive(parsed, ctx, client, deps, mediaKind);
21633
+ case "derive": return await executeDerive(parsed, ctx, client, deps, mediaKind, { audioMode: options.audioMode });
21548
21634
  case "batch": return await executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManifest);
21549
21635
  }
21550
21636
  } finally {
@@ -21560,7 +21646,8 @@ async function executeAudioCommand(command, parsed, deps = defaultDeps$6, option
21560
21646
  if (parsed.options.input && command !== "generate") throw new Error("Audio --input is only supported with generate");
21561
21647
  return executeForgeCommand(command, await prepareAudioParsedArgs(parsed, options.mode), deps, {
21562
21648
  mediaKind: audioProfile.mediaKind,
21563
- saveBatchManifest: audioProfile.savesBatchManifest
21649
+ saveBatchManifest: audioProfile.savesBatchManifest,
21650
+ audioMode: options.mode
21564
21651
  });
21565
21652
  }
21566
21653
  async function executeVideoCommand(command, parsed, deps = defaultDeps$6) {
@@ -21583,12 +21670,17 @@ async function prepareAudioParsedArgs(parsed, mode) {
21583
21670
  const input = await readFile(options.input, "utf8");
21584
21671
  positionals.push(input);
21585
21672
  }
21673
+ if (!options.follow) validateAudioModeRequiredVoiceOptions(options, effectiveMode);
21586
21674
  return {
21587
21675
  options,
21588
21676
  positionals
21589
21677
  };
21590
21678
  }
21591
- async function executeGenerate(parsed, ctx, client, deps, mediaKind) {
21679
+ function validateAudioModeRequiredVoiceOptions(options, mode) {
21680
+ if (mode === "speech" && !normalizeCliOption(options.voice)) throw new Error("Speech generation requires --voice <voice_id>. Run: makefx audio voices");
21681
+ if (mode === "dialogue" && !normalizeCliOption(options["dialogue-voices"] ?? options.dialogueVoices)) throw new Error("Dialogue generation requires --dialogue-voices <voice_id,voice_id>. Run: makefx audio voices");
21682
+ }
21683
+ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptions = {}) {
21592
21684
  const prompt = getPrompt(parsed, "generate");
21593
21685
  const outputPath = getOutputPath$1(parsed);
21594
21686
  const name = requireOption(parsed, "name");
@@ -21619,7 +21711,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind) {
21619
21711
  ...musicProvider ? { musicProvider } : {},
21620
21712
  ...videoAudioOptions,
21621
21713
  ...videoOptions,
21622
- onStarted: (started) => printFollowHint(started, ctx, outputPath, "generate", mediaKind)
21714
+ onStarted: (started) => printFollowHint(started, ctx, outputPath, "generate", mediaKind, followOptions)
21623
21715
  });
21624
21716
  const productionRecord = await placeProductionRecordFromScene({
21625
21717
  command: "generate",
@@ -21721,7 +21813,7 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
21721
21813
  printResult(result, outputPath, ctx, manifestPath, productionRecord);
21722
21814
  return result;
21723
21815
  }
21724
- async function executeDerive(parsed, ctx, client, deps, mediaKind) {
21816
+ async function executeDerive(parsed, ctx, client, deps, mediaKind, followOptions = {}) {
21725
21817
  const prompt = getPrompt(parsed, "derive");
21726
21818
  const outputPath = getOutputPath$1(parsed);
21727
21819
  const name = requireOption(parsed, "name");
@@ -21752,7 +21844,7 @@ async function executeDerive(parsed, ctx, client, deps, mediaKind) {
21752
21844
  mediaKind,
21753
21845
  ...videoAudioOptions,
21754
21846
  ...videoOptions,
21755
- onStarted: (started) => printFollowHint(started, ctx, outputPath, "derive", mediaKind)
21847
+ onStarted: (started) => printFollowHint(started, ctx, outputPath, "derive", mediaKind, followOptions)
21756
21848
  });
21757
21849
  const productionRecord = await placeProductionRecordFromScene({
21758
21850
  command: "derive",
@@ -22175,16 +22267,17 @@ function printResult(result, outputPath, ctx, manifestPath, productionRecord) {
22175
22267
  if (productionRecord) console.log(` Production: ${productionRecord.production_id} (${productionRecord.id})`);
22176
22268
  console.log(` Web: ${ctx.baseUrl}/spaces/${ctx.spaceId}/assets/${variant.asset_id}`);
22177
22269
  }
22178
- function printFollowHint(started, ctx, outputPath, command, mediaKind) {
22270
+ function printFollowHint(started, ctx, outputPath, command, mediaKind, options = {}) {
22179
22271
  console.log(` Started variant: ${started.jobId}`);
22180
- console.log(` Follow: ${formatFollowCommand(started.jobId, outputPath, ctx, command, mediaKind)}`);
22272
+ console.log(` Follow: ${formatFollowCommand(started.jobId, outputPath, ctx, command, mediaKind, options)}`);
22181
22273
  }
22182
- function formatFollowCommand(variantId, outputPath, ctx, command, mediaKind) {
22274
+ function formatFollowCommand(variantId, outputPath, ctx, command, mediaKind, options = {}) {
22183
22275
  const baseCommand = mediaKind === "audio" ? [
22184
22276
  "makefx",
22185
22277
  "audio",
22278
+ options.audioMode,
22186
22279
  "generate"
22187
- ] : mediaKind === "video" ? [
22280
+ ].filter((part) => Boolean(part)) : mediaKind === "video" ? [
22188
22281
  "makefx",
22189
22282
  "video",
22190
22283
  command
@@ -22250,10 +22343,11 @@ function validateVideoAudioOptions(parsed, mediaKind) {
22250
22343
  if (parsed.options.audio !== void 0 && (parsed.options["no-audio"] !== void 0 || parsed.options.noAudio !== void 0)) throw new Error("Pass either --audio or --no-audio, not both");
22251
22344
  }
22252
22345
  function parseVideoAudioOptions(parsed, mediaKind) {
22253
- if (mediaKind !== "video") return {};
22254
- if (parsed.options.audio !== void 0) return { generateAudio: true };
22255
- if (parsed.options["no-audio"] !== void 0 || parsed.options.noAudio !== void 0) return { generateAudio: false };
22256
- return { generateAudio: false };
22346
+ if (mediaKind === "video") {
22347
+ if (parsed.options.audio !== void 0) return { generateAudio: true };
22348
+ if (parsed.options["no-audio"] !== void 0 || parsed.options.noAudio !== void 0) return { generateAudio: false };
22349
+ }
22350
+ return {};
22257
22351
  }
22258
22352
  function parseVideoGenerationOptions(parsed, mediaKind) {
22259
22353
  const resolutionValue = readOptionalOption(parsed, "resolution", "resolution") ?? readOptionalOption(parsed, "video-resolution", "videoResolution");
@@ -22568,9 +22662,9 @@ Usage:
22568
22662
  makefx audio <${modes}> batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
22569
22663
  makefx audio music generate "prompt" --provider lyria --name <name> -o <file> [--space <id>]
22570
22664
 
22571
- Voice selection:
22665
+ Voice selection (run 'makefx audio voices' to list voice IDs):
22572
22666
  makefx audio speech generate "text" --voice <voice_id> --name <name> -o <file>
22573
- makefx audio dialogue generate --input script.txt --voice <fallback_id> --dialogue-voices <voice_id,voice_id> --name <name> -o <file>
22667
+ makefx audio dialogue generate --input script.txt --dialogue-voices <voice_id,voice_id> --name <name> -o <file>
22574
22668
 
22575
22669
  Low-level compatibility:
22576
22670
  makefx audio generate "prompt" --name <name> --type <type> -o <file> [--space <id>]
@@ -22618,8 +22712,8 @@ Video:
22618
22712
  --tier <tier> Veo model tier: ${tierValues}
22619
22713
 
22620
22714
  Audio:
22621
- --audio Request native synchronized Veo audio
22622
- --no-audio Request a silent video (default)
22715
+ --audio Request native synchronized Veo audio (default)
22716
+ --no-audio Request a silent video; MakeFX records this and adds a silent-video instruction.
22623
22717
 
22624
22718
  Production metadata:
22625
22719
  --scene-label <label> --timeline-start-ms <ms> --duration-ms <ms>
@@ -23513,9 +23607,11 @@ Options:
23513
23607
  }
23514
23608
  //#endregion
23515
23609
  //#region src/cli/index.ts
23516
- var CLI_VERSION = "0.1.0+8fe959ed0b7b";
23610
+ var CLI_VERSION = "0.1.2+2907a84228d9";
23517
23611
  async function main() {
23518
- const [, , command, ...args] = process$1.argv;
23612
+ const argv = process$1.argv.slice(2);
23613
+ if (argv[0] === "--") argv.shift();
23614
+ const [command, ...args] = argv;
23519
23615
  if (command === "--version" || command === "version") {
23520
23616
  console.log(CLI_VERSION);
23521
23617
  return;
@@ -23702,12 +23798,15 @@ Project:
23702
23798
  Billing (Polar.sh):
23703
23799
  billing status Show sync status (pending, failed, synced events)
23704
23800
  billing check Run operational checks for workers, meters, and sync health
23801
+ billing reconcile --user-id <id>
23802
+ Compare local billable usage with Polar usage
23705
23803
  billing retry-failed Retry all failed events
23706
23804
 
23707
23805
  Spaces:
23708
23806
  spaces List all spaces
23709
- spaces --details List spaces with asset summaries
23710
- spaces --id <id> Show details for a specific space
23807
+ spaces --json List all spaces as JSON
23808
+ spaces --details [--json] List spaces with asset summaries
23809
+ spaces --id <id> [--json] Show details for a specific space
23711
23810
  spaces create <name> Create a new space
23712
23811
 
23713
23812
  Listen:
@@ -23800,6 +23899,7 @@ Usage:
23800
23899
  Subcommands:
23801
23900
  status Show sync status (pending, failed, synced events)
23802
23901
  check Run operational checks for workers, Polar meters, and sync health
23902
+ reconcile Compare local billable usage with Polar usage for one user
23803
23903
  retry-failed Reset failed events for retry (next cron will sync them)
23804
23904
 
23805
23905
  Options:
@@ -23811,8 +23911,9 @@ function printSpacesHelp() {
23811
23911
  console.log(`
23812
23912
  Usage:
23813
23913
  makefx spaces
23814
- makefx spaces --details
23815
- makefx spaces --id <space_id>
23914
+ makefx spaces --json
23915
+ makefx spaces --details [--json]
23916
+ makefx spaces --id <space_id> [--json]
23816
23917
  makefx spaces create <name>
23817
23918
  makefx spaces create --name "My Space" [--init] [--json]
23818
23919
  `);
@@ -23989,8 +24090,7 @@ Usage:
23989
24090
  ${videoCapabilityHelp()}
23990
24091
 
23991
24092
  Audio:
23992
- --audio Request native synchronized Veo audio
23993
- --no-audio Request a silent video (default)
24093
+ Video defaults to generated audio. Use --no-audio to record a silent-video request and add a silent-video instruction.
23994
24094
 
23995
24095
  Production metadata:
23996
24096
  --scene-label <label> --timeline-start-ms <ms> --duration-ms <ms>
@@ -24007,8 +24107,7 @@ Usage:
24007
24107
  ${videoCapabilityHelp()}
24008
24108
 
24009
24109
  Audio:
24010
- --audio Request native synchronized Veo audio
24011
- --no-audio Request a silent video (default)
24110
+ Video defaults to generated audio. Use --no-audio to record a silent-video request and add a silent-video instruction.
24012
24111
 
24013
24112
  Production metadata:
24014
24113
  --scene-label <label> --timeline-start-ms <ms> --duration-ms <ms>
@@ -24025,8 +24124,7 @@ Usage:
24025
24124
  ${videoCapabilityHelp()}
24026
24125
 
24027
24126
  Audio:
24028
- --audio Request native synchronized Veo audio
24029
- --no-audio Request a silent video (default)
24127
+ Video defaults to generated audio. Use --no-audio to record a silent-video request and add a silent-video instruction.
24030
24128
 
24031
24129
  Production metadata:
24032
24130
  --scene-label <label> --timeline-start-ms <ms> --duration-ms <ms>
@@ -24044,8 +24142,7 @@ Usage:
24044
24142
  ${videoCapabilityHelp()}
24045
24143
 
24046
24144
  Audio:
24047
- --audio Request native synchronized Veo audio
24048
- --no-audio Request a silent video (default)
24145
+ Video defaults to generated audio. Use --no-audio to record a silent-video request and add a silent-video instruction.
24049
24146
 
24050
24147
  Production metadata:
24051
24148
  --scene-label <label> --timeline-start-ms <ms> --duration-ms <ms>
package/package.json CHANGED
@@ -1,111 +1,25 @@
1
1
  {
2
2
  "name": "makefx",
3
- "version": "0.1.0",
4
- "description": "Make Effects CLI and Cloudflare app for AI-assisted game asset production.",
3
+ "version": "0.1.2",
4
+ "description": "Command-line interface for AI-assisted game asset production with Make Effects.",
5
+ "license": "MIT",
5
6
  "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/krasnoperov/inventory.git"
10
+ },
6
11
  "bin": {
7
- "makefx": "dist/cli/makefx.mjs"
12
+ "makefx": "makefx.mjs"
8
13
  },
9
14
  "files": [
10
- "dist/cli/makefx.mjs",
15
+ "makefx.mjs",
11
16
  "README.md"
12
17
  ],
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "dependencies": {},
13
22
  "publishConfig": {
14
23
  "access": "public"
15
- },
16
- "packageManager": "pnpm@11.1.1",
17
- "scripts": {
18
- "bootstrap:worktree": "node ./scripts/bootstrap-worktree.mjs",
19
- "dev": "CLOUDFLARE_VITE_CONFIG_PATH=wrangler.dev.toml vite",
20
- "build": "vite build && pnpm run package:frontend-start && pnpm run build:cli",
21
- "package:frontend-start": "node scripts/package-frontend-start.mjs",
22
- "build:cli": "node scripts/build-cli.mjs",
23
- "deploy": "pnpm run build && pnpm exec wrangler deploy",
24
- "deploy:production": "CLOUDFLARE_ENV=production pnpm run build && pnpm exec wrangler deploy --env production",
25
- "deploy:stage": "pnpm run build && pnpm exec wrangler deploy",
26
- "test": "node ./scripts/run-node-tests.mjs",
27
- "test:components": "playwright test --config playwright.component.config.ts",
28
- "test:components:ci": "node ./scripts/run-component-ci.mjs",
29
- "test:e2e:media-foundation": "pnpm run build && node scripts/e2e-media-foundation.mjs",
30
- "test:e2e:cli-forge": "pnpm run build && node scripts/e2e-cli-forge.mjs",
31
- "auth:session-state": "node scripts/auth/mint-session.mjs",
32
- "lint": "oxlint --config .oxlintrc.json src scripts && pnpm d1:guard && pnpm lint:tokens",
33
- "lint:tokens": "pnpm tokens:guard && pnpm tokens:snapshot:check && pnpm tokens:roundtrip",
34
- "tokens:guard": "node scripts/check-design-tokens.mjs",
35
- "tokens:snapshot": "node scripts/snapshot-design-tokens.mjs",
36
- "tokens:snapshot:check": "node scripts/snapshot-design-tokens.mjs --check",
37
- "tokens:roundtrip": "node scripts/generate-css-from-tokens.mjs --check",
38
- "d1:guard": "node scripts/check-no-d1-transactions.mjs",
39
- "typecheck": "tsc --noEmit",
40
- "ci:pre-push": "pnpm run typecheck && pnpm run lint && pnpm run build && pnpm test --test-reporter=dot && pnpm run test:components:ci",
41
- "db:migrate": "node --import @swc-node/register/esm-register scripts/db-migrate.ts",
42
- "db:migrate:stage": "node --import @swc-node/register/esm-register scripts/db-migrate.ts stage remote",
43
- "db:migrate:production": "node --import @swc-node/register/esm-register scripts/db-migrate.ts production remote",
44
- "cli": "node dist/cli/makefx.mjs",
45
- "cli:dev": "node --import @swc-node/register/esm-register src/cli/index.ts",
46
- "stories": "LADLE=true ladle serve",
47
- "stories:build": "LADLE=true ladle build",
48
- "style-reference": "node scripts/export-style-reference.mjs",
49
- "style-reference:rebuild": "node scripts/export-style-reference.mjs --rebuild"
50
- },
51
- "dependencies": {
52
- "@anthropic-ai/sdk": "^0.71.0",
53
- "@google/genai": "^1.30.0",
54
- "@hono/zod-openapi": "^1.4.0",
55
- "@openai/agents": "^0.3.3",
56
- "@polar-sh/sdk": "^0.41.5",
57
- "@react-oauth/google": "^0.12.2",
58
- "@tanstack/react-query": "^5.90.11",
59
- "@tanstack/react-router": "^1.170.15",
60
- "@tanstack/react-start": "^1.168.25",
61
- "@types/d3-force": "^3.0.10",
62
- "@types/dagre": "^0.7.53",
63
- "@xyflow/react": "^12.9.3",
64
- "better-sqlite3": "^12.4.6",
65
- "d3-force": "^3.0.0",
66
- "dagre": "^0.8.5",
67
- "dotenv": "^17.2.3",
68
- "fflate": "^0.8.2",
69
- "hono": "^4.10.6",
70
- "inversify": "^7.10.4",
71
- "jose": "^6.1.2",
72
- "kysely": "^0.28.8",
73
- "kysely-d1": "^0.4.0",
74
- "nanoid": "^5.1.11",
75
- "react": "^19.2.0",
76
- "react-dom": "^19.2.0",
77
- "react-markdown": "^10.1.0",
78
- "reflect-metadata": "^0.2.2",
79
- "remark-gfm": "^4.0.1",
80
- "standardwebhooks": "1.0.0",
81
- "uuid": "^13.0.0",
82
- "ws": "^8.21.0",
83
- "zod": "^4.4.3",
84
- "zustand": "^5.0.8"
85
- },
86
- "devDependencies": {
87
- "@cloudflare/ai": "^1.2.2",
88
- "@cloudflare/vite-plugin": "^1.39.0",
89
- "@cloudflare/workers-types": "4.20260317.1",
90
- "@csstools/postcss-light-dark-function": "^3.0.0",
91
- "@ladle/react": "^5.1.1",
92
- "@playwright/test": "^1.59.1",
93
- "@swc-node/register": "^1.11.1",
94
- "@tanstack/router-plugin": "^1.168.18",
95
- "@types/better-sqlite3": "^7.6.13",
96
- "@types/node": "^24.10.1",
97
- "@types/react": "^19.2.7",
98
- "@types/react-dom": "^19.2.3",
99
- "@types/ws": "^8.18.1",
100
- "@vitejs/plugin-react": "^5.1.1",
101
- "express": "^5.1.0",
102
- "oxlint": "1.67.0",
103
- "postcss": "^8.5.6",
104
- "postcss-preset-env": "^10.4.0",
105
- "prettier": "^3.6.2",
106
- "rollup-plugin-visualizer": "^6.0.5",
107
- "typescript": "^5.9.3",
108
- "vite": "^8.0.14",
109
- "wrangler": "4.95.0"
110
24
  }
111
25
  }