@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 +128 -0
- package/dist/commands/actions.d.ts +2 -0
- package/dist/commands/actions.d.ts.map +1 -0
- package/dist/commands/actions.js +59 -0
- package/dist/commands/db.js +1 -1
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +44 -0
- package/dist/commands/doctor.js +5 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -2
- package/dist/commands/onboard.d.ts +7 -0
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +34 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -2
- package/dist/lib/actions-client.d.ts +16 -0
- package/dist/lib/actions-client.d.ts.map +1 -0
- package/dist/lib/actions-client.js +150 -0
- package/dist/lib/onboarding.js +3 -3
- package/package.json +3 -2
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 @@
|
|
|
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
|
+
}
|
package/dist/commands/db.js
CHANGED
|
@@ -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
|
|
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 @@
|
|
|
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
|
+
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
117
|
-
console.log(' pnpm
|
|
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":"
|
|
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"}
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
|
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":"
|
|
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"}
|
package/dist/commands/onboard.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
31
|
+
run = true;
|
|
12
32
|
}
|
|
13
|
-
|
|
14
|
-
|
|
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 (
|
|
20
|
-
|
|
37
|
+
if (env.CI === 'true') {
|
|
38
|
+
run = false;
|
|
21
39
|
}
|
|
22
|
-
return { yes,
|
|
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.
|
|
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
|
|
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>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
+
}
|
package/dist/lib/onboarding.js
CHANGED
|
@@ -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
|
|
497
|
-
console.log(
|
|
498
|
-
console.log(
|
|
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.
|
|
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
|
".": {
|