@embeddables/cli 0.7.8 → 0.7.9

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/dist/cli.js CHANGED
@@ -109,13 +109,17 @@ program
109
109
  .description('Pull an embeddable from the cloud')
110
110
  .option('-i, --id <id>', 'Embeddable ID to pull (interactive selection if not provided)')
111
111
  .option('-o, --out <path>', 'Output json path')
112
+ .option('--version <version>', 'Version to pull (number or "latest"); if omitted, choose from 100 most recent')
112
113
  .option('-b, --branch <branch_id>', 'Embeddable branch ID')
113
114
  .option('-f, --fix', 'Fix by removing components missing required props (warn instead of error)')
114
115
  .option('-p, --preserve', 'Preserve component order in config for forward compile')
115
116
  .action(async (opts) => {
116
117
  await requireLogin('pull');
117
118
  await setSentryContextForCommand();
118
- await runPull(opts);
119
+ await runPull({
120
+ ...opts,
121
+ version: opts.version,
122
+ });
119
123
  });
120
124
  program
121
125
  .command('save')
@@ -3,6 +3,8 @@ export type RunPullOptions = {
3
3
  out?: string;
4
4
  branch?: string;
5
5
  branchName?: string;
6
+ /** Version to pull (number or "latest"). If omitted and embeddable is known, prompt from 100 most recent. */
7
+ version?: string | number;
6
8
  /** When true, pull main and clear saved branch (e.g. when user explicitly selects "main" in branch switcher). */
7
9
  useMain?: boolean;
8
10
  fix?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAuHA,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,iBAgSjD"}
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AA6HA,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,6GAA6G;IAC7G,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,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,iBA+SjD"}
@@ -5,7 +5,7 @@ import prompts from 'prompts';
5
5
  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
- import { promptForProject, promptForEmbeddable, fetchEmbeddableMetadata } from '../prompts/index.js';
8
+ import { promptForProject, promptForEmbeddable, fetchEmbeddableMetadata, fetchRecentVersions, promptForVersion, } from '../prompts/index.js';
9
9
  import { captureException, createLogger, exit } from '../logger.js';
10
10
  import { getSentryContextFromEmbeddableConfig, getSentryContextFromProjectConfig, setSentryContext, } from '../sentry-context.js';
11
11
  import * as stdout from '../stdout.js';
@@ -175,10 +175,6 @@ export async function runPull(opts) {
175
175
  const currentFromConfig = opts.useMain || opts.branch != null ? null : getCurrentBranchFromConfig(embeddableId);
176
176
  const effectiveBranch = opts.useMain ? undefined : (opts.branch ?? currentFromConfig?.branchId);
177
177
  const effectiveBranchName = opts.useMain ? undefined : (opts.branchName ?? currentFromConfig?.branchName);
178
- let url = `https://engine.embeddables.com/${embeddableId}?version=latest`;
179
- if (effectiveBranch) {
180
- url += `&embeddable_branch=${effectiveBranch}`;
181
- }
182
178
  const outPath = opts.out || path.join('embeddables', embeddableId, '.generated', 'embeddable.json');
183
179
  const branchLabel = effectiveBranch
184
180
  ? effectiveBranchName
@@ -188,6 +184,24 @@ export async function runPull(opts) {
188
184
  if (effectiveBranch) {
189
185
  setSentryContext({ branch: { id: effectiveBranch, name: effectiveBranchName ?? null } });
190
186
  }
187
+ // Resolve version: explicit --version, or interactive choice from 100 most recent
188
+ let versionParam;
189
+ if (opts.version !== undefined && opts.version !== '') {
190
+ const v = String(opts.version).toLowerCase();
191
+ versionParam = v === 'latest' ? 'latest' : v;
192
+ }
193
+ else {
194
+ stdout.print(pc.cyan('Fetching recent versions...'));
195
+ const recentVersions = await fetchRecentVersions(embeddableId, effectiveBranch ?? null);
196
+ const selected = await promptForVersion(recentVersions, {
197
+ message: 'Select a version to pull:',
198
+ });
199
+ versionParam = selected === null ? 'latest' : String(selected);
200
+ stdout.print('');
201
+ }
202
+ let url = `https://engine.embeddables.com/${embeddableId}?version=${versionParam}`;
203
+ if (effectiveBranch)
204
+ url += `&embeddable_branch=${effectiveBranch}`;
191
205
  logger.info('pull started');
192
206
  stdout.print(pc.cyan(`Pulling branch: ${pc.bold(branchLabel)}`));
193
207
  stdout.print(`Fetching embeddable from ${url}...`);
@@ -4,6 +4,8 @@ export { fetchProjectEmbeddables, fetchEmbeddableMetadata, promptForEmbeddable,
4
4
  export type { EmbeddableInfo, EmbeddableMetadata, LocalEmbeddable, PromptForEmbeddableOptions, } from './embeddables.js';
5
5
  export { fetchBranches, promptForBranch } from './branches.js';
6
6
  export type { BranchInfo } from './branches.js';
7
+ export { fetchRecentVersions, promptForVersion } from './versions.js';
8
+ export type { VersionInfo } from './versions.js';
7
9
  export { fetchProjectExperiments, promptForExperiment, } from './experiments.js';
8
10
  export type { ExperimentInfo, PromptForExperimentOptions } from './experiments.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEzE,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,0BAA0B,GAC3B,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EACL,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEzE,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,0BAA0B,GAC3B,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,EACL,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA"}
@@ -2,4 +2,5 @@
2
2
  export { fetchProjects, promptForProject } from './projects.js';
3
3
  export { fetchProjectEmbeddables, fetchEmbeddableMetadata, promptForEmbeddable, discoverLocalEmbeddables, promptForLocalEmbeddable, } from './embeddables.js';
4
4
  export { fetchBranches, promptForBranch } from './branches.js';
5
+ export { fetchRecentVersions, promptForVersion } from './versions.js';
5
6
  export { fetchProjectExperiments, promptForExperiment, } from './experiments.js';
@@ -0,0 +1,18 @@
1
+ export interface VersionInfo {
2
+ version_number: number;
3
+ created_at: string | null;
4
+ }
5
+ /**
6
+ * Fetch up to 100 most recent version numbers for an embeddable (flow).
7
+ * Uses flow_versions for the given flow_id and branch (null = main).
8
+ * Returns unique version numbers in descending order.
9
+ */
10
+ export declare function fetchRecentVersions(flowId: string, branchId: string | null): Promise<VersionInfo[]>;
11
+ /**
12
+ * Prompt the user to select a version from the list.
13
+ * Returns the selected version number, or null for "Latest".
14
+ */
15
+ export declare function promptForVersion(versions: VersionInfo[], options?: {
16
+ message?: string;
17
+ }): Promise<number | null>;
18
+ //# sourceMappingURL=versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/prompts/versions.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAID;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CA8CxB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkDxB"}
@@ -0,0 +1,99 @@
1
+ import pc from 'picocolors';
2
+ import prompts from 'prompts';
3
+ import { getAuthenticatedSupabaseClient } from '../auth/index.js';
4
+ import * as stdout from '../stdout.js';
5
+ import { formatDate } from '../helpers/dates.js';
6
+ const RECENT_VERSIONS_LIMIT = 100;
7
+ /**
8
+ * Fetch up to 100 most recent version numbers for an embeddable (flow).
9
+ * Uses flow_versions for the given flow_id and branch (null = main).
10
+ * Returns unique version numbers in descending order.
11
+ */
12
+ export async function fetchRecentVersions(flowId, branchId) {
13
+ const supabase = await getAuthenticatedSupabaseClient();
14
+ if (!supabase) {
15
+ return [];
16
+ }
17
+ try {
18
+ let query = supabase
19
+ .from('flow_versions')
20
+ .select('version_number, created_at')
21
+ .eq('flow_id', flowId)
22
+ .order('version_number', { ascending: false })
23
+ .limit(RECENT_VERSIONS_LIMIT * 2); // fetch extra then dedupe
24
+ if (branchId === null) {
25
+ query = query.is('branch_id', null);
26
+ }
27
+ else {
28
+ query = query.eq('branch_id', branchId);
29
+ }
30
+ const { data, error } = await query;
31
+ if (error) {
32
+ stdout.warn(pc.yellow(`Could not fetch versions: ${error.message}`));
33
+ return [];
34
+ }
35
+ // Dedupe by version_number, preserving order (most recent first)
36
+ const seen = new Set();
37
+ const versions = [];
38
+ for (const row of data || []) {
39
+ const v = row.version_number;
40
+ if (typeof v !== 'number' || seen.has(v))
41
+ continue;
42
+ seen.add(v);
43
+ versions.push({
44
+ version_number: v,
45
+ created_at: row.created_at ?? null,
46
+ });
47
+ if (versions.length >= RECENT_VERSIONS_LIMIT)
48
+ break;
49
+ }
50
+ return versions;
51
+ }
52
+ catch (err) {
53
+ stdout.warn(pc.yellow(`Could not fetch versions: ${err}`));
54
+ return [];
55
+ }
56
+ }
57
+ /**
58
+ * Prompt the user to select a version from the list.
59
+ * Returns the selected version number, or null for "Latest".
60
+ */
61
+ export async function promptForVersion(versions, options = {}) {
62
+ const choices = [];
63
+ if (versions.length === 0) {
64
+ choices.push({
65
+ title: pc.bold('Latest'),
66
+ value: 'latest',
67
+ description: 'Current published version',
68
+ });
69
+ }
70
+ else {
71
+ for (let i = 0; i < versions.length; i++) {
72
+ const v = versions[i];
73
+ const date = v.created_at ? formatDate(v.created_at) : '';
74
+ const isLatest = i === 0;
75
+ choices.push({
76
+ title: isLatest ? `Version ${v.version_number} (Latest)` : `Version ${v.version_number}`,
77
+ value: isLatest ? 'latest' : v.version_number,
78
+ description: date || undefined,
79
+ });
80
+ }
81
+ }
82
+ const response = await prompts({
83
+ type: 'autocomplete',
84
+ name: 'version',
85
+ message: options.message ?? 'Select a version to pull:',
86
+ choices,
87
+ suggest: (input, choicesList) => Promise.resolve(choicesList.filter((c) => c.value === 'latest' ||
88
+ String(c.value).includes(input) ||
89
+ (typeof c.title === 'string' && c.title.toLowerCase().includes(input.toLowerCase())))),
90
+ }, {
91
+ onCancel: () => {
92
+ process.exit(0);
93
+ },
94
+ });
95
+ if (response.version === 'latest' || response.version === undefined) {
96
+ return null;
97
+ }
98
+ return typeof response.version === 'number' ? response.version : null;
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddables/cli",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "embeddables": "./bin/embeddables.mjs"