@funnelsgrove/cli 0.1.8 → 0.1.10

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
@@ -7,24 +7,25 @@ npm install -g @funnelsgrove/cli
7
7
  fgrove login
8
8
  ```
9
9
 
10
- Set the active project and funnel:
10
+ Sync a funnel into its own local folder:
11
11
 
12
12
  ```bash
13
- fgrove use --project claimbee --funnel claimbee-ios
14
- fgrove status
13
+ fgrove sync down --funnel claimbee-ios --dir ./claimbee-ios
14
+ cd ./claimbee-ios
15
15
  ```
16
16
 
17
17
  Common workflow:
18
18
 
19
19
  ```bash
20
- fgrove sync down --dir ./claimbee-ios
21
- cd ./claimbee-ios
20
+ fgrove status
22
21
  fgrove docs
23
22
  fgrove sync up --message 'Update funnel copy'
24
23
  fgrove publish --env preview
25
24
  ```
26
25
 
27
- Use `fgrove env pull --dir <local-dir>` to refresh only the ignored local `.env` file after project settings change, without replacing source files.
26
+ Inside a synced folder, `fgrove` reads `.funnelsgrove-sync.json` first, so you do not need to run `fgrove use` when switching between local funnel directories. Use `fgrove use` only when you want a global fallback context for commands outside a synced folder.
27
+
28
+ Use `fgrove env pull` from a synced folder to refresh only the ignored local `.env` file after project settings change, without replacing source files.
28
29
 
29
30
  GitHub sync workflow:
30
31
 
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ import { type ActiveContext } from './authStore.js';
3
+ import { type SyncManifest } from './localSync.js';
4
+ type SyncTargetIdInput = {
5
+ explicitWorkspaceId?: string;
6
+ explicitFunnelId?: string;
7
+ manifest?: Pick<SyncManifest, 'workspaceId' | 'funnelId'> | null;
8
+ active?: Pick<ActiveContext, 'workspaceId' | 'funnelId'> | null;
9
+ defaultWorkspaceId?: string;
10
+ };
11
+ export declare function resolveSyncTargetIds(input: SyncTargetIdInput): {
12
+ workspaceId: string;
13
+ funnelId?: string;
14
+ };
15
+ export declare function isCliEntrypoint(invokedPath: string | undefined, modulePath: string, realpath?: (filePath: string) => string): boolean;
2
16
  export {};
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from 'node:fs';
2
+ import { readFileSync, realpathSync } from 'node:fs';
3
3
  import { cp, mkdir } from 'node:fs/promises';
4
4
  import { createInterface } from 'node:readline/promises';
5
5
  import path from 'node:path';
@@ -37,6 +37,30 @@ const DEFAULT_API_URL = 'https://api.funnelsgrove.com/trpc';
37
37
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
38
38
  const PUBLISH_POLL_INTERVAL_MS = 2_000;
39
39
  const PUBLISH_WAIT_TIMEOUT_MS = 30 * 60_000;
40
+ export function resolveSyncTargetIds(input) {
41
+ const workspaceId = input.explicitWorkspaceId ||
42
+ input.manifest?.workspaceId ||
43
+ input.active?.workspaceId ||
44
+ input.defaultWorkspaceId;
45
+ if (!workspaceId) {
46
+ throw new Error('No workspace found for this account.');
47
+ }
48
+ return {
49
+ workspaceId,
50
+ funnelId: input.explicitFunnelId || input.manifest?.funnelId || input.active?.funnelId,
51
+ };
52
+ }
53
+ export function isCliEntrypoint(invokedPath, modulePath, realpath = realpathSync) {
54
+ if (!invokedPath) {
55
+ return false;
56
+ }
57
+ try {
58
+ return realpath(path.resolve(invokedPath)) === realpath(modulePath);
59
+ }
60
+ catch {
61
+ return path.resolve(invokedPath) === path.resolve(modulePath);
62
+ }
63
+ }
40
64
  const getApiUrl = () => {
41
65
  const options = program.opts();
42
66
  return options.apiUrl || process.env.FUNNELSGROVE_API_URL || DEFAULT_API_URL;
@@ -218,18 +242,24 @@ const resolveSyncTarget = async (input) => {
218
242
  const sourceDir = path.resolve(process.cwd(), input.dir || '.');
219
243
  const manifest = await readSyncManifest(sourceDir);
220
244
  const active = await loadActiveContext(getConfigPath());
221
- const workspaceId = input.workspace
222
- ? await resolveWorkspaceId(input.token, input.workspace)
223
- : manifest?.workspaceId || active?.workspaceId || await resolveWorkspaceId(input.token);
224
- const funnelId = input.funnel
225
- ? await resolveFunnelId(input.token, workspaceId, input.funnel)
226
- : manifest?.funnelId || active?.funnelId;
227
- if (!funnelId) {
228
- throw new Error('No active funnel. Run `fgrove use --funnel <id-or-slug>` or pass `--funnel`.');
245
+ const explicitWorkspaceId = input.workspace ? await resolveWorkspaceId(input.token, input.workspace) : undefined;
246
+ const fallbackWorkspaceId = explicitWorkspaceId || manifest?.workspaceId || active?.workspaceId || await resolveWorkspaceId(input.token);
247
+ const explicitFunnelId = input.funnel
248
+ ? await resolveFunnelId(input.token, fallbackWorkspaceId, input.funnel)
249
+ : undefined;
250
+ const resolved = resolveSyncTargetIds({
251
+ explicitWorkspaceId,
252
+ explicitFunnelId,
253
+ manifest,
254
+ active,
255
+ defaultWorkspaceId: fallbackWorkspaceId,
256
+ });
257
+ if (!resolved.funnelId) {
258
+ throw new Error('No synced funnel found. Run from a synced funnel directory, pass `--funnel`, or set a fallback with `fgrove use --funnel <id-or-slug>`.');
229
259
  }
230
260
  return {
231
- workspaceId,
232
- funnelId,
261
+ workspaceId: resolved.workspaceId,
262
+ funnelId: resolved.funnelId,
233
263
  sourceDir,
234
264
  manifest,
235
265
  };
@@ -277,8 +307,8 @@ program
277
307
  .option('--config <path>', 'Auth config path', process.env.FUNNELSGROVE_CONFIG || getDefaultAuthConfigPath());
278
308
  addExamples(program, [
279
309
  'fgrove login',
280
- 'fgrove use --project claimbee --funnel claimbee-ios',
281
- 'fgrove sync down --dir ./claimbee-ios',
310
+ 'fgrove sync down --funnel claimbee-ios --dir ./claimbee-ios',
311
+ 'cd ./claimbee-ios && fgrove status',
282
312
  'fgrove publish --env preview',
283
313
  ]);
284
314
  addExamples(program
@@ -372,9 +402,9 @@ addExamples(program
372
402
  console.log(`user\t${me.user.email || me.user.id}`);
373
403
  console.log(`api\t${getApiUrl()}`);
374
404
  console.log(`config\t${getConfigPath()}`);
375
- console.log(`workspace\t${active?.workspaceName || active?.workspaceSlug || active?.workspaceId || me.workspace?.name || 'Not set'}`);
376
- console.log(`project\t${active?.projectName || active?.projectSlug || active?.projectId || 'Not set'}`);
377
- console.log(`funnel\t${active?.funnelName || active?.funnelSlug || active?.funnelId || 'Not set'}`);
405
+ console.log(`workspace\t${manifest?.workspaceId || active?.workspaceName || active?.workspaceSlug || active?.workspaceId || me.workspace?.name || 'Not set'}`);
406
+ console.log(`project\t${manifest ? 'Not set' : active?.projectName || active?.projectSlug || active?.projectId || 'Not set'}`);
407
+ console.log(`funnel\t${manifest?.funnelId || active?.funnelName || active?.funnelSlug || active?.funnelId || 'Not set'}`);
378
408
  if (manifest) {
379
409
  console.log(`localWorkspace\t${manifest.workspaceId}`);
380
410
  console.log(`localFunnel\t${manifest.funnelId}`);
@@ -441,7 +471,7 @@ addExamples(funnelsCommand
441
471
  console.log(`${result.funnel.id}\t${result.funnel.name}\t${result.funnel.slug}`);
442
472
  });
443
473
  const syncCommand = addExamples(program.command('sync').description('Sync funnel source'), [
444
- 'fgrove sync down --dir ./claimbee-ios',
474
+ 'fgrove sync down --funnel claimbee-ios --dir ./claimbee-ios',
445
475
  'fgrove sync up --message "Update copy"',
446
476
  ]);
447
477
  addExamples(syncCommand
@@ -450,7 +480,6 @@ addExamples(syncCommand
450
480
  .option('--workspace <id-or-slug-or-name>', 'Workspace id, slug, or name')
451
481
  .option('--funnel <id-or-slug>', 'Funnel id or slug')
452
482
  .requiredOption('--dir <path>', 'Local target directory'), [
453
- 'fgrove sync down --dir ./claimbee-ios',
454
483
  'fgrove sync down --funnel claimbee-ios --dir ./claimbee-ios',
455
484
  ])
456
485
  .action(async (options) => {
@@ -827,7 +856,9 @@ addExamples(program
827
856
  console.log(' npm install');
828
857
  console.log(' npm run dev');
829
858
  });
830
- program.parseAsync().catch((error) => {
831
- console.error(error instanceof Error ? error.message : String(error));
832
- process.exitCode = 1;
833
- });
859
+ if (isCliEntrypoint(process.argv[1], fileURLToPath(import.meta.url))) {
860
+ program.parseAsync().catch((error) => {
861
+ console.error(error instanceof Error ? error.message : String(error));
862
+ process.exitCode = 1;
863
+ });
864
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "FunnelsGrove command-line tools for editing, syncing, and publishing funnels",
5
5
  "type": "module",
6
6
  "bin": {