@doubledigit/cli 0.1.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/LICENSE +21 -0
- package/dist/codegen.d.ts +12 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +107 -0
- package/dist/commands/add.d.ts +26 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +548 -0
- package/dist/commands/browse.d.ts +8 -0
- package/dist/commands/browse.d.ts.map +1 -0
- package/dist/commands/browse.js +116 -0
- package/dist/commands/create.d.ts +12 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +218 -0
- package/dist/commands/db.d.ts +2 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +64 -0
- package/dist/commands/disable.d.ts +5 -0
- package/dist/commands/disable.d.ts.map +1 -0
- package/dist/commands/disable.js +29 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +88 -0
- package/dist/commands/enable.d.ts +5 -0
- package/dist/commands/enable.d.ts.map +1 -0
- package/dist/commands/enable.js +29 -0
- package/dist/commands/info.d.ts +8 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/info.js +84 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +44 -0
- package/dist/commands/marketplace.d.ts +11 -0
- package/dist/commands/marketplace.d.ts.map +1 -0
- package/dist/commands/marketplace.js +205 -0
- package/dist/commands/onboard.d.ts +2 -0
- package/dist/commands/onboard.d.ts.map +1 -0
- package/dist/commands/onboard.js +58 -0
- package/dist/commands/outdated.d.ts +8 -0
- package/dist/commands/outdated.d.ts.map +1 -0
- package/dist/commands/outdated.js +107 -0
- package/dist/commands/reconcile.d.ts +12 -0
- package/dist/commands/reconcile.d.ts.map +1 -0
- package/dist/commands/reconcile.js +175 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +37 -0
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +34 -0
- package/dist/commands/uninstall.d.ts +14 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +190 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +37 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +181 -0
- package/dist/lib/github-auth.d.ts +8 -0
- package/dist/lib/github-auth.d.ts.map +1 -0
- package/dist/lib/github-auth.js +30 -0
- package/dist/lib/lock-file.d.ts +67 -0
- package/dist/lib/lock-file.d.ts.map +1 -0
- package/dist/lib/lock-file.js +117 -0
- package/dist/lib/marketplace-schema.d.ts +607 -0
- package/dist/lib/marketplace-schema.d.ts.map +1 -0
- package/dist/lib/marketplace-schema.js +111 -0
- package/dist/lib/marketplace.d.ts +57 -0
- package/dist/lib/marketplace.d.ts.map +1 -0
- package/dist/lib/marketplace.js +270 -0
- package/dist/lib/onboarding.d.ts +84 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +1004 -0
- package/dist/lib/rewrite-extension-tsconfig.d.ts +22 -0
- package/dist/lib/rewrite-extension-tsconfig.d.ts.map +1 -0
- package/dist/lib/rewrite-extension-tsconfig.js +80 -0
- package/dist/lib/source-parser.d.ts +35 -0
- package/dist/lib/source-parser.d.ts.map +1 -0
- package/dist/lib/source-parser.js +121 -0
- package/dist/lib/validators.d.ts +73 -0
- package/dist/lib/validators.d.ts.map +1 -0
- package/dist/lib/validators.js +435 -0
- package/dist/paths.d.ts +46 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +85 -0
- package/dist/scanner.d.ts +41 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +100 -0
- package/package.json +49 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dd marketplace <subcommand> — Manage marketplace registrations.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* add <source> Register a marketplace (GitHub repo with .doubledigit/marketplace.json)
|
|
6
|
+
* list List registered marketplaces
|
|
7
|
+
* update [name] Update marketplace catalog(s)
|
|
8
|
+
* remove <name> Remove a registered marketplace
|
|
9
|
+
*/
|
|
10
|
+
import { readKnownMarketplaces, addKnownMarketplace, removeKnownMarketplace, fetchMarketplaceManifest, updateMarketplace, writeCachedMarketplace, readCachedMarketplace, assertValidMarketplaceName, } from '../lib/marketplace.js';
|
|
11
|
+
import { resolveWorkspacePaths } from '../paths.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// marketplace add <source> [--name <name>] [--ref <ref>]
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
async function marketplaceAdd(args) {
|
|
16
|
+
const paths = resolveWorkspacePaths();
|
|
17
|
+
let source;
|
|
18
|
+
let nameOverride;
|
|
19
|
+
let ref;
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
if (args[i] === '--name') {
|
|
22
|
+
i++;
|
|
23
|
+
nameOverride = args[i];
|
|
24
|
+
}
|
|
25
|
+
else if (args[i] === '--ref') {
|
|
26
|
+
i++;
|
|
27
|
+
ref = args[i];
|
|
28
|
+
}
|
|
29
|
+
else if (!args[i].startsWith('--')) {
|
|
30
|
+
source = args[i];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!source) {
|
|
34
|
+
console.error('Error: <source> is required.\n' +
|
|
35
|
+
'Usage: dd marketplace add <owner/repo> [--name <name>] [--ref <ref>]');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
console.log(`\n🏪 Registering marketplace from ${source}...\n`);
|
|
39
|
+
// Fetch and validate the manifest
|
|
40
|
+
console.log(' Fetching marketplace manifest...');
|
|
41
|
+
let manifest;
|
|
42
|
+
try {
|
|
43
|
+
manifest = await fetchMarketplaceManifest(source, undefined, ref);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error(`\n❌ Failed to fetch marketplace manifest:\n ${err instanceof Error ? err.message : err}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const name = nameOverride || manifest.name;
|
|
50
|
+
// Validate marketplace name early for friendly CLI errors
|
|
51
|
+
try {
|
|
52
|
+
assertValidMarketplaceName(name);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
console.error(`\n❌ Invalid marketplace name: "${name}"\n` +
|
|
56
|
+
' Names must start with an alphanumeric character and contain only [a-zA-Z0-9_-].\n' +
|
|
57
|
+
' Use --name <safe-name> to override.');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
// Check if already registered
|
|
61
|
+
const known = readKnownMarketplaces(paths.root);
|
|
62
|
+
if (name in known.marketplaces) {
|
|
63
|
+
console.error(`\n❌ Marketplace "${name}" is already registered.\n` +
|
|
64
|
+
' Use `dd marketplace update` to refresh, or `dd marketplace remove` first.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
console.log(` ✔ Found marketplace: ${manifest.name}`);
|
|
68
|
+
console.log(` ✔ ${manifest.extensions.length} extension(s) available`);
|
|
69
|
+
// Register + cache
|
|
70
|
+
const entry = {
|
|
71
|
+
source,
|
|
72
|
+
manifestPath: '.doubledigit/marketplace.json',
|
|
73
|
+
ref,
|
|
74
|
+
cachedAt: new Date().toISOString(),
|
|
75
|
+
extensionCount: manifest.extensions.length,
|
|
76
|
+
};
|
|
77
|
+
addKnownMarketplace(paths.root, name, entry);
|
|
78
|
+
writeCachedMarketplace(paths.root, name, manifest);
|
|
79
|
+
console.log(`\n✅ Marketplace "${name}" registered successfully!`);
|
|
80
|
+
console.log(`\n Browse extensions: dd browse ${name}`);
|
|
81
|
+
console.log(` Install from it: dd add <name>@${name}\n`);
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// marketplace list
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
async function marketplaceList() {
|
|
87
|
+
const paths = resolveWorkspacePaths();
|
|
88
|
+
const known = readKnownMarketplaces(paths.root);
|
|
89
|
+
const entries = Object.entries(known.marketplaces);
|
|
90
|
+
if (entries.length === 0) {
|
|
91
|
+
console.log('\nNo marketplaces registered.\n\n' +
|
|
92
|
+
'Add one with:\n' +
|
|
93
|
+
' dd marketplace add owner/repo\n');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log('\n🏪 Registered marketplaces:\n');
|
|
97
|
+
for (const [name, entry] of entries) {
|
|
98
|
+
const cached = readCachedMarketplace(paths.root, name);
|
|
99
|
+
const extCount = entry.extensionCount ?? cached?.manifest.extensions.length ?? '?';
|
|
100
|
+
const cachedAt = entry.cachedAt
|
|
101
|
+
? new Date(entry.cachedAt).toLocaleDateString()
|
|
102
|
+
: 'never';
|
|
103
|
+
console.log(` ${name}`);
|
|
104
|
+
console.log(` Source: ${entry.source}`);
|
|
105
|
+
console.log(` Extensions: ${extCount}`);
|
|
106
|
+
console.log(` Updated: ${cachedAt}`);
|
|
107
|
+
console.log('');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// marketplace update [name]
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
async function marketplaceUpdate(args) {
|
|
114
|
+
const paths = resolveWorkspacePaths();
|
|
115
|
+
const known = readKnownMarketplaces(paths.root);
|
|
116
|
+
const targetName = args[0];
|
|
117
|
+
if (Object.keys(known.marketplaces).length === 0) {
|
|
118
|
+
console.log('\nNo marketplaces registered. Nothing to update.\n');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const names = targetName
|
|
122
|
+
? [targetName]
|
|
123
|
+
: Object.keys(known.marketplaces);
|
|
124
|
+
console.log('\n🔄 Updating marketplace catalog(s)...\n');
|
|
125
|
+
for (const name of names) {
|
|
126
|
+
if (!(name in known.marketplaces)) {
|
|
127
|
+
console.error(` ✘ Marketplace "${name}" is not registered.`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const manifest = await updateMarketplace(paths.root, name);
|
|
132
|
+
console.log(` ✔ ${name} — ${manifest.extensions.length} extension(s)`);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
console.error(` ✘ ${name} — ${err instanceof Error ? err.message : err}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
console.log('\n✅ Marketplace update complete.\n');
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// marketplace remove <name>
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
async function marketplaceRemove(args) {
|
|
144
|
+
const name = args[0];
|
|
145
|
+
if (!name) {
|
|
146
|
+
console.error('Error: <name> is required.\nUsage: dd marketplace remove <name>');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const paths = resolveWorkspacePaths();
|
|
150
|
+
const removed = removeKnownMarketplace(paths.root, name);
|
|
151
|
+
if (!removed) {
|
|
152
|
+
console.error(`\n❌ Marketplace "${name}" is not registered.\n`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
console.log(`\n✅ Marketplace "${name}" removed.\n`);
|
|
156
|
+
}
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// Public dispatcher
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
const HELP = `
|
|
161
|
+
dd marketplace — Manage marketplace registrations
|
|
162
|
+
|
|
163
|
+
Subcommands:
|
|
164
|
+
add <source> Register a marketplace (GitHub repo with .doubledigit/marketplace.json)
|
|
165
|
+
list List registered marketplaces
|
|
166
|
+
update [name] Update marketplace catalog(s) — updates all if no name given
|
|
167
|
+
remove <name> Remove a registered marketplace
|
|
168
|
+
|
|
169
|
+
Examples:
|
|
170
|
+
dd marketplace add digitaldouble/dd-marketplace
|
|
171
|
+
dd marketplace add digitaldouble/dd-marketplace --name community
|
|
172
|
+
dd marketplace list
|
|
173
|
+
dd marketplace update community
|
|
174
|
+
dd marketplace remove community
|
|
175
|
+
`;
|
|
176
|
+
export async function marketplace(args) {
|
|
177
|
+
const subcommand = args[0];
|
|
178
|
+
const subArgs = args.slice(1);
|
|
179
|
+
switch (subcommand) {
|
|
180
|
+
case 'add':
|
|
181
|
+
await marketplaceAdd(subArgs);
|
|
182
|
+
break;
|
|
183
|
+
case 'list':
|
|
184
|
+
case 'ls':
|
|
185
|
+
await marketplaceList();
|
|
186
|
+
break;
|
|
187
|
+
case 'update':
|
|
188
|
+
case 'refresh':
|
|
189
|
+
await marketplaceUpdate(subArgs);
|
|
190
|
+
break;
|
|
191
|
+
case 'remove':
|
|
192
|
+
case 'rm':
|
|
193
|
+
await marketplaceRemove(subArgs);
|
|
194
|
+
break;
|
|
195
|
+
case '--help':
|
|
196
|
+
case '-h':
|
|
197
|
+
case undefined:
|
|
198
|
+
console.log(HELP);
|
|
199
|
+
break;
|
|
200
|
+
default:
|
|
201
|
+
console.error(`Unknown marketplace subcommand: ${subcommand}`);
|
|
202
|
+
console.log(HELP);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"AAuCA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD3D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { resolveWorkspacePaths } from '../paths.js';
|
|
2
|
+
import { applyRuntimeDatabaseUrl, captureCommand, databaseRuntimeToEnvOptions, ensureDatabaseReady, ensureLocalEnv, localDependenciesInstalled, printNextSteps, runChecked, startDevServer, } from '../lib/onboarding.js';
|
|
3
|
+
function parseOnboardArgs(args) {
|
|
4
|
+
let yes = false;
|
|
5
|
+
let start = true;
|
|
6
|
+
for (const arg of args) {
|
|
7
|
+
if (arg === '--yes' || arg === '-y') {
|
|
8
|
+
yes = true;
|
|
9
|
+
}
|
|
10
|
+
if (arg === '--no-start' || arg === '--setup-only') {
|
|
11
|
+
start = false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (process.env.CI === 'true') {
|
|
15
|
+
start = false;
|
|
16
|
+
}
|
|
17
|
+
return { yes, start };
|
|
18
|
+
}
|
|
19
|
+
export async function onboard(args) {
|
|
20
|
+
const paths = resolveWorkspacePaths();
|
|
21
|
+
const options = parseOnboardArgs(args);
|
|
22
|
+
console.log('\n🚀 Double Digit onboarding\n');
|
|
23
|
+
const envSetup = ensureLocalEnv(paths);
|
|
24
|
+
console.log(`✔ ${envSetup.created ? 'Created' : 'Found'} ${envSetup.envPath.replace(`${paths.root}/`, '')}`);
|
|
25
|
+
if (envSetup.updatedKeys.length > 0) {
|
|
26
|
+
console.log(` Updated: ${envSetup.updatedKeys.join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
const installedBefore = localDependenciesInstalled(paths);
|
|
29
|
+
runChecked('pnpm', ['install'], paths.root, 'Dependency install');
|
|
30
|
+
console.log(`✔ Dependencies ${installedBefore ? 'verified' : 'installed'}`);
|
|
31
|
+
const databaseRuntime = await ensureDatabaseReady(paths, {
|
|
32
|
+
yes: options.yes,
|
|
33
|
+
allowDockerFallback: true,
|
|
34
|
+
});
|
|
35
|
+
const runtimeEnv = applyRuntimeDatabaseUrl(ensureLocalEnv(paths, databaseRuntimeToEnvOptions(databaseRuntime)).env, databaseRuntime);
|
|
36
|
+
console.log(`✔ ${databaseRuntime.mode === 'embedded' ? 'Embedded' : databaseRuntime.mode === 'docker' ? 'Docker' : 'External'} PostgreSQL is ready`);
|
|
37
|
+
if (databaseRuntime.mode === 'embedded' && databaseRuntime.embeddedPort) {
|
|
38
|
+
console.log(` Port: ${databaseRuntime.embeddedPort}`);
|
|
39
|
+
}
|
|
40
|
+
if (databaseRuntime.mode === 'docker' && databaseRuntime.dockerPort) {
|
|
41
|
+
console.log(` Docker host port: ${databaseRuntime.dockerPort}`);
|
|
42
|
+
}
|
|
43
|
+
runChecked('pnpm', ['db:migrate'], paths.root, 'Migration run');
|
|
44
|
+
console.log('✔ Migrations applied');
|
|
45
|
+
runChecked('pnpm', ['payload:generate-types:main'], paths.root, 'Type generation');
|
|
46
|
+
console.log('✔ Payload types generated');
|
|
47
|
+
const migrationStatus = captureCommand('pnpm', ['db:migrate:status'], paths.root);
|
|
48
|
+
if (migrationStatus.ok && migrationStatus.output.trim()) {
|
|
49
|
+
console.log('\nMigration status:');
|
|
50
|
+
console.log(migrationStatus.output.trim());
|
|
51
|
+
}
|
|
52
|
+
if (options.start) {
|
|
53
|
+
console.log('\nStarting development server...\n');
|
|
54
|
+
await startDevServer(paths, runtimeEnv);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
printNextSteps();
|
|
58
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dd outdated — Compare installed extensions against marketplace catalogs.
|
|
3
|
+
*
|
|
4
|
+
* For each lock entry with a `marketplace` field, looks up the current
|
|
5
|
+
* marketplace catalog and reports version, SHA, or ref differences.
|
|
6
|
+
*/
|
|
7
|
+
export declare function outdated(): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=outdated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outdated.d.ts","sourceRoot":"","sources":["../../src/commands/outdated.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAkH9C"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dd outdated — Compare installed extensions against marketplace catalogs.
|
|
3
|
+
*
|
|
4
|
+
* For each lock entry with a `marketplace` field, looks up the current
|
|
5
|
+
* marketplace catalog and reports version, SHA, or ref differences.
|
|
6
|
+
*/
|
|
7
|
+
import { resolveWorkspacePaths } from '../paths.js';
|
|
8
|
+
import { readLockFile } from '../lib/lock-file.js';
|
|
9
|
+
import { readCachedMarketplace } from '../lib/marketplace.js';
|
|
10
|
+
export async function outdated() {
|
|
11
|
+
const paths = resolveWorkspacePaths();
|
|
12
|
+
const lock = readLockFile(paths.lockFilePath);
|
|
13
|
+
const entries = Object.entries(lock.apps);
|
|
14
|
+
if (entries.length === 0) {
|
|
15
|
+
console.log('\nNo extensions installed.\n');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const marketplaceEntries = entries.filter(([, e]) => e.marketplace);
|
|
19
|
+
if (marketplaceEntries.length === 0) {
|
|
20
|
+
console.log('\nNo marketplace-installed extensions found. Nothing to check.\n');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const outdatedList = [];
|
|
24
|
+
const unchecked = [];
|
|
25
|
+
for (const [name, entry] of marketplaceEntries) {
|
|
26
|
+
const mpName = entry.marketplace;
|
|
27
|
+
const cached = readCachedMarketplace(paths.root, mpName);
|
|
28
|
+
if (!cached) {
|
|
29
|
+
unchecked.push({ name, marketplace: mpName, reason: 'catalog not cached' });
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const ext = cached.manifest.extensions.find((e) => e.name === name);
|
|
33
|
+
if (!ext) {
|
|
34
|
+
unchecked.push({ name, marketplace: mpName, reason: 'removed from marketplace' });
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const versionDiff = entry.marketplaceVersion !== undefined &&
|
|
38
|
+
ext.version !== undefined &&
|
|
39
|
+
entry.marketplaceVersion !== ext.version;
|
|
40
|
+
const shaDiff = ext.source.sha && entry.sha !== ext.source.sha;
|
|
41
|
+
const refDiff = ext.source.ref && entry.ref !== ext.source.ref;
|
|
42
|
+
if (versionDiff || shaDiff || refDiff) {
|
|
43
|
+
outdatedList.push({
|
|
44
|
+
name,
|
|
45
|
+
marketplace: mpName,
|
|
46
|
+
installedVersion: entry.marketplaceVersion,
|
|
47
|
+
latestVersion: ext.version,
|
|
48
|
+
installedSha: entry.sha,
|
|
49
|
+
latestSha: ext.source.sha,
|
|
50
|
+
installedRef: entry.ref,
|
|
51
|
+
latestRef: ext.source.ref,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (outdatedList.length === 0 && unchecked.length === 0) {
|
|
56
|
+
console.log('\n✅ All marketplace extensions are up to date.\n');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (outdatedList.length > 0) {
|
|
60
|
+
console.log('\n📋 Outdated extensions:\n');
|
|
61
|
+
// Table header
|
|
62
|
+
const nameW = Math.max(10, ...outdatedList.map((e) => e.name.length));
|
|
63
|
+
const mpW = Math.max(12, ...outdatedList.map((e) => e.marketplace.length));
|
|
64
|
+
const header = [
|
|
65
|
+
'Name'.padEnd(nameW),
|
|
66
|
+
'Marketplace'.padEnd(mpW),
|
|
67
|
+
'Installed'.padEnd(16),
|
|
68
|
+
'Latest'.padEnd(16),
|
|
69
|
+
'Field',
|
|
70
|
+
].join(' ');
|
|
71
|
+
console.log(` ${header}`);
|
|
72
|
+
console.log(` ${'─'.repeat(header.length)}`);
|
|
73
|
+
for (const e of outdatedList) {
|
|
74
|
+
const diffs = [];
|
|
75
|
+
if (e.latestVersion && e.installedVersion !== e.latestVersion) {
|
|
76
|
+
diffs.push({ field: 'version', installed: e.installedVersion ?? '–', latest: e.latestVersion });
|
|
77
|
+
}
|
|
78
|
+
if (e.latestSha && e.installedSha !== e.latestSha) {
|
|
79
|
+
diffs.push({
|
|
80
|
+
field: 'sha',
|
|
81
|
+
installed: e.installedSha ? e.installedSha.slice(0, 12) : '–',
|
|
82
|
+
latest: e.latestSha.slice(0, 12),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (e.latestRef && e.installedRef !== e.latestRef && !e.latestVersion) {
|
|
86
|
+
diffs.push({ field: 'ref', installed: e.installedRef ?? '–', latest: e.latestRef });
|
|
87
|
+
}
|
|
88
|
+
for (const d of diffs) {
|
|
89
|
+
const row = [
|
|
90
|
+
e.name.padEnd(nameW),
|
|
91
|
+
e.marketplace.padEnd(mpW),
|
|
92
|
+
d.installed.padEnd(16),
|
|
93
|
+
d.latest.padEnd(16),
|
|
94
|
+
d.field,
|
|
95
|
+
].join(' ');
|
|
96
|
+
console.log(` ${row}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (unchecked.length > 0) {
|
|
101
|
+
console.log('\n⚠ Could not check:\n');
|
|
102
|
+
for (const u of unchecked) {
|
|
103
|
+
console.log(` ${u.name} (${u.marketplace}): ${u.reason}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
console.log('');
|
|
107
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dd reconcile — Comprehensive drift detection for installed extensions.
|
|
3
|
+
*
|
|
4
|
+
* Compares marketplace entries vs lock file vs vendored content and reports:
|
|
5
|
+
* - Source drift: marketplace extension source changed vs lock entry
|
|
6
|
+
* - Local modifications: content hash changed since install
|
|
7
|
+
* - Missing install path: lock entry exists but directory is gone
|
|
8
|
+
* - Marketplace removal: lock entry references a marketplace extension no longer in the catalog
|
|
9
|
+
* - Version/SHA mismatch: marketplace has newer version/SHA than lock
|
|
10
|
+
*/
|
|
11
|
+
export declare function reconcile(): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/commands/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwBH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAiL/C"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dd reconcile — Comprehensive drift detection for installed extensions.
|
|
3
|
+
*
|
|
4
|
+
* Compares marketplace entries vs lock file vs vendored content and reports:
|
|
5
|
+
* - Source drift: marketplace extension source changed vs lock entry
|
|
6
|
+
* - Local modifications: content hash changed since install
|
|
7
|
+
* - Missing install path: lock entry exists but directory is gone
|
|
8
|
+
* - Marketplace removal: lock entry references a marketplace extension no longer in the catalog
|
|
9
|
+
* - Version/SHA mismatch: marketplace has newer version/SHA than lock
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { resolveWorkspacePaths, installDirForKind, installRelPath } from '../paths.js';
|
|
14
|
+
import { readLockFile, computeContentHash } from '../lib/lock-file.js';
|
|
15
|
+
import { readCachedMarketplace, readKnownMarketplaces, extensionSourceToGhString } from '../lib/marketplace.js';
|
|
16
|
+
const SEVERITY_ICON = {
|
|
17
|
+
error: '✘',
|
|
18
|
+
warning: '⚠',
|
|
19
|
+
info: 'ℹ',
|
|
20
|
+
};
|
|
21
|
+
export async function reconcile() {
|
|
22
|
+
const paths = resolveWorkspacePaths();
|
|
23
|
+
const lock = readLockFile(paths.lockFilePath);
|
|
24
|
+
const entries = Object.entries(lock.apps);
|
|
25
|
+
if (entries.length === 0) {
|
|
26
|
+
console.log('\nNo extensions installed. Nothing to reconcile.\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log('\n🔍 Reconciling installed extensions...\n');
|
|
30
|
+
const issues = [];
|
|
31
|
+
for (const [name, entry] of entries) {
|
|
32
|
+
// 1. Missing install path
|
|
33
|
+
let installDir;
|
|
34
|
+
if (entry.installPath) {
|
|
35
|
+
installDir = path.join(paths.root, entry.installPath);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Legacy entry without installPath: probe extensions/ first, then packages/
|
|
39
|
+
const kind = entry.kind ?? 'micro-app';
|
|
40
|
+
const extDir = installDirForKind(paths, kind, name);
|
|
41
|
+
installDir = fs.existsSync(extDir) ? extDir : path.join(paths.packagesDir, name);
|
|
42
|
+
}
|
|
43
|
+
if (!fs.existsSync(installDir)) {
|
|
44
|
+
const searchedPaths = entry.installPath
|
|
45
|
+
? [entry.installPath]
|
|
46
|
+
: [
|
|
47
|
+
installRelPath(entry.kind ?? 'micro-app', name),
|
|
48
|
+
`packages/${name}`,
|
|
49
|
+
];
|
|
50
|
+
issues.push({
|
|
51
|
+
name,
|
|
52
|
+
severity: 'error',
|
|
53
|
+
category: 'Missing install path',
|
|
54
|
+
message: `Extension not found. Searched: ${searchedPaths.join(', ')}`,
|
|
55
|
+
});
|
|
56
|
+
continue; // Can't check content hash if directory is missing
|
|
57
|
+
}
|
|
58
|
+
// 2. Local modifications (content hash drift)
|
|
59
|
+
const currentHash = await computeContentHash(installDir);
|
|
60
|
+
if (currentHash !== entry.contentHash) {
|
|
61
|
+
issues.push({
|
|
62
|
+
name,
|
|
63
|
+
severity: 'warning',
|
|
64
|
+
category: 'Local modifications',
|
|
65
|
+
message: `Content hash changed (lock: ${entry.contentHash.slice(0, 12)}… → current: ${currentHash.slice(0, 12)}…)`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// 3. Marketplace-specific checks
|
|
69
|
+
if (entry.marketplace) {
|
|
70
|
+
const known = readKnownMarketplaces(paths.root);
|
|
71
|
+
if (!(entry.marketplace in known.marketplaces)) {
|
|
72
|
+
issues.push({
|
|
73
|
+
name,
|
|
74
|
+
severity: 'warning',
|
|
75
|
+
category: 'Marketplace removed',
|
|
76
|
+
message: `Marketplace "${entry.marketplace}" is no longer registered`,
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const cached = readCachedMarketplace(paths.root, entry.marketplace);
|
|
81
|
+
if (!cached) {
|
|
82
|
+
issues.push({
|
|
83
|
+
name,
|
|
84
|
+
severity: 'info',
|
|
85
|
+
category: 'Marketplace not cached',
|
|
86
|
+
message: `Catalog for "${entry.marketplace}" not cached — run: dd marketplace update ${entry.marketplace}`,
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const ext = cached.manifest.extensions.find((e) => e.name === name);
|
|
91
|
+
if (!ext) {
|
|
92
|
+
// 4. Marketplace removal
|
|
93
|
+
issues.push({
|
|
94
|
+
name,
|
|
95
|
+
severity: 'error',
|
|
96
|
+
category: 'Marketplace removal',
|
|
97
|
+
message: `Extension no longer listed in "${entry.marketplace}" catalog`,
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
// 5. Source drift — compare only repo + path; ref/SHA drift is reported separately
|
|
102
|
+
const stripSourceFragment = (value) => value.replace(/^gh:/, '').split('#', 1)[0];
|
|
103
|
+
const lockResolvedBase = stripSourceFragment(entry.resolvedSource);
|
|
104
|
+
const marketplaceSourceBase = stripSourceFragment(extensionSourceToGhString(ext));
|
|
105
|
+
if (lockResolvedBase !== marketplaceSourceBase) {
|
|
106
|
+
issues.push({
|
|
107
|
+
name,
|
|
108
|
+
severity: 'warning',
|
|
109
|
+
category: 'Source drift',
|
|
110
|
+
message: `Marketplace source changed (lock: gh:${lockResolvedBase} → marketplace: gh:${marketplaceSourceBase})`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// 6. Version mismatch — compare marketplace version, not ref
|
|
114
|
+
if (ext.version && entry.marketplaceVersion && ext.version !== entry.marketplaceVersion) {
|
|
115
|
+
issues.push({
|
|
116
|
+
name,
|
|
117
|
+
severity: 'info',
|
|
118
|
+
category: 'Version mismatch',
|
|
119
|
+
message: `Installed marketplace version "${entry.marketplaceVersion}" differs from latest "${ext.version}"`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else if (ext.version && !entry.marketplaceVersion && entry.ref && ext.version !== entry.ref) {
|
|
123
|
+
// Legacy lock entries without marketplaceVersion: fall back to ref comparison
|
|
124
|
+
issues.push({
|
|
125
|
+
name,
|
|
126
|
+
severity: 'info',
|
|
127
|
+
category: 'Version mismatch',
|
|
128
|
+
message: `Installed ref "${entry.ref}" differs from marketplace version "${ext.version}" (legacy lock entry)`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (ext.source.ref && entry.ref && ext.source.ref !== entry.ref) {
|
|
132
|
+
issues.push({
|
|
133
|
+
name,
|
|
134
|
+
severity: 'info',
|
|
135
|
+
category: 'Ref mismatch',
|
|
136
|
+
message: `Installed ref "${entry.ref}" differs from marketplace ref "${ext.source.ref}"`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (ext.source.sha && entry.sha && ext.source.sha !== entry.sha) {
|
|
140
|
+
issues.push({
|
|
141
|
+
name,
|
|
142
|
+
severity: 'info',
|
|
143
|
+
category: 'SHA mismatch',
|
|
144
|
+
message: `Installed SHA ${entry.sha.slice(0, 12)}… differs from marketplace SHA ${ext.source.sha.slice(0, 12)}…`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Print results
|
|
150
|
+
if (issues.length === 0) {
|
|
151
|
+
console.log('✅ All extensions are in sync. No drift detected.\n');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const errors = issues.filter((i) => i.severity === 'error');
|
|
155
|
+
const warnings = issues.filter((i) => i.severity === 'warning');
|
|
156
|
+
const infos = issues.filter((i) => i.severity === 'info');
|
|
157
|
+
const printGroup = (label, items) => {
|
|
158
|
+
if (items.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
console.log(`${label}:\n`);
|
|
161
|
+
for (const issue of items) {
|
|
162
|
+
const icon = SEVERITY_ICON[issue.severity];
|
|
163
|
+
console.log(` ${icon} ${issue.name} — ${issue.category}`);
|
|
164
|
+
console.log(` ${issue.message}`);
|
|
165
|
+
}
|
|
166
|
+
console.log('');
|
|
167
|
+
};
|
|
168
|
+
printGroup('Errors', errors);
|
|
169
|
+
printGroup('Warnings', warnings);
|
|
170
|
+
printGroup('Info', infos);
|
|
171
|
+
console.log(`Summary: ${errors.length} error(s), ${warnings.length} warning(s), ${infos.length} info(s)\n`);
|
|
172
|
+
if (errors.length > 0) {
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAcA,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CA4CzC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { resolveWorkspacePaths } from '../paths.js';
|
|
3
|
+
import { applyRuntimeDatabaseUrl, databaseRuntimeToEnvOptions, ensureDatabaseReady, ensureLocalEnv, getGeneratedTypesPath, localDependenciesInstalled, runChecked, startDevServer, } from '../lib/onboarding.js';
|
|
4
|
+
export async function run() {
|
|
5
|
+
const paths = resolveWorkspacePaths();
|
|
6
|
+
console.log('\n🚀 Double Digit run\n');
|
|
7
|
+
const envSetup = ensureLocalEnv(paths);
|
|
8
|
+
console.log(`✔ ${envSetup.created ? 'Created' : 'Found'} ${envSetup.envPath.replace(`${paths.root}/`, '')}`);
|
|
9
|
+
if (envSetup.updatedKeys.length > 0) {
|
|
10
|
+
console.log(` Updated: ${envSetup.updatedKeys.join(', ')}`);
|
|
11
|
+
}
|
|
12
|
+
const installedBefore = localDependenciesInstalled(paths);
|
|
13
|
+
if (!installedBefore) {
|
|
14
|
+
runChecked('pnpm', ['install'], paths.root, 'Dependency install');
|
|
15
|
+
console.log('✔ Dependencies installed');
|
|
16
|
+
}
|
|
17
|
+
const runtime = await ensureDatabaseReady(paths, {
|
|
18
|
+
yes: true,
|
|
19
|
+
allowDockerFallback: true,
|
|
20
|
+
});
|
|
21
|
+
const runtimeEnv = applyRuntimeDatabaseUrl(ensureLocalEnv(paths, databaseRuntimeToEnvOptions(runtime)).env, runtime);
|
|
22
|
+
console.log(`✔ ${runtime.mode === 'embedded' ? 'Embedded' : runtime.mode === 'docker' ? 'Docker' : 'External'} PostgreSQL is ready`);
|
|
23
|
+
if (runtime.mode === 'embedded' && runtime.embeddedPort) {
|
|
24
|
+
console.log(` Port: ${runtime.embeddedPort}`);
|
|
25
|
+
}
|
|
26
|
+
if (runtime.mode === 'docker' && runtime.dockerPort) {
|
|
27
|
+
console.log(` Docker host port: ${runtime.dockerPort}`);
|
|
28
|
+
}
|
|
29
|
+
runChecked('pnpm', ['db:migrate'], paths.root, 'Migration run');
|
|
30
|
+
console.log('✔ Migrations applied');
|
|
31
|
+
if (!fs.existsSync(getGeneratedTypesPath(paths))) {
|
|
32
|
+
runChecked('pnpm', ['payload:generate-types:main'], paths.root, 'Type generation');
|
|
33
|
+
console.log('✔ Payload types generated');
|
|
34
|
+
}
|
|
35
|
+
console.log('\nStarting development server...\n');
|
|
36
|
+
await startDevServer(paths, runtimeEnv);
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA8B1C"}
|