@doubledigit/cli 0.2.0 → 0.3.0

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 ADDED
@@ -0,0 +1,128 @@
1
+ # @doubledigit/cli
2
+
3
+ CLI for Double Digit local setup, project bootstrapping, extension management, and database workflows.
4
+
5
+ Double Digit is a pluggable Next.js/Payload developer control plane. The CLI is the supported command surface for creating a new Double Digit project and managing an existing local workspace.
6
+
7
+ ## Usage
8
+
9
+ Run the published package directly:
10
+
11
+ ```bash
12
+ npx @doubledigit/cli init my-project
13
+ pnpm dlx @doubledigit/cli init my-project
14
+ ```
15
+
16
+ Inside a Double Digit project, use the repo-local shortcut:
17
+
18
+ ```bash
19
+ pnpm dev
20
+ pnpm dd doctor
21
+ pnpm onboard
22
+ pnpm onboard -- --no-run
23
+ pnpm dd actions remotion-hub
24
+ ```
25
+
26
+ If installed globally, the binary is available as `dd`:
27
+
28
+ ```bash
29
+ dd dev
30
+ dd doctor
31
+ dd onboard
32
+ dd onboard --no-run
33
+ dd actions remotion-hub
34
+ ```
35
+
36
+ ## Requirements
37
+
38
+ - Node.js 20 or newer
39
+ - pnpm 9 or newer
40
+
41
+ Enable pnpm through Corepack when needed:
42
+
43
+ ```bash
44
+ corepack enable
45
+ ```
46
+
47
+ ## Core Commands
48
+
49
+ ```bash
50
+ dd init <project-name> # scaffold a new Double Digit project
51
+ dd doctor # check local prerequisites and project health
52
+ dd onboard # prepare env, dependencies, DB, migrations, types, and start
53
+ dd onboard --no-run # setup only
54
+ dd run # lower-level runtime bootstrap/start command
55
+ dd dev # start DB + app without migrations or seed data
56
+ dd db status # inspect migration state
57
+ dd db migrate # run migrations
58
+ dd db create <target> <name> # create a migration for shared or micro-app schema
59
+ dd actions <app> [action] # discover and invoke micro-app actions
60
+ dd add <source> # install an extension
61
+ dd sync # regenerate workspace registries
62
+ dd list # list discovered micro-apps
63
+ dd enable <name> # enable a micro-app
64
+ dd disable <name> # disable a micro-app
65
+ dd remove <name> # remove a micro-app package and wiring
66
+ dd info <name> # show detailed extension metadata
67
+ dd outdated # compare marketplace installs against cached catalogs
68
+ dd reconcile # detect registry, lock file, and package drift
69
+ dd marketplace <subcommand> # manage marketplace registrations
70
+ dd browse # browse marketplace extensions
71
+ ```
72
+
73
+ `add` is also available as `install`, and `remove` is also available as `uninstall`.
74
+
75
+ ## New Project Bootstrap
76
+
77
+ ```bash
78
+ npx @doubledigit/cli init my-project --yes
79
+ cd my-project
80
+ pnpm onboard
81
+ ```
82
+
83
+ Useful `init` options:
84
+
85
+ - `--yes` skips prompts and uses defaults.
86
+ - `--run` bootstraps and starts the app after scaffolding.
87
+ - `--skip-install` skips dependency installation.
88
+ - `--no-git` removes the cloned `.git` directory from the generated project.
89
+
90
+ ## Existing Project Setup
91
+
92
+ Use `onboard` for first-time setup and startup:
93
+
94
+ ```bash
95
+ pnpm onboard
96
+ ```
97
+
98
+ Pass `--no-run` when you want setup-only work:
99
+
100
+ ```bash
101
+ pnpm onboard -- --no-run
102
+ ```
103
+
104
+ Leave `DATABASE_URL` empty in `apps/main-app/.env` to use the default CLI-managed embedded PostgreSQL flow. Provide `DATABASE_URL` through the environment or `.env` when using your own PostgreSQL instance.
105
+
106
+ Use `dd dev` when you want the app and database ready without running migrations or seed data. It defaults to port `3000` and accepts `--port`, for example:
107
+
108
+ ```bash
109
+ dd dev --port 3000
110
+ ```
111
+
112
+ ## Action Invocations
113
+
114
+ Use `dd actions` to discover or call enabled micro-app actions through the running app:
115
+
116
+ ```bash
117
+ dd actions remotion-hub
118
+ dd actions remotion-hub search-components --query "animated chart" --limit 5
119
+ ```
120
+
121
+ The command resolves the app URL from `DD_APP_URL`, `APP_URL`, or `BETTER_AUTH_URL` and prints JSON responses to stdout.
122
+
123
+ ## Links
124
+
125
+ - Repository: https://github.com/crystalphantom/double-digit
126
+ - Issues: https://github.com/crystalphantom/double-digit/issues
127
+ - Getting started: https://github.com/crystalphantom/double-digit/blob/main/docs/getting-started.md
128
+ - Architecture: https://github.com/crystalphantom/double-digit/blob/main/docs/architecture.md
@@ -0,0 +1,2 @@
1
+ export declare function actions(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/commands/actions.ts"],"names":[],"mappings":"AAyDA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAa3D"}
@@ -0,0 +1,59 @@
1
+ import { readConfig, isEntryEnabled } from '../config.js';
2
+ import { DEFAULT_APP_URL } from '../lib/onboarding.js';
3
+ import { buildActionRequest, fetchActionRequest, parseActionsArgs, } from '../lib/actions-client.js';
4
+ import { resolveWorkspacePaths } from '../paths.js';
5
+ import { scanWorkspace } from '../scanner.js';
6
+ const HELP = `
7
+ dd actions — Discover and invoke micro-app actions
8
+
9
+ Usage:
10
+ dd actions <micro-app>
11
+ dd actions <micro-app> <action> [options]
12
+
13
+ Options:
14
+ --url, --app-url <url> App URL (default: DD_APP_URL, APP_URL, or ${DEFAULT_APP_URL})
15
+ --json <json> JSON request body to send to the action
16
+ --query <text> Set input.query
17
+ --limit <number> Set input.limit
18
+ --namespace <namespace> Set input namespace/filter
19
+ --slug <slug> Set input.slug
20
+ --id-or-slug <value> Set input.idOrSlug
21
+ --kind <kind> Set input kind/filter
22
+ --preview-type <type> Set input previewType/filter
23
+ --tag, --tags <tags> Add one tag or comma-separated tags to filters.tags
24
+
25
+ Examples:
26
+ dd actions remotion-hub
27
+ dd actions remotion-hub search-components --query "animated chart" --limit 5
28
+ dd actions remotion-hub get-component --namespace doubledigit --slug motion-strip
29
+ dd actions remotion-hub get-registry-payload --json '{"namespace":"doubledigit","slug":"motion-strip"}'
30
+ `;
31
+ function resolveAppUrl(parsedUrl) {
32
+ return parsedUrl
33
+ ?? process.env.DD_APP_URL
34
+ ?? process.env.APP_URL
35
+ ?? process.env.BETTER_AUTH_URL
36
+ ?? DEFAULT_APP_URL;
37
+ }
38
+ function validateMicroApp(microApp) {
39
+ const paths = resolveWorkspacePaths();
40
+ const app = scanWorkspace(paths).microApps.find((candidate) => candidate.key === microApp);
41
+ if (!app) {
42
+ throw new Error(`Unknown micro-app: ${microApp}`);
43
+ }
44
+ const config = readConfig(paths.configPath);
45
+ if (!isEntryEnabled(config.apps?.[microApp])) {
46
+ throw new Error(`Micro-app is disabled: ${microApp}`);
47
+ }
48
+ }
49
+ export async function actions(args) {
50
+ const parsed = parseActionsArgs(args);
51
+ if (parsed.help || !parsed.microApp) {
52
+ console.log(HELP);
53
+ return;
54
+ }
55
+ validateMicroApp(parsed.microApp);
56
+ const request = buildActionRequest(parsed, resolveAppUrl(parsed.appUrl));
57
+ const result = await fetchActionRequest(request);
58
+ console.log(JSON.stringify(result, null, 2));
59
+ }
@@ -8,7 +8,7 @@ async function dbStatus(args) {
8
8
  const databaseMode = databasePreference.mode;
9
9
  const databaseUrl = databasePreference.databaseUrl;
10
10
  if (!databaseUrl) {
11
- console.error('Error: no database configuration is available. Run `pnpm dd onboard` first.');
11
+ console.error('Error: no database configuration is available. Run `pnpm onboard` first.');
12
12
  process.exit(1);
13
13
  }
14
14
  const info = parseDatabaseUrl(databaseUrl);
@@ -0,0 +1,2 @@
1
+ export declare function dev(args?: string[]): Promise<void>;
2
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AA+BA,wBAAsB,GAAG,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkC5D"}
@@ -0,0 +1,44 @@
1
+ import { resolveWorkspacePaths } from '../paths.js';
2
+ import { applyRuntimeDatabaseUrl, databaseRuntimeToEnvOptions, ensureDatabaseReady, ensureLocalEnv, startDevServer, } from '../lib/onboarding.js';
3
+ function applyDevArgs(args) {
4
+ for (let index = 0; index < args.length; index++) {
5
+ const arg = args[index];
6
+ if (arg === '--port' || arg === '-p') {
7
+ const value = args[index + 1];
8
+ if (!value || value.startsWith('-')) {
9
+ throw new Error(`${arg} requires a port value.`);
10
+ }
11
+ process.env.APP_PORT = value;
12
+ index++;
13
+ continue;
14
+ }
15
+ if (arg.startsWith('--port=')) {
16
+ process.env.APP_PORT = arg.slice('--port='.length);
17
+ continue;
18
+ }
19
+ throw new Error(`Unsupported pnpm dev argument: ${arg}`);
20
+ }
21
+ }
22
+ export async function dev(args = []) {
23
+ applyDevArgs(args);
24
+ process.env.APP_PORT ??= '3000';
25
+ const paths = resolveWorkspacePaths();
26
+ const envSetup = ensureLocalEnv(paths);
27
+ const runtime = await ensureDatabaseReady(paths, {
28
+ yes: true,
29
+ allowDockerFallback: true,
30
+ });
31
+ const runtimeEnv = applyRuntimeDatabaseUrl(ensureLocalEnv(paths, databaseRuntimeToEnvOptions(runtime)).env, runtime);
32
+ console.log(`✔ ${runtime.mode === 'embedded' ? 'Embedded' : runtime.mode === 'docker' ? 'Docker' : 'External'} PostgreSQL is ready`);
33
+ if (runtime.mode === 'embedded' && runtime.embeddedPort) {
34
+ console.log(` Port: ${runtime.embeddedPort}`);
35
+ }
36
+ if (runtime.mode === 'docker' && runtime.dockerPort) {
37
+ console.log(` Docker host port: ${runtime.dockerPort}`);
38
+ }
39
+ if (envSetup.updatedKeys.length > 0) {
40
+ console.log(` Updated ${envSetup.envPath.replace(`${paths.root}/`, '')}: ${envSetup.updatedKeys.join(', ')}`);
41
+ }
42
+ console.log('Starting development server without running migrations or seed data...\n');
43
+ await startDevServer(paths, runtimeEnv);
44
+ }
@@ -62,19 +62,19 @@ export async function doctor() {
62
62
  ? embeddedSupport.supported
63
63
  ? dbReachable
64
64
  ? 'Embedded PostgreSQL is reachable.'
65
- : 'Embedded PostgreSQL is not running yet. `pnpm dev` or `dd run` will start it automatically.'
65
+ : 'Embedded PostgreSQL is not running yet. `pnpm onboard` will start it automatically.'
66
66
  : embeddedSupport.reason || 'Embedded PostgreSQL is not supported on this host.'
67
67
  : dbReachable
68
68
  ? 'PostgreSQL is reachable from DATABASE_URL.'
69
69
  : dockerAvailable()
70
- ? 'Database is not reachable yet. Run `pnpm dd onboard` or start Docker/Postgres manually.'
70
+ ? 'Database is not reachable yet. Run `pnpm onboard` or start Docker/Postgres manually.'
71
71
  : 'Database is not reachable. Start PostgreSQL or update DATABASE_URL.',
72
72
  });
73
73
  if (databaseMode === 'embedded' && embeddedSupport.supported && !dbReachable) {
74
74
  checks.push({
75
75
  label: 'Migration status',
76
76
  ok: true,
77
- detail: 'Skipped until embedded PostgreSQL starts. `pnpm dev` or `dd run` will start it automatically.',
77
+ detail: 'Skipped until embedded PostgreSQL starts. `pnpm onboard` will start it automatically.',
78
78
  });
79
79
  }
80
80
  else {
@@ -113,8 +113,8 @@ export async function doctor() {
113
113
  const failed = checks.filter((check) => !check.ok);
114
114
  if (failed.length > 0) {
115
115
  console.log('\nDoctor found issues. Recommended next steps:\n');
116
- console.log(' pnpm dd onboard # fix setup issues');
117
- console.log(' pnpm dev # then start the app\n');
116
+ console.log(' pnpm onboard # fix setup issues and start the app');
117
+ console.log(' pnpm onboard -- --no-run # setup only\n');
118
118
  process.exitCode = 1;
119
119
  return;
120
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA0JA,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+ExD"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAyJA,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+ExD"}
@@ -96,8 +96,7 @@ function printInitNextSteps(projectName, skipInstall) {
96
96
  console.log(`cd ${projectName}`);
97
97
  if (skipInstall) {
98
98
  console.log('pnpm install');
99
- console.log('pnpm dd onboard');
100
- console.log('pnpm dd run');
99
+ console.log('pnpm onboard');
101
100
  return;
102
101
  }
103
102
  printNextSteps();
@@ -1,2 +1,9 @@
1
+ interface OnboardOptions {
2
+ yes: boolean;
3
+ run: boolean;
4
+ help: boolean;
5
+ }
6
+ export declare function parseOnboardArgs(args: string[], env?: NodeJS.ProcessEnv): OnboardOptions;
1
7
  export declare function onboard(args: string[]): Promise<void>;
8
+ export {};
2
9
  //# sourceMappingURL=onboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"AA6CA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD3D"}
1
+ {"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"AAaA,UAAU,cAAc;IACtB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAmBD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,cAAc,CA4BhB;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyD3D"}
@@ -1,29 +1,51 @@
1
1
  import { resolveWorkspacePaths } from '../paths.js';
2
2
  import { applyRuntimeDatabaseUrl, captureCommand, databaseRuntimeToEnvOptions, ensureDatabaseReady, ensureLocalEnv, localDependenciesInstalled, printNextSteps, runChecked, startDevServer, } from '../lib/onboarding.js';
3
- function parseOnboardArgs(args) {
3
+ const ONBOARD_HELP = `Usage: dd onboard [--yes] [--no-run]
4
+
5
+ Prepare the local development environment and start the app.
6
+
7
+ Options:
8
+ --yes, -y Skip interactive prompts and use defaults.
9
+ --no-run Run setup only and exit without starting Next.js.
10
+ --no-start Alias for --no-run.
11
+ --setup-only Alias for --no-run.
12
+ --start Compatibility alias; running is now the default.
13
+ --help, -h Show this help message.
14
+
15
+ Examples:
16
+ dd onboard
17
+ dd onboard --no-run
18
+ APP_PORT=3111 dd onboard`;
19
+ export function parseOnboardArgs(args, env = process.env) {
4
20
  let yes = false;
5
- let start = false;
21
+ let run = true;
22
+ let help = false;
6
23
  for (const arg of args) {
24
+ if (arg === '--help' || arg === '-h') {
25
+ help = true;
26
+ }
7
27
  if (arg === '--yes' || arg === '-y') {
8
28
  yes = true;
9
29
  }
10
30
  if (arg === '--start') {
11
- start = true;
31
+ run = true;
12
32
  }
13
- // Keep --no-start and --setup-only as compatibility aliases (no-ops now
14
- // that the default is already no-start).
15
- if (arg === '--no-start' || arg === '--setup-only') {
16
- start = false;
33
+ if (arg === '--no-run' || arg === '--no-start' || arg === '--setup-only') {
34
+ run = false;
17
35
  }
18
36
  }
19
- if (process.env.CI === 'true') {
20
- start = false;
37
+ if (env.CI === 'true') {
38
+ run = false;
21
39
  }
22
- return { yes, start };
40
+ return { yes, run, help };
23
41
  }
24
42
  export async function onboard(args) {
25
- const paths = resolveWorkspacePaths();
26
43
  const options = parseOnboardArgs(args);
44
+ if (options.help) {
45
+ console.log(ONBOARD_HELP);
46
+ return;
47
+ }
48
+ const paths = resolveWorkspacePaths();
27
49
  console.log('\n🚀 Double Digit onboarding\n');
28
50
  const envSetup = ensureLocalEnv(paths);
29
51
  console.log(`✔ ${envSetup.created ? 'Created' : 'Found'} ${envSetup.envPath.replace(`${paths.root}/`, '')}`);
@@ -54,7 +76,7 @@ export async function onboard(args) {
54
76
  console.log('\nMigration status:');
55
77
  console.log(migrationStatus.output.trim());
56
78
  }
57
- if (options.start) {
79
+ if (options.run) {
58
80
  console.log('\nStarting development server...\n');
59
81
  await startDevServer(paths, runtimeEnv);
60
82
  return;
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * without importing the full extension-management dependency graph first.
7
7
  */
8
8
  declare const command: string, args: string[];
9
- declare const HELP = "\n@doubledigit/cli \u2014 Manage extensions and local setup\n\nCommands:\n doctor Check local prerequisites and project health\n onboard Prepare a local development environment (setup only)\n run Start the local app with automatic DB bootstrap\n db <subcommand> Database helpers (status, migrate, create)\n create <name> Scaffold a new micro-app from the template\n init <project-name> Scaffold a new Double Digit project\n add|install <source> Install an extension from GitHub or a marketplace\n sync Regenerate micro-apps.ts from dd-apps.config.json\n enable <name> Enable a micro-app (updates config + runs sync)\n disable <name> Disable a micro-app (updates config + runs sync)\n uninstall|remove <name> Completely remove a micro-app\n list List all discovered micro-apps with enabled/disabled status\n info <name> Show detailed info about an installed extension\n outdated Check for outdated marketplace extensions\n reconcile Detect drift between lock file, marketplace, and local files\n marketplace <sub> Manage marketplace registrations (add/list/update/remove)\n browse [marketplace] Browse available extensions in registered marketplaces\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n dd doctor\n dd onboard --yes\n dd onboard --start # setup + start the dev server\n dd init my-project # scaffold and bootstrap a fresh project\n dd init my-project --run # scaffold + bootstrap + start\n dd init my-project --skip-install --no-git\n dd run\n dd db status\n dd create invoice-tracker\n dd add gh:owner/repo/extensions/micro-apps/habit-tracker\n dd add habit-tracker@community\n dd info habit-tracker\n dd reconcile\n dd marketplace add digitaldouble/dd-marketplace\n dd browse community\n DD_APPS=tasks,agent-v2 dd sync\n";
9
+ declare const HELP = "\n@doubledigit/cli \u2014 Manage extensions and local setup\n\nCommands:\n doctor Check local prerequisites and project health\n onboard Prepare local development and start the app\n run Start the local app with automatic DB bootstrap\n dev Start DB + app without migrations or seed data\n db <subcommand> Database helpers (status, migrate, create)\n actions <app> [action] Discover and invoke micro-app actions\n create <name> Scaffold a new micro-app from the template\n init <project-name> Scaffold a new Double Digit project\n add|install <source> Install an extension from GitHub or a marketplace\n sync Regenerate micro-apps.ts from dd-apps.config.json\n enable <name> Enable a micro-app (updates config + runs sync)\n disable <name> Disable a micro-app (updates config + runs sync)\n uninstall|remove <name> Completely remove a micro-app\n list List all discovered micro-apps with enabled/disabled status\n info <name> Show detailed info about an installed extension\n outdated Check for outdated marketplace extensions\n reconcile Detect drift between lock file, marketplace, and local files\n marketplace <sub> Manage marketplace registrations (add/list/update/remove)\n browse [marketplace] Browse available extensions in registered marketplaces\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n dd doctor\n dd onboard --yes\n dd onboard # setup + start the dev server\n dd onboard --no-run # setup only\n dd init my-project # scaffold and bootstrap a fresh project\n dd init my-project --run # scaffold + bootstrap + start\n dd init my-project --skip-install --no-git\n dd run\n dd dev\n dd db status\n dd actions remotion-hub search-components --query \"animated chart\"\n dd create invoice-tracker\n dd add gh:owner/repo/extensions/micro-apps/habit-tracker\n dd add habit-tracker@community\n dd info habit-tracker\n dd reconcile\n dd marketplace add digitaldouble/dd-marketplace\n dd browse community\n DD_APPS=tasks,agent-v2 dd sync\n";
10
10
  declare function requireArg(value: string | undefined, usage: string): string;
11
11
  declare function runAddCommand(rawArgs: string[]): Promise<void>;
12
12
  declare function main(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,QAAA,MAAW,OAAO,UAAK,IAAI,UAAgB,CAAC;AAE5C,QAAA,MAAM,IAAI,y8DA0CT,CAAC;AAEF,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,iBAAe,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB7D;AAED,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyHnC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,QAAA,MAAW,OAAO,UAAK,IAAI,UAAgB,CAAC;AAE5C,QAAA,MAAM,IAAI,ysEA+CT,CAAC;AAEF,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,iBAAe,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB7D;AAED,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsInC"}
package/dist/index.js CHANGED
@@ -12,9 +12,11 @@ const HELP = `
12
12
 
13
13
  Commands:
14
14
  doctor Check local prerequisites and project health
15
- onboard Prepare a local development environment (setup only)
15
+ onboard Prepare local development and start the app
16
16
  run Start the local app with automatic DB bootstrap
17
+ dev Start DB + app without migrations or seed data
17
18
  db <subcommand> Database helpers (status, migrate, create)
19
+ actions <app> [action] Discover and invoke micro-app actions
18
20
  create <name> Scaffold a new micro-app from the template
19
21
  init <project-name> Scaffold a new Double Digit project
20
22
  add|install <source> Install an extension from GitHub or a marketplace
@@ -35,12 +37,15 @@ Options:
35
37
  Examples:
36
38
  dd doctor
37
39
  dd onboard --yes
38
- dd onboard --start # setup + start the dev server
40
+ dd onboard # setup + start the dev server
41
+ dd onboard --no-run # setup only
39
42
  dd init my-project # scaffold and bootstrap a fresh project
40
43
  dd init my-project --run # scaffold + bootstrap + start
41
44
  dd init my-project --skip-install --no-git
42
45
  dd run
46
+ dd dev
43
47
  dd db status
48
+ dd actions remotion-hub search-components --query "animated chart"
44
49
  dd create invoice-tracker
45
50
  dd add gh:owner/repo/extensions/micro-apps/habit-tracker
46
51
  dd add habit-tracker@community
@@ -98,11 +103,22 @@ async function main() {
98
103
  await run();
99
104
  break;
100
105
  }
106
+ case 'dev': {
107
+ const { dev } = await import('./commands/dev.js');
108
+ await dev(args);
109
+ break;
110
+ }
101
111
  case 'db': {
102
112
  const { db } = await import('./commands/db.js');
103
113
  await db(args);
104
114
  break;
105
115
  }
116
+ case 'actions':
117
+ case 'action': {
118
+ const { actions } = await import('./commands/actions.js');
119
+ await actions(args);
120
+ break;
121
+ }
106
122
  case 'create': {
107
123
  const name = requireArg(args[0], 'dd create <name>');
108
124
  const { create } = await import('./commands/create.js');
@@ -0,0 +1,16 @@
1
+ export interface ParsedActionsArgs {
2
+ microApp?: string;
3
+ actionName?: string;
4
+ appUrl?: string;
5
+ input: Record<string, unknown>;
6
+ help: boolean;
7
+ }
8
+ export interface ActionRequest {
9
+ url: string;
10
+ init: RequestInit;
11
+ }
12
+ export declare function parseActionsArgs(args: string[]): ParsedActionsArgs;
13
+ export declare function normalizeAppUrl(appUrl: string): string;
14
+ export declare function buildActionRequest(parsed: Pick<ParsedActionsArgs, 'microApp' | 'actionName' | 'input'>, appUrl: string): ActionRequest;
15
+ export declare function fetchActionRequest(request: ActionRequest): Promise<unknown>;
16
+ //# sourceMappingURL=actions-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions-client.d.ts","sourceRoot":"","sources":["../../src/lib/actions-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;CACnB;AA2DD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAsDlE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,UAE7C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAoBtI;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAajF"}
@@ -0,0 +1,150 @@
1
+ function requireValue(args, index, flag) {
2
+ const value = args[index + 1];
3
+ if (!value || value.startsWith('--')) {
4
+ throw new Error(`${flag} requires a value`);
5
+ }
6
+ return value;
7
+ }
8
+ function parseJson(value) {
9
+ try {
10
+ return JSON.parse(value);
11
+ }
12
+ catch {
13
+ throw new Error('--json must be valid JSON');
14
+ }
15
+ }
16
+ function asFilters(input) {
17
+ const filters = input.filters;
18
+ if (filters && typeof filters === 'object' && !Array.isArray(filters)) {
19
+ return filters;
20
+ }
21
+ const next = {};
22
+ input.filters = next;
23
+ return next;
24
+ }
25
+ function setInputValue(input, key, value) {
26
+ if (key === 'limit') {
27
+ const numeric = Number(value);
28
+ if (!Number.isFinite(numeric) || numeric <= 0) {
29
+ throw new Error('--limit must be a positive number');
30
+ }
31
+ input.limit = numeric;
32
+ return;
33
+ }
34
+ if (key === 'namespace' || key === 'kind' || key === 'previewType') {
35
+ input[key] = value;
36
+ asFilters(input)[key] = value;
37
+ return;
38
+ }
39
+ input[key] = value;
40
+ }
41
+ function addTag(input, value) {
42
+ const tags = value
43
+ .split(',')
44
+ .map((tag) => tag.trim())
45
+ .filter(Boolean);
46
+ if (tags.length === 0)
47
+ return;
48
+ const filters = asFilters(input);
49
+ const existing = Array.isArray(filters.tags) ? filters.tags : [];
50
+ filters.tags = [...existing, ...tags];
51
+ }
52
+ export function parseActionsArgs(args) {
53
+ const input = {};
54
+ const positional = [];
55
+ let appUrl;
56
+ let help = false;
57
+ for (let i = 0; i < args.length; i++) {
58
+ const arg = args[i];
59
+ if (arg === '--help' || arg === '-h') {
60
+ help = true;
61
+ }
62
+ else if (arg === '--url' || arg === '--app-url') {
63
+ appUrl = requireValue(args, i, arg);
64
+ i++;
65
+ }
66
+ else if (arg === '--json') {
67
+ Object.assign(input, parseJson(requireValue(args, i, arg)));
68
+ i++;
69
+ }
70
+ else if (arg === '--query') {
71
+ setInputValue(input, 'query', requireValue(args, i, arg));
72
+ i++;
73
+ }
74
+ else if (arg === '--limit') {
75
+ setInputValue(input, 'limit', requireValue(args, i, arg));
76
+ i++;
77
+ }
78
+ else if (arg === '--namespace') {
79
+ setInputValue(input, 'namespace', requireValue(args, i, arg));
80
+ i++;
81
+ }
82
+ else if (arg === '--slug') {
83
+ setInputValue(input, 'slug', requireValue(args, i, arg));
84
+ i++;
85
+ }
86
+ else if (arg === '--id-or-slug') {
87
+ setInputValue(input, 'idOrSlug', requireValue(args, i, arg));
88
+ i++;
89
+ }
90
+ else if (arg === '--kind') {
91
+ setInputValue(input, 'kind', requireValue(args, i, arg));
92
+ i++;
93
+ }
94
+ else if (arg === '--preview-type') {
95
+ setInputValue(input, 'previewType', requireValue(args, i, arg));
96
+ i++;
97
+ }
98
+ else if (arg === '--tag' || arg === '--tags') {
99
+ addTag(input, requireValue(args, i, arg));
100
+ i++;
101
+ }
102
+ else if (arg.startsWith('--')) {
103
+ throw new Error(`Unknown option: ${arg}`);
104
+ }
105
+ else {
106
+ positional.push(arg);
107
+ }
108
+ }
109
+ return {
110
+ microApp: positional[0],
111
+ actionName: positional[1],
112
+ appUrl,
113
+ input,
114
+ help,
115
+ };
116
+ }
117
+ export function normalizeAppUrl(appUrl) {
118
+ return appUrl.trim().replace(/\/+$/, '');
119
+ }
120
+ export function buildActionRequest(parsed, appUrl) {
121
+ if (!parsed.microApp) {
122
+ throw new Error('missing micro-app name');
123
+ }
124
+ const base = normalizeAppUrl(appUrl);
125
+ const actionPath = parsed.actionName
126
+ ? `/${encodeURIComponent(parsed.actionName)}`
127
+ : '';
128
+ return {
129
+ url: `${base}/api/${encodeURIComponent(parsed.microApp)}/actions${actionPath}`,
130
+ init: parsed.actionName
131
+ ? {
132
+ method: 'POST',
133
+ headers: { 'Content-Type': 'application/json' },
134
+ body: JSON.stringify(parsed.input),
135
+ }
136
+ : { method: 'GET' },
137
+ };
138
+ }
139
+ export async function fetchActionRequest(request) {
140
+ const response = await fetch(request.url, request.init);
141
+ const text = await response.text();
142
+ const payload = text ? JSON.parse(text) : null;
143
+ if (!response.ok) {
144
+ const error = payload && typeof payload === 'object' && 'error' in payload
145
+ ? String(payload.error)
146
+ : `Request failed with HTTP ${response.status}`;
147
+ throw new Error(error);
148
+ }
149
+ return payload;
150
+ }
@@ -493,9 +493,9 @@ export async function startDevServer(paths, envOverrides = {}) {
493
493
  }
494
494
  export function printNextSteps() {
495
495
  console.log("\n✅ Setup complete! Next steps:\n");
496
- console.log(" pnpm dev # start the development server");
497
- console.log(` open ${DEFAULT_APP_URL}`);
498
- console.log(` open ${buildAppUrl(DEFAULT_APP_PORT)}/admin`);
496
+ console.log(" pnpm dev # start DB + app without migrations (default port 3000)");
497
+ console.log(" pnpm onboard # setup + start through the DD CLI (default port 3111)");
498
+ console.log(" open http://localhost:3000");
499
499
  console.log("");
500
500
  }
501
501
  function createEmbeddedPostgresLogBuffer(limit = EMBEDDED_LOG_BUFFER_LIMIT) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doubledigit/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "CLI for Double Digit local setup and extension management.",
6
6
  "license": "MIT",
@@ -25,7 +25,8 @@
25
25
  "dd": "./dist/index.js"
26
26
  },
27
27
  "files": [
28
- "dist"
28
+ "dist",
29
+ "README.md"
29
30
  ],
30
31
  "exports": {
31
32
  ".": {