@ductape/cli 0.2.22 → 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.
Files changed (103) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +7 -0
  3. package/dist/commands/generate.js +1 -1
  4. package/dist/commands/migrate-codebase.d.ts +176 -0
  5. package/dist/commands/migrate-codebase.js +753 -0
  6. package/dist/commands/migration-artifact.d.ts +32 -0
  7. package/dist/commands/migration-artifact.js +48 -0
  8. package/dist/commands/migration-assurance.d.ts +10 -0
  9. package/dist/commands/migration-assurance.js +120 -0
  10. package/dist/commands/migration-database.d.ts +21 -0
  11. package/dist/commands/migration-database.js +162 -0
  12. package/dist/commands/migration-e2e.d.ts +68 -0
  13. package/dist/commands/migration-e2e.js +189 -0
  14. package/dist/commands/migration-environments.d.ts +20 -0
  15. package/dist/commands/migration-environments.js +58 -0
  16. package/dist/commands/migration-generated.d.ts +68 -0
  17. package/dist/commands/migration-generated.js +217 -0
  18. package/dist/commands/migration-portfolio.d.ts +88 -0
  19. package/dist/commands/migration-portfolio.js +223 -0
  20. package/dist/commands/migration-products.d.ts +23 -0
  21. package/dist/commands/migration-products.js +88 -0
  22. package/dist/commands/migration-review.d.ts +248 -0
  23. package/dist/commands/migration-review.js +624 -0
  24. package/dist/commands/migration-secrets.d.ts +24 -0
  25. package/dist/commands/migration-secrets.js +93 -0
  26. package/dist/commands/migration-slice.d.ts +132 -0
  27. package/dist/commands/migration-slice.js +266 -0
  28. package/dist/commands/migration-verification.d.ts +12 -0
  29. package/dist/commands/migration-verification.js +194 -0
  30. package/dist/commands/products.d.ts +1 -0
  31. package/dist/commands/products.js +33 -1
  32. package/dist/commands/secrets.d.ts +8 -0
  33. package/dist/commands/secrets.js +44 -0
  34. package/dist/index.js +354 -2
  35. package/dist/lib/integrations.js +6 -1
  36. package/dist/lib/migration-artifact.d.ts +13 -0
  37. package/dist/lib/migration-artifact.js +103 -0
  38. package/package.json +8 -1
  39. package/API_PARITY.md +0 -58
  40. package/DB_MIGRATE_PLAN.md +0 -223
  41. package/ductape.example.ts +0 -18
  42. package/src/commands/apply.ts +0 -153
  43. package/src/commands/apps-import.ts +0 -43
  44. package/src/commands/apps.ts +0 -92
  45. package/src/commands/cloud.ts +0 -68
  46. package/src/commands/completion.ts +0 -119
  47. package/src/commands/db-migrate.ts +0 -209
  48. package/src/commands/db-schema.ts +0 -429
  49. package/src/commands/db.ts +0 -63
  50. package/src/commands/generate.ts +0 -74
  51. package/src/commands/graph.ts +0 -51
  52. package/src/commands/init.ts +0 -39
  53. package/src/commands/install.ts +0 -76
  54. package/src/commands/link.ts +0 -84
  55. package/src/commands/login.ts +0 -145
  56. package/src/commands/logout.ts +0 -7
  57. package/src/commands/platform.ts +0 -123
  58. package/src/commands/products.ts +0 -228
  59. package/src/commands/profiles.ts +0 -23
  60. package/src/commands/resources.ts +0 -213
  61. package/src/commands/secrets.ts +0 -46
  62. package/src/commands/unlink.ts +0 -15
  63. package/src/commands/whoami.ts +0 -27
  64. package/src/commands/workspaces.ts +0 -98
  65. package/src/index.ts +0 -523
  66. package/src/lib/api-client.ts +0 -88
  67. package/src/lib/app-import.ts +0 -82
  68. package/src/lib/apply-loaders.ts +0 -30
  69. package/src/lib/config.ts +0 -230
  70. package/src/lib/context-store.ts +0 -77
  71. package/src/lib/db-methods.ts +0 -56
  72. package/src/lib/db-types.ts +0 -104
  73. package/src/lib/encryption.ts +0 -12
  74. package/src/lib/forms/enums.ts +0 -52
  75. package/src/lib/forms/index.ts +0 -8
  76. package/src/lib/forms/prompt.ts +0 -209
  77. package/src/lib/forms/schemas.ts +0 -288
  78. package/src/lib/forms/types.ts +0 -27
  79. package/src/lib/http.ts +0 -76
  80. package/src/lib/integrations.ts +0 -105
  81. package/src/lib/interactive-opts.ts +0 -13
  82. package/src/lib/migration-files.ts +0 -58
  83. package/src/lib/output.ts +0 -39
  84. package/src/lib/platform-api.ts +0 -209
  85. package/src/lib/proxy/context.ts +0 -94
  86. package/src/lib/proxy/db-proxy.ts +0 -40
  87. package/src/lib/proxy/graph-proxy.ts +0 -64
  88. package/src/lib/proxy/sdk-proxy.ts +0 -58
  89. package/src/lib/read-body.ts +0 -57
  90. package/src/lib/resources.ts +0 -160
  91. package/src/lib/schema-loader.ts +0 -152
  92. package/src/lib/snippets.ts +0 -165
  93. package/src/lib/templates.ts +0 -157
  94. package/src/lib/workspace-context.ts +0 -43
  95. package/src/lib/workspaces.ts +0 -283
  96. package/test/apply-loaders.test.ts +0 -40
  97. package/test/encryption.test.ts +0 -20
  98. package/test/forms.test.ts +0 -25
  99. package/test/http.test.ts +0 -15
  100. package/test/platform-api.test.ts +0 -42
  101. package/test/resources.test.ts +0 -23
  102. package/test/workspace-resolve.test.ts +0 -29
  103. package/tsconfig.json +0 -15
@@ -0,0 +1,23 @@
1
+ export declare function initializeProductMap(opts: {
2
+ analysis: string;
3
+ definition: string;
4
+ output: string;
5
+ }): {
6
+ path: string;
7
+ };
8
+ export declare function validateProductMap(file: string): {
9
+ artifact: string;
10
+ ready: boolean;
11
+ blockers: string[];
12
+ };
13
+ export declare function runMigrationProductsInit(opts: {
14
+ analysis: string;
15
+ definition: string;
16
+ output: string;
17
+ json?: boolean;
18
+ }): void;
19
+ export declare function runMigrationProductsValidate(opts: {
20
+ file: string;
21
+ strict?: boolean;
22
+ json?: boolean;
23
+ }): void;
@@ -0,0 +1,88 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { printJson } from '../lib/output.js';
4
+ import { readMigrationJson, writeMigrationJson } from '../lib/migration-artifact.js';
5
+ function read(file) {
6
+ return readMigrationJson(file);
7
+ }
8
+ export function initializeProductMap(opts) {
9
+ const analysisPath = path.resolve(opts.analysis);
10
+ const definition = read(opts.definition);
11
+ if (!Array.isArray(definition.mappings) || !Array.isArray(definition.assets) ||
12
+ !Array.isArray(definition.promotions)) {
13
+ throw new Error('Definition requires mappings, assets, and promotions arrays.');
14
+ }
15
+ const artifact = {
16
+ version: 1,
17
+ artifact_kind: 'ductape_service_product_map',
18
+ analysis: analysisPath,
19
+ ...definition,
20
+ };
21
+ const output = path.resolve(opts.output);
22
+ if (fs.existsSync(output))
23
+ throw new Error(`Service-to-product map already exists: ${output}`);
24
+ fs.mkdirSync(path.dirname(output), { recursive: true });
25
+ writeMigrationJson(output, artifact, { createOnly: true });
26
+ return { path: output };
27
+ }
28
+ export function validateProductMap(file) {
29
+ const resolved = path.resolve(file);
30
+ const artifact = read(resolved);
31
+ if (artifact.artifact_kind !== 'ductape_service_product_map')
32
+ throw new Error('Invalid service-to-product map.');
33
+ const analysis = read(artifact.analysis);
34
+ const blockers = [];
35
+ const assignment = new Map();
36
+ for (const mapping of artifact.mappings) {
37
+ if (!mapping.service || !mapping.product_tag || !mapping.environments.length || !mapping.evidence) {
38
+ blockers.push(`mapping ${mapping.service || '<unknown>'}: service, product, environments, and evidence are required`);
39
+ }
40
+ for (const manifest of mapping.manifests) {
41
+ assignment.set(manifest, [...(assignment.get(manifest) ?? []), mapping.service]);
42
+ }
43
+ }
44
+ for (const unit of analysis.workspace_units ?? []) {
45
+ const owners = assignment.get(unit.manifest) ?? [];
46
+ if (owners.length === 0)
47
+ blockers.push(`workspace unit is unmapped: ${unit.manifest}`);
48
+ if (owners.length > 1)
49
+ blockers.push(`workspace unit has multiple service owners: ${unit.manifest}`);
50
+ }
51
+ for (const manifest of assignment.keys()) {
52
+ if (!(analysis.workspace_units ?? []).some((unit) => unit.manifest === manifest)) {
53
+ blockers.push(`mapping references unknown workspace unit: ${manifest}`);
54
+ }
55
+ }
56
+ for (const asset of artifact.assets) {
57
+ if (!asset.kind || !asset.tag || !asset.product_tag || !asset.inventory_evidence) {
58
+ blockers.push(`asset ${asset.tag || '<unknown>'}: identity and inventory evidence are required`);
59
+ }
60
+ for (const environment of asset.required_environments) {
61
+ if (!asset.configured_environments.includes(environment)) {
62
+ blockers.push(`asset ${asset.kind}:${asset.tag} missing environment configuration: ${environment}`);
63
+ }
64
+ }
65
+ }
66
+ for (const promotion of artifact.promotions) {
67
+ if (!promotion.product_tag || !promotion.from || !promotion.to ||
68
+ !promotion.entry_conditions.length || !promotion.validation.length || !promotion.rollback.length) {
69
+ blockers.push(`promotion ${promotion.from || '?'} -> ${promotion.to || '?'} is incomplete`);
70
+ }
71
+ }
72
+ const mappedProducts = new Set(artifact.mappings.map((mapping) => mapping.product_tag));
73
+ for (const product of mappedProducts) {
74
+ if (!artifact.promotions.some((promotion) => promotion.product_tag === product)) {
75
+ blockers.push(`product has no promotion plan: ${product}`);
76
+ }
77
+ }
78
+ return { artifact: resolved, ready: blockers.length === 0, blockers };
79
+ }
80
+ export function runMigrationProductsInit(opts) {
81
+ printJson(initializeProductMap(opts), Boolean(opts.json));
82
+ }
83
+ export function runMigrationProductsValidate(opts) {
84
+ const result = validateProductMap(opts.file);
85
+ printJson(result, Boolean(opts.json));
86
+ if (opts.strict && !result.ready)
87
+ throw new Error('Service-to-product mapping is not ready.');
88
+ }
@@ -0,0 +1,248 @@
1
+ type ReviewStatus = 'pending' | 'reviewed' | 'excluded';
2
+ type ParityStatus = 'pending' | 'verified' | 'not_applicable';
3
+ type ReferenceKind = 'import' | 'caller' | 'callee' | 'implements' | 'event' | 'test' | 'config' | 'schema';
4
+ interface ParityEvidence {
5
+ status: ParityStatus;
6
+ evidence: string[];
7
+ reason?: string;
8
+ }
9
+ interface ReviewEntry {
10
+ file: string;
11
+ status: ReviewStatus;
12
+ safety: 'names_only' | 'normal';
13
+ baseline_checksum: string | null;
14
+ reviewed_checksum: string | null;
15
+ purpose: string;
16
+ dependencies: string[];
17
+ follow_up_files: string[];
18
+ references: Array<{
19
+ kind: ReferenceKind;
20
+ target: string;
21
+ external: boolean;
22
+ evidence: string;
23
+ }>;
24
+ interfaces: string[];
25
+ findings: Array<{
26
+ kind: string;
27
+ evidence: string;
28
+ location: {
29
+ start_line: number;
30
+ end_line: number;
31
+ };
32
+ symbol: {
33
+ kind: 'module' | 'class' | 'interface' | 'function' | 'method' | 'field' | 'route' | 'event' | 'schema' | 'other';
34
+ name: string;
35
+ qualified_name?: string;
36
+ };
37
+ classification: 'observed' | 'inferred' | 'proposed' | 'confirmed';
38
+ confidence: 'low' | 'medium' | 'high';
39
+ requires_followup: string[];
40
+ }>;
41
+ exclusion_reason?: string;
42
+ exclusion?: {
43
+ classification: 'generated' | 'vendor' | 'build' | 'snapshot' | 'other';
44
+ reason: string;
45
+ owner?: string;
46
+ generator?: string;
47
+ regeneration_test?: string;
48
+ };
49
+ interface_parity: ParityEvidence;
50
+ functional_parity: ParityEvidence;
51
+ operational_parity: ParityEvidence;
52
+ }
53
+ interface ReviewLedger {
54
+ version: 1;
55
+ artifact_kind: 'ai_migration_review_ledger';
56
+ source: string;
57
+ created_at: string;
58
+ revision: number;
59
+ events: Array<{
60
+ revision: number;
61
+ timestamp: string;
62
+ actor: string;
63
+ tool: '@ductape/cli';
64
+ tool_version: string;
65
+ operation: string;
66
+ state_hash: string;
67
+ previous_hash: string | null;
68
+ hash: string;
69
+ }>;
70
+ entries: ReviewEntry[];
71
+ }
72
+ interface LedgerSignature {
73
+ version: 1;
74
+ artifact_kind: 'ductape_review_ledger_signature';
75
+ ledger: string;
76
+ ledger_checksum: string;
77
+ key_id: string;
78
+ algorithm: 'Ed25519';
79
+ signed_at: string;
80
+ signature: string;
81
+ }
82
+ export declare function initializeReviewLedger(analysisPath: string, ledgerPath?: string): {
83
+ ledger: ReviewLedger;
84
+ path: string;
85
+ };
86
+ export declare function validateReviewLedger(ledgerPath: string): {
87
+ ledger: string;
88
+ source: string;
89
+ ready: boolean;
90
+ coverage: {
91
+ total: number;
92
+ reviewed: number;
93
+ excluded: number;
94
+ pending: number;
95
+ percent: number;
96
+ };
97
+ blockers: {
98
+ pending: string[];
99
+ stale: string[];
100
+ invalid_exclusions: string[];
101
+ invalid_generated_exclusions: string[];
102
+ missing_follow_ups: {
103
+ file: string;
104
+ follow_up: string;
105
+ }[];
106
+ uncovered_references: {
107
+ file: string;
108
+ kind: ReferenceKind;
109
+ target: string;
110
+ }[];
111
+ missing_finding_provenance: {
112
+ file: string;
113
+ finding: number;
114
+ }[];
115
+ missing_symbol_provenance: {
116
+ file: string;
117
+ finding: number;
118
+ }[];
119
+ probable_moves: {
120
+ from: string;
121
+ candidates: string[];
122
+ }[];
123
+ invalidated_dependents: string[];
124
+ audit_errors: string[];
125
+ missing_context: string[];
126
+ incomplete_parity: {
127
+ file: string;
128
+ concern: string;
129
+ }[];
130
+ };
131
+ };
132
+ export declare function snapshotReviewedFile(ledgerPath: string, file: string): ReviewEntry;
133
+ export declare function recordReviewContext(ledgerPath: string, file: string, dataPath: string): ReviewEntry;
134
+ export declare function addFollowUpFile(ledgerPath: string, fromFile: string, followUpFile: string): ReviewEntry;
135
+ export declare function excludeReviewFile(ledgerPath: string, file: string, exclusion: NonNullable<ReviewEntry['exclusion']>): ReviewEntry;
136
+ export declare function setParityEvidence(ledgerPath: string, file: string, concern: 'interface' | 'functional' | 'operational', status: Exclude<ParityStatus, 'pending'>, evidence?: string, reason?: string): ReviewEntry;
137
+ export declare function reopenReviewFile(ledgerPath: string, file: string): ReviewEntry;
138
+ export declare function moveReviewedFile(ledgerPath: string, from: string, to: string): ReviewEntry;
139
+ export declare function signReviewLedger(opts: {
140
+ ledger: string;
141
+ privateKey: string;
142
+ keyId: string;
143
+ output?: string;
144
+ }): {
145
+ path: string;
146
+ signature: LedgerSignature;
147
+ };
148
+ export declare function verifyReviewLedgerSignature(opts: {
149
+ ledger: string;
150
+ signature: string;
151
+ publicKey: string;
152
+ }): {
153
+ ledger: string;
154
+ key_id: string;
155
+ valid: boolean;
156
+ };
157
+ export declare function mergeReviewLedgers(opts: {
158
+ base: string;
159
+ current: string;
160
+ incoming: string;
161
+ output: string;
162
+ }): {
163
+ merged: boolean;
164
+ conflicts: string[];
165
+ output: null;
166
+ } | {
167
+ merged: boolean;
168
+ conflicts: never[];
169
+ output: string;
170
+ };
171
+ export declare function runMigrationReviewInit(opts: {
172
+ analysis: string;
173
+ output?: string;
174
+ json?: boolean;
175
+ }): void;
176
+ export declare function runMigrationReviewValidate(opts: {
177
+ ledger: string;
178
+ strict?: boolean;
179
+ json?: boolean;
180
+ }): void;
181
+ export declare function runMigrationReviewSnapshot(opts: {
182
+ ledger: string;
183
+ file: string;
184
+ json?: boolean;
185
+ }): void;
186
+ export declare function runMigrationReviewRecord(opts: {
187
+ ledger: string;
188
+ file: string;
189
+ data: string;
190
+ json?: boolean;
191
+ }): void;
192
+ export declare function runMigrationReviewFollowUp(opts: {
193
+ ledger: string;
194
+ file: string;
195
+ followUp: string;
196
+ json?: boolean;
197
+ }): void;
198
+ export declare function runMigrationReviewExclude(opts: {
199
+ ledger: string;
200
+ file: string;
201
+ classification: NonNullable<ReviewEntry['exclusion']>['classification'];
202
+ reason: string;
203
+ owner?: string;
204
+ generator?: string;
205
+ regenerationTest?: string;
206
+ json?: boolean;
207
+ }): void;
208
+ export declare function runMigrationReviewParity(opts: {
209
+ ledger: string;
210
+ file: string;
211
+ concern: 'interface' | 'functional' | 'operational';
212
+ status: 'verified' | 'not_applicable';
213
+ evidence?: string;
214
+ reason?: string;
215
+ json?: boolean;
216
+ }): void;
217
+ export declare function runMigrationReviewReopen(opts: {
218
+ ledger: string;
219
+ file: string;
220
+ json?: boolean;
221
+ }): void;
222
+ export declare function runMigrationReviewMove(opts: {
223
+ ledger: string;
224
+ from: string;
225
+ to: string;
226
+ json?: boolean;
227
+ }): void;
228
+ export declare function runMigrationReviewSign(opts: {
229
+ ledger: string;
230
+ privateKey: string;
231
+ keyId: string;
232
+ output?: string;
233
+ json?: boolean;
234
+ }): void;
235
+ export declare function runMigrationReviewVerifySignature(opts: {
236
+ ledger: string;
237
+ signature: string;
238
+ publicKey: string;
239
+ json?: boolean;
240
+ }): void;
241
+ export declare function runMigrationReviewMerge(opts: {
242
+ base: string;
243
+ current: string;
244
+ incoming: string;
245
+ output: string;
246
+ json?: boolean;
247
+ }): void;
248
+ export {};