@ddt-tools/cli 0.2.0 → 0.2.5

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 (205) hide show
  1. package/dist/advise-tests-YNMKVJCD.js +87 -0
  2. package/dist/advise-tests-YNMKVJCD.js.map +1 -0
  3. package/dist/ai-NTNPYEKZ.js +86 -0
  4. package/dist/ai-NTNPYEKZ.js.map +1 -0
  5. package/dist/anonymize-LERTWUQO.js +139 -0
  6. package/dist/anonymize-LERTWUQO.js.map +1 -0
  7. package/dist/approval-GGZGKIU4.js +73 -0
  8. package/dist/approval-GGZGKIU4.js.map +1 -0
  9. package/dist/approval-chain-GWJKZHVU.js +118 -0
  10. package/dist/approval-chain-GWJKZHVU.js.map +1 -0
  11. package/dist/audit-log-2PH55BU4.js +159 -0
  12. package/dist/audit-log-2PH55BU4.js.map +1 -0
  13. package/dist/backlog-QNXGOUF4.js +76 -0
  14. package/dist/backlog-QNXGOUF4.js.map +1 -0
  15. package/dist/bisect-W3XKKRWG.js +111 -0
  16. package/dist/bisect-W3XKKRWG.js.map +1 -0
  17. package/dist/bookmarks-XVOGXGMC.js +107 -0
  18. package/dist/bookmarks-XVOGXGMC.js.map +1 -0
  19. package/dist/branch-S3I2IJGQ.js +103 -0
  20. package/dist/branch-S3I2IJGQ.js.map +1 -0
  21. package/dist/build-MP3JQEFO.js +20 -0
  22. package/dist/build-MP3JQEFO.js.map +1 -0
  23. package/dist/catalog-3J3NFNXP.js +137 -0
  24. package/dist/catalog-3J3NFNXP.js.map +1 -0
  25. package/dist/changelog-ZQAH3ULB.js +216 -0
  26. package/dist/changelog-ZQAH3ULB.js.map +1 -0
  27. package/dist/chunk-2FT6HXKS.js +55 -0
  28. package/dist/chunk-2FT6HXKS.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/chunk-DGUM43GV.js.map +1 -0
  31. package/dist/chunk-DL3V7UJ2.js +25 -0
  32. package/dist/chunk-DL3V7UJ2.js.map +1 -0
  33. package/dist/chunk-VM2H4LAO.js +15 -0
  34. package/dist/chunk-VM2H4LAO.js.map +1 -0
  35. package/dist/chunk-XFXG347C.js +40 -0
  36. package/dist/chunk-XFXG347C.js.map +1 -0
  37. package/dist/cli.js +504 -19402
  38. package/dist/cli.js.map +1 -1
  39. package/dist/compare-IOEATL6G.js +435 -0
  40. package/dist/compare-IOEATL6G.js.map +1 -0
  41. package/dist/compare-profiles-H33CXZPD.js +219 -0
  42. package/dist/compare-profiles-H33CXZPD.js.map +1 -0
  43. package/dist/completion-ZSNCQKJ2.js +89 -0
  44. package/dist/completion-ZSNCQKJ2.js.map +1 -0
  45. package/dist/connection-CDGVEFUC.js +148 -0
  46. package/dist/connection-CDGVEFUC.js.map +1 -0
  47. package/dist/cost-estimate-S2MKHT2H.js +321 -0
  48. package/dist/cost-estimate-S2MKHT2H.js.map +1 -0
  49. package/dist/data-compare-46ZI7KHL.js +128 -0
  50. package/dist/data-compare-46ZI7KHL.js.map +1 -0
  51. package/dist/data-fit-WGEPLD5S.js +127 -0
  52. package/dist/data-fit-WGEPLD5S.js.map +1 -0
  53. package/dist/deploy-status-4H5KJFRC.js +58 -0
  54. package/dist/deploy-status-4H5KJFRC.js.map +1 -0
  55. package/dist/design-ILX3ZSWW.js +135 -0
  56. package/dist/design-ILX3ZSWW.js.map +1 -0
  57. package/dist/diagnose-WPUL67E4.js +150 -0
  58. package/dist/diagnose-WPUL67E4.js.map +1 -0
  59. package/dist/discover-DEO2R5T6.js +78 -0
  60. package/dist/discover-DEO2R5T6.js.map +1 -0
  61. package/dist/docs-QNY3MUVO.js +183 -0
  62. package/dist/docs-QNY3MUVO.js.map +1 -0
  63. package/dist/drift-FDRNPWQA.js +233 -0
  64. package/dist/drift-FDRNPWQA.js.map +1 -0
  65. package/dist/drift-gate-6BWWWMHW.js +103 -0
  66. package/dist/drift-gate-6BWWWMHW.js.map +1 -0
  67. package/dist/error-lookup-4R3Y4RBC.js +56 -0
  68. package/dist/error-lookup-4R3Y4RBC.js.map +1 -0
  69. package/dist/errorReporting-LX6WT4JH.js +109 -0
  70. package/dist/errorReporting-LX6WT4JH.js.map +1 -0
  71. package/dist/exec-JOLH5LPT.js +122 -0
  72. package/dist/exec-JOLH5LPT.js.map +1 -0
  73. package/dist/explain-NS26WE2Y.js +189 -0
  74. package/dist/explain-NS26WE2Y.js.map +1 -0
  75. package/dist/explorer-GSYYYOAL.js +58 -0
  76. package/dist/explorer-GSYYYOAL.js.map +1 -0
  77. package/dist/extract-4LWEZG4O.js +152 -0
  78. package/dist/extract-4LWEZG4O.js.map +1 -0
  79. package/dist/features-KQV4OFIZ.js +54 -0
  80. package/dist/features-KQV4OFIZ.js.map +1 -0
  81. package/dist/feedback-CBLGXUEG.js +158 -0
  82. package/dist/feedback-CBLGXUEG.js.map +1 -0
  83. package/dist/find-SMXRCZ76.js +176 -0
  84. package/dist/find-SMXRCZ76.js.map +1 -0
  85. package/dist/format-HMGG6MY3.js +277 -0
  86. package/dist/format-HMGG6MY3.js.map +1 -0
  87. package/dist/generate-W7VLBDLI.js +160 -0
  88. package/dist/generate-W7VLBDLI.js.map +1 -0
  89. package/dist/graph-YYL5UYCJ.js +168 -0
  90. package/dist/graph-YYL5UYCJ.js.map +1 -0
  91. package/dist/history-GDRFP4PG.js +184 -0
  92. package/dist/history-GDRFP4PG.js.map +1 -0
  93. package/dist/hosts-DRFZTMIJ.js +45 -0
  94. package/dist/hosts-DRFZTMIJ.js.map +1 -0
  95. package/dist/impact-A4NU6CB2.js +63 -0
  96. package/dist/impact-A4NU6CB2.js.map +1 -0
  97. package/dist/import-EGOVKTLX.js +29 -0
  98. package/dist/import-EGOVKTLX.js.map +1 -0
  99. package/dist/import-script-R5RXPDH6.js +79 -0
  100. package/dist/import-script-R5RXPDH6.js.map +1 -0
  101. package/dist/index.cjs +11 -5
  102. package/dist/index.cjs.map +1 -1
  103. package/dist/index.js +8 -2
  104. package/dist/index.js.map +1 -1
  105. package/dist/init-EAOGNGXI.js +54 -0
  106. package/dist/init-EAOGNGXI.js.map +1 -0
  107. package/dist/install-hooks-G3Y5LVXK.js +109 -0
  108. package/dist/install-hooks-G3Y5LVXK.js.map +1 -0
  109. package/dist/license-Z5YSC7XQ.js +43 -0
  110. package/dist/license-Z5YSC7XQ.js.map +1 -0
  111. package/dist/lineage-C5CGVP36.js +555 -0
  112. package/dist/lineage-C5CGVP36.js.map +1 -0
  113. package/dist/lint-AQFPZ3WG.js +144 -0
  114. package/dist/lint-AQFPZ3WG.js.map +1 -0
  115. package/dist/mcp-6ZXOAF7S.js +343 -0
  116. package/dist/mcp-6ZXOAF7S.js.map +1 -0
  117. package/dist/migrate-from-dbt-K4ELOWUD.js +156 -0
  118. package/dist/migrate-from-dbt-K4ELOWUD.js.map +1 -0
  119. package/dist/migrate-platform-E7VZFPO5.js +91 -0
  120. package/dist/migrate-platform-E7VZFPO5.js.map +1 -0
  121. package/dist/optimize-WUJ5ZN5Y.js +109 -0
  122. package/dist/optimize-WUJ5ZN5Y.js.map +1 -0
  123. package/dist/perf-UULZSREY.js +200 -0
  124. package/dist/perf-UULZSREY.js.map +1 -0
  125. package/dist/pii-QHU32VML.js +146 -0
  126. package/dist/pii-QHU32VML.js.map +1 -0
  127. package/dist/pilot-BR6GVK32.js +29 -0
  128. package/dist/pilot-BR6GVK32.js.map +1 -0
  129. package/dist/pr-comment-2FOA3EXG.js +81 -0
  130. package/dist/pr-comment-2FOA3EXG.js.map +1 -0
  131. package/dist/preview-XNY422OU.js +46 -0
  132. package/dist/preview-XNY422OU.js.map +1 -0
  133. package/dist/profile-SQTBNKYS.js +98 -0
  134. package/dist/profile-SQTBNKYS.js.map +1 -0
  135. package/dist/promote-FSGUPIPD.js +417 -0
  136. package/dist/promote-FSGUPIPD.js.map +1 -0
  137. package/dist/publish-HLP3XHM5.js +766 -0
  138. package/dist/publish-HLP3XHM5.js.map +1 -0
  139. package/dist/purge-Y5IOTXKA.js +56 -0
  140. package/dist/purge-Y5IOTXKA.js.map +1 -0
  141. package/dist/query-log-SDDGMJLJ.js +112 -0
  142. package/dist/query-log-SDDGMJLJ.js.map +1 -0
  143. package/dist/refactor-TC7S43F2.js +5809 -0
  144. package/dist/refactor-TC7S43F2.js.map +1 -0
  145. package/dist/refresh-MDJYOYV5.js +39 -0
  146. package/dist/refresh-MDJYOYV5.js.map +1 -0
  147. package/dist/replay-E4664A5K.js +118 -0
  148. package/dist/replay-E4664A5K.js.map +1 -0
  149. package/dist/revert-QWQWCJJB.js +111 -0
  150. package/dist/revert-QWQWCJJB.js.map +1 -0
  151. package/dist/review-7CAVLD67.js +164 -0
  152. package/dist/review-7CAVLD67.js.map +1 -0
  153. package/dist/rollback-suggest-C6D5YFCA.js +79 -0
  154. package/dist/rollback-suggest-C6D5YFCA.js.map +1 -0
  155. package/dist/safer-alternative-QR4QEFUV.js +84 -0
  156. package/dist/safer-alternative-QR4QEFUV.js.map +1 -0
  157. package/dist/safety-OFWUFLK4.js +165 -0
  158. package/dist/safety-OFWUFLK4.js.map +1 -0
  159. package/dist/savings-MEBE4TXI.js +95 -0
  160. package/dist/savings-MEBE4TXI.js.map +1 -0
  161. package/dist/scan-secrets-XCUBMLHL.js +54 -0
  162. package/dist/scan-secrets-XCUBMLHL.js.map +1 -0
  163. package/dist/schema-7JZIG6QR.js +447 -0
  164. package/dist/schema-7JZIG6QR.js.map +1 -0
  165. package/dist/script-BMYVBHFR.js +167 -0
  166. package/dist/script-BMYVBHFR.js.map +1 -0
  167. package/dist/search-TA3C3AZT.js +151 -0
  168. package/dist/search-TA3C3AZT.js.map +1 -0
  169. package/dist/seed-W4Q3L2IU.js +101 -0
  170. package/dist/seed-W4Q3L2IU.js.map +1 -0
  171. package/dist/sketch-6B2V6FJV.js +83 -0
  172. package/dist/sketch-6B2V6FJV.js.map +1 -0
  173. package/dist/snapshot-YMVS322L.js +171 -0
  174. package/dist/snapshot-YMVS322L.js.map +1 -0
  175. package/dist/snippets-EVTN63OU.js +74 -0
  176. package/dist/snippets-EVTN63OU.js.map +1 -0
  177. package/dist/standards-FGJW3CQL.js +238 -0
  178. package/dist/standards-FGJW3CQL.js.map +1 -0
  179. package/dist/suggest-V3LVIFZ5.js +44 -0
  180. package/dist/suggest-V3LVIFZ5.js.map +1 -0
  181. package/dist/suggest-constraints-EX2FCWOQ.js +154 -0
  182. package/dist/suggest-constraints-EX2FCWOQ.js.map +1 -0
  183. package/dist/suite-YTQ3CNX5.js +85 -0
  184. package/dist/suite-YTQ3CNX5.js.map +1 -0
  185. package/dist/telemetry-KOIY3NEQ.js +90 -0
  186. package/dist/telemetry-KOIY3NEQ.js.map +1 -0
  187. package/dist/template-MUJ6X6LN.js +396 -0
  188. package/dist/template-MUJ6X6LN.js.map +1 -0
  189. package/dist/test-XFSQHR2S.js +169 -0
  190. package/dist/test-XFSQHR2S.js.map +1 -0
  191. package/dist/trial-GFTGYCR3.js +31 -0
  192. package/dist/trial-GFTGYCR3.js.map +1 -0
  193. package/dist/validate-LFDEZFFH.js +107 -0
  194. package/dist/validate-LFDEZFFH.js.map +1 -0
  195. package/dist/verify-KRDYOJCR.js +76 -0
  196. package/dist/verify-KRDYOJCR.js.map +1 -0
  197. package/dist/watch-FSG23RR3.js +80 -0
  198. package/dist/watch-FSG23RR3.js.map +1 -0
  199. package/dist/xcompare-U4TXTTIR.js +87 -0
  200. package/dist/xcompare-U4TXTTIR.js.map +1 -0
  201. package/package.json +2 -2
  202. package/dist/cli.cjs +0 -19298
  203. package/dist/cli.cjs.map +0 -1
  204. package/dist/cli.d.cts +0 -1
  205. package/dist/cli.d.ts +0 -1
@@ -0,0 +1,219 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/compare-profiles.ts
4
+ import { promises as fs } from "fs";
5
+ import { Command } from "commander";
6
+ import { catalog, compareProfiles, loadProject, pac, parseProjectModel } from "@ddt-tools/core";
7
+ function compareProfilesCommand() {
8
+ const cmd = new Command("compare-profiles");
9
+ cmd.description("Manage saved compare profiles (.ddt/compare-profiles.json).");
10
+ cmd.command("list").description("List every saved profile.").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json", "Emit JSON instead of a human table.").action(async (opts) => {
11
+ const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
12
+ const all = await store.list();
13
+ if (opts.json) {
14
+ console.log(JSON.stringify(all, null, 2));
15
+ return;
16
+ }
17
+ if (all.length === 0) {
18
+ console.log("(no compare profiles saved yet)");
19
+ console.log(` store: ${store.path}`);
20
+ return;
21
+ }
22
+ console.log(`${all.length} compare profile(s):`);
23
+ for (const p of all) {
24
+ console.log(` ${p.id.padEnd(24)} ${p.name}`);
25
+ console.log(` source: ${p.source.kind}=${p.source.reference}`);
26
+ console.log(` target: ${p.target.kind}=${p.target.reference}`);
27
+ console.log(` mappings: ${p.mappings.length} updated: ${p.updatedAt}`);
28
+ }
29
+ });
30
+ cmd.command("show").description("Show one profile by id.").argument("<id>", "Profile id (from `compare-profiles list`).").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json", "Emit JSON instead of a human table.").action(async (id, opts) => {
31
+ const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
32
+ const p = await store.get(String(id));
33
+ if (!p) {
34
+ console.error(
35
+ `No profile with id "${id}". Run \`ddt compare-profiles list\` to enumerate.`
36
+ );
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+ if (opts.json) {
41
+ console.log(JSON.stringify(p, null, 2));
42
+ return;
43
+ }
44
+ console.log(`Profile: ${p.name} (${p.id})`);
45
+ if (p.description) console.log(` ${p.description}`);
46
+ console.log(
47
+ ` source: ${p.source.kind}=${p.source.reference}${p.source.catalog ? ` cat=${p.source.catalog}` : ""}${p.source.schema ? ` schema=${p.source.schema}` : ""}`
48
+ );
49
+ console.log(
50
+ ` target: ${p.target.kind}=${p.target.reference}${p.target.catalog ? ` cat=${p.target.catalog}` : ""}${p.target.schema ? ` schema=${p.target.schema}` : ""}`
51
+ );
52
+ console.log(` case-sensitive: ${p.caseSensitive ? "yes" : "no"}`);
53
+ console.log(` rewrite-inside-strings: ${p.rewriteInsideStrings ? "yes" : "no"}`);
54
+ console.log(` updatedAt: ${p.updatedAt}`);
55
+ if (p.mappings.length === 0) {
56
+ console.log(" mappings: (none \u2014 identity)");
57
+ } else {
58
+ console.log(` mappings (${p.mappings.length}):`);
59
+ for (const m of p.mappings) console.log(` ${m.source} => ${m.target}`);
60
+ }
61
+ });
62
+ cmd.command("save").description("Upsert a profile from a JSON file or stdin.").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json-file <path>", 'Path to a JSON file containing one profile. Use "-" for stdin.').action(async (opts) => {
63
+ if (!opts.jsonFile) {
64
+ throw new Error('--json-file is required (use a path or "-" for stdin).');
65
+ }
66
+ const raw = String(opts.jsonFile) === "-" ? await readStdin() : await fs.readFile(String(opts.jsonFile), "utf8");
67
+ const parsed = JSON.parse(raw);
68
+ if (!parsed?.id || !parsed?.name || !parsed?.source || !parsed?.target) {
69
+ throw new Error(
70
+ "Profile JSON must contain at minimum: id, name, source, target. mappings defaults to []."
71
+ );
72
+ }
73
+ const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
74
+ const stamped = await store.upsert({ ...parsed, mappings: parsed.mappings ?? [] });
75
+ console.log(`Saved profile "${stamped.name}" (${stamped.id})`);
76
+ console.log(` store: ${store.path}`);
77
+ });
78
+ cmd.command("remove").description("Delete one profile by id.").argument("<id>", "Profile id (from `compare-profiles list`).").option("--root <path>", "Project root. Default cwd.", process.cwd()).action(async (id, opts) => {
79
+ const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
80
+ const removed = await store.remove(String(id));
81
+ if (removed) {
82
+ console.log(`Removed profile "${id}".`);
83
+ } else {
84
+ console.warn(`No profile with id "${id}" \u2014 nothing to remove.`);
85
+ }
86
+ });
87
+ cmd.command("preview").description(
88
+ "Preview which FQNs match between the profile's source and target. Local-only \u2014 no workspace round-trip."
89
+ ).argument("<id>", "Profile id (from `compare-profiles list`).").option("--root <path>", "Project root. Default cwd.", process.cwd()).option(
90
+ "--examples <n>",
91
+ "Max example FQNs to show per bucket in human output. Default 5.",
92
+ (v) => parseInt(v, 10),
93
+ 5
94
+ ).option("--json", "Emit the full PreviewSummary as JSON.").action(async (id, opts) => {
95
+ const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });
96
+ const profile = await store.get(String(id));
97
+ if (!profile) {
98
+ console.error(
99
+ `No profile with id "${id}". Run \`ddt compare-profiles list\` to enumerate.`
100
+ );
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ const root = String(opts.root);
105
+ const source = await resolveEndpointFqns(profile.source, root);
106
+ const target = await resolveEndpointFqns(profile.target, root);
107
+ const summary = compareProfiles.previewMatch({
108
+ source: source.fqns,
109
+ target: target.fqns,
110
+ mappings: profile.mappings,
111
+ ...profile.caseSensitive ? { caseSensitive: true } : {}
112
+ });
113
+ if (opts.json) {
114
+ console.log(
115
+ JSON.stringify(
116
+ {
117
+ profile: { id: profile.id, name: profile.name },
118
+ source: {
119
+ kind: profile.source.kind,
120
+ reference: profile.source.reference,
121
+ count: source.fqns.length
122
+ },
123
+ target: {
124
+ kind: profile.target.kind,
125
+ reference: profile.target.reference,
126
+ count: target.fqns.length
127
+ },
128
+ summary
129
+ },
130
+ null,
131
+ 2
132
+ )
133
+ );
134
+ return;
135
+ }
136
+ console.log(`Preview: ${profile.name} (${profile.id})`);
137
+ console.log(
138
+ ` source (${profile.source.kind}=${profile.source.reference}): ${source.fqns.length} object(s)${source.note ? ` \u2014 ${source.note}` : ""}`
139
+ );
140
+ console.log(
141
+ ` target (${profile.target.kind}=${profile.target.reference}): ${target.fqns.length} object(s)${target.note ? ` \u2014 ${target.note}` : ""}`
142
+ );
143
+ console.log("");
144
+ console.log(` matched: ${summary.matchedCount}`);
145
+ console.log(` source-only: ${summary.sourceOnlyCount}`);
146
+ console.log(` target-only: ${summary.targetOnlyCount}`);
147
+ const exN = Math.max(0, Number(opts.examples));
148
+ printBucket("matched", summary.matched, exN);
149
+ printBucket("source-only", summary.sourceOnly, exN);
150
+ printBucket("target-only", summary.targetOnly, exN);
151
+ if (summary.matchedCount === 0 && (summary.sourceOnlyCount > 0 || summary.targetOnlyCount > 0)) {
152
+ console.warn("No FQNs matched \u2014 check the profile's scope and mapping rules.");
153
+ }
154
+ });
155
+ return cmd;
156
+ }
157
+ async function resolveEndpointFqns(endpoint, root) {
158
+ if (endpoint.kind === "connection") {
159
+ const cache = new catalog.CatalogCache({ root, connection: endpoint.reference });
160
+ const snapshot = await cache.get();
161
+ if (snapshot.catalogs.length === 0) {
162
+ return {
163
+ fqns: [],
164
+ note: `empty catalog cache at ${cache.path} \u2014 run \`ddt catalog refresh --connection ${endpoint.reference}\` first`
165
+ };
166
+ }
167
+ return { fqns: fqnsFromSnapshot(snapshot, endpoint.catalog, endpoint.schema) };
168
+ }
169
+ if (endpoint.kind === "pac") {
170
+ const contents = await pac.readPac(endpoint.reference);
171
+ return { fqns: fqnsFromObjects(contents.model, endpoint.catalog, endpoint.schema) };
172
+ }
173
+ const loaded = await loadProject(endpoint.reference);
174
+ const model = await parseProjectModel(loaded);
175
+ return { fqns: fqnsFromObjects(model, endpoint.catalog, endpoint.schema) };
176
+ }
177
+ function fqnsFromSnapshot(snapshot, catalogScope, schemaScope) {
178
+ const sameId = (a, b) => !!a && !!b && a.toUpperCase() === b.toUpperCase();
179
+ const out = [];
180
+ for (const cat of snapshot.catalogs) {
181
+ if (catalogScope && !sameId(cat.catalog, catalogScope)) continue;
182
+ for (const sc of cat.schemas) {
183
+ if (schemaScope && !sameId(sc.schema, schemaScope)) continue;
184
+ for (const obj of sc.objects) {
185
+ out.push({ database: obj.catalog, schema: obj.schema, name: obj.name });
186
+ }
187
+ }
188
+ }
189
+ return out;
190
+ }
191
+ function fqnsFromObjects(model, catalogScope, schemaScope) {
192
+ const sameId = (a, b) => !!a && !!b && a.toUpperCase() === b.toUpperCase();
193
+ const out = [];
194
+ for (const obj of model) {
195
+ if (catalogScope && !sameId(obj.fqn.database, catalogScope)) continue;
196
+ if (schemaScope && !sameId(obj.fqn.schema, schemaScope)) continue;
197
+ out.push(obj.fqn);
198
+ }
199
+ return out;
200
+ }
201
+ function printBucket(label, items, exampleN) {
202
+ if (items.length === 0) return;
203
+ console.log("");
204
+ console.log(
205
+ ` ${label} examples (showing ${Math.min(exampleN, items.length)} of ${items.length}):`
206
+ );
207
+ for (const fqn of items.slice(0, exampleN)) console.log(` ${fqn}`);
208
+ }
209
+ async function readStdin() {
210
+ const chunks = [];
211
+ for await (const chunk of process.stdin) {
212
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
213
+ }
214
+ return Buffer.concat(chunks).toString("utf8");
215
+ }
216
+ export {
217
+ compareProfilesCommand
218
+ };
219
+ //# sourceMappingURL=compare-profiles-H33CXZPD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/compare-profiles.ts"],"sourcesContent":["/**\n * `ddt compare-profiles` — manage saved comparison profiles.\n *\n * A profile is \"Source X (connection|pac|project) → Target Y, with these\n * mapping rules\", persisted to `<cwd>/.ddt/compare-profiles.json`. The\n * eventual VS Code webview reads/writes the same store; this CLI gives\n * power-users the same affordances headlessly.\n *\n * Subcommands:\n * list — list all saved profiles (table or JSON)\n * show <id> — print one profile (table or JSON)\n * save — upsert a profile from a JSON file (--json-file <path>) or stdin\n * remove <id> — delete a profile\n * preview <id> — run `previewMatch` over the saved profile's\n * source + target endpoints and report counts +\n * examples of matched / source-only / target-only\n * FQNs. Catches typo'd schema names before a\n * full extract + diff round-trip.\n *\n * `preview` resolves each endpoint locally:\n * - kind=connection — reads the EE1 catalog cache at\n * `<root>/.ddt/cache/<reference>/catalog.msgpack` (legacy\n * `catalog.json` also accepted). Run\n * `ddt catalog refresh --connection <reference>` first.\n * - kind=pac — reads the `.ddtpac` archive at `reference`.\n * - kind=project — reads the `.ddtproj` at `reference`.\n */\nimport { promises as fs } from 'node:fs';\nimport { Command } from 'commander';\nimport { catalog, compareProfiles, loadProject, pac, parseProjectModel } from '@ddt-tools/core';\nimport type { DatabricksObject, FullyQualifiedName } from '@ddt-tools/core/model';\n\nexport function compareProfilesCommand(): Command {\n const cmd = new Command('compare-profiles');\n cmd.description('Manage saved compare profiles (.ddt/compare-profiles.json).');\n\n cmd\n .command('list')\n .description('List every saved profile.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json', 'Emit JSON instead of a human table.')\n .action(async (opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const all = await store.list();\n if (opts.json) {\n console.log(JSON.stringify(all, null, 2));\n return;\n }\n if (all.length === 0) {\n console.log('(no compare profiles saved yet)');\n console.log(` store: ${store.path}`);\n return;\n }\n console.log(`${all.length} compare profile(s):`);\n for (const p of all) {\n console.log(` ${p.id.padEnd(24)} ${p.name}`);\n console.log(` source: ${p.source.kind}=${p.source.reference}`);\n console.log(` target: ${p.target.kind}=${p.target.reference}`);\n console.log(` mappings: ${p.mappings.length} updated: ${p.updatedAt}`);\n }\n });\n\n cmd\n .command('show')\n .description('Show one profile by id.')\n .argument('<id>', 'Profile id (from `compare-profiles list`).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json', 'Emit JSON instead of a human table.')\n .action(async (id, opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const p = await store.get(String(id));\n if (!p) {\n console.error(\n `No profile with id \"${id}\". Run \\`ddt compare-profiles list\\` to enumerate.`,\n );\n process.exitCode = 1;\n return;\n }\n if (opts.json) {\n console.log(JSON.stringify(p, null, 2));\n return;\n }\n console.log(`Profile: ${p.name} (${p.id})`);\n if (p.description) console.log(` ${p.description}`);\n console.log(\n ` source: ${p.source.kind}=${p.source.reference}${p.source.catalog ? ` cat=${p.source.catalog}` : ''}${p.source.schema ? ` schema=${p.source.schema}` : ''}`,\n );\n console.log(\n ` target: ${p.target.kind}=${p.target.reference}${p.target.catalog ? ` cat=${p.target.catalog}` : ''}${p.target.schema ? ` schema=${p.target.schema}` : ''}`,\n );\n console.log(` case-sensitive: ${p.caseSensitive ? 'yes' : 'no'}`);\n console.log(` rewrite-inside-strings: ${p.rewriteInsideStrings ? 'yes' : 'no'}`);\n console.log(` updatedAt: ${p.updatedAt}`);\n if (p.mappings.length === 0) {\n console.log(' mappings: (none — identity)');\n } else {\n console.log(` mappings (${p.mappings.length}):`);\n for (const m of p.mappings) console.log(` ${m.source} => ${m.target}`);\n }\n });\n\n cmd\n .command('save')\n .description('Upsert a profile from a JSON file or stdin.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json-file <path>', 'Path to a JSON file containing one profile. Use \"-\" for stdin.')\n .action(async (opts) => {\n if (!opts.jsonFile) {\n throw new Error('--json-file is required (use a path or \"-\" for stdin).');\n }\n const raw =\n String(opts.jsonFile) === '-'\n ? await readStdin()\n : await fs.readFile(String(opts.jsonFile), 'utf8');\n const parsed = JSON.parse(raw) as compareProfiles.CompareProfile;\n if (!parsed?.id || !parsed?.name || !parsed?.source || !parsed?.target) {\n throw new Error(\n 'Profile JSON must contain at minimum: id, name, source, target. mappings defaults to [].',\n );\n }\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const stamped = await store.upsert({ ...parsed, mappings: parsed.mappings ?? [] });\n console.log(`Saved profile \"${stamped.name}\" (${stamped.id})`);\n console.log(` store: ${store.path}`);\n });\n\n cmd\n .command('remove')\n .description('Delete one profile by id.')\n .argument('<id>', 'Profile id (from `compare-profiles list`).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .action(async (id, opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const removed = await store.remove(String(id));\n if (removed) {\n console.log(`Removed profile \"${id}\".`);\n } else {\n console.warn(`No profile with id \"${id}\" — nothing to remove.`);\n }\n });\n\n cmd\n .command('preview')\n .description(\n \"Preview which FQNs match between the profile's source and target. Local-only — no workspace round-trip.\",\n )\n .argument('<id>', 'Profile id (from `compare-profiles list`).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option(\n '--examples <n>',\n 'Max example FQNs to show per bucket in human output. Default 5.',\n (v) => parseInt(v, 10),\n 5,\n )\n .option('--json', 'Emit the full PreviewSummary as JSON.')\n .action(async (id, opts) => {\n const store = new compareProfiles.CompareProfilesStore({ root: String(opts.root) });\n const profile = await store.get(String(id));\n if (!profile) {\n console.error(\n `No profile with id \"${id}\". Run \\`ddt compare-profiles list\\` to enumerate.`,\n );\n process.exitCode = 1;\n return;\n }\n\n const root = String(opts.root);\n const source = await resolveEndpointFqns(profile.source, root);\n const target = await resolveEndpointFqns(profile.target, root);\n\n const summary = compareProfiles.previewMatch({\n source: source.fqns,\n target: target.fqns,\n mappings: profile.mappings,\n ...(profile.caseSensitive ? { caseSensitive: true } : {}),\n });\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n {\n profile: { id: profile.id, name: profile.name },\n source: {\n kind: profile.source.kind,\n reference: profile.source.reference,\n count: source.fqns.length,\n },\n target: {\n kind: profile.target.kind,\n reference: profile.target.reference,\n count: target.fqns.length,\n },\n summary,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(`Preview: ${profile.name} (${profile.id})`);\n console.log(\n ` source (${profile.source.kind}=${profile.source.reference}): ${source.fqns.length} object(s)${source.note ? ` — ${source.note}` : ''}`,\n );\n console.log(\n ` target (${profile.target.kind}=${profile.target.reference}): ${target.fqns.length} object(s)${target.note ? ` — ${target.note}` : ''}`,\n );\n console.log('');\n console.log(` matched: ${summary.matchedCount}`);\n console.log(` source-only: ${summary.sourceOnlyCount}`);\n console.log(` target-only: ${summary.targetOnlyCount}`);\n\n const exN = Math.max(0, Number(opts.examples));\n printBucket('matched', summary.matched, exN);\n printBucket('source-only', summary.sourceOnly, exN);\n printBucket('target-only', summary.targetOnly, exN);\n\n if (\n summary.matchedCount === 0 &&\n (summary.sourceOnlyCount > 0 || summary.targetOnlyCount > 0)\n ) {\n console.warn(\"No FQNs matched — check the profile's scope and mapping rules.\");\n }\n });\n\n return cmd;\n}\n\ninterface EndpointResolution {\n fqns: FullyQualifiedName[];\n note?: string;\n}\n\nasync function resolveEndpointFqns(\n endpoint: compareProfiles.CompareProfile['source'],\n root: string,\n): Promise<EndpointResolution> {\n if (endpoint.kind === 'connection') {\n const cache = new catalog.CatalogCache({ root, connection: endpoint.reference });\n const snapshot = await cache.get();\n if (snapshot.catalogs.length === 0) {\n return {\n fqns: [],\n note: `empty catalog cache at ${cache.path} — run \\`ddt catalog refresh --connection ${endpoint.reference}\\` first`,\n };\n }\n return { fqns: fqnsFromSnapshot(snapshot, endpoint.catalog, endpoint.schema) };\n }\n if (endpoint.kind === 'pac') {\n const contents = await pac.readPac(endpoint.reference);\n return { fqns: fqnsFromObjects(contents.model, endpoint.catalog, endpoint.schema) };\n }\n // kind === 'project'\n const loaded = await loadProject(endpoint.reference);\n const model = await parseProjectModel(loaded);\n return { fqns: fqnsFromObjects(model, endpoint.catalog, endpoint.schema) };\n}\n\nfunction fqnsFromSnapshot(\n snapshot: catalog.CatalogSnapshot,\n catalogScope: string | undefined,\n schemaScope: string | undefined,\n): FullyQualifiedName[] {\n const sameId = (a: string | undefined, b: string | undefined): boolean =>\n !!a && !!b && a.toUpperCase() === b.toUpperCase();\n const out: FullyQualifiedName[] = [];\n for (const cat of snapshot.catalogs) {\n if (catalogScope && !sameId(cat.catalog, catalogScope)) continue;\n for (const sc of cat.schemas) {\n if (schemaScope && !sameId(sc.schema, schemaScope)) continue;\n for (const obj of sc.objects) {\n // FQN uses `database` for UC's catalog — paired-file convention.\n out.push({ database: obj.catalog, schema: obj.schema, name: obj.name });\n }\n }\n }\n return out;\n}\n\nfunction fqnsFromObjects(\n model: readonly DatabricksObject[],\n catalogScope: string | undefined,\n schemaScope: string | undefined,\n): FullyQualifiedName[] {\n const sameId = (a: string | undefined, b: string | undefined): boolean =>\n !!a && !!b && a.toUpperCase() === b.toUpperCase();\n const out: FullyQualifiedName[] = [];\n for (const obj of model) {\n // FQN uses `database` for UC's catalog — paired-file convention.\n if (catalogScope && !sameId(obj.fqn.database, catalogScope)) continue;\n if (schemaScope && !sameId(obj.fqn.schema, schemaScope)) continue;\n out.push(obj.fqn);\n }\n return out;\n}\n\nfunction printBucket(label: string, items: readonly string[], exampleN: number): void {\n if (items.length === 0) return;\n console.log('');\n console.log(\n ` ${label} examples (showing ${Math.min(exampleN, items.length)} of ${items.length}):`,\n );\n for (const fqn of items.slice(0, exampleN)) console.log(` ${fqn}`);\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,89 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/completion.ts
7
+ import { Command } from "commander";
8
+ function completionCommand() {
9
+ return new Command("completion").description("Emit a shell completion script (bash | zsh | fish | powershell).").argument("<shell>", "bash | zsh | fish | powershell").action((shell) => {
10
+ const cmds = [
11
+ "connection",
12
+ "init",
13
+ "validate",
14
+ "compare",
15
+ "build",
16
+ "publish",
17
+ "script",
18
+ "import",
19
+ "extract",
20
+ "drift",
21
+ "refactor",
22
+ "format",
23
+ "completion",
24
+ "telemetry",
25
+ "license"
26
+ ];
27
+ switch (shell) {
28
+ case "bash":
29
+ console.log(bashScript(cmds));
30
+ return;
31
+ case "zsh":
32
+ console.log(zshScript(cmds));
33
+ return;
34
+ case "fish":
35
+ console.log(fishScript(cmds));
36
+ return;
37
+ case "powershell":
38
+ case "pwsh":
39
+ console.log(powerShellScript(cmds));
40
+ return;
41
+ default:
42
+ logger.error(`Unsupported shell: ${shell}. Use bash | zsh | fish | powershell.`);
43
+ process.exitCode = 1;
44
+ }
45
+ });
46
+ }
47
+ function bashScript(cmds) {
48
+ return `# ddt bash completion. Source from ~/.bashrc or drop in /etc/bash_completion.d/.
49
+ _ddt() {
50
+ local cur prev
51
+ COMPREPLY=()
52
+ cur="\${COMP_WORDS[COMP_CWORD]}"
53
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
54
+ if [[ $COMP_CWORD -eq 1 ]]; then
55
+ COMPREPLY=( $(compgen -W "${cmds.join(" ")}" -- "$cur") )
56
+ return 0
57
+ fi
58
+ }
59
+ complete -F _ddt ddt
60
+ `;
61
+ }
62
+ function zshScript(cmds) {
63
+ return `#compdef ddt
64
+ _ddt() {
65
+ local -a commands
66
+ commands=(${cmds.map((c) => `'${c}:'`).join(" ")})
67
+ _describe 'command' commands
68
+ }
69
+ _ddt "$@"
70
+ `;
71
+ }
72
+ function fishScript(cmds) {
73
+ return cmds.map((c) => `complete -c ddt -f -n '__fish_use_subcommand' -a '${c}'`).join("\n");
74
+ }
75
+ function powerShellScript(cmds) {
76
+ return `# ddt PowerShell completion. Add to your $PROFILE.
77
+ Register-ArgumentCompleter -Native -CommandName ddt -ScriptBlock {
78
+ param($wordToComplete, $commandAst, $cursorPosition)
79
+ $commands = @(${cmds.map((c) => `'${c}'`).join(", ")})
80
+ $commands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
81
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
82
+ }
83
+ }
84
+ `;
85
+ }
86
+ export {
87
+ completionCommand
88
+ };
89
+ //# sourceMappingURL=completion-ZSNCQKJ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/completion.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { logger } from '../util/logger.js';\n\n/**\n * `ddt completion` — emit a shell completion script for bash / zsh / fish.\n * Pipe into your shell init: `ddt completion zsh > ~/.zsh/completions/_ddt`.\n *\n * Completion content is intentionally minimal — command names + the\n * known top-level flags. Subcommand-aware completion (i.e. completing\n * profile names after `ddt connection test`) would require a separate\n * shell helper that calls `ddt connection list`; out of scope.\n */\nexport function completionCommand(): Command {\n return new Command('completion')\n .description('Emit a shell completion script (bash | zsh | fish | powershell).')\n .argument('<shell>', 'bash | zsh | fish | powershell')\n .action((shell: string) => {\n const cmds = [\n 'connection',\n 'init',\n 'validate',\n 'compare',\n 'build',\n 'publish',\n 'script',\n 'import',\n 'extract',\n 'drift',\n 'refactor',\n 'format',\n 'completion',\n 'telemetry',\n 'license',\n ];\n switch (shell) {\n case 'bash':\n console.log(bashScript(cmds));\n return;\n case 'zsh':\n console.log(zshScript(cmds));\n return;\n case 'fish':\n console.log(fishScript(cmds));\n return;\n case 'powershell':\n case 'pwsh':\n console.log(powerShellScript(cmds));\n return;\n default:\n // RH4.6 — match the SDT side (exitCode + logger.error) rather than\n // throwing, so unknown-shell handling is identical across both CLIs.\n logger.error(`Unsupported shell: ${shell}. Use bash | zsh | fish | powershell.`);\n process.exitCode = 1;\n }\n });\n}\n\nfunction bashScript(cmds: string[]): string {\n return `# ddt bash completion. Source from ~/.bashrc or drop in /etc/bash_completion.d/.\n_ddt() {\n local cur prev\n COMPREPLY=()\n cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n if [[ $COMP_CWORD -eq 1 ]]; then\n COMPREPLY=( $(compgen -W \"${cmds.join(' ')}\" -- \"$cur\") )\n return 0\n fi\n}\ncomplete -F _ddt ddt\n`;\n}\n\nfunction zshScript(cmds: string[]): string {\n return `#compdef ddt\n_ddt() {\n local -a commands\n commands=(${cmds.map((c) => `'${c}:'`).join(' ')})\n _describe 'command' commands\n}\n_ddt \"$@\"\n`;\n}\n\nfunction fishScript(cmds: string[]): string {\n return cmds.map((c) => `complete -c ddt -f -n '__fish_use_subcommand' -a '${c}'`).join('\\n');\n}\n\nfunction powerShellScript(cmds: string[]): string {\n return `# ddt PowerShell completion. Add to your $PROFILE.\nRegister-ArgumentCompleter -Native -CommandName ddt -ScriptBlock {\n param($wordToComplete, $commandAst, $cursorPosition)\n $commands = @(${cmds.map((c) => `'${c}'`).join(', ')})\n $commands | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }\n}\n`;\n}\n"],"mappings":";;;;;;AAAA,SAAS,eAAe;AAajB,SAAS,oBAA6B;AAC3C,SAAO,IAAI,QAAQ,YAAY,EAC5B,YAAY,kEAAkE,EAC9E,SAAS,WAAW,gCAAgC,EACpD,OAAO,CAAC,UAAkB;AACzB,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,gBAAQ,IAAI,WAAW,IAAI,CAAC;AAC5B;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,UAAU,IAAI,CAAC;AAC3B;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,WAAW,IAAI,CAAC;AAC5B;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,gBAAQ,IAAI,iBAAiB,IAAI,CAAC;AAClC;AAAA,MACF;AAGE,eAAO,MAAM,sBAAsB,KAAK,uCAAuC;AAC/E,gBAAQ,WAAW;AAAA,IACvB;AAAA,EACF,CAAC;AACL;AAEA,SAAS,WAAW,MAAwB;AAC1C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAOuB,KAAK,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAM9C;AAEA,SAAS,UAAU,MAAwB;AACzC,SAAO;AAAA;AAAA;AAAA,cAGK,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAKlD;AAEA,SAAS,WAAW,MAAwB;AAC1C,SAAO,KAAK,IAAI,CAAC,MAAM,qDAAqD,CAAC,GAAG,EAAE,KAAK,IAAI;AAC7F;AAEA,SAAS,iBAAiB,MAAwB;AAChD,SAAO;AAAA;AAAA;AAAA,kBAGS,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAMtD;","names":[]}
@@ -0,0 +1,148 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/connection.ts
4
+ import { Command } from "commander";
5
+ import {
6
+ createConnection,
7
+ defaultProfilesPath,
8
+ getProfile,
9
+ loadProfiles,
10
+ removeProfile,
11
+ upsertProfile
12
+ } from "@ddt-tools/core";
13
+ function connectionCommand() {
14
+ const cmd = new Command("connection").description("Manage Databricks connection profiles.");
15
+ cmd.command("add [name]").description("Store a new connection profile.").option("--name <name>", "Profile name (flag form of the positional name arg)").option("--host <host>", "Workspace host, e.g. dbc-12345-abcd.cloud.databricks.com").option("--auth <method>", "One of: pat | oauth-m2m | oauth-u2m | azure-ad | google-idc", "pat").option(
16
+ "--token <token>",
17
+ "PAT token \u2014 use env:DATABRICKS_TOKEN to reference an env var, never inline a secret"
18
+ ).option("--client-id <id>", "OAuth M2M / Azure AD client ID").option("--client-secret <secret>", "OAuth M2M / Azure AD client secret").option("--tenant-id <id>", "Azure AD tenant ID").option(
19
+ "--service-account-key-path <path>",
20
+ "Path to a GCP service-account key JSON file (for google-idc auth)"
21
+ ).requiredOption("--warehouse-id <id>", "SQL warehouse ID for DDL operations").option("--catalog <catalog>", "Default Unity Catalog catalog").option("--schema <schema>", "Default schema").action(async (nameArg, opts) => {
22
+ const name = opts.name ? String(opts.name) : nameArg ?? "";
23
+ if (!name) {
24
+ throw new Error(
25
+ "connection add needs a profile name \u2014 pass it positionally (`ddt connection add <name>`) or via `--name`."
26
+ );
27
+ }
28
+ if (!opts.host) throw new Error("--host is required");
29
+ const auth = buildAuth(opts);
30
+ const profile = {
31
+ name,
32
+ platform: "Databricks",
33
+ auth,
34
+ warehouseId: String(opts.warehouseId),
35
+ ...opts.catalog ? { catalog: String(opts.catalog) } : {},
36
+ ...opts.schema ? { schema: String(opts.schema) } : {}
37
+ };
38
+ await upsertProfile(profile);
39
+ console.log(`Saved profile "${name}" \u2192 ${defaultProfilesPath()}`);
40
+ });
41
+ cmd.command("list").description("List configured Databricks profiles.").action(async () => {
42
+ const profiles = await loadProfiles();
43
+ if (profiles.length === 0) {
44
+ console.log(`(no profiles configured at ${defaultProfilesPath()})`);
45
+ return;
46
+ }
47
+ for (const p of profiles) {
48
+ console.log(`${p.name} ${p.auth.method} ${p.auth.host} warehouse=${p.warehouseId}`);
49
+ }
50
+ });
51
+ cmd.command("get <name>").description("Print a profile (secrets are redacted).").action(async (name) => {
52
+ const p = await getProfile(name);
53
+ const redacted = {
54
+ ...p,
55
+ auth: redactAuth(p.auth)
56
+ };
57
+ console.log(JSON.stringify(redacted, null, 2));
58
+ });
59
+ cmd.command("remove <name>").description("Delete a connection profile.").action(async (name) => {
60
+ const removed = await removeProfile(name);
61
+ console.log(removed ? `Removed profile "${name}".` : `No profile "${name}" found.`);
62
+ if (!removed) process.exitCode = 1;
63
+ });
64
+ cmd.command("test <name>").description(
65
+ "Probe a connection profile via the workspace REST API (`/api/2.0/preview/scim/v2/Me`)."
66
+ ).action(async (name) => {
67
+ const profile = await getProfile(name);
68
+ const conn = createConnection(profile);
69
+ try {
70
+ await conn.connect();
71
+ const info = await conn.test();
72
+ console.log(`OK ${name}`);
73
+ console.log(` host: ${profile.auth.host}`);
74
+ console.log(` account: ${info.account}`);
75
+ console.log(` version: ${info.version}`);
76
+ } catch (err) {
77
+ console.error(`FAIL ${name}: ${err instanceof Error ? err.message : String(err)}`);
78
+ process.exitCode = 1;
79
+ } finally {
80
+ await conn.disconnect();
81
+ }
82
+ });
83
+ return cmd;
84
+ }
85
+ function buildAuth(opts) {
86
+ const host = String(opts.host);
87
+ const method = String(opts.auth ?? "pat").toLowerCase();
88
+ switch (method) {
89
+ case "pat":
90
+ case "personal_access_token":
91
+ if (!opts.token) throw new Error("--token is required for PAT auth");
92
+ return { method: "PERSONAL_ACCESS_TOKEN", host, token: String(opts.token) };
93
+ case "oauth-m2m":
94
+ case "oauth_m2m":
95
+ if (!opts.clientId || !opts.clientSecret) {
96
+ throw new Error("--client-id and --client-secret are required for OAuth M2M");
97
+ }
98
+ return {
99
+ method: "OAUTH_M2M",
100
+ host,
101
+ clientId: String(opts.clientId),
102
+ clientSecret: String(opts.clientSecret)
103
+ };
104
+ case "oauth-u2m":
105
+ case "oauth_u2m":
106
+ return { method: "OAUTH_U2M", host };
107
+ case "azure-ad":
108
+ case "azure_ad":
109
+ if (!opts.tenantId || !opts.clientId) {
110
+ throw new Error("--tenant-id and --client-id are required for Azure AD");
111
+ }
112
+ return {
113
+ method: "AZURE_AD",
114
+ host,
115
+ tenantId: String(opts.tenantId),
116
+ clientId: String(opts.clientId),
117
+ ...opts.clientSecret ? { clientSecret: String(opts.clientSecret) } : {}
118
+ };
119
+ case "google-idc":
120
+ case "google_idc":
121
+ if (!opts.serviceAccountKeyPath) {
122
+ throw new Error("--service-account-key-path is required for google-idc auth");
123
+ }
124
+ return {
125
+ method: "GOOGLE_IDC",
126
+ host,
127
+ serviceAccountKeyPath: String(opts.serviceAccountKeyPath)
128
+ };
129
+ default:
130
+ throw new Error(`Unknown --auth value: ${method}`);
131
+ }
132
+ }
133
+ function redactAuth(auth) {
134
+ if (auth.method === "PERSONAL_ACCESS_TOKEN" && auth.token && !auth.token.startsWith("env:")) {
135
+ return { ...auth, token: "<redacted>" };
136
+ }
137
+ if (auth.method === "OAUTH_M2M" && auth.clientSecret && !auth.clientSecret.startsWith("env:")) {
138
+ return { ...auth, clientSecret: "<redacted>" };
139
+ }
140
+ if (auth.method === "AZURE_AD" && auth.clientSecret && !auth.clientSecret.startsWith("env:")) {
141
+ return { ...auth, clientSecret: "<redacted>" };
142
+ }
143
+ return auth;
144
+ }
145
+ export {
146
+ connectionCommand
147
+ };
148
+ //# sourceMappingURL=connection-CDGVEFUC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/connection.ts"],"sourcesContent":["import { Command } from 'commander';\nimport {\n createConnection,\n defaultProfilesPath,\n getProfile,\n loadProfiles,\n removeProfile,\n upsertProfile,\n type DatabricksConnectionProfile,\n type DatabricksAuth,\n} from '@ddt-tools/core';\n\n/**\n * `ddt connection` — manage Databricks workspace connection profiles.\n *\n * Profiles live at `~/.ddt/profiles.json` (overridable via `$DDT_PROFILES_PATH`).\n * add / list / get / remove are wired today. `test` (probe a profile via\n * the workspace REST API) waits on the OAuth U2M flow before it can land.\n */\nexport function connectionCommand(): Command {\n const cmd = new Command('connection').description('Manage Databricks connection profiles.');\n\n cmd\n .command('add [name]')\n .description('Store a new connection profile.')\n .option('--name <name>', 'Profile name (flag form of the positional name arg)')\n .option('--host <host>', 'Workspace host, e.g. dbc-12345-abcd.cloud.databricks.com')\n .option('--auth <method>', 'One of: pat | oauth-m2m | oauth-u2m | azure-ad | google-idc', 'pat')\n .option(\n '--token <token>',\n 'PAT token — use env:DATABRICKS_TOKEN to reference an env var, never inline a secret',\n )\n .option('--client-id <id>', 'OAuth M2M / Azure AD client ID')\n .option('--client-secret <secret>', 'OAuth M2M / Azure AD client secret')\n .option('--tenant-id <id>', 'Azure AD tenant ID')\n .option(\n '--service-account-key-path <path>',\n 'Path to a GCP service-account key JSON file (for google-idc auth)',\n )\n .requiredOption('--warehouse-id <id>', 'SQL warehouse ID for DDL operations')\n .option('--catalog <catalog>', 'Default Unity Catalog catalog')\n .option('--schema <schema>', 'Default schema')\n .action(async (nameArg: string | undefined, opts) => {\n // Accept the profile name either positionally (`ddt connection add prod`)\n // or via `--name prod` (the flag form `sdt connection add` uses, and what\n // GETTING_STARTED documents canonically). Flag wins when both are given.\n const name = opts.name ? String(opts.name) : (nameArg ?? '');\n if (!name) {\n throw new Error(\n 'connection add needs a profile name — pass it positionally (`ddt connection add <name>`) or via `--name`.',\n );\n }\n if (!opts.host) throw new Error('--host is required');\n const auth = buildAuth(opts);\n const profile: DatabricksConnectionProfile = {\n name,\n platform: 'Databricks',\n auth,\n warehouseId: String(opts.warehouseId),\n ...(opts.catalog ? { catalog: String(opts.catalog) } : {}),\n ...(opts.schema ? { schema: String(opts.schema) } : {}),\n };\n await upsertProfile(profile);\n console.log(`Saved profile \"${name}\" → ${defaultProfilesPath()}`);\n });\n\n cmd\n .command('list')\n .description('List configured Databricks profiles.')\n .action(async () => {\n const profiles = await loadProfiles();\n if (profiles.length === 0) {\n console.log(`(no profiles configured at ${defaultProfilesPath()})`);\n return;\n }\n for (const p of profiles) {\n console.log(`${p.name}\\t${p.auth.method}\\t${p.auth.host}\\twarehouse=${p.warehouseId}`);\n }\n });\n\n cmd\n .command('get <name>')\n .description('Print a profile (secrets are redacted).')\n .action(async (name: string) => {\n const p = await getProfile(name);\n const redacted: DatabricksConnectionProfile = {\n ...p,\n auth: redactAuth(p.auth),\n };\n console.log(JSON.stringify(redacted, null, 2));\n });\n\n cmd\n .command('remove <name>')\n .description('Delete a connection profile.')\n .action(async (name: string) => {\n const removed = await removeProfile(name);\n console.log(removed ? `Removed profile \"${name}\".` : `No profile \"${name}\" found.`);\n if (!removed) process.exitCode = 1;\n });\n\n cmd\n .command('test <name>')\n .description(\n 'Probe a connection profile via the workspace REST API (`/api/2.0/preview/scim/v2/Me`).',\n )\n .action(async (name: string) => {\n const profile = await getProfile(name);\n const conn = createConnection(profile);\n try {\n await conn.connect();\n const info = await conn.test();\n console.log(`OK ${name}`);\n console.log(` host: ${profile.auth.host}`);\n console.log(` account: ${info.account}`);\n console.log(` version: ${info.version}`);\n } catch (err) {\n console.error(`FAIL ${name}: ${err instanceof Error ? err.message : String(err)}`);\n process.exitCode = 1;\n } finally {\n await conn.disconnect();\n }\n });\n\n return cmd;\n}\n\nfunction buildAuth(opts: {\n host?: string;\n auth?: string;\n token?: string;\n clientId?: string;\n clientSecret?: string;\n tenantId?: string;\n serviceAccountKeyPath?: string;\n}): DatabricksAuth {\n const host = String(opts.host);\n const method = String(opts.auth ?? 'pat').toLowerCase();\n switch (method) {\n case 'pat':\n case 'personal_access_token':\n if (!opts.token) throw new Error('--token is required for PAT auth');\n return { method: 'PERSONAL_ACCESS_TOKEN', host, token: String(opts.token) };\n case 'oauth-m2m':\n case 'oauth_m2m':\n if (!opts.clientId || !opts.clientSecret) {\n throw new Error('--client-id and --client-secret are required for OAuth M2M');\n }\n return {\n method: 'OAUTH_M2M',\n host,\n clientId: String(opts.clientId),\n clientSecret: String(opts.clientSecret),\n };\n case 'oauth-u2m':\n case 'oauth_u2m':\n return { method: 'OAUTH_U2M', host };\n case 'azure-ad':\n case 'azure_ad':\n if (!opts.tenantId || !opts.clientId) {\n throw new Error('--tenant-id and --client-id are required for Azure AD');\n }\n return {\n method: 'AZURE_AD',\n host,\n tenantId: String(opts.tenantId),\n clientId: String(opts.clientId),\n ...(opts.clientSecret ? { clientSecret: String(opts.clientSecret) } : {}),\n };\n case 'google-idc':\n case 'google_idc':\n if (!opts.serviceAccountKeyPath) {\n throw new Error('--service-account-key-path is required for google-idc auth');\n }\n return {\n method: 'GOOGLE_IDC',\n host,\n serviceAccountKeyPath: String(opts.serviceAccountKeyPath),\n };\n default:\n throw new Error(`Unknown --auth value: ${method}`);\n }\n}\n\nfunction redactAuth(auth: DatabricksAuth): DatabricksAuth {\n if (auth.method === 'PERSONAL_ACCESS_TOKEN' && auth.token && !auth.token.startsWith('env:')) {\n return { ...auth, token: '<redacted>' };\n }\n if (auth.method === 'OAUTH_M2M' && auth.clientSecret && !auth.clientSecret.startsWith('env:')) {\n return { ...auth, clientSecret: '<redacted>' };\n }\n if (auth.method === 'AZURE_AD' && auth.clientSecret && !auth.clientSecret.startsWith('env:')) {\n return { ...auth, clientSecret: '<redacted>' };\n }\n return auth;\n}\n"],"mappings":"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","names":[]}