@codeyam/codeyam-cli 0.1.29 → 0.1.31

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 (66) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +1 -1
  4. package/analyzer-template/packages/aws/package.json +1 -1
  5. package/analyzer-template/packages/database/package.json +1 -1
  6. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +39 -3
  7. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  8. package/codeyam-cli/src/commands/editor.js +133 -0
  9. package/codeyam-cli/src/commands/editor.js.map +1 -1
  10. package/codeyam-cli/src/commands/init.js +20 -0
  11. package/codeyam-cli/src/commands/init.js.map +1 -1
  12. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
  13. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  14. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +712 -2
  15. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -1
  16. package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
  17. package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
  18. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
  19. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js.map +1 -0
  20. package/codeyam-cli/src/utils/editorRoadmap.js +290 -17
  21. package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -1
  22. package/codeyam-cli/src/utils/envFile.js +90 -0
  23. package/codeyam-cli/src/utils/envFile.js.map +1 -0
  24. package/codeyam-cli/src/utils/handoffContext.js +257 -0
  25. package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
  26. package/codeyam-cli/src/utils/install-skills.js +36 -6
  27. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  28. package/codeyam-cli/src/utils/techStackConfig.js +38 -0
  29. package/codeyam-cli/src/utils/techStackConfig.js.map +1 -0
  30. package/codeyam-cli/src/utils/techStackConfig.test.js +85 -0
  31. package/codeyam-cli/src/utils/techStackConfig.test.js.map +1 -0
  32. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +119 -1
  33. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -1
  34. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +115 -0
  35. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  36. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  37. package/codeyam-cli/src/webserver/build/client/assets/{MiniClaudeChat-BusrvT2F.js → MiniClaudeChat-Bs2_Oua4.js} +1 -1
  38. package/codeyam-cli/src/webserver/build/client/assets/api.editor-database-verify-l0sNRNKZ.js +1 -0
  39. package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
  40. package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
  41. package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
  42. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DOXe0Qx7.js +161 -0
  43. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-Ce1s4OQ1.js → entity._sha._-pc-vc6wO.js} +1 -1
  44. package/codeyam-cli/src/webserver/build/client/assets/globals-L-aUIeux.css +1 -0
  45. package/codeyam-cli/src/webserver/build/client/assets/manifest-30c44d84.js +1 -0
  46. package/codeyam-cli/src/webserver/build/client/assets/{root-CVjDQwjJ.js → root-CLedrjXQ.js} +7 -7
  47. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-CTJYMVFP.js → analysisRunner-CuR5TvUx.js} +1 -1
  48. package/codeyam-cli/src/webserver/build/server/assets/{index-CCth4Hgw.js → index-D4MWAsqb.js} +1 -1
  49. package/codeyam-cli/src/webserver/build/server/assets/init-JObA4lXD.js +14 -0
  50. package/codeyam-cli/src/webserver/build/server/assets/server-build-i8OXK4oL.js +765 -0
  51. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  52. package/codeyam-cli/src/webserver/build-info.json +5 -5
  53. package/codeyam-cli/src/webserver/editorProxy.js +77 -4
  54. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  55. package/codeyam-cli/src/webserver/terminalServer.js +81 -11
  56. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  57. package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
  58. package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
  59. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
  60. package/codeyam-cli/templates/seed-adapters/supabase.ts +185 -84
  61. package/package.json +1 -1
  62. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-785deXbZ.js +0 -147
  63. package/codeyam-cli/src/webserver/build/client/assets/globals-Bt7TsgQz.css +0 -1
  64. package/codeyam-cli/src/webserver/build/client/assets/manifest-3d8cde80.js +0 -1
  65. package/codeyam-cli/src/webserver/build/server/assets/init-UXl-3vVp.js +0 -10
  66. package/codeyam-cli/src/webserver/build/server/assets/server-build-DSW2mE30.js +0 -741
@@ -0,0 +1,59 @@
1
+ # CodeYam Editor Project (Gemini)
2
+
3
+ This project is being built with CodeYam's Code + Data Editor. The core principle is **code and data are developed together** — every component should have scenario data that drives what it renders.
4
+
5
+ ## Architecture: Components + Scenarios
6
+
7
+ ### Component Structure
8
+
9
+ Build with a clear component hierarchy so individual components can be previewed in isolation:
10
+
11
+ ```
12
+ app/
13
+ components/
14
+ Dashboard.tsx ← page-level component
15
+ TaskList.tsx ← displays a list, gets data from API or props
16
+ TaskCard.tsx ← single item, pure props
17
+ EmptyState.tsx ← shown when no data
18
+ ```
19
+
20
+ ### Scenarios
21
+
22
+ A **scenario** is a named set of data that drives how the app renders. Scenarios exist at two levels:
23
+
24
+ - **App-level scenarios**: Full application state.
25
+ - **Component-level scenarios**: Props for a single component in isolation.
26
+
27
+ **Always register scenarios with CodeYam** using the CLI:
28
+
29
+ ```bash
30
+ codeyam editor register @.codeyam/tmp/scenario.json
31
+ ```
32
+
33
+ **To list existing scenarios:** `codeyam editor scenarios`
34
+
35
+ ## Workflow
36
+
37
+ 1. Build a component
38
+ 2. Create scenarios with rich, realistic data.
39
+ 3. Register the scenarios with CodeYam.
40
+ 4. Preview each scenario to verify the component handles all states.
41
+ 5. Repeat for the next component.
42
+
43
+ ## Key Conventions
44
+
45
+ - Component files go in `app/components/` (or `src/components/`)
46
+ - One component per file, default export.
47
+ - Components accept props that can be provided by scenarios.
48
+
49
+ ## AI Handoff
50
+
51
+ Since you are working in an AI-assisted workflow, you must maintain a handoff summary for other AI agents (or yourself in a later session).
52
+
53
+ **At the end of every step, update the handoff summary:**
54
+
55
+ ```bash
56
+ codeyam editor handoff '{"summary": "Built the X component and registered 3 scenarios. Ready to start on Y."}'
57
+ ```
58
+
59
+ This summary will be displayed when starting a new session or switching between AI providers (Claude <-> Gemini).
@@ -55,6 +55,7 @@ dev.db-journal
55
55
  .codeyam/editor-user-prompt.txt
56
56
  .codeyam/editor-mode-context.md
57
57
  .codeyam/dev-mode-context.md
58
+ .codeyam/handoff-context.md
58
59
  .codeyam/logs/
59
60
  .codeyam/llm-calls/
60
61
  .codeyam/captures/
@@ -10,28 +10,39 @@
10
10
  * "tableName": [{ "column": "value", ... }, ...],
11
11
  * "_auth": { // optional
12
12
  * "email": "alice@example.com",
13
- * "password": "test123"
13
+ * "password": "test123" // optional — default used if omitted
14
14
  * }
15
15
  * }
16
16
  *
17
17
  * When _auth is present, the adapter:
18
18
  * 1. Creates the user if they don't exist (auto-confirms email)
19
- * 2. Signs in with signInWithPassword to get a valid session
20
- * 3. Writes session cookies to .codeyam/tmp/seed-session.json
19
+ * 2. Builds a synthetic JWT with far-future expiry (no real sign-in needed)
20
+ * 3. Writes session cookies + auth mocks to .codeyam/tmp/seed-session.json
21
21
  * so the CodeYam proxy can inject them into the browser
22
22
  *
23
+ * The password field is optional — if omitted, a default dev password is used.
24
+ * No real JWTs are stored; the preload module intercepts all auth API calls.
25
+ *
26
+ * Data operations use direct PostgreSQL (pg) for speed — TRUNCATE CASCADE
27
+ * and batched INSERT are ~2x faster than individual PostgREST HTTP calls.
28
+ *
23
29
  * Requirements:
30
+ * - DATABASE_URL env var with a PostgreSQL connection string (session pooler recommended)
24
31
  * - A Supabase project URL (https://<ref>.supabase.co) in any env var
25
32
  * - A secret key (sb_secret_... or legacy eyJ... service_role JWT) in any env var
26
33
  *
27
- * The adapter scans ALL env vars by value pattern no specific naming required.
34
+ * The adapter scans ALL env vars by value pattern for Supabase URL and secret key —
35
+ * no specific naming required. DATABASE_URL must be set explicitly.
28
36
  */
29
37
 
30
38
  import { createClient } from '@supabase/supabase-js';
31
39
  import { Prisma } from '@prisma/client';
40
+ import pg from 'pg';
32
41
  import * as fs from 'fs';
33
42
  import * as path from 'path';
34
43
 
44
+ const { Client } = pg;
45
+
35
46
  /**
36
47
  * Scan all env vars for values matching a pattern.
37
48
  * Returns ALL unique matches (not just the first) so callers can disambiguate.
@@ -66,7 +77,6 @@ const supabaseUrl = findAllEnvByPattern(
66
77
 
67
78
  // New-format keys are unambiguous by prefix
68
79
  const newSecretKey = findAllEnvByPattern(/^sb_secret_/)[0];
69
- const newAnonKey = findAllEnvByPattern(/^sb_publishable_/)[0];
70
80
 
71
81
  // Legacy JWT keys — disambiguate by decoding the role claim
72
82
  const legacyJwts = findAllEnvByPattern(
@@ -75,10 +85,8 @@ const legacyJwts = findAllEnvByPattern(
75
85
  const legacyServiceRole = legacyJwts.find(
76
86
  (jwt) => getJwtRole(jwt) === 'service_role',
77
87
  );
78
- const legacyAnon = legacyJwts.find((jwt) => getJwtRole(jwt) === 'anon');
79
88
 
80
89
  const secretKey = newSecretKey || legacyServiceRole;
81
- const anonKey = newAnonKey || legacyAnon;
82
90
 
83
91
  if (!supabaseUrl || !secretKey) {
84
92
  console.error(
@@ -93,7 +101,17 @@ if (!supabaseUrl || !secretKey) {
93
101
  process.exit(1);
94
102
  }
95
103
 
96
- // Admin client for user management (uses secret key to bypass RLS)
104
+ // DATABASE_URL for direct PostgreSQL access (session pooler recommended for speed)
105
+ const databaseUrl = process.env.DATABASE_URL;
106
+ if (!databaseUrl || !databaseUrl.startsWith('postgresql://')) {
107
+ console.error('DATABASE_URL must be set to a PostgreSQL connection string.');
108
+ console.error(
109
+ 'Use the session pooler connection string (port 5432) from your Supabase dashboard.',
110
+ );
111
+ process.exit(1);
112
+ }
113
+
114
+ // Admin client for user management only (uses secret key to bypass RLS)
97
115
  const supabase = createClient(supabaseUrl, secretKey, {
98
116
  auth: { autoRefreshToken: false, persistSession: false },
99
117
  });
@@ -112,7 +130,7 @@ function getProjectRef(): string {
112
130
  /**
113
131
  * Build a mapping from lowercased seed-data keys to actual PostgreSQL table names.
114
132
  * Prisma creates tables with the model name (PascalCase) unless @@map is used.
115
- * PostgREST requires the exact table name, so we need this translation.
133
+ * PostgreSQL requires the exact table name, so we need this translation.
116
134
  */
117
135
  function buildTableNameMap(): Record<string, string> {
118
136
  const map: Record<string, string> = {};
@@ -127,6 +145,25 @@ function buildTableNameMap(): Record<string, string> {
127
145
  return map;
128
146
  }
129
147
 
148
+ /**
149
+ * Build column name mapping from Prisma field names to database column names.
150
+ * Prisma uses camelCase field names but generates snake_case columns unless @map is used.
151
+ */
152
+ function buildColumnMap(tableName: string): Record<string, string> {
153
+ const map: Record<string, string> = {};
154
+ for (const model of Prisma.dmmf.datamodel.models) {
155
+ const dbName = (model as any).dbName || model.name;
156
+ if (dbName !== tableName) continue;
157
+ for (const field of model.fields) {
158
+ if (field.kind === 'object') continue; // Skip relation fields
159
+ const colName = (field as any).dbName || field.name;
160
+ map[field.name] = colName;
161
+ }
162
+ break;
163
+ }
164
+ return map;
165
+ }
166
+
130
167
  async function seedTables(seed: Record<string, unknown[]>) {
131
168
  if (Object.keys(seed).length === 0) return;
132
169
 
@@ -134,7 +171,7 @@ async function seedTables(seed: Record<string, unknown[]>) {
134
171
 
135
172
  // Discover ALL models from the Prisma schema — not just the tables in the seed data.
136
173
  // This ensures FK-dependent tables are cleared even when the seed only contains
137
- // the parent table's data. Every editor project has Prisma installed.
174
+ // the parent table's data.
138
175
  const allTables = Prisma.dmmf.datamodel.models.map(
139
176
  (m) => (m as any).dbName || m.name,
140
177
  );
@@ -143,34 +180,102 @@ async function seedTables(seed: Record<string, unknown[]>) {
143
180
  `Clearing ${allTables.length} tables, seeding: ${Object.keys(seed).join(', ')}`,
144
181
  );
145
182
 
146
- // Clear ALL tables in reverse order (children before parents for FK safety)
147
- for (const table of [...allTables].reverse()) {
148
- const { error } = await supabase.from(table).delete().gte('id', 0);
149
- if (error) {
150
- const { error: error2 } = await supabase
151
- .from(table)
152
- .delete()
153
- .not('id', 'is', null);
154
- if (error2) {
155
- console.warn(` Could not clear ${table}: ${error2.message}`);
156
- } else {
157
- console.log(` Cleared ${table}`);
183
+ const client = new Client({
184
+ connectionString: databaseUrl,
185
+ ssl: { rejectUnauthorized: false },
186
+ });
187
+ await client.connect();
188
+
189
+ try {
190
+ // TRUNCATE all tables in one statement — CASCADE handles FK dependencies
191
+ // automatically, so table order doesn't matter. Much faster than
192
+ // individual DELETE FROM queries via PostgREST.
193
+ const quoted = allTables.map((t) => `"${t}"`).join(', ');
194
+ await client.query(`TRUNCATE ${quoted} CASCADE`);
195
+ console.log(` Cleared ${allTables.length} tables`);
196
+
197
+ // Reset auto-increment sequences so IDs start from 1 on each scenario switch.
198
+ // Without this, IDs keep climbing and hardcoded URLs like /drinks/1 break.
199
+ for (const table of allTables) {
200
+ try {
201
+ await client.query(
202
+ `SELECT setval(pg_get_serial_sequence('"${table}"', 'id'), 1, false)`,
203
+ );
204
+ } catch {
205
+ // Table may not have an 'id' serial column — skip
158
206
  }
159
- } else {
160
- console.log(` Cleared ${table}`);
161
207
  }
208
+
209
+ // Insert seed data using batched INSERT for speed
210
+ for (const [seedKey, rows] of Object.entries(seed)) {
211
+ if (!Array.isArray(rows) || rows.length === 0) continue;
212
+ const table = tableMap[seedKey] || seedKey;
213
+ const columnMap = buildColumnMap(table);
214
+
215
+ // Get column names from the first row's keys, mapped to DB column names
216
+ const fieldNames = Object.keys(rows[0] as Record<string, unknown>);
217
+ const dbColumns = fieldNames.map((f) => columnMap[f] || f);
218
+ const quotedCols = dbColumns.map((c) => `"${c}"`).join(', ');
219
+
220
+ // Build parameterized VALUES clause for all rows at once
221
+ const params: unknown[] = [];
222
+ const valueClauses: string[] = [];
223
+ for (let i = 0; i < rows.length; i++) {
224
+ const row = rows[i] as Record<string, unknown>;
225
+ const placeholders: string[] = [];
226
+ for (const field of fieldNames) {
227
+ params.push(row[field]);
228
+ placeholders.push(`$${params.length}`);
229
+ }
230
+ valueClauses.push(`(${placeholders.join(', ')})`);
231
+ }
232
+
233
+ await client.query(
234
+ `INSERT INTO "${table}" (${quotedCols}) VALUES ${valueClauses.join(', ')}`,
235
+ params,
236
+ );
237
+ console.log(` Seeded ${rows.length} rows into ${table}`);
238
+ }
239
+ } finally {
240
+ await client.end();
162
241
  }
242
+ }
163
243
 
164
- // Insert seed data — translate seed keys to actual table names
165
- for (const [seedKey, rows] of Object.entries(seed)) {
166
- if (!Array.isArray(rows) || rows.length === 0) continue;
167
- const table = tableMap[seedKey] || seedKey;
168
- const { error } = await supabase.from(table).insert(rows);
169
- if (error) {
170
- console.error(` Failed to seed ${table}: ${error.message}`);
171
- process.exit(1);
244
+ /**
245
+ * Export mode: dump current database state to a JSON file.
246
+ * Used by CodeYam to save interactive changes back to scenario seed data.
247
+ *
248
+ * Usage: npx tsx .codeyam/seed-adapter.ts --export <output-path.json>
249
+ */
250
+ async function exportData(outputPath: string) {
251
+ const tableMap = buildTableNameMap();
252
+ const modelNames = Prisma.dmmf.datamodel.models.map((m) => m.name);
253
+
254
+ const client = new Client({
255
+ connectionString: databaseUrl,
256
+ ssl: { rejectUnauthorized: false },
257
+ });
258
+ await client.connect();
259
+
260
+ try {
261
+ const seed: Record<string, unknown[]> = {};
262
+ for (const model of modelNames) {
263
+ const camelCase = model.charAt(0).toLowerCase() + model.slice(1);
264
+ const table = tableMap[camelCase] || model;
265
+ try {
266
+ const result = await client.query(`SELECT * FROM "${table}"`);
267
+ if (result.rows.length > 0) {
268
+ seed[camelCase] = result.rows;
269
+ }
270
+ } catch {
271
+ // Skip tables that can't be queried
272
+ }
172
273
  }
173
- console.log(` Seeded ${rows.length} rows into ${table}`);
274
+
275
+ fs.writeFileSync(outputPath, JSON.stringify(seed, null, 2));
276
+ console.log(`Exported ${Object.keys(seed).length} tables`);
277
+ } finally {
278
+ await client.end();
174
279
  }
175
280
  }
176
281
 
@@ -210,17 +315,25 @@ function buildUserResponse(user: { id: string; email?: string }) {
210
315
  };
211
316
  }
212
317
 
318
+ const DEFAULT_AUTH_PASSWORD = 'codeyam-dev-password-123!';
319
+
213
320
  /**
214
- * Handle auth: create user, sign in, write session cookies.
321
+ * Handle auth: create user, build synthetic session, write session cookies.
215
322
  * Returns the user ID so callers can replace __AUTH_USER_ID__ placeholders in seed data.
323
+ *
324
+ * No real sign-in is performed — the session cookie uses a synthetic JWT with
325
+ * far-future expiry, and externalApis mocks intercept all Supabase auth API calls.
326
+ * This avoids storing real tokens (security risk + expiration) in scenario files.
216
327
  */
217
328
  async function handleAuth(auth: {
218
329
  email: string;
219
- password: string;
330
+ password?: string;
220
331
  }): Promise<string> {
221
- const { email, password } = auth;
332
+ const email = auth.email;
333
+ const password = auth.password || DEFAULT_AUTH_PASSWORD;
222
334
 
223
- // Create user if they don't exist (auto-confirm email so sign-in works)
335
+ // Create user if they don't exist (auto-confirm email)
336
+ // We still need the auth.users row for PostgREST FK relationships.
224
337
  const { data: createData, error: createError } =
225
338
  await supabase.auth.admin.createUser({
226
339
  email,
@@ -228,52 +341,36 @@ async function handleAuth(auth: {
228
341
  email_confirm: true,
229
342
  });
230
343
 
344
+ let userId: string;
345
+
231
346
  if (createError) {
232
347
  if (createError.message.includes('already been registered')) {
233
- // User exists — update their password so sign-in works even if the
234
- // scenario specifies a different password than a previous run.
348
+ // User exists — look up their ID
235
349
  const { data: listData } = await supabase.auth.admin.listUsers();
236
350
  const existingUser = listData?.users?.find((u) => u.email === email);
237
351
  if (existingUser) {
238
- await supabase.auth.admin.updateUserById(existingUser.id, {
239
- password,
240
- });
241
- console.log(` Updated password for existing user ${email}`);
352
+ userId = existingUser.id;
353
+ console.log(` Found existing user ${email} (user: ${userId})`);
354
+ } else {
355
+ console.error(
356
+ ` User ${email} reported as registered but not found in listUsers`,
357
+ );
358
+ process.exit(1);
242
359
  }
243
360
  } else {
244
361
  console.error(` Failed to create auth user: ${createError.message}`);
245
362
  process.exit(1);
246
363
  }
364
+ } else {
365
+ userId = createData!.user.id;
366
+ console.log(` Created user ${email} (user: ${userId})`);
247
367
  }
248
368
 
249
- // Sign in to get a valid session
250
- // Use a separate client with the publishable/anon key for sign-in (if available)
251
- const signInClient =
252
- anonKey && anonKey !== secretKey
253
- ? createClient(supabaseUrl!, anonKey, {
254
- auth: { autoRefreshToken: false, persistSession: false },
255
- })
256
- : supabase;
257
-
258
- const { data: signInData, error: signInError } =
259
- await signInClient.auth.signInWithPassword({ email, password });
260
-
261
- if (signInError || !signInData.session) {
262
- console.error(
263
- ` Failed to sign in as ${email}: ${signInError?.message || 'no session returned'}`,
264
- );
265
- process.exit(1);
266
- }
267
-
268
- const userId = signInData.user.id;
269
- console.log(` Signed in as ${email} (user: ${userId})`);
270
-
271
- // Use the REAL JWT from Supabase sign-in for the session cookie so auth
272
- // works immediately against the real Supabase API. The externalApis below
273
- // provide a fallback for when the CLI supports preload-based auth
274
- // interception (no real Supabase calls needed at render time).
275
- const fakeJwt = buildFakeJwt(userId, email);
276
- const userResponse = buildUserResponse(signInData.user);
369
+ // Build synthetic JWT and mock responses — no real sign-in needed.
370
+ // The preload module intercepts server-side getUser() calls so Next.js
371
+ // middleware returns the mock user without hitting real Supabase.
372
+ const fakeJwt = buildFakeJwt(userId!, email);
373
+ const userResponse = buildUserResponse({ id: userId!, email });
277
374
 
278
375
  const projectRef = getProjectRef();
279
376
  const sessionOutput = {
@@ -281,19 +378,16 @@ async function handleAuth(auth: {
281
378
  {
282
379
  name: `sb-${projectRef}-auth-token`,
283
380
  value: JSON.stringify({
284
- access_token: signInData.session.access_token,
285
- refresh_token: signInData.session.refresh_token,
381
+ access_token: fakeJwt,
382
+ refresh_token: 'codeyam-mock-refresh-token',
286
383
  token_type: 'bearer',
287
- expires_in: signInData.session.expires_in,
288
- expires_at: signInData.session.expires_at,
384
+ expires_in: 315360000,
385
+ expires_at: 9999999999,
289
386
  }),
290
387
  path: '/',
291
388
  sameSite: 'Lax' as const,
292
389
  },
293
390
  ],
294
- // External API mocks — when the CLI supports propagating these, the
295
- // preload module will intercept server-side getUser() calls so Next.js
296
- // middleware returns the mock user without hitting real Supabase.
297
391
  externalApis: {
298
392
  [`${supabaseUrl}/auth/v1/user`]: {
299
393
  body: userResponse,
@@ -319,7 +413,7 @@ async function handleAuth(auth: {
319
413
  fs.writeFileSync(outputPath, JSON.stringify(sessionOutput, null, 2));
320
414
  console.log(` Session cookies + auth mocks written to ${outputPath}`);
321
415
 
322
- return userId;
416
+ return userId!;
323
417
  }
324
418
 
325
419
  /**
@@ -358,7 +452,7 @@ async function main() {
358
452
  // in seed data (e.g. for user_id foreign key columns with Supabase RLS)
359
453
  if (auth) {
360
454
  const userId = await handleAuth(
361
- auth as { email: string; password: string },
455
+ auth as { email: string; password?: string },
362
456
  );
363
457
  seed = replaceAuthPlaceholders(seed, userId);
364
458
  }
@@ -368,7 +462,14 @@ async function main() {
368
462
  console.log('Seed complete');
369
463
  }
370
464
 
371
- main().catch((e) => {
372
- console.error('Seed adapter error:', e);
373
- process.exit(1);
374
- });
465
+ if (process.argv[2] === '--export') {
466
+ exportData(process.argv[3]).catch((e) => {
467
+ console.error('Seed adapter export error:', e);
468
+ process.exit(1);
469
+ });
470
+ } else {
471
+ main().catch((e) => {
472
+ console.error('Seed adapter error:', e);
473
+ process.exit(1);
474
+ });
475
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeyam/codeyam-cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Local development CLI for CodeYam analysis",
5
5
  "type": "module",
6
6
  "bin": {