@lightdash/cli 2.562.0 → 2.563.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 (176) hide show
  1. package/dist/.tsbuildinfo.build +1 -1
  2. package/dist/agentConnectionConnectOption.d.ts +8 -0
  3. package/dist/agentConnectionConnectOption.d.ts.map +1 -0
  4. package/dist/agentConnectionConnectOption.js +39 -0
  5. package/dist/agentConnectionOption.d.ts +3 -0
  6. package/dist/agentConnectionOption.d.ts.map +1 -0
  7. package/dist/agentConnectionOption.js +48 -0
  8. package/dist/agentConnections/agentTokenProvider.d.ts +13 -0
  9. package/dist/agentConnections/agentTokenProvider.d.ts.map +1 -0
  10. package/dist/agentConnections/agentTokenProvider.js +294 -0
  11. package/dist/agentConnections/apiUrl.d.ts +4 -0
  12. package/dist/agentConnections/apiUrl.d.ts.map +1 -0
  13. package/dist/agentConnections/apiUrl.js +37 -0
  14. package/dist/agentConnections/commandPolicy.d.ts +14 -0
  15. package/dist/agentConnections/commandPolicy.d.ts.map +1 -0
  16. package/dist/agentConnections/commandPolicy.js +108 -0
  17. package/dist/agentConnections/configLock.d.ts +12 -0
  18. package/dist/agentConnections/configLock.d.ts.map +1 -0
  19. package/dist/agentConnections/configLock.js +191 -0
  20. package/dist/agentConnections/connectInputs.d.ts +20 -0
  21. package/dist/agentConnections/connectInputs.d.ts.map +1 -0
  22. package/dist/agentConnections/connectInputs.js +49 -0
  23. package/dist/agentConnections/fixtures.d.ts +3 -0
  24. package/dist/agentConnections/fixtures.d.ts.map +1 -0
  25. package/dist/agentConnections/fixtures.js +36 -0
  26. package/dist/agentConnections/initialToken.d.ts +26 -0
  27. package/dist/agentConnections/initialToken.d.ts.map +1 -0
  28. package/dist/agentConnections/initialToken.js +93 -0
  29. package/dist/agentConnections/lifecycle.d.ts +17 -0
  30. package/dist/agentConnections/lifecycle.d.ts.map +1 -0
  31. package/dist/agentConnections/lifecycle.js +44 -0
  32. package/dist/agentConnections/personalRecovery.d.ts +27 -0
  33. package/dist/agentConnections/personalRecovery.d.ts.map +1 -0
  34. package/dist/agentConnections/personalRecovery.js +71 -0
  35. package/dist/agentConnections/projectSelection.d.ts +4 -0
  36. package/dist/agentConnections/projectSelection.d.ts.map +1 -0
  37. package/dist/agentConnections/projectSelection.js +48 -0
  38. package/dist/agentConnections/providerProcess.fixture.d.ts +2 -0
  39. package/dist/agentConnections/providerProcess.fixture.d.ts.map +1 -0
  40. package/dist/agentConnections/providerProcess.fixture.js +19 -0
  41. package/dist/agentConnections/requestPolicy.d.ts +11 -0
  42. package/dist/agentConnections/requestPolicy.d.ts.map +1 -0
  43. package/dist/agentConnections/requestPolicy.js +76 -0
  44. package/dist/agentConnections/schema.d.ts +66 -0
  45. package/dist/agentConnections/schema.d.ts.map +1 -0
  46. package/dist/agentConnections/schema.js +184 -0
  47. package/dist/agentConnections/statusResponse.d.ts +10 -0
  48. package/dist/agentConnections/statusResponse.d.ts.map +1 -0
  49. package/dist/agentConnections/statusResponse.js +146 -0
  50. package/dist/agentConnections/store.d.ts +36 -0
  51. package/dist/agentConnections/store.d.ts.map +1 -0
  52. package/dist/agentConnections/store.js +275 -0
  53. package/dist/agentConnections/warehouseStatus.d.ts +30 -0
  54. package/dist/agentConnections/warehouseStatus.d.ts.map +1 -0
  55. package/dist/agentConnections/warehouseStatus.js +184 -0
  56. package/dist/analytics/analytics.d.ts.map +1 -1
  57. package/dist/analytics/analytics.js +4 -0
  58. package/dist/config.d.ts +3 -1
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +2 -2
  61. package/dist/credentials.d.ts +27 -0
  62. package/dist/credentials.d.ts.map +1 -0
  63. package/dist/credentials.js +161 -0
  64. package/dist/dbt/models.d.ts.map +1 -1
  65. package/dist/dbt/models.js +45 -14
  66. package/dist/handlers/agentAccess.d.ts.map +1 -1
  67. package/dist/handlers/agentAccess.js +9 -1
  68. package/dist/handlers/agentConnect.d.ts.map +1 -1
  69. package/dist/handlers/agentConnect.js +2 -0
  70. package/dist/handlers/agentConnectionConnect.d.ts +5 -0
  71. package/dist/handlers/agentConnectionConnect.d.ts.map +1 -0
  72. package/dist/handlers/agentConnectionConnect.js +371 -0
  73. package/dist/handlers/agentConnectionStatus.d.ts +7 -0
  74. package/dist/handlers/agentConnectionStatus.d.ts.map +1 -0
  75. package/dist/handlers/agentConnectionStatus.js +228 -0
  76. package/dist/handlers/agentRevoke.d.ts +7 -0
  77. package/dist/handlers/agentRevoke.d.ts.map +1 -0
  78. package/dist/handlers/agentRevoke.js +144 -0
  79. package/dist/handlers/agentStatus.d.ts +4 -1
  80. package/dist/handlers/agentStatus.d.ts.map +1 -1
  81. package/dist/handlers/agentStatus.js +7 -0
  82. package/dist/handlers/apps/createApp.d.ts.map +1 -1
  83. package/dist/handlers/apps/createApp.js +2 -0
  84. package/dist/handlers/apps/preview.d.ts.map +1 -1
  85. package/dist/handlers/apps/preview.js +6 -0
  86. package/dist/handlers/apps/previewProxy.d.ts.map +1 -1
  87. package/dist/handlers/apps/previewProxy.js +2 -0
  88. package/dist/handlers/apps/validate.d.ts.map +1 -1
  89. package/dist/handlers/apps/validate.js +20 -8
  90. package/dist/handlers/compile.d.ts.map +1 -1
  91. package/dist/handlers/compile.js +3 -0
  92. package/dist/handlers/connectSnowflake.d.ts.map +1 -1
  93. package/dist/handlers/connectSnowflake.js +2 -0
  94. package/dist/handlers/createProject.d.ts.map +1 -1
  95. package/dist/handlers/createProject.js +2 -0
  96. package/dist/handlers/dbt/apiClient.d.ts +4 -2
  97. package/dist/handlers/dbt/apiClient.d.ts.map +1 -1
  98. package/dist/handlers/dbt/apiClient.js +138 -4
  99. package/dist/handlers/dbt/compile.d.ts.map +1 -1
  100. package/dist/handlers/dbt/compile.js +13 -2
  101. package/dist/handlers/dbt/getDbtProfileTargetName.d.ts.map +1 -1
  102. package/dist/handlers/dbt/getDbtProfileTargetName.js +12 -4
  103. package/dist/handlers/dbt/getDbtVersion.d.ts.map +1 -1
  104. package/dist/handlers/dbt/getDbtVersion.js +12 -4
  105. package/dist/handlers/dbt/getWarehouseClient.d.ts.map +1 -1
  106. package/dist/handlers/dbt/getWarehouseClient.js +15 -5
  107. package/dist/handlers/dbt/refresh.d.ts.map +1 -1
  108. package/dist/handlers/dbt/refresh.js +2 -0
  109. package/dist/handlers/dbt/run.d.ts.map +1 -1
  110. package/dist/handlers/dbt/run.js +10 -3
  111. package/dist/handlers/deploy.d.ts.map +1 -1
  112. package/dist/handlers/deploy.js +17 -1
  113. package/dist/handlers/diagnostics.d.ts.map +1 -1
  114. package/dist/handlers/diagnostics.js +2 -0
  115. package/dist/handlers/download.d.ts.map +1 -1
  116. package/dist/handlers/download.js +19 -5
  117. package/dist/handlers/exportChartImage.d.ts.map +1 -1
  118. package/dist/handlers/exportChartImage.js +2 -0
  119. package/dist/handlers/generateExposures.d.ts.map +1 -1
  120. package/dist/handlers/generateExposures.js +4 -1
  121. package/dist/handlers/getProject.d.ts.map +1 -1
  122. package/dist/handlers/getProject.js +25 -1
  123. package/dist/handlers/listProjects.d.ts.map +1 -1
  124. package/dist/handlers/listProjects.js +2 -0
  125. package/dist/handlers/login/connectionBrowser.d.ts +2 -0
  126. package/dist/handlers/login/connectionBrowser.d.ts.map +1 -0
  127. package/dist/handlers/login/connectionBrowser.js +22 -0
  128. package/dist/handlers/login/loopback.d.ts +15 -0
  129. package/dist/handlers/login/loopback.d.ts.map +1 -0
  130. package/dist/handlers/login/loopback.js +135 -0
  131. package/dist/handlers/login/oauth.d.ts.map +1 -1
  132. package/dist/handlers/login/oauth.js +16 -3
  133. package/dist/handlers/login/pat.d.ts.map +1 -1
  134. package/dist/handlers/login/pat.js +2 -0
  135. package/dist/handlers/login.d.ts.map +1 -1
  136. package/dist/handlers/login.js +8 -1
  137. package/dist/handlers/oauthLogin.d.ts.map +1 -1
  138. package/dist/handlers/oauthLogin.js +2 -0
  139. package/dist/handlers/ordinary/harness.mocks.d.ts +42 -0
  140. package/dist/handlers/ordinary/harness.mocks.d.ts.map +1 -0
  141. package/dist/handlers/ordinary/harness.mocks.js +180 -0
  142. package/dist/handlers/preAggregateAudit.d.ts.map +1 -1
  143. package/dist/handlers/preAggregateAudit.js +2 -0
  144. package/dist/handlers/preview.d.ts.map +1 -1
  145. package/dist/handlers/preview.js +4 -0
  146. package/dist/handlers/renameHandler.d.ts.map +1 -1
  147. package/dist/handlers/renameHandler.js +2 -0
  148. package/dist/handlers/renameProject.d.ts.map +1 -1
  149. package/dist/handlers/renameProject.js +2 -0
  150. package/dist/handlers/resolveProjectFlag.d.ts.map +1 -1
  151. package/dist/handlers/resolveProjectFlag.js +5 -0
  152. package/dist/handlers/runChart.d.ts.map +1 -1
  153. package/dist/handlers/runChart.js +7 -2
  154. package/dist/handlers/selectProject.d.ts.map +1 -1
  155. package/dist/handlers/selectProject.js +5 -0
  156. package/dist/handlers/setProject.d.ts.map +1 -1
  157. package/dist/handlers/setProject.js +5 -0
  158. package/dist/handlers/setWarehouse.d.ts.map +1 -1
  159. package/dist/handlers/setWarehouse.js +2 -0
  160. package/dist/handlers/slugUpdate.d.ts.map +1 -1
  161. package/dist/handlers/slugUpdate.js +2 -0
  162. package/dist/handlers/sourceSelection.d.ts.map +1 -1
  163. package/dist/handlers/sourceSelection.js +13 -2
  164. package/dist/handlers/sql.d.ts.map +1 -1
  165. package/dist/handlers/sql.js +7 -2
  166. package/dist/handlers/utils.d.ts +3 -0
  167. package/dist/handlers/utils.d.ts.map +1 -1
  168. package/dist/handlers/utils.js +39 -0
  169. package/dist/handlers/validate.d.ts.map +1 -1
  170. package/dist/handlers/validate.js +2 -0
  171. package/dist/handlers/warehouseCatalog.d.ts.map +1 -1
  172. package/dist/handlers/warehouseCatalog.js +7 -2
  173. package/dist/index.js +43 -3
  174. package/dist/lightdash-config/index.d.ts.map +1 -1
  175. package/dist/lightdash-config/index.js +4 -1
  176. package/package.json +3 -3
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.atomicWrite = exports.acquireLock = exports.readPrivateFile = exports.privateDirectory = exports.isFsError = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const crypto_1 = require("crypto");
6
+ const fs_1 = require("fs");
7
+ const os_1 = require("os");
8
+ const path_1 = tslib_1.__importDefault(require("path"));
9
+ const isFsError = (error, code) => error instanceof Error && 'code' in error && error.code === code;
10
+ exports.isFsError = isFsError;
11
+ const privateDirectory = async (directory) => {
12
+ await fs_1.promises.mkdir(directory, { recursive: true, mode: 0o700 });
13
+ if (process.platform !== 'win32')
14
+ await fs_1.promises.chmod(directory, 0o700);
15
+ };
16
+ exports.privateDirectory = privateDirectory;
17
+ const readPrivateFile = async (file) => {
18
+ try {
19
+ if (process.platform !== 'win32')
20
+ await fs_1.promises.chmod(file, 0o600);
21
+ return await fs_1.promises.readFile(file, 'utf8');
22
+ }
23
+ catch (error) {
24
+ if ((0, exports.isFsError)(error, 'ENOENT'))
25
+ return null;
26
+ throw new Error('Cannot read the agent connection file.');
27
+ }
28
+ };
29
+ exports.readPrivateFile = readPrivateFile;
30
+ const readOwner = async (file) => {
31
+ const contents = await (0, exports.readPrivateFile)(file);
32
+ if (contents === null)
33
+ return null;
34
+ try {
35
+ const value = JSON.parse(contents);
36
+ if (typeof value !== 'object' ||
37
+ value === null ||
38
+ !('ownerId' in value) ||
39
+ typeof value.ownerId !== 'string' ||
40
+ !value.ownerId ||
41
+ !('pid' in value) ||
42
+ typeof value.pid !== 'number' ||
43
+ !Number.isInteger(value.pid) ||
44
+ value.pid <= 0 ||
45
+ !('host' in value) ||
46
+ typeof value.host !== 'string' ||
47
+ !('startedAt' in value) ||
48
+ typeof value.startedAt !== 'string' ||
49
+ !Number.isFinite(Date.parse(value.startedAt)))
50
+ return null;
51
+ return {
52
+ ownerId: value.ownerId,
53
+ pid: value.pid,
54
+ host: value.host,
55
+ startedAt: value.startedAt,
56
+ };
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ };
62
+ const isDeadLocalOwner = (owner) => {
63
+ if (owner.host !== (0, os_1.hostname)())
64
+ return false;
65
+ try {
66
+ process.kill(owner.pid, 0);
67
+ return false;
68
+ }
69
+ catch (error) {
70
+ return (0, exports.isFsError)(error, 'ESRCH');
71
+ }
72
+ };
73
+ const exists = async (file) => {
74
+ try {
75
+ await fs_1.promises.stat(file);
76
+ return true;
77
+ }
78
+ catch (error) {
79
+ if ((0, exports.isFsError)(error, 'ENOENT'))
80
+ return false;
81
+ throw error;
82
+ }
83
+ };
84
+ const releaseOwner = async (file, ownerId) => {
85
+ if ((await readOwner(file))?.ownerId === ownerId)
86
+ await fs_1.promises.unlink(file);
87
+ };
88
+ const createOwner = async (file) => {
89
+ let handle;
90
+ try {
91
+ handle = await fs_1.promises.open(file, 'wx', 0o600);
92
+ }
93
+ catch (error) {
94
+ if ((0, exports.isFsError)(error, 'EEXIST'))
95
+ return null;
96
+ throw new Error('Cannot lock the agent connection file.');
97
+ }
98
+ const owner = {
99
+ ownerId: (0, crypto_1.randomUUID)(),
100
+ pid: process.pid,
101
+ host: (0, os_1.hostname)(),
102
+ startedAt: new Date().toISOString(),
103
+ };
104
+ try {
105
+ await handle.writeFile(JSON.stringify(owner));
106
+ await handle.sync();
107
+ }
108
+ finally {
109
+ await handle.close();
110
+ }
111
+ return owner;
112
+ };
113
+ const acquireLock = async (file, options = {
114
+ waitMs: 30_000,
115
+ pollMs: 25,
116
+ }) => {
117
+ await (0, exports.privateDirectory)(path_1.default.dirname(file));
118
+ const recovery = `${file}.recovery`;
119
+ const deadline = performance.now() + options.waitMs;
120
+ const attempt = async () => {
121
+ if (!(await exists(recovery))) {
122
+ const owner = await createOwner(file);
123
+ if (owner) {
124
+ if (!(await exists(recovery)))
125
+ return () => releaseOwner(file, owner.ownerId);
126
+ await releaseOwner(file, owner.ownerId);
127
+ }
128
+ else {
129
+ const stale = await readOwner(file);
130
+ if (stale && isDeadLocalOwner(stale)) {
131
+ const sentinel = await createOwner(recovery);
132
+ if (sentinel) {
133
+ try {
134
+ const current = await readOwner(file);
135
+ if (current?.ownerId === stale.ownerId &&
136
+ isDeadLocalOwner(current))
137
+ await fs_1.promises.unlink(file);
138
+ }
139
+ finally {
140
+ await releaseOwner(recovery, sentinel.ownerId);
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ await new Promise((resolve) => {
147
+ setTimeout(resolve, options.pollMs + Math.floor(Math.random() * options.pollMs));
148
+ });
149
+ return performance.now() < deadline ? attempt() : null;
150
+ };
151
+ const release = await attempt();
152
+ if (release !== null)
153
+ return release;
154
+ if (await exists(recovery))
155
+ throw new Error('Interrupted config lock recovery. Confirm no CLI process is using this config before removing the recovery sentinel.');
156
+ throw new Error('Another CLI process is updating this connection. Try again shortly.');
157
+ };
158
+ exports.acquireLock = acquireLock;
159
+ const atomicWrite = async (file, contents, commit = {}) => {
160
+ await (0, exports.privateDirectory)(path_1.default.dirname(file));
161
+ const temporary = `${file}.${(0, crypto_1.randomUUID)()}.tmp`;
162
+ const handle = await fs_1.promises.open(temporary, 'wx', 0o600);
163
+ try {
164
+ try {
165
+ await handle.writeFile(contents);
166
+ await handle.sync();
167
+ }
168
+ finally {
169
+ await handle.close();
170
+ }
171
+ commit.beforeCommit?.();
172
+ await fs_1.promises.rename(temporary, file);
173
+ commit.onCommitted?.();
174
+ if (process.platform !== 'win32') {
175
+ const directory = await fs_1.promises.open(path_1.default.dirname(file), 'r');
176
+ try {
177
+ await directory.sync();
178
+ }
179
+ finally {
180
+ await directory.close();
181
+ }
182
+ }
183
+ }
184
+ finally {
185
+ await fs_1.promises.unlink(temporary).catch((error) => {
186
+ if (!(0, exports.isFsError)(error, 'ENOENT'))
187
+ throw new Error('Cannot clean up the agent connection file.');
188
+ });
189
+ }
190
+ };
191
+ exports.atomicWrite = atomicWrite;
@@ -0,0 +1,20 @@
1
+ import { AgentCapability } from '@lightdash/common';
2
+ export type ConnectionConnectOptions = {
3
+ verbose: boolean;
4
+ name?: string;
5
+ projects?: string[];
6
+ capabilities?: string;
7
+ expiresIn?: string;
8
+ oauthPort?: string;
9
+ timeout?: number;
10
+ };
11
+ export declare const connectionConnectInputs: (options: ConnectionConnectOptions) => {
12
+ name: string;
13
+ projects: string[] | null;
14
+ capabilities: AgentCapability[];
15
+ expiresInSeconds: number;
16
+ port: number;
17
+ timeout: number;
18
+ };
19
+ export type ConnectionConnectInputs = ReturnType<typeof connectionConnectInputs>;
20
+ //# sourceMappingURL=connectInputs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectInputs.d.ts","sourceRoot":"","sources":["../../src/agentConnections/connectInputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,MAAM,wBAAwB,GAAG;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,eAAO,MAAM,uBAAuB,YAAa,wBAAwB;;;;;;;CA+DxE,CAAC;AACF,MAAM,MAAM,uBAAuB,GAAG,UAAU,CAC5C,OAAO,uBAAuB,CACjC,CAAC"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connectionConnectInputs = void 0;
4
+ const common_1 = require("@lightdash/common");
5
+ const credentials_1 = require("../credentials");
6
+ const connectionConnectInputs = (options) => {
7
+ const name = options.name ?? 'default';
8
+ (0, credentials_1.validateAgentConnectionSelection)(name);
9
+ const projects = options.projects === undefined ? null : [...new Set(options.projects)];
10
+ if (projects?.some((project) => !project ||
11
+ project !== project.trim() ||
12
+ /[/\\?#\x00-\x1f\x7f]/.test(project)) ||
13
+ projects?.length === 0)
14
+ throw new Error('Specify a non-empty project UUID or slug without URL or path separators.');
15
+ const known = Object.values(common_1.AgentCapability);
16
+ const requested = options.capabilities === undefined
17
+ ? known
18
+ : options.capabilities.split(',');
19
+ const capabilities = [...new Set(requested)].map((value) => {
20
+ const capability = known.find((key) => key === value);
21
+ if (capability === undefined)
22
+ throw new Error(`Invalid capability. Valid keys: ${known.join(', ')}.`);
23
+ return capability;
24
+ });
25
+ const duration = /^(\d+)(s|m|h|d)$/.exec(options.expiresIn ?? '24h');
26
+ const units = { s: 1, m: 60, h: 3600, d: 86400 };
27
+ const expiresInSeconds = duration
28
+ ? Number(duration[1]) * units[duration[2]]
29
+ : NaN;
30
+ if (!Number.isSafeInteger(expiresInSeconds) ||
31
+ expiresInSeconds < 1 ||
32
+ expiresInSeconds > 604800)
33
+ throw new Error('Expiry must be a positive integer duration in s, m, h or d, no greater than 7d.');
34
+ const rawPort = options.oauthPort ?? process.env.LIGHTDASH_OAUTH_PORT;
35
+ const port = rawPort === undefined ? 0 : Number(rawPort);
36
+ if (rawPort !== undefined &&
37
+ (!/^\d+$/.test(rawPort) ||
38
+ !Number.isInteger(port) ||
39
+ port < 1 ||
40
+ port > 65535))
41
+ throw new Error('Port must be a number between 1 and 65535');
42
+ const timeout = options.timeout ?? 180;
43
+ if (!Number.isFinite(timeout) ||
44
+ timeout <= 0 ||
45
+ timeout * 1000 > 2147483647)
46
+ throw new Error('Timeout must be a positive number of seconds no greater than 2147483.647');
47
+ return { name, projects, capabilities, expiresInSeconds, port, timeout };
48
+ };
49
+ exports.connectionConnectInputs = connectionConnectInputs;
@@ -0,0 +1,3 @@
1
+ import type { AgentConnectionSlot } from './schema';
2
+ export declare const slotFixture: () => AgentConnectionSlot;
3
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/agentConnections/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEpD,eAAO,MAAM,WAAW,QAAO,mBA+B7B,CAAC"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.slotFixture = void 0;
4
+ const slotFixture = () => ({
5
+ instanceUrl: 'https://example.com',
6
+ organizationUuid: '11111111-1111-4111-8111-111111111111',
7
+ personUuid: '22222222-2222-4222-8222-222222222222',
8
+ name: 'default',
9
+ clientId: 'lightdash-cli',
10
+ resource: 'https://example.com',
11
+ grantUuid: '33333333-3333-4333-8333-333333333333',
12
+ grantRevision: null,
13
+ approvedAt: new Date(Date.now() - 3_600_000).toISOString(),
14
+ grantExpiresAt: new Date(Date.now() + 86_400_000).toISOString(),
15
+ tokens: {
16
+ accessToken: 'private-access',
17
+ accessTokenExpiresAt: new Date(Date.now() + 3_600_000).toISOString(),
18
+ refreshToken: 'private-refresh',
19
+ refreshTokenExpiresAt: null,
20
+ scopes: ['read', 'write'],
21
+ generation: 1,
22
+ lastRefreshAttemptUuid: null,
23
+ },
24
+ lastReported: {
25
+ checkedAt: new Date().toISOString(),
26
+ limitsStatus: 'ok',
27
+ approvedProjects: [],
28
+ effectiveProjects: [],
29
+ status: 'active',
30
+ lastUsedAt: null,
31
+ revokedAt: null,
32
+ },
33
+ refreshState: { kind: 'idle' },
34
+ revokeState: { kind: 'none' },
35
+ });
36
+ exports.slotFixture = slotFixture;
@@ -0,0 +1,26 @@
1
+ export declare const unboundConnection = "The server returned a token that is not bound to this agent connection. No credential was saved.";
2
+ export declare class ConnectionProofError extends Error {
3
+ constructor(message?: string);
4
+ }
5
+ export declare const exchangeInitialConnectionToken: ({ instanceUrl, code, verifier, redirectUri, grantUuid, signal, proxyAuthorization, }: {
6
+ instanceUrl: string;
7
+ code: string;
8
+ verifier: string;
9
+ redirectUri: string;
10
+ grantUuid: string | null;
11
+ signal: AbortSignal;
12
+ proxyAuthorization: string | null;
13
+ }) => Promise<{
14
+ grantUuid: string;
15
+ grantRevision: number | null;
16
+ tokens: {
17
+ accessToken: string;
18
+ refreshToken: string;
19
+ accessTokenExpiresAt: string;
20
+ refreshTokenExpiresAt: string | null;
21
+ scopes: string[];
22
+ generation: number;
23
+ lastRefreshAttemptUuid: null;
24
+ };
25
+ }>;
26
+ //# sourceMappingURL=initialToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialToken.d.ts","sourceRoot":"","sources":["../../src/agentConnections/initialToken.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,qGACwE,CAAC;AACvG,qBAAa,oBAAqB,SAAQ,KAAK;IAC3C,YAAY,OAAO,SAAoB,EAEtC;CACJ;AACD,eAAO,MAAM,8BAA8B,yFAQxC;IACC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;;;;QAyEW,WAAW;QACX,YAAY;QACZ,oBAAoB;QAGpB,qBAAqB;QAMrB,MAAM;QACN,UAAU;QACV,sBAAsB;;EAGjC,CAAC"}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exchangeInitialConnectionToken = exports.ConnectionProofError = exports.unboundConnection = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const apiUrl_1 = require("./apiUrl");
6
+ const statusResponse_1 = require("./statusResponse");
7
+ exports.unboundConnection = 'The server returned a token that is not bound to this agent connection. No credential was saved.';
8
+ class ConnectionProofError extends Error {
9
+ constructor(message = exports.unboundConnection) {
10
+ super(message);
11
+ }
12
+ }
13
+ exports.ConnectionProofError = ConnectionProofError;
14
+ const exchangeInitialConnectionToken = async ({ instanceUrl, code, verifier, redirectUri, grantUuid, signal, proxyAuthorization, }) => {
15
+ const fetch = (0, apiUrl_1.createAgentConnectionFetch)(instanceUrl);
16
+ const startedAt = Date.now();
17
+ const response = await fetch('/api/v1/oauth/token', {
18
+ method: 'POST',
19
+ headers: {
20
+ 'Content-Type': 'application/x-www-form-urlencoded',
21
+ ...(proxyAuthorization === null
22
+ ? {}
23
+ : { 'Proxy-Authorization': proxyAuthorization }),
24
+ },
25
+ body: new URLSearchParams({
26
+ grant_type: 'authorization_code',
27
+ code,
28
+ code_verifier: verifier,
29
+ redirect_uri: redirectUri,
30
+ client_id: 'lightdash-cli',
31
+ resource: instanceUrl,
32
+ }),
33
+ redirect: 'error',
34
+ signal,
35
+ });
36
+ if (!response.ok)
37
+ throw new ConnectionProofError();
38
+ const value = await response.json();
39
+ const token = (0, statusResponse_1.responseRecord)(value);
40
+ const metadata = token.agent_connection === undefined
41
+ ? null
42
+ : (0, statusResponse_1.responseRecord)(token.agent_connection);
43
+ const hint = metadata?.grant_uuid ?? grantUuid;
44
+ if (hint === null || hint === undefined)
45
+ throw new ConnectionProofError('The server did not identify an agent connection. No credential was saved.');
46
+ const revision = metadata?.grant_revision ?? null;
47
+ const lifetime = token.expires_in;
48
+ const refreshLifetime = token.refresh_expires_in ?? null;
49
+ if (typeof hint !== 'string' ||
50
+ !(0, uuid_1.validate)(hint) ||
51
+ (grantUuid !== null && (!(0, uuid_1.validate)(grantUuid) || grantUuid !== hint)) ||
52
+ (metadata !== null && metadata.grant_uuid !== hint) ||
53
+ (revision !== null &&
54
+ (typeof revision !== 'number' ||
55
+ !Number.isSafeInteger(revision) ||
56
+ revision < 1)) ||
57
+ typeof token.access_token !== 'string' ||
58
+ !token.access_token ||
59
+ typeof token.refresh_token !== 'string' ||
60
+ !token.refresh_token ||
61
+ typeof token.token_type !== 'string' ||
62
+ token.token_type.toLowerCase() !== 'bearer' ||
63
+ typeof lifetime !== 'number' ||
64
+ !Number.isSafeInteger(lifetime) ||
65
+ lifetime < 1 ||
66
+ lifetime > 2147483647 ||
67
+ (refreshLifetime !== null &&
68
+ (typeof refreshLifetime !== 'number' ||
69
+ !Number.isSafeInteger(refreshLifetime) ||
70
+ refreshLifetime < 1 ||
71
+ refreshLifetime > 2147483647)) ||
72
+ typeof token.scope !== 'string' ||
73
+ !/^[\x21\x23-\x5b\x5d-\x7e]+(?: [\x21\x23-\x5b\x5d-\x7e]+)*$/.test(token.scope) ||
74
+ (token.resource !== undefined && token.resource !== instanceUrl) ||
75
+ (token.issuer !== undefined && token.issuer !== instanceUrl))
76
+ throw new ConnectionProofError();
77
+ return {
78
+ grantUuid: hint,
79
+ grantRevision: revision,
80
+ tokens: {
81
+ accessToken: token.access_token,
82
+ refreshToken: token.refresh_token,
83
+ accessTokenExpiresAt: new Date(startedAt + lifetime * 1000).toISOString(),
84
+ refreshTokenExpiresAt: refreshLifetime === null
85
+ ? null
86
+ : new Date(startedAt + refreshLifetime * 1000).toISOString(),
87
+ scopes: [...new Set(token.scope.split(' '))],
88
+ generation: 1,
89
+ lastRefreshAttemptUuid: null,
90
+ },
91
+ };
92
+ };
93
+ exports.exchangeInitialConnectionToken = exchangeInitialConnectionToken;
@@ -0,0 +1,17 @@
1
+ import { type Config } from '../config';
2
+ import { type AgentConnectionSlot, type SlotKey } from './schema';
3
+ export type LifecycleOperation = {
4
+ kind: 'status' | 'revoke';
5
+ grantUuid: string;
6
+ generation: number;
7
+ };
8
+ export declare class AgentConnectionRequestError extends Error {
9
+ readonly state: 'rejected' | 'unreachable';
10
+ constructor(state: 'rejected' | 'unreachable');
11
+ }
12
+ export declare const selectLifecycleSlot: (name: string | undefined) => string;
13
+ export declare const connectionKey: (config: Config, name: string) => SlotKey | null;
14
+ export declare const sameGeneration: (current: AgentConnectionSlot | null, slot: AgentConnectionSlot) => current is AgentConnectionSlot;
15
+ export declare const settingsUrl: (instanceUrl: string) => string;
16
+ export declare const revocationRecovery = "This credential cannot confirm revocation. Revoke the connection in the app, or use agent revoke --name <slot> --human with a matching personal login.";
17
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/agentConnections/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAKxC,OAAO,EAEH,KAAK,mBAAmB,EACxB,KAAK,OAAO,EACf,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,kBAAkB,GAAG;IAC7B,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AACF,qBAAa,2BAA4B,SAAQ,KAAK;aACtB,KAAK,EAAE,UAAU,GAAG,aAAa;IAA7D,YAA4B,KAAK,EAAE,UAAU,GAAG,aAAa,EAM5D;CACJ;AACD,eAAO,MAAM,mBAAmB,SAAU,MAAM,GAAG,SAAS,KAAG,MAW9D,CAAC;AACF,eAAO,MAAM,aAAa,WAAY,MAAM,QAAQ,MAAM,KAAG,OAAO,GAAG,IAUzD,CAAC;AACf,eAAO,MAAM,cAAc,YACd,mBAAmB,GAAG,IAAI,QAC7B,mBAAmB,KAC1B,OAAO,IAAI,mBAG0C,CAAC;AACzD,eAAO,MAAM,WAAW,gBAAiB,MAAM,KAAG,MACK,CAAC;AACxD,eAAO,MAAM,kBAAkB,2JAC6H,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.revocationRecovery = exports.settingsUrl = exports.sameGeneration = exports.connectionKey = exports.selectLifecycleSlot = exports.AgentConnectionRequestError = void 0;
4
+ const credentials_1 = require("../credentials");
5
+ const schema_1 = require("./schema");
6
+ class AgentConnectionRequestError extends Error {
7
+ state;
8
+ constructor(state) {
9
+ super(state === 'rejected'
10
+ ? 'The server rejected this connection credential. Reconnect or check its status in the app.'
11
+ : 'Could not reach the server. Connection status is unconfirmed.');
12
+ this.state = state;
13
+ }
14
+ }
15
+ exports.AgentConnectionRequestError = AgentConnectionRequestError;
16
+ const selectLifecycleSlot = (name) => {
17
+ const selection = (0, credentials_1.getAgentConnectionSelection)();
18
+ if (name !== undefined && selection !== null && name !== selection)
19
+ throw new Error('The connection name and global agent selection conflict.');
20
+ const selected = name ?? selection;
21
+ if (selected === null)
22
+ throw new Error('Specify an agent connection with --name <slot>.');
23
+ (0, credentials_1.validateAgentConnectionSelection)(selected);
24
+ return selected;
25
+ };
26
+ exports.selectLifecycleSlot = selectLifecycleSlot;
27
+ const connectionKey = (config, name) => config.context?.serverUrl &&
28
+ config.user?.userUuid &&
29
+ config.user.organizationUuid
30
+ ? {
31
+ instanceUrl: (0, schema_1.canonicalInstanceUrl)(config.context.serverUrl),
32
+ personUuid: config.user.userUuid,
33
+ organizationUuid: config.user.organizationUuid,
34
+ name,
35
+ }
36
+ : null;
37
+ exports.connectionKey = connectionKey;
38
+ const sameGeneration = (current, slot) => current !== null &&
39
+ current.grantUuid === slot.grantUuid &&
40
+ current.tokens.generation === slot.tokens.generation;
41
+ exports.sameGeneration = sameGeneration;
42
+ const settingsUrl = (instanceUrl) => `${instanceUrl}/generalSettings/myAgentConnections`;
43
+ exports.settingsUrl = settingsUrl;
44
+ exports.revocationRecovery = 'This credential cannot confirm revocation. Revoke the connection in the app, or use agent revoke --name <slot> --human with a matching personal login.';
@@ -0,0 +1,27 @@
1
+ import fetch from 'node-fetch';
2
+ import { type Config } from '../config';
3
+ import { type AgentConnectionSlot } from './schema';
4
+ export declare class PersonalRecoveryError extends Error {
5
+ constructor(mode?: 'recovery' | 'bootstrap');
6
+ }
7
+ export declare const capturePersonalBootstrap: (config: Config) => {
8
+ instanceUrl: string;
9
+ proxyAuthorization: string | null;
10
+ publicTextIsSafe: (value: string, extraSecrets?: readonly string[]) => boolean;
11
+ publicSecrets: (string | undefined)[];
12
+ fetch: ({ method, url, signal, body, }: {
13
+ method: 'GET' | 'DELETE' | 'POST';
14
+ url: string;
15
+ signal: AbortSignal;
16
+ body: string | undefined;
17
+ }) => Promise<fetch.Response>;
18
+ };
19
+ export declare const capturePersonalRecovery: (config: Config) => (slot: AgentConnectionSlot) => {
20
+ fetch: (request: {
21
+ method: 'GET' | 'DELETE';
22
+ url: string;
23
+ signal: AbortSignal;
24
+ body: undefined;
25
+ }) => Promise<fetch.Response>;
26
+ };
27
+ //# sourceMappingURL=personalRecovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personalRecovery.d.ts","sourceRoot":"","sources":["../../src/agentConnections/personalRecovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAIxC,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAG1E,qBAAa,qBAAsB,SAAQ,KAAK;IAC5C,YAAY,IAAI,GAAE,UAAU,GAAG,WAAwB,EAMtD;CACJ;AAqDD,eAAO,MAAM,wBAAwB,WAAY,MAAM;;;8BA9BhC,MAAM,iBACC,SAAS,MAAM,EAAE;;4CAahC;QACC,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClC,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,WAAW,CAAC;QACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;CAmBZ,CAAC;AAEF,eAAO,MAAM,uBAAuB,WAAY,MAAM,YAEpC,mBAAmB;IAGzB,KAAK,YAAkB;QACnB,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC;QACzB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,WAAW,CAAC;QACpB,IAAI,EAAE,SAAS,CAAC;KACnB;CAQZ,CAAC"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.capturePersonalRecovery = exports.capturePersonalBootstrap = exports.PersonalRecoveryError = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
6
+ const credentials_1 = require("../credentials");
7
+ const utils_1 = require("../handlers/utils");
8
+ const apiUrl_1 = require("./apiUrl");
9
+ const schema_1 = require("./schema");
10
+ const statusResponse_1 = require("./statusResponse");
11
+ class PersonalRecoveryError extends Error {
12
+ constructor(mode = 'recovery') {
13
+ super(mode === 'bootstrap'
14
+ ? 'Agent connection approval requires a personal CLI login. Service account credentials cannot start approval.'
15
+ : 'The personal CLI login does not match this connection person and organisation.');
16
+ }
17
+ }
18
+ exports.PersonalRecoveryError = PersonalRecoveryError;
19
+ const capturePersonalTransport = (config) => {
20
+ const instanceUrl = config.context?.serverUrl;
21
+ const token = config.context?.apiKey;
22
+ const headers = token ? (0, utils_1.buildRequestHeaders)(token) : null;
23
+ if (headers && config.context?.proxyAuthorization)
24
+ headers['Proxy-Authorization'] = config.context.proxyAuthorization;
25
+ return (expectedInstance) => {
26
+ if (!instanceUrl ||
27
+ !token ||
28
+ !headers ||
29
+ (0, credentials_1.humanCredential)(token).kind !== 'human_api_key' ||
30
+ (expectedInstance !== null &&
31
+ (0, schema_1.canonicalInstanceUrl)(instanceUrl) !== expectedInstance))
32
+ throw new PersonalRecoveryError();
33
+ const canonical = (0, schema_1.canonicalInstanceUrl)(instanceUrl);
34
+ return {
35
+ instanceUrl: canonical,
36
+ proxyAuthorization: headers['Proxy-Authorization'] ?? null,
37
+ publicTextIsSafe: (value, extraSecrets = []) => (0, statusResponse_1.publicTextIsSafe)(value, [token, headers['Proxy-Authorization'], ...extraSecrets], 8192),
38
+ publicSecrets: [token, headers['Proxy-Authorization']],
39
+ fetch: async ({ method, url, signal, body, }) => {
40
+ const response = await (0, node_fetch_1.default)((0, apiUrl_1.agentConnectionApiUrl)(instanceUrl, url), { method, headers, signal, body, redirect: 'error' });
41
+ return response;
42
+ },
43
+ };
44
+ };
45
+ };
46
+ const capturePersonalBootstrap = (config) => {
47
+ try {
48
+ return capturePersonalTransport(config)(null);
49
+ }
50
+ catch (error) {
51
+ if (error instanceof PersonalRecoveryError)
52
+ throw new PersonalRecoveryError('bootstrap');
53
+ throw error;
54
+ }
55
+ };
56
+ exports.capturePersonalBootstrap = capturePersonalBootstrap;
57
+ const capturePersonalRecovery = (config) => {
58
+ const capture = capturePersonalTransport(config);
59
+ return (slot) => {
60
+ const transport = capture(slot.instanceUrl);
61
+ return {
62
+ fetch: async (request) => {
63
+ const response = await transport.fetch(request);
64
+ if (!response.ok)
65
+ throw new Error('Personal recovery was not confirmed.');
66
+ return response;
67
+ },
68
+ };
69
+ };
70
+ };
71
+ exports.capturePersonalRecovery = capturePersonalRecovery;
@@ -0,0 +1,4 @@
1
+ import { type Config } from '../config';
2
+ export declare const resolveAgentConnectionProject: (value?: string) => Promise<string>;
3
+ export declare const configWithAgentProject: (config: Config) => Promise<Config>;
4
+ //# sourceMappingURL=projectSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectSelection.d.ts","sourceRoot":"","sources":["../../src/agentConnections/projectSelection.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAInD,eAAO,MAAM,6BAA6B,WAC9B,MAAM,KACf,OAAO,CAAC,MAAM,CAyChB,CAAC;AAEF,eAAO,MAAM,sBAAsB,WACvB,MAAM,KACf,OAAO,CAAC,MAAM,CAUf,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configWithAgentProject = exports.resolveAgentConnectionProject = void 0;
4
+ const common_1 = require("@lightdash/common");
5
+ const config_1 = require("../config");
6
+ const credentials_1 = require("../credentials");
7
+ const apiClient_1 = require("../handlers/dbt/apiClient");
8
+ const resolveAgentConnectionProject = async (value) => {
9
+ if (value &&
10
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value))
11
+ return value;
12
+ const credential = await (0, credentials_1.resolveCredential)(await (0, config_1.getConfig)({ readOnly: true }));
13
+ if (credential.kind !== 'agent_connection')
14
+ throw new Error('An agent connection credential is required.');
15
+ const approved = credential.slot.lastReported.approvedProjects;
16
+ if (value === undefined) {
17
+ if (approved.length === 1)
18
+ return approved[0].projectUuid;
19
+ throw new common_1.ParameterError('This agent connection needs an explicit project. Pass --project on a command that supports it, or select a connection with one approved project.');
20
+ }
21
+ const matches = await approved.reduce(async (pending, { projectUuid }) => {
22
+ const found = await pending;
23
+ const project = await (0, apiClient_1.lightdashApi)({
24
+ method: 'GET',
25
+ url: `/api/v1/projects/${projectUuid}`,
26
+ body: undefined,
27
+ });
28
+ if (project.projectUuid !== projectUuid)
29
+ throw new Error('The server returned a different project for this agent connection.');
30
+ return project.slug === value ? [...found, projectUuid] : found;
31
+ }, Promise.resolve([]));
32
+ if (matches.length !== 1)
33
+ throw new common_1.ParameterError('The project slug must match exactly one approved, accessible project for this agent connection. Pass --project with its UUID.');
34
+ return matches[0];
35
+ };
36
+ exports.resolveAgentConnectionProject = resolveAgentConnectionProject;
37
+ const configWithAgentProject = async (config) => ({
38
+ ...config,
39
+ context: {
40
+ ...config.context,
41
+ project: await (0, exports.resolveAgentConnectionProject)(),
42
+ projectName: undefined,
43
+ previewProject: undefined,
44
+ previewName: undefined,
45
+ source: undefined,
46
+ },
47
+ });
48
+ exports.configWithAgentProject = configWithAgentProject;