@lightdash/cli 2.562.0 → 2.564.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,8 @@
1
+ import { type Command } from 'commander';
2
+ export declare const registerAgentConnectionConnect: (command: Command, legacyHandler: (options: {
3
+ verbose: boolean;
4
+ project?: string;
5
+ oauthPort?: number;
6
+ timeout?: number;
7
+ }, command: Command) => Promise<void>) => void;
8
+ //# sourceMappingURL=agentConnectionConnectOption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentConnectionConnectOption.d.ts","sourceRoot":"","sources":["../src/agentConnectionConnectOption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAEjD,eAAO,MAAM,8BAA8B,YAC9B,OAAO,iBACD,CACX,OAAO,EAAE;IACL,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,EACD,OAAO,EAAE,OAAO,KACf,OAAO,CAAC,IAAI,CAAC,KACnB,IA8EF,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAgentConnectionConnect = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const commander_1 = require("commander");
6
+ const registerAgentConnectionConnect = (command, legacyHandler) => {
7
+ command
8
+ .addOption(new commander_1.Option('--name <slot>', 'Name the agent connection slot').hideHelp())
9
+ .addOption(new commander_1.Option('--capabilities <keys>', 'Request these agent actions').hideHelp())
10
+ .addOption(new commander_1.Option('--expires-in <duration>', 'Request this connection lifetime').hideHelp())
11
+ .addOption(new commander_1.Option('--connection', 'Request an agent connection credential').hideHelp());
12
+ command.on('option:project', (value) => {
13
+ const previous = command.getOptionValue('connectionProjects');
14
+ const projects = Array.isArray(previous)
15
+ ? previous.filter((item) => typeof item === 'string')
16
+ : [];
17
+ command.setOptionValue('connectionProjects', [...projects, value]);
18
+ });
19
+ command.on('option:oauth-port', (value) => command.setOptionValue('connectionOauthPort', value));
20
+ command.action(function dispatch(...args) {
21
+ const [options] = args;
22
+ if (options.name !== undefined ||
23
+ options.capabilities !== undefined ||
24
+ options.expiresIn !== undefined ||
25
+ options.connection) {
26
+ return Promise.resolve().then(() => tslib_1.__importStar(require('./handlers/agentConnectionConnect'))).then(({ agentConnectionConnectHandler }) => agentConnectionConnectHandler({
27
+ verbose: options.verbose,
28
+ name: options.name,
29
+ capabilities: options.capabilities,
30
+ expiresIn: options.expiresIn,
31
+ projects: options.connectionProjects,
32
+ oauthPort: options.connectionOauthPort,
33
+ timeout: options.timeout,
34
+ }));
35
+ }
36
+ return legacyHandler.apply(this, args);
37
+ });
38
+ };
39
+ exports.registerAgentConnectionConnect = registerAgentConnectionConnect;
@@ -0,0 +1,3 @@
1
+ import { type Command } from 'commander';
2
+ export declare const registerAgentConnectionOption: (program: Command, resetInvocation?: () => void) => void;
3
+ //# sourceMappingURL=agentConnectionOption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentConnectionOption.d.ts","sourceRoot":"","sources":["../src/agentConnectionOption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAUjD,eAAO,MAAM,6BAA6B,YAC7B,OAAO,oBACC,MAAM,IAAI,KAC5B,IAuDF,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAgentConnectionOption = void 0;
4
+ const commander_1 = require("commander");
5
+ const commandPolicy_1 = require("./agentConnections/commandPolicy");
6
+ const credentials_1 = require("./credentials");
7
+ const registerAgentConnectionOption = (program, resetInvocation = () => { }) => {
8
+ program.addOption(new commander_1.Option('--agent-connection <slot>', 'Use an agent connection slot').hideHelp());
9
+ const { parseAsync } = program;
10
+ const parseWithContext = function parseWithSelection(...args) {
11
+ this.setOptionValueWithSource('agentConnection', undefined, 'default');
12
+ const agent = this.commands.find((command) => command.name() === 'agent');
13
+ for (const command of agent?.commands ?? []) {
14
+ let options = [];
15
+ if (command.name() === 'connect')
16
+ options = [
17
+ 'name',
18
+ 'capabilities',
19
+ 'expiresIn',
20
+ 'connection',
21
+ 'connectionProjects',
22
+ 'connectionOauthPort',
23
+ ];
24
+ else if (command.name() === 'status')
25
+ options = ['name', 'json'];
26
+ else if (command.name() === 'revoke')
27
+ options = ['name', 'json', 'human'];
28
+ for (const option of options)
29
+ command.setOptionValueWithSource(option, undefined, 'default');
30
+ }
31
+ resetInvocation();
32
+ const environmentSelection = process.env.LIGHTDASH_AGENT_CONNECTION ?? null;
33
+ return (0, credentials_1.runWithParsedAgentConnectionSelection)(() => {
34
+ const selection = this.opts().agentConnection;
35
+ if (selection !== undefined && typeof selection !== 'string')
36
+ throw new Error('Invalid agent connection selection.');
37
+ return selection ?? environmentSelection;
38
+ }, environmentSelection !== null ||
39
+ (args[0] ?? process.argv).some((argument) => argument.startsWith('--agent-connection')), () => parseAsync.apply(this, args));
40
+ };
41
+ Object.assign(program, { parseAsync: parseWithContext });
42
+ program.hook('preAction', (_root, command) => {
43
+ if ((0, credentials_1.getAgentConnectionSelection)() === null)
44
+ return;
45
+ (0, commandPolicy_1.assertAgentCommandAllowed)((0, commandPolicy_1.agentCommandPath)(command), command.opts());
46
+ });
47
+ };
48
+ exports.registerAgentConnectionOption = registerAgentConnectionOption;
@@ -0,0 +1,13 @@
1
+ import { type AgentConnectionSlot, type SlotKey } from './schema';
2
+ import { AgentConnectionStore } from './store';
3
+ export declare const assertUsableSlot: (slot: AgentConnectionSlot, now: number) => void;
4
+ export declare class AgentTokenProvider {
5
+ private readonly store;
6
+ private readonly verified;
7
+ constructor(store?: AgentConnectionStore);
8
+ private verify;
9
+ private reconnect;
10
+ getToken(key: SlotKey, rejectedGeneration?: number | null, proxyAuthorization?: string | null): Promise<AgentConnectionSlot>;
11
+ getLifecycleToken(key: SlotKey, operation: import('./lifecycle').LifecycleOperation, rejectedGeneration: number | null, proxyAuthorization: string | null): Promise<AgentConnectionSlot>;
12
+ }
13
+ //# sourceMappingURL=agentTokenProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentTokenProvider.d.ts","sourceRoot":"","sources":["../../src/agentConnections/agentTokenProvider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAoB,MAAM,SAAS,CAAC;AAwDjE,eAAO,MAAM,gBAAgB,SACnB,mBAAmB,OACpB,MAAM,KACZ,IAsCF,CAAC;AAEF,qBAAa,kBAAkB;IAIvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAH1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C,YACqB,KAAK,GAAE,oBAAiD,EACzE;YAEU,MAAM;YA+DN,SAAS;IAkBjB,QAAQ,CACV,GAAG,EAAE,OAAO,EACZ,kBAAkB,GAAE,MAAM,GAAG,IAAW,EACxC,kBAAkB,GAAE,MAAM,GAAG,IAAW,GACzC,OAAO,CAAC,mBAAmB,CAAC,CAgK9B;IACK,iBAAiB,CACnB,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,OAAO,aAAa,EAAE,kBAAkB,EACnD,kBAAkB,EAAE,MAAM,GAAG,IAAI,EACjC,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAClC,OAAO,CAAC,mBAAmB,CAAC,CA0B9B;CACJ"}
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentTokenProvider = exports.assertUsableSlot = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@lightdash/common");
6
+ const ajv_1 = tslib_1.__importDefault(require("ajv"));
7
+ const crypto_1 = require("crypto");
8
+ const apiUrl_1 = require("./apiUrl");
9
+ const schema_1 = require("./schema");
10
+ const store_1 = require("./store");
11
+ const createResponseValidator = () => new ajv_1.default().compile({
12
+ type: 'object',
13
+ required: [
14
+ 'access_token',
15
+ 'refresh_token',
16
+ 'token_type',
17
+ 'expires_in',
18
+ 'scope',
19
+ ],
20
+ properties: {
21
+ access_token: { type: 'string', minLength: 1 },
22
+ refresh_token: { type: 'string', minLength: 1 },
23
+ token_type: {
24
+ type: 'string',
25
+ pattern: '^[Bb][Ee][Aa][Rr][Ee][Rr]$',
26
+ },
27
+ expires_in: { type: 'integer', minimum: 1, maximum: 2_147_483_647 },
28
+ scope: { type: 'string', minLength: 1 },
29
+ },
30
+ });
31
+ let responseValidator = null;
32
+ const validateResponse = (value) => {
33
+ responseValidator ??= createResponseValidator();
34
+ return responseValidator(value);
35
+ };
36
+ const record = (value) => {
37
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
38
+ throw new Error('The server returned invalid connection data.');
39
+ return Object.fromEntries(Object.entries(value));
40
+ };
41
+ const results = async (response) => {
42
+ if (!response.ok)
43
+ throw new Error('The server rejected this connection credential.');
44
+ const value = await response.json();
45
+ const payload = record(value);
46
+ if (payload.status !== 'ok')
47
+ throw new Error('The server returned invalid connection data.');
48
+ return record(payload.results);
49
+ };
50
+ const reconnectError = () => new Error('Reconnect before using this agent connection. Its refresh outcome is uncertain.');
51
+ const assertUsableSlot = (slot, now) => {
52
+ if (Date.parse(slot.grantExpiresAt) <= now ||
53
+ slot.lastReported.status === 'expired')
54
+ throw new Error('This agent connection expired. Reconnect before using this slot.');
55
+ if (slot.lastReported.status === 'revoked' ||
56
+ slot.lastReported.revokedAt !== null)
57
+ throw new Error('This agent connection was revoked. Reconnect before using this slot.');
58
+ if (slot.lastReported.limitsStatus !== 'ok')
59
+ throw new Error('This agent connection has invalid limits. Reconnect before using this slot.');
60
+ switch (slot.revokeState.kind) {
61
+ case 'none':
62
+ break;
63
+ case 'unconfirmed':
64
+ throw new Error('Revocation of this agent connection is unconfirmed. Reconnect before using this slot.');
65
+ default:
66
+ (0, common_1.assertUnreachable)(slot.revokeState, 'Unknown revoke state');
67
+ }
68
+ switch (slot.refreshState.kind) {
69
+ case 'idle':
70
+ break;
71
+ case 'in_progress':
72
+ case 'reconnect_required':
73
+ throw reconnectError();
74
+ default:
75
+ (0, common_1.assertUnreachable)(slot.refreshState, 'Unknown refresh state');
76
+ }
77
+ };
78
+ exports.assertUsableSlot = assertUsableSlot;
79
+ class AgentTokenProvider {
80
+ store;
81
+ verified = new Set();
82
+ constructor(store = new store_1.AgentConnectionStore()) {
83
+ this.store = store;
84
+ }
85
+ async verify(slot, proxyAuthorization) {
86
+ const fetch = (0, apiUrl_1.createAgentConnectionFetch)(slot.instanceUrl);
87
+ const headers = {
88
+ Authorization: `Bearer ${slot.tokens.accessToken}`,
89
+ ...(proxyAuthorization === null
90
+ ? {}
91
+ : { 'Proxy-Authorization': proxyAuthorization }),
92
+ };
93
+ const user = await results(await fetch(`${slot.instanceUrl}/api/v1/user`, {
94
+ headers,
95
+ redirect: 'error',
96
+ signal: AbortSignal.timeout(15_000),
97
+ }));
98
+ if (user.userUuid !== slot.personUuid ||
99
+ user.organizationUuid !== slot.organizationUuid)
100
+ throw new Error('Agent connection identity does not match the configured person and organisation.');
101
+ const status = await results(await fetch(`${slot.instanceUrl}/api/v2/user/agent-connections/${slot.grantUuid}/status`, {
102
+ headers,
103
+ redirect: 'error',
104
+ signal: AbortSignal.timeout(15_000),
105
+ }));
106
+ if (status.grantUuid !== slot.grantUuid ||
107
+ status.clientId !== slot.clientId ||
108
+ status.approvedAt !== slot.approvedAt ||
109
+ status.expiresAt !== slot.grantExpiresAt)
110
+ throw new Error('Agent connection grant does not match the saved approval.');
111
+ const checked = (0, schema_1.validateSlot)({
112
+ ...slot,
113
+ lastReported: {
114
+ checkedAt: new Date().toISOString(),
115
+ limitsStatus: status.limitsStatus,
116
+ approvedProjects: status.approvedProjects,
117
+ effectiveProjects: status.effectiveProjects,
118
+ status: status.status,
119
+ lastUsedAt: status.lastUsedAt,
120
+ revokedAt: status.revokedAt,
121
+ },
122
+ }, slot);
123
+ (0, exports.assertUsableSlot)(checked, Date.now());
124
+ return checked;
125
+ }
126
+ async reconnect(locked, slot) {
127
+ try {
128
+ await locked.saveSlot({
129
+ ...slot,
130
+ refreshState: {
131
+ kind: 'reconnect_required',
132
+ since: new Date().toISOString(),
133
+ },
134
+ });
135
+ }
136
+ catch {
137
+ throw reconnectError();
138
+ }
139
+ throw reconnectError();
140
+ }
141
+ async getToken(key, rejectedGeneration = null, proxyAuthorization = null) {
142
+ return this.store.withLock(async (locked) => {
143
+ let slot = await locked.readSlot(key);
144
+ if (slot === null)
145
+ throw new Error('Agent connection not found for this instance, person and organisation. Reconnect before using this slot.');
146
+ let journal;
147
+ try {
148
+ await locked.discardStaleJournal(slot);
149
+ journal = await locked.readJournal(key);
150
+ }
151
+ catch {
152
+ return this.reconnect(locked, slot);
153
+ }
154
+ if (journal !== null) {
155
+ if (journal.grantUuid === slot.grantUuid &&
156
+ slot.tokens.generation === journal.generation + 1 &&
157
+ slot.tokens.lastRefreshAttemptUuid ===
158
+ journal.attemptUuid &&
159
+ slot.refreshState.kind === 'idle')
160
+ await locked.clearJournal(key);
161
+ else
162
+ return this.reconnect(locked, slot);
163
+ }
164
+ if (slot.refreshState.kind === 'in_progress')
165
+ return this.reconnect(locked, slot);
166
+ (0, exports.assertUsableSlot)(slot, Date.now());
167
+ const remaining = Date.parse(slot.tokens.accessTokenExpiresAt) - Date.now();
168
+ const lifetime = Date.parse(slot.tokens.accessTokenExpiresAt) -
169
+ Date.parse(slot.lastReported.checkedAt);
170
+ const earlyWindow = Math.min(60_000, Math.max(0, lifetime / 10));
171
+ const mustRefresh = remaining <= earlyWindow ||
172
+ rejectedGeneration === slot.tokens.generation;
173
+ if (mustRefresh) {
174
+ if (slot.tokens.refreshTokenExpiresAt !== null &&
175
+ Date.parse(slot.tokens.refreshTokenExpiresAt) <= Date.now())
176
+ throw new Error('This agent connection refresh token expired. Reconnect before using this slot.');
177
+ const attemptUuid = (0, crypto_1.randomUUID)();
178
+ const startedAt = Date.now();
179
+ const pending = {
180
+ ...slot,
181
+ refreshState: {
182
+ kind: 'in_progress',
183
+ attemptUuid,
184
+ startedAt: new Date(startedAt).toISOString(),
185
+ },
186
+ };
187
+ try {
188
+ await locked.saveSlot(pending);
189
+ await locked.writeJournal(key, {
190
+ grantUuid: slot.grantUuid,
191
+ attemptUuid,
192
+ generation: slot.tokens.generation,
193
+ });
194
+ const body = new URLSearchParams({
195
+ grant_type: 'refresh_token',
196
+ client_id: 'lightdash-cli',
197
+ refresh_token: slot.tokens.refreshToken,
198
+ resource: slot.resource,
199
+ scope: slot.tokens.scopes.join(' '),
200
+ });
201
+ const fetch = (0, apiUrl_1.createAgentConnectionFetch)(slot.instanceUrl);
202
+ const response = await fetch(`${slot.instanceUrl}/api/v1/oauth/token`, {
203
+ method: 'POST',
204
+ body,
205
+ redirect: 'error',
206
+ signal: AbortSignal.timeout(15_000),
207
+ headers: proxyAuthorization === null
208
+ ? {}
209
+ : {
210
+ 'Proxy-Authorization': proxyAuthorization,
211
+ },
212
+ });
213
+ if (!response.ok)
214
+ throw reconnectError();
215
+ const value = await response.json();
216
+ if (!validateResponse(value) ||
217
+ value.refresh_token === slot.tokens.refreshToken)
218
+ throw reconnectError();
219
+ const scopes = value.scope.split(' ');
220
+ const savedScopes = slot.tokens.scopes;
221
+ if (scopes.length !== slot.tokens.scopes.length ||
222
+ new Set(scopes).size !== scopes.length ||
223
+ scopes.some((scope) => !savedScopes.includes(scope)))
224
+ throw reconnectError();
225
+ const dateHeader = response.headers.get('date');
226
+ const serverTime = dateHeader === null
227
+ ? startedAt
228
+ : Date.parse(dateHeader);
229
+ if (!Number.isFinite(serverTime) ||
230
+ Math.max(Date.now(), serverTime) >=
231
+ Date.parse(slot.grantExpiresAt))
232
+ throw reconnectError();
233
+ const expiresAt = Math.min(startedAt + value.expires_in * 1000, serverTime + value.expires_in * 1000, Date.parse(slot.grantExpiresAt));
234
+ if (expiresAt <= Date.now())
235
+ throw reconnectError();
236
+ slot = await this.verify({
237
+ ...slot,
238
+ tokens: {
239
+ ...slot.tokens,
240
+ accessToken: value.access_token,
241
+ refreshToken: value.refresh_token,
242
+ accessTokenExpiresAt: new Date(expiresAt).toISOString(),
243
+ generation: slot.tokens.generation + 1,
244
+ lastRefreshAttemptUuid: attemptUuid,
245
+ },
246
+ refreshState: { kind: 'idle' },
247
+ }, proxyAuthorization);
248
+ await locked.saveSlot(slot);
249
+ }
250
+ catch {
251
+ return this.reconnect(locked, pending);
252
+ }
253
+ await locked.clearJournal(key);
254
+ }
255
+ const verificationKey = JSON.stringify([
256
+ slot.instanceUrl,
257
+ slot.organizationUuid,
258
+ slot.personUuid,
259
+ slot.grantUuid,
260
+ slot.tokens.generation,
261
+ ]);
262
+ if (!mustRefresh && !this.verified.has(verificationKey)) {
263
+ try {
264
+ slot = await this.verify(slot, proxyAuthorization);
265
+ }
266
+ catch {
267
+ throw new Error('The server could not verify this agent connection. Reconnect or check its status in the app.');
268
+ }
269
+ }
270
+ this.verified.add(verificationKey);
271
+ return slot;
272
+ });
273
+ }
274
+ async getLifecycleToken(key, operation, rejectedGeneration, proxyAuthorization) {
275
+ const { revocationRecovery } = await Promise.resolve().then(() => tslib_1.__importStar(require('./lifecycle')));
276
+ const slot = await this.store.readLifecycleSlot(key);
277
+ if (!slot ||
278
+ slot.grantUuid !== operation.grantUuid ||
279
+ slot.tokens.generation !== operation.generation)
280
+ throw new Error('The local connection changed. Run the command again.');
281
+ if (operation.kind === 'revoke') {
282
+ if (Date.parse(slot.tokens.accessTokenExpiresAt) <= Date.now() ||
283
+ Date.parse(slot.grantExpiresAt) <= Date.now())
284
+ throw new Error(revocationRecovery);
285
+ return slot;
286
+ }
287
+ (0, exports.assertUsableSlot)(slot, Date.now());
288
+ if (Date.parse(slot.tokens.accessTokenExpiresAt) > Date.now() &&
289
+ rejectedGeneration === null)
290
+ return slot;
291
+ return this.getToken(key, rejectedGeneration, proxyAuthorization);
292
+ }
293
+ }
294
+ exports.AgentTokenProvider = AgentTokenProvider;
@@ -0,0 +1,4 @@
1
+ import fetch, { type RequestInit } from 'node-fetch';
2
+ export declare const agentConnectionApiUrl: (instanceUrl: string, input: string) => string;
3
+ export declare const createAgentConnectionFetch: (instanceUrl: string) => (url: string, init: RequestInit) => Promise<fetch.Response>;
4
+ //# sourceMappingURL=apiUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiUrl.d.ts","sourceRoot":"","sources":["../../src/agentConnections/apiUrl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAGrD,eAAO,MAAM,qBAAqB,gBACjB,MAAM,SACZ,MAAM,KACd,MAkCF,CAAC;AACF,eAAO,MAAM,0BAA0B,gBACrB,MAAM,WAAW,MAAM,QAAQ,WAAW,4BACA,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAgentConnectionFetch = exports.agentConnectionApiUrl = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
6
+ const schema_1 = require("./schema");
7
+ const agentConnectionApiUrl = (instanceUrl, input) => {
8
+ const instance = (0, schema_1.canonicalInstanceUrl)(instanceUrl);
9
+ const base = new URL(instance);
10
+ const path = input.split(/[?#]/, 1)[0];
11
+ const invalid = () => new Error('Agent connection requests must stay within the selected instance API.');
12
+ if (input !== input.trim() || input.startsWith('//') || path.includes('\\'))
13
+ throw invalid();
14
+ let decodedPath = path;
15
+ while (/%[0-9a-f]{2}/i.test(decodedPath)) {
16
+ if (/%(?:2f|5c|2e)/i.test(decodedPath))
17
+ throw invalid();
18
+ try {
19
+ decodedPath = decodeURIComponent(decodedPath);
20
+ }
21
+ catch {
22
+ throw invalid();
23
+ }
24
+ }
25
+ const destination = new URL(/^[a-z][a-z0-9+.-]*:/i.test(input)
26
+ ? input
27
+ : `${instance}/${input.replace(/^\//, '')}`);
28
+ if (destination.origin !== base.origin ||
29
+ destination.username ||
30
+ destination.password ||
31
+ !destination.pathname.startsWith(`${base.pathname.replace(/\/$/, '')}/api/`))
32
+ throw invalid();
33
+ return destination.href;
34
+ };
35
+ exports.agentConnectionApiUrl = agentConnectionApiUrl;
36
+ const createAgentConnectionFetch = (instanceUrl) => (url, init) => (0, node_fetch_1.default)((0, exports.agentConnectionApiUrl)(instanceUrl, url), init);
37
+ exports.createAgentConnectionFetch = createAgentConnectionFetch;
@@ -0,0 +1,14 @@
1
+ import type { Command } from 'commander';
2
+ type CommandPolicy = {
3
+ disposition: 'admit' | 'local';
4
+ reason: string;
5
+ } | {
6
+ disposition: 'refuse';
7
+ reason: string;
8
+ message: string;
9
+ };
10
+ export declare const COMMAND_POLICY: Readonly<Record<string, CommandPolicy>>;
11
+ export declare const assertAgentCommandAllowed: (name: string, options?: Readonly<Record<string, unknown>>) => void;
12
+ export declare const agentCommandPath: (command: Command) => string;
13
+ export {};
14
+ //# sourceMappingURL=commandPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandPolicy.d.ts","sourceRoot":"","sources":["../../src/agentConnections/commandPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,KAAK,aAAa,GACZ;IAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,WAAW,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAcjE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CA6DlE,CAAC;AAqBF,eAAO,MAAM,yBAAyB,SAC5B,MAAM,YACH,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAC3C,IA6BF,CAAC;AAEF,eAAO,MAAM,gBAAgB,YAAa,OAAO,KAAG,MASnD,CAAC"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentCommandPath = exports.assertAgentCommandAllowed = exports.COMMAND_POLICY = void 0;
4
+ const credentials_1 = require("../credentials");
5
+ const local = (reason) => ({
6
+ disposition: 'local',
7
+ reason,
8
+ });
9
+ const admit = (reason) => ({
10
+ disposition: 'admit',
11
+ reason,
12
+ });
13
+ const refuse = (reason, message = credentials_1.agentCommandUnavailable) => ({ disposition: 'refuse', reason, message });
14
+ exports.COMMAND_POLICY = {
15
+ 'upgrade-check': local('Public release index only'),
16
+ 'connect-snowflake': refuse('Separate warehouse credential deposit'),
17
+ login: refuse('Personal bootstrap', credentials_1.personalLoginRequired),
18
+ agent: local('Command group'),
19
+ 'agent connect': refuse('Personal bootstrap', credentials_1.personalLoginRequired),
20
+ 'agent status': admit('Own connection status'),
21
+ 'agent revoke': admit('Own connection revocation'),
22
+ config: local('Command group'),
23
+ 'config set-project': refuse('Saves a personal default; pass --project on the command'),
24
+ 'config set-source': refuse('Saves a personal default; pass --project on the command'),
25
+ 'config list-projects': refuse('Organisation-wide discovery'),
26
+ 'config get-project': admit('Read-only configuration with credential validation'),
27
+ 'config rename-project': refuse('Project lifecycle'),
28
+ 'config unset-project': refuse('Changes personal defaults'),
29
+ dbt: local('Command group'),
30
+ 'dbt run': local('Local dbt execution'),
31
+ compile: local('Local compilation with selected optional server reads'),
32
+ preview: refuse('Project lifecycle'),
33
+ 'start-preview': refuse('Project lifecycle'),
34
+ 'stop-preview': refuse('Project lifecycle'),
35
+ download: admit('Project content reads; organisation and shared skills modes refused'),
36
+ upload: refuse('Complete bundle preflight is not yet supported; refuse before any write'),
37
+ apps: local('Command group'),
38
+ 'apps create': refuse('Unsupported authoring context'),
39
+ 'apps preview': refuse('Unsupported arbitrary API proxy'),
40
+ 'apps validate': local('Local validation with selected optional catalogue reads'),
41
+ 'apps build': local('Local build'),
42
+ deploy: admit('Existing approved project without source or target overrides'),
43
+ refresh: refuse('Required global job polling is unavailable'),
44
+ validate: refuse('Server validation and global job polling are unavailable'),
45
+ generate: local('Local model generation'),
46
+ rename: refuse('Queued rename is unavailable'),
47
+ 'slug-update': refuse('Slug rename is unavailable'),
48
+ 'generate-exposures': admit('Approved project exposures'),
49
+ diagnostics: refuse('Personal credential diagnostics'),
50
+ lint: local('Local lint'),
51
+ 'warehouse-catalog': admit('Approved project catalogue'),
52
+ 'pre-aggregate-audit': refuse('Server audit is unavailable'),
53
+ 'pre-aggregate-check-external': local('Local warehouse check'),
54
+ sql: admit('Approved project SQL query'),
55
+ 'run-chart': admit('Approved project metric query'),
56
+ 'set-warehouse': refuse('Warehouse credential configuration'),
57
+ 'export-chart-image': refuse('Public image export is unavailable'),
58
+ 'install-skills': local('Public GitHub assets only'),
59
+ };
60
+ const refusedModes = {
61
+ deploy: ['create', 'source', 'target', 'organizationCredentials'],
62
+ download: ['organization', 'skills', 'includeSkills', 'includeAll'],
63
+ upload: [
64
+ 'validate',
65
+ 'organization',
66
+ 'skills',
67
+ 'deleteSkills',
68
+ 'apps',
69
+ 'includeApps',
70
+ 'appsOnly',
71
+ 'chartTypes',
72
+ 'includeChartTypes',
73
+ 'chartTypesOnly',
74
+ 'spacesOnly',
75
+ 'public',
76
+ ],
77
+ };
78
+ const assertAgentCommandAllowed = (name, options = {}) => {
79
+ if ((0, credentials_1.getAgentConnectionSelection)() === null)
80
+ return;
81
+ const mode = refusedModes[name]?.find((option) => {
82
+ const value = options[option];
83
+ return Array.isArray(value)
84
+ ? value.length > 0
85
+ : value !== undefined && value !== null && value !== false;
86
+ });
87
+ if (mode) {
88
+ const flag = mode.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
89
+ throw new Error(`${credentials_1.agentCommandUnavailable}\n--${flag} is not available with an agent connection.`);
90
+ }
91
+ const policy = Object.prototype.hasOwnProperty.call(exports.COMMAND_POLICY, name)
92
+ ? exports.COMMAND_POLICY[name]
93
+ : undefined;
94
+ if (!policy || policy.disposition === 'refuse') {
95
+ const advice = name === 'config set-project' || name === 'config set-source'
96
+ ? '\nPass --project on the command instead of saving a personal default.'
97
+ : '';
98
+ throw new Error(`${policy?.disposition === 'refuse' ? policy.message : credentials_1.agentCommandUnavailable}${advice}`);
99
+ }
100
+ };
101
+ exports.assertAgentCommandAllowed = assertAgentCommandAllowed;
102
+ const agentCommandPath = (command) => {
103
+ const parts = [];
104
+ for (let current = command; current.parent; current = current.parent)
105
+ parts.unshift(current.name());
106
+ return parts.join(' ');
107
+ };
108
+ exports.agentCommandPath = agentCommandPath;
@@ -0,0 +1,12 @@
1
+ export declare const isFsError: (error: unknown, code: string) => boolean;
2
+ export declare const privateDirectory: (directory: string) => Promise<void>;
3
+ export declare const readPrivateFile: (file: string) => Promise<string | null>;
4
+ export declare const acquireLock: (file: string, options?: {
5
+ waitMs: number;
6
+ pollMs: number;
7
+ }) => Promise<() => Promise<void>>;
8
+ export declare const atomicWrite: (file: string, contents: string, commit?: {
9
+ beforeCommit?: () => void;
10
+ onCommitted?: () => void;
11
+ }) => Promise<void>;
12
+ //# sourceMappingURL=configLock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configLock.d.ts","sourceRoot":"","sources":["../../src/agentConnections/configLock.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,SAAS,UAAW,OAAO,QAAQ,MAAM,KAAG,OACW,CAAC;AAErE,eAAO,MAAM,gBAAgB,cAAqB,MAAM,KAAG,OAAO,CAAC,IAAI,CAGtE,CAAC;AAEF,eAAO,MAAM,eAAe,SAAgB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAQzE,CAAC;AAoFF,eAAO,MAAM,WAAW,SACd,MAAM,YACH;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KAI5C,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CA+C7B,CAAC;AAEF,eAAO,MAAM,WAAW,SACd,MAAM,YACF,MAAM,WACR;IAAE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,KAChE,OAAO,CAAC,IAAI,CA4Bd,CAAC"}