@embeddables/cli 0.6.11 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prompts/custom/build-funnel.md +1 -1
- package/dist/cli.js +7 -10
- package/dist/commands/branch.d.ts.map +1 -1
- package/dist/commands/branch.js +20 -15
- package/dist/commands/build-workbench.d.ts.map +1 -1
- package/dist/commands/build-workbench.js +37 -31
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +12 -3
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +34 -21
- package/dist/commands/experiments-connect.d.ts.map +1 -1
- package/dist/commands/experiments-connect.js +43 -30
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +49 -48
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +35 -25
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +10 -6
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +49 -36
- package/dist/commands/save.d.ts.map +1 -1
- package/dist/commands/save.js +79 -53
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +10 -9
- package/dist/compiler/reverse.d.ts.map +1 -1
- package/dist/compiler/reverse.js +18 -17
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/prompts/branches.d.ts.map +1 -1
- package/dist/prompts/branches.js +3 -2
- package/dist/prompts/embeddables.d.ts.map +1 -1
- package/dist/prompts/embeddables.js +8 -7
- package/dist/prompts/experiments.d.ts.map +1 -1
- package/dist/prompts/experiments.js +5 -4
- package/dist/prompts/projects.d.ts.map +1 -1
- package/dist/prompts/projects.js +4 -3
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/server.js +33 -32
- package/package.json +2 -1
package/dist/commands/init.js
CHANGED
|
@@ -6,6 +6,7 @@ import prompts from 'prompts';
|
|
|
6
6
|
import { writeProjectConfig, readProjectConfig } from '../config/index.js';
|
|
7
7
|
import { isLoggedIn } from '../auth/index.js';
|
|
8
8
|
import { promptForProject } from '../prompts/index.js';
|
|
9
|
+
import * as stdout from '../stdout.js';
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
11
12
|
/** Recursively copy a directory, creating target dirs as needed. */
|
|
@@ -43,7 +44,7 @@ function writeTypeStubs(cwd) {
|
|
|
43
44
|
fs.copyFileSync(typesBuilderDts, path.join(typesDir, 'types.d.ts'));
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
|
-
|
|
47
|
+
stdout.warn(pc.yellow(' ⚠ Could not find types-builder.d.ts — run `npm run build` first'));
|
|
47
48
|
}
|
|
48
49
|
// React JSX runtime types (needed for "jsx": "react-jsx" in tsconfig)
|
|
49
50
|
fs.writeFileSync(path.join(typesDir, 'react-jsx-runtime.d.ts'), `export namespace JSX {
|
|
@@ -104,9 +105,9 @@ export async function runInit(opts) {
|
|
|
104
105
|
const cwd = process.cwd();
|
|
105
106
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
106
107
|
const embeddablesDir = path.join(cwd, 'embeddables');
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
stdout.print('');
|
|
109
|
+
stdout.print(pc.bold(pc.cyan(' Embeddables Project Setup')));
|
|
110
|
+
stdout.print('');
|
|
110
111
|
const existingConfig = readProjectConfig();
|
|
111
112
|
// Step 1: Get Embeddables project
|
|
112
113
|
let projectId = opts.projectId;
|
|
@@ -115,7 +116,7 @@ export async function runInit(opts) {
|
|
|
115
116
|
let selectedOrgTitle;
|
|
116
117
|
if (!projectId && !opts.yes) {
|
|
117
118
|
if (existingConfig?.project_id) {
|
|
118
|
-
|
|
119
|
+
stdout.print(pc.gray(` Using existing project ID: ${existingConfig.project_id}`));
|
|
119
120
|
projectId = existingConfig.project_id;
|
|
120
121
|
selectedProjectTitle = existingConfig.project_name || undefined;
|
|
121
122
|
selectedOrgId = existingConfig.org_id || undefined;
|
|
@@ -123,7 +124,7 @@ export async function runInit(opts) {
|
|
|
123
124
|
}
|
|
124
125
|
else if (isLoggedIn()) {
|
|
125
126
|
// Fetch and show project list
|
|
126
|
-
|
|
127
|
+
stdout.print(pc.gray(' Fetching projects...'));
|
|
127
128
|
const selectedProject = await promptForProject({
|
|
128
129
|
allowSkip: true,
|
|
129
130
|
message: 'Select your Embeddables project:',
|
|
@@ -137,7 +138,7 @@ export async function runInit(opts) {
|
|
|
137
138
|
}
|
|
138
139
|
else {
|
|
139
140
|
// Not logged in - offer manual entry or skip
|
|
140
|
-
|
|
141
|
+
stdout.print(pc.gray(' (Login with "embeddables login" to select from your projects)'));
|
|
141
142
|
const response = await prompts({
|
|
142
143
|
type: 'text',
|
|
143
144
|
name: 'projectId',
|
|
@@ -145,7 +146,7 @@ export async function runInit(opts) {
|
|
|
145
146
|
hint: 'e.g., proj_abc123 - leave empty to skip',
|
|
146
147
|
}, {
|
|
147
148
|
onCancel: () => {
|
|
148
|
-
|
|
149
|
+
stdout.print(pc.gray('\n Cancelled.'));
|
|
149
150
|
process.exit(0);
|
|
150
151
|
},
|
|
151
152
|
});
|
|
@@ -160,7 +161,7 @@ export async function runInit(opts) {
|
|
|
160
161
|
project_id: projectId,
|
|
161
162
|
project_name: selectedProjectTitle,
|
|
162
163
|
});
|
|
163
|
-
|
|
164
|
+
stdout.print(pc.green(' ✓ Created embeddables.json'));
|
|
164
165
|
}
|
|
165
166
|
// Create or update .gitignore
|
|
166
167
|
const gitignoreEntries = ['**/.generated/', '**/.types/', '.DS_Store'];
|
|
@@ -176,28 +177,28 @@ export async function runInit(opts) {
|
|
|
176
177
|
: `# Embeddables\n${gitignoreEntries.join('\n')}\n`;
|
|
177
178
|
fs.writeFileSync(gitignorePath, existingGitignore + embeddablesSection, 'utf8');
|
|
178
179
|
if (existingGitignore) {
|
|
179
|
-
|
|
180
|
+
stdout.print(pc.green(' ✓ Updated .gitignore'));
|
|
180
181
|
}
|
|
181
182
|
else {
|
|
182
|
-
|
|
183
|
+
stdout.print(pc.green(' ✓ Created .gitignore'));
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
else {
|
|
186
|
-
|
|
187
|
+
stdout.print(pc.gray(' ✓ .gitignore already configured'));
|
|
187
188
|
}
|
|
188
189
|
// Create embeddables directory
|
|
189
190
|
if (!fs.existsSync(embeddablesDir)) {
|
|
190
191
|
fs.mkdirSync(embeddablesDir, { recursive: true });
|
|
191
|
-
|
|
192
|
+
stdout.print(pc.green(' ✓ Created embeddables/ directory'));
|
|
192
193
|
}
|
|
193
194
|
else {
|
|
194
|
-
|
|
195
|
+
stdout.print(pc.gray(' ✓ embeddables/ directory exists'));
|
|
195
196
|
}
|
|
196
|
-
// Inject .cursor/ and .
|
|
197
|
+
// Inject .cursor/ and .claude/ from source .prompts/embeddables-cli.md (or copy if pre-built dirs exist)
|
|
197
198
|
const packageRoot = path.resolve(__dirname, '..', '..');
|
|
198
199
|
const promptsSource = path.join(packageRoot, '.prompts', 'embeddables-cli.md');
|
|
199
200
|
const sourceCursorDir = path.join(packageRoot, '.cursor');
|
|
200
|
-
const
|
|
201
|
+
const sourceClaudeDir = path.join(packageRoot, '.claude');
|
|
201
202
|
if (fs.existsSync(promptsSource)) {
|
|
202
203
|
const content = fs.readFileSync(promptsSource, 'utf8');
|
|
203
204
|
const cursorFrontmatter = `---
|
|
@@ -212,28 +213,28 @@ This is an Embeddables project managed by the Embeddables CLI. The CLI transform
|
|
|
212
213
|
|
|
213
214
|
For full context when editing embeddables (pages, components, styles, actions, computed fields), see:
|
|
214
215
|
|
|
215
|
-
- **.
|
|
216
|
+
- **.claude/embeddables-cli.md** — Embeddables CLI structure, types, file layout, and conventions.
|
|
216
217
|
|
|
217
218
|
All TypeScript types are in \`.types/\` (generated by \`embeddables init\`). Use \`embeddables dev\` to run the dev server and \`embeddables pull\` / \`embeddables save\` to sync with the project.
|
|
218
219
|
`;
|
|
219
220
|
const cursorRulesDir = path.join(cwd, '.cursor', 'rules');
|
|
220
|
-
const
|
|
221
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
221
222
|
fs.mkdirSync(cursorRulesDir, { recursive: true });
|
|
222
|
-
fs.mkdirSync(
|
|
223
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
223
224
|
fs.writeFileSync(path.join(cursorRulesDir, 'embeddables-cli.md'), cursorFrontmatter + content, 'utf8');
|
|
224
|
-
fs.writeFileSync(path.join(
|
|
225
|
-
fs.writeFileSync(path.join(
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
fs.writeFileSync(path.join(claudeDir, 'CLAUDE.md'), claudeIntro, 'utf8');
|
|
226
|
+
fs.writeFileSync(path.join(claudeDir, 'embeddables-cli.md'), content, 'utf8');
|
|
227
|
+
stdout.print(pc.green(' ✓ Injected .cursor/ rules'));
|
|
228
|
+
stdout.print(pc.green(' ✓ Injected .claude/'));
|
|
228
229
|
}
|
|
229
|
-
else if (fs.existsSync(sourceCursorDir) || fs.existsSync(
|
|
230
|
+
else if (fs.existsSync(sourceCursorDir) || fs.existsSync(sourceClaudeDir)) {
|
|
230
231
|
if (fs.existsSync(sourceCursorDir)) {
|
|
231
232
|
copyDirSync(sourceCursorDir, path.join(cwd, '.cursor'));
|
|
232
|
-
|
|
233
|
+
stdout.print(pc.green(' ✓ Injected .cursor/ rules'));
|
|
233
234
|
}
|
|
234
|
-
if (fs.existsSync(
|
|
235
|
-
copyDirSync(
|
|
236
|
-
|
|
235
|
+
if (fs.existsSync(sourceClaudeDir)) {
|
|
236
|
+
copyDirSync(sourceClaudeDir, path.join(cwd, '.claude'));
|
|
237
|
+
stdout.print(pc.green(' ✓ Injected .claude/'));
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
// Create tsconfig.json for editor support (JSX, type checking)
|
|
@@ -259,16 +260,16 @@ All TypeScript types are in \`.types/\` (generated by \`embeddables init\`). Use
|
|
|
259
260
|
include: ['embeddables'],
|
|
260
261
|
};
|
|
261
262
|
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n', 'utf8');
|
|
262
|
-
|
|
263
|
+
stdout.print(pc.green(' ✓ Created tsconfig.json'));
|
|
263
264
|
}
|
|
264
265
|
else {
|
|
265
|
-
|
|
266
|
+
stdout.print(pc.gray(' ✓ tsconfig.json already exists'));
|
|
266
267
|
}
|
|
267
268
|
// Migrate .types from old location (embeddables/.types/) to project root (.types/)
|
|
268
269
|
const oldTypesDir = path.join(embeddablesDir, '.types');
|
|
269
270
|
if (fs.existsSync(oldTypesDir)) {
|
|
270
271
|
fs.rmSync(oldTypesDir, { recursive: true });
|
|
271
|
-
|
|
272
|
+
stdout.print(pc.green(' ✓ Removed old embeddables/.types/'));
|
|
272
273
|
}
|
|
273
274
|
// Fix tsconfig paths from older versions
|
|
274
275
|
if (fs.existsSync(tsconfigPath)) {
|
|
@@ -286,7 +287,7 @@ All TypeScript types are in \`.types/\` (generated by \`embeddables init\`). Use
|
|
|
286
287
|
}
|
|
287
288
|
if (updated) {
|
|
288
289
|
fs.writeFileSync(tsconfigPath, tsconfigContent, 'utf8');
|
|
289
|
-
|
|
290
|
+
stdout.print(pc.green(' ✓ Updated tsconfig.json paths'));
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
293
|
// Remove old .types/types-builder.d.ts (now named .types/types.d.ts)
|
|
@@ -296,25 +297,25 @@ All TypeScript types are in \`.types/\` (generated by \`embeddables init\`). Use
|
|
|
296
297
|
}
|
|
297
298
|
// Generate type declaration stubs for editor support (no npm install needed)
|
|
298
299
|
writeTypeStubs(cwd);
|
|
299
|
-
|
|
300
|
+
stdout.print(pc.green(' ✓ Generated type declarations'));
|
|
300
301
|
// Print next steps
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
stdout.print('');
|
|
303
|
+
stdout.print(pc.bold(' Next steps:'));
|
|
304
|
+
stdout.print('');
|
|
305
|
+
stdout.print(pc.cyan(' 1. Login to Embeddables:'));
|
|
306
|
+
stdout.print(pc.white(' embeddables login'));
|
|
307
|
+
stdout.print('');
|
|
307
308
|
if (projectId) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
stdout.print(pc.cyan(' 2. Pull an embeddable:'));
|
|
310
|
+
stdout.print(pc.white(' embeddables pull'));
|
|
311
|
+
stdout.print(pc.gray(' (will show a list of embeddables in your project)'));
|
|
311
312
|
}
|
|
312
313
|
else {
|
|
313
|
-
|
|
314
|
-
|
|
314
|
+
stdout.print(pc.cyan(' 2. Pull an existing embeddable:'));
|
|
315
|
+
stdout.print(pc.white(' embeddables pull --id <embeddable-id>'));
|
|
315
316
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
stdout.print('');
|
|
318
|
+
stdout.print(pc.cyan(' 3. Start developing:'));
|
|
319
|
+
stdout.print(pc.white(' embeddables dev'));
|
|
320
|
+
stdout.print('');
|
|
320
321
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAYA,wBAAsB,QAAQ,kBAiI7B"}
|
package/dist/commands/login.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { getSupabaseClient, writeAuthConfig, readAuthConfig, SUPABASE_URL, SUPABASE_ANON_KEY, } from '../auth/index.js';
|
|
4
|
+
import { captureException, createLogger, exit } from '../logger.js';
|
|
5
|
+
import * as stdout from '../stdout.js';
|
|
4
6
|
export async function runLogin() {
|
|
7
|
+
const logger = createLogger('runLogin');
|
|
5
8
|
try {
|
|
6
9
|
const supabase = getSupabaseClient();
|
|
7
10
|
// Check if already logged in
|
|
@@ -14,7 +17,7 @@ export async function runLogin() {
|
|
|
14
17
|
initial: false,
|
|
15
18
|
});
|
|
16
19
|
if (!overwrite) {
|
|
17
|
-
|
|
20
|
+
stdout.print(pc.green('Login cancelled.'));
|
|
18
21
|
return;
|
|
19
22
|
}
|
|
20
23
|
}
|
|
@@ -33,23 +36,24 @@ export async function runLogin() {
|
|
|
33
36
|
},
|
|
34
37
|
});
|
|
35
38
|
if (!email) {
|
|
36
|
-
|
|
39
|
+
stdout.print(pc.red('Login cancelled.'));
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
stdout.print('');
|
|
43
|
+
stdout.print(pc.cyan('Sending verification code to your email...'));
|
|
41
44
|
// Send OTP code
|
|
42
45
|
const { error: sendError } = await supabase.auth.signInWithOtp({
|
|
43
46
|
email,
|
|
44
47
|
});
|
|
45
48
|
if (sendError) {
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
stdout.error(pc.red(`Error sending verification code: ${sendError.message}`));
|
|
50
|
+
logger.error('failed to send OTP', { message: sendError.message });
|
|
51
|
+
await exit(1);
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
stdout.print('');
|
|
54
|
+
stdout.print(pc.green('✓ Verification code sent!'));
|
|
55
|
+
stdout.print(pc.dim('Please check your email for the code.'));
|
|
56
|
+
stdout.print('');
|
|
53
57
|
// Prompt for OTP code
|
|
54
58
|
const { code } = await prompts({
|
|
55
59
|
type: 'text',
|
|
@@ -65,11 +69,11 @@ export async function runLogin() {
|
|
|
65
69
|
},
|
|
66
70
|
});
|
|
67
71
|
if (!code) {
|
|
68
|
-
|
|
72
|
+
stdout.print(pc.red('Login cancelled.'));
|
|
69
73
|
return;
|
|
70
74
|
}
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
stdout.print('');
|
|
76
|
+
stdout.print(pc.cyan('Verifying code...'));
|
|
73
77
|
// Verify OTP code
|
|
74
78
|
const { data, error: verifyError } = await supabase.auth.verifyOtp({
|
|
75
79
|
email,
|
|
@@ -77,12 +81,15 @@ export async function runLogin() {
|
|
|
77
81
|
type: 'email',
|
|
78
82
|
});
|
|
79
83
|
if (verifyError) {
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
stdout.error(pc.red(`Error verifying code: ${verifyError.message}`));
|
|
85
|
+
logger.error('failed to verify OTP', { message: verifyError.message });
|
|
86
|
+
await exit(1);
|
|
82
87
|
}
|
|
83
88
|
if (!data.session) {
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
stdout.error(pc.red('No session returned from verification'));
|
|
90
|
+
logger.error('no session returned from verification');
|
|
91
|
+
await exit(1);
|
|
92
|
+
return;
|
|
86
93
|
}
|
|
87
94
|
// Store auth config
|
|
88
95
|
const config = {
|
|
@@ -93,20 +100,23 @@ export async function runLogin() {
|
|
|
93
100
|
supabase_anon_key: SUPABASE_ANON_KEY,
|
|
94
101
|
};
|
|
95
102
|
writeAuthConfig(config);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
stdout.print('');
|
|
104
|
+
stdout.print(pc.green('✓ Successfully logged in!'));
|
|
105
|
+
logger.info('login complete');
|
|
106
|
+
stdout.print('');
|
|
99
107
|
}
|
|
100
108
|
catch (error) {
|
|
101
109
|
if (error.name === 'ExitPrompt') {
|
|
102
110
|
// User cancelled prompt (Ctrl+C)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
stdout.print('');
|
|
112
|
+
stdout.print(pc.red('Login cancelled.'));
|
|
113
|
+
await exit(0);
|
|
106
114
|
}
|
|
107
115
|
else {
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
captureException(error);
|
|
117
|
+
stdout.error(pc.red(`Error during login: ${error.message}`));
|
|
118
|
+
logger.error('login failed', { message: error.message });
|
|
119
|
+
await exit(1);
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAKA,wBAAsB,SAAS,kBAiB9B"}
|
package/dist/commands/logout.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { deleteAuthConfig, isLoggedIn } from '../auth/index.js';
|
|
3
|
+
import { createLogger, exit } from '../logger.js';
|
|
4
|
+
import * as stdout from '../stdout.js';
|
|
3
5
|
export async function runLogout() {
|
|
6
|
+
const logger = createLogger('runLogout');
|
|
4
7
|
try {
|
|
5
8
|
if (!isLoggedIn()) {
|
|
6
|
-
|
|
9
|
+
stdout.print(pc.yellow('You are not currently logged in.'));
|
|
7
10
|
return;
|
|
8
11
|
}
|
|
9
12
|
deleteAuthConfig();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
stdout.print('');
|
|
14
|
+
stdout.print(pc.green('✓ Successfully logged out!'));
|
|
15
|
+
stdout.print('');
|
|
13
16
|
}
|
|
14
17
|
catch (error) {
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
stdout.error(pc.red(`Error during logout: ${error.message}`));
|
|
19
|
+
logger.error('logout failed', { message: error.message });
|
|
20
|
+
await exit(1);
|
|
17
21
|
}
|
|
18
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAkHA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iHAAiH;IACjH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,iBA8QjD"}
|
package/dist/commands/pull.js
CHANGED
|
@@ -6,6 +6,8 @@ import { reverseCompile } from '../compiler/reverse.js';
|
|
|
6
6
|
import { getAccessToken, isLoggedIn } from '../auth/index.js';
|
|
7
7
|
import { getProjectId, writeProjectConfig } from '../config/index.js';
|
|
8
8
|
import { promptForProject, promptForEmbeddable, fetchEmbeddableMetadata } from '../prompts/index.js';
|
|
9
|
+
import { captureException, createLogger, exit } from '../logger.js';
|
|
10
|
+
import * as stdout from '../stdout.js';
|
|
9
11
|
import { inferEmbeddableFromCwd } from '../helpers/utils.js';
|
|
10
12
|
/**
|
|
11
13
|
* Normalize custom_validation_function strings so literal "\\n" (backslash-n) becomes real newline.
|
|
@@ -105,10 +107,11 @@ function writePullMetadataToConfig(embeddableId, version, branch, branchName) {
|
|
|
105
107
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
106
108
|
}
|
|
107
109
|
catch (err) {
|
|
108
|
-
|
|
110
|
+
stdout.warn(pc.yellow(`Could not write config.json: ${err instanceof Error ? err.message : err}`));
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
export async function runPull(opts) {
|
|
114
|
+
const logger = createLogger('runPull');
|
|
112
115
|
const inferred = inferEmbeddableFromCwd();
|
|
113
116
|
let embeddableId = opts.id ?? inferred?.embeddableId;
|
|
114
117
|
if (inferred && !opts.id && embeddableId) {
|
|
@@ -117,20 +120,24 @@ export async function runPull(opts) {
|
|
|
117
120
|
// If no ID provided, try to get it interactively
|
|
118
121
|
if (!embeddableId) {
|
|
119
122
|
if (!isLoggedIn()) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
stdout.error(pc.red('No embeddable ID provided and not logged in.'));
|
|
124
|
+
stdout.print('');
|
|
125
|
+
stdout.print(pc.gray('Either:'));
|
|
126
|
+
stdout.print(pc.gray(' 1. Use --id <embeddable-id> to specify an embeddable'));
|
|
127
|
+
stdout.print(pc.gray(' 2. Run "embeddables login" first for interactive selection'));
|
|
128
|
+
logger.error('not logged in and no embeddable id');
|
|
129
|
+
await exit(1);
|
|
130
|
+
return;
|
|
126
131
|
}
|
|
127
132
|
let projectId = getProjectId();
|
|
128
133
|
// If no project ID configured, prompt for project selection
|
|
129
134
|
if (!projectId) {
|
|
130
|
-
|
|
135
|
+
stdout.print(pc.cyan('Fetching projects...'));
|
|
131
136
|
const selectedProject = await promptForProject();
|
|
132
137
|
if (!selectedProject) {
|
|
133
|
-
|
|
138
|
+
logger.error('project selection failed');
|
|
139
|
+
await exit(1);
|
|
140
|
+
return;
|
|
134
141
|
}
|
|
135
142
|
projectId = selectedProject.id;
|
|
136
143
|
// Save the selected project to config
|
|
@@ -140,18 +147,20 @@ export async function runPull(opts) {
|
|
|
140
147
|
project_id: projectId,
|
|
141
148
|
project_name: selectedProject.title || undefined,
|
|
142
149
|
});
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
stdout.print(pc.green(`✓ Wrote project config to embeddables.json`));
|
|
151
|
+
stdout.print('');
|
|
145
152
|
}
|
|
146
|
-
|
|
153
|
+
stdout.print(pc.cyan('Fetching embeddables from project...'));
|
|
147
154
|
const selected = await promptForEmbeddable(projectId, {
|
|
148
155
|
message: 'Select an embeddable to pull:',
|
|
149
156
|
});
|
|
150
157
|
if (!selected) {
|
|
151
|
-
|
|
158
|
+
logger.error('embeddable selection failed');
|
|
159
|
+
await exit(1);
|
|
160
|
+
return;
|
|
152
161
|
}
|
|
153
162
|
embeddableId = selected;
|
|
154
|
-
|
|
163
|
+
stdout.print('');
|
|
155
164
|
}
|
|
156
165
|
// When useMain, pull main and ignore/clear saved branch. Otherwise stay on current branch when no --branch.
|
|
157
166
|
const currentFromConfig = opts.useMain || opts.branch != null ? null : getCurrentBranchFromConfig(embeddableId);
|
|
@@ -167,8 +176,9 @@ export async function runPull(opts) {
|
|
|
167
176
|
? `${effectiveBranchName} (${effectiveBranch})`
|
|
168
177
|
: effectiveBranch
|
|
169
178
|
: 'main';
|
|
170
|
-
|
|
171
|
-
|
|
179
|
+
logger.info('pull started', { embeddableId, branch: effectiveBranch ?? 'main' });
|
|
180
|
+
stdout.print(pc.cyan(`Pulling branch: ${pc.bold(branchLabel)}`));
|
|
181
|
+
stdout.print(`Fetching embeddable from ${url}...`);
|
|
172
182
|
let pullVersion;
|
|
173
183
|
try {
|
|
174
184
|
// Add authentication header if available
|
|
@@ -194,7 +204,7 @@ export async function runPull(opts) {
|
|
|
194
204
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
195
205
|
const flowJson = JSON.stringify(flow, null, 2);
|
|
196
206
|
fs.writeFileSync(outPath, flowJson, 'utf8');
|
|
197
|
-
|
|
207
|
+
stdout.print(pc.cyan(`✓ Wrote embeddable JSON to ${outPath}`));
|
|
198
208
|
// Also save version-specific file (e.g. embeddable-main@135.json or embeddable-my_branch@135.json)
|
|
199
209
|
pullVersion =
|
|
200
210
|
data.version ?? data.embeddable_version ?? flow.version;
|
|
@@ -205,7 +215,7 @@ export async function runPull(opts) {
|
|
|
205
215
|
const versionedBasename = getVersionedBasename(versionStr, branchSlug);
|
|
206
216
|
const versionedPath = path.join(path.dirname(outPath), versionedBasename);
|
|
207
217
|
fs.writeFileSync(versionedPath, flowJson, 'utf8');
|
|
208
|
-
|
|
218
|
+
stdout.print(pc.cyan(`✓ Wrote versioned embeddable JSON to ${versionedPath}`));
|
|
209
219
|
}
|
|
210
220
|
// Persist _version and _branch_id in config.json immediately so they survive reverseCompile
|
|
211
221
|
writePullMetadataToConfig(embeddableId, version, effectiveBranch, effectiveBranchName);
|
|
@@ -215,7 +225,7 @@ export async function runPull(opts) {
|
|
|
215
225
|
const metadataPath = path.join('embeddables', embeddableId, 'metadata.json');
|
|
216
226
|
fs.mkdirSync(path.dirname(metadataPath), { recursive: true });
|
|
217
227
|
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf8');
|
|
218
|
-
|
|
228
|
+
stdout.print(pc.cyan(`✓ Wrote flow metadata to ${metadataPath}`));
|
|
219
229
|
}
|
|
220
230
|
// Clear existing pages, styles, computed-fields, actions, and global-components before generating new ones
|
|
221
231
|
const pagesDir = path.join('embeddables', embeddableId, 'pages');
|
|
@@ -228,28 +238,28 @@ export async function runPull(opts) {
|
|
|
228
238
|
for (const page of existingPages) {
|
|
229
239
|
fs.unlinkSync(path.join(pagesDir, page));
|
|
230
240
|
}
|
|
231
|
-
|
|
241
|
+
stdout.print(`${pc.gray(`Cleared ${existingPages.length} existing page(s)`)}`);
|
|
232
242
|
}
|
|
233
243
|
if (fs.existsSync(stylesDir)) {
|
|
234
244
|
const existingStyles = fs.readdirSync(stylesDir).filter((f) => f.endsWith('.css'));
|
|
235
245
|
for (const style of existingStyles) {
|
|
236
246
|
fs.unlinkSync(path.join(stylesDir, style));
|
|
237
247
|
}
|
|
238
|
-
|
|
248
|
+
stdout.print(`${pc.gray(`Cleared ${existingStyles.length} existing style file(s)`)}`);
|
|
239
249
|
}
|
|
240
250
|
if (fs.existsSync(computedFieldsDir)) {
|
|
241
251
|
const existingFields = fs.readdirSync(computedFieldsDir).filter((f) => f.endsWith('.js'));
|
|
242
252
|
for (const field of existingFields) {
|
|
243
253
|
fs.unlinkSync(path.join(computedFieldsDir, field));
|
|
244
254
|
}
|
|
245
|
-
|
|
255
|
+
stdout.print(`${pc.gray(`Cleared ${existingFields.length} existing computed field(s)`)}`);
|
|
246
256
|
}
|
|
247
257
|
if (fs.existsSync(actionsDir)) {
|
|
248
258
|
const existingActions = fs.readdirSync(actionsDir).filter((f) => f.endsWith('.js'));
|
|
249
259
|
for (const action of existingActions) {
|
|
250
260
|
fs.unlinkSync(path.join(actionsDir, action));
|
|
251
261
|
}
|
|
252
|
-
|
|
262
|
+
stdout.print(`${pc.gray(`Cleared ${existingActions.length} existing action(s)`)}`);
|
|
253
263
|
}
|
|
254
264
|
if (fs.existsSync(globalComponentsDir)) {
|
|
255
265
|
const existingGlobalComponents = fs
|
|
@@ -258,7 +268,7 @@ export async function runPull(opts) {
|
|
|
258
268
|
for (const component of existingGlobalComponents) {
|
|
259
269
|
fs.unlinkSync(path.join(globalComponentsDir, component));
|
|
260
270
|
}
|
|
261
|
-
|
|
271
|
+
stdout.print(`${pc.gray(`Cleared ${existingGlobalComponents.length} existing global component(s)`)}`);
|
|
262
272
|
}
|
|
263
273
|
// Run reverse compiler (pass pullMetadata so config.json gets _version and _branch_id even on fix retry)
|
|
264
274
|
const versionNum = version != null && !isNaN(Number(version)) ? Number(version) : undefined;
|
|
@@ -278,10 +288,10 @@ export async function runPull(opts) {
|
|
|
278
288
|
catch (compileError) {
|
|
279
289
|
// If fix mode wasn't already enabled, offer to retry with fix mode
|
|
280
290
|
if (!opts.fix && compileError instanceof Error) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
291
|
+
stdout.print('');
|
|
292
|
+
stdout.error(pc.red('Error during reverse compile:'));
|
|
293
|
+
stdout.error(pc.yellow(` ${compileError.message}`));
|
|
294
|
+
stdout.print('');
|
|
285
295
|
const response = await prompts({
|
|
286
296
|
type: 'confirm',
|
|
287
297
|
name: 'fix',
|
|
@@ -294,9 +304,9 @@ export async function runPull(opts) {
|
|
|
294
304
|
});
|
|
295
305
|
if (response.fix) {
|
|
296
306
|
usedFix = true;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
307
|
+
stdout.print('');
|
|
308
|
+
stdout.print(pc.cyan('Retrying with auto-fix enabled...'));
|
|
309
|
+
stdout.print('');
|
|
300
310
|
await reverseCompile(flow, embeddableId, {
|
|
301
311
|
fix: true,
|
|
302
312
|
preserve: opts.preserve,
|
|
@@ -304,7 +314,7 @@ export async function runPull(opts) {
|
|
|
304
314
|
});
|
|
305
315
|
}
|
|
306
316
|
else {
|
|
307
|
-
|
|
317
|
+
await exit(1);
|
|
308
318
|
}
|
|
309
319
|
}
|
|
310
320
|
else {
|
|
@@ -315,19 +325,22 @@ export async function runPull(opts) {
|
|
|
315
325
|
if (usedFix) {
|
|
316
326
|
const flowJson = JSON.stringify(flow, null, 2);
|
|
317
327
|
fs.writeFileSync(outPath, flowJson, 'utf8');
|
|
318
|
-
|
|
328
|
+
stdout.print(pc.cyan(`✓ Wrote embeddable JSON to ${outPath} (with fixes applied)`));
|
|
319
329
|
if (version != null) {
|
|
320
330
|
const versionStr = typeof version === 'string' ? version : String(version);
|
|
321
331
|
const versionedBasename = getVersionedBasename(versionStr, branchSlug);
|
|
322
332
|
const versionedPath = path.join(path.dirname(outPath), versionedBasename);
|
|
323
333
|
fs.writeFileSync(versionedPath, flowJson, 'utf8');
|
|
324
|
-
|
|
334
|
+
stdout.print(pc.cyan(`✓ Wrote versioned embeddable JSON to ${versionedPath} (with fixes applied)`));
|
|
325
335
|
}
|
|
326
336
|
}
|
|
337
|
+
logger.info('pull complete', { embeddableId, version: String(pullVersion ?? 'unknown') });
|
|
327
338
|
}
|
|
328
339
|
catch (error) {
|
|
329
|
-
|
|
330
|
-
|
|
340
|
+
captureException(error);
|
|
341
|
+
stdout.error(`Error pulling embeddable: ${error instanceof Error ? error.message : String(error)}`);
|
|
342
|
+
logger.error('pull failed', { message: error instanceof Error ? error.message : String(error) });
|
|
343
|
+
await exit(1);
|
|
331
344
|
}
|
|
332
345
|
finally {
|
|
333
346
|
// Always persist _version and _branch_id once we've fetched (so config is correct even if reverseCompile failed or was retried with fix)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../../src/commands/save.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../../src/commands/save.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,qBAAqB,CAAA;AAkC5B,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAA;AAE3B,MAAM,MAAM,2BAA2B,GAAG;IACxC,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,uBAAuB,CAAC,EAAE,sBAAsB,EAAE,CAAA;IAClD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B,CAAA;AAkND,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,iBAqBA"}
|