@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.
- package/dist/.tsbuildinfo.build +1 -1
- package/dist/agentConnectionConnectOption.d.ts +8 -0
- package/dist/agentConnectionConnectOption.d.ts.map +1 -0
- package/dist/agentConnectionConnectOption.js +39 -0
- package/dist/agentConnectionOption.d.ts +3 -0
- package/dist/agentConnectionOption.d.ts.map +1 -0
- package/dist/agentConnectionOption.js +48 -0
- package/dist/agentConnections/agentTokenProvider.d.ts +13 -0
- package/dist/agentConnections/agentTokenProvider.d.ts.map +1 -0
- package/dist/agentConnections/agentTokenProvider.js +294 -0
- package/dist/agentConnections/apiUrl.d.ts +4 -0
- package/dist/agentConnections/apiUrl.d.ts.map +1 -0
- package/dist/agentConnections/apiUrl.js +37 -0
- package/dist/agentConnections/commandPolicy.d.ts +14 -0
- package/dist/agentConnections/commandPolicy.d.ts.map +1 -0
- package/dist/agentConnections/commandPolicy.js +108 -0
- package/dist/agentConnections/configLock.d.ts +12 -0
- package/dist/agentConnections/configLock.d.ts.map +1 -0
- package/dist/agentConnections/configLock.js +191 -0
- package/dist/agentConnections/connectInputs.d.ts +20 -0
- package/dist/agentConnections/connectInputs.d.ts.map +1 -0
- package/dist/agentConnections/connectInputs.js +49 -0
- package/dist/agentConnections/fixtures.d.ts +3 -0
- package/dist/agentConnections/fixtures.d.ts.map +1 -0
- package/dist/agentConnections/fixtures.js +36 -0
- package/dist/agentConnections/initialToken.d.ts +26 -0
- package/dist/agentConnections/initialToken.d.ts.map +1 -0
- package/dist/agentConnections/initialToken.js +93 -0
- package/dist/agentConnections/lifecycle.d.ts +17 -0
- package/dist/agentConnections/lifecycle.d.ts.map +1 -0
- package/dist/agentConnections/lifecycle.js +44 -0
- package/dist/agentConnections/personalRecovery.d.ts +27 -0
- package/dist/agentConnections/personalRecovery.d.ts.map +1 -0
- package/dist/agentConnections/personalRecovery.js +71 -0
- package/dist/agentConnections/projectSelection.d.ts +4 -0
- package/dist/agentConnections/projectSelection.d.ts.map +1 -0
- package/dist/agentConnections/projectSelection.js +48 -0
- package/dist/agentConnections/providerProcess.fixture.d.ts +2 -0
- package/dist/agentConnections/providerProcess.fixture.d.ts.map +1 -0
- package/dist/agentConnections/providerProcess.fixture.js +19 -0
- package/dist/agentConnections/requestPolicy.d.ts +11 -0
- package/dist/agentConnections/requestPolicy.d.ts.map +1 -0
- package/dist/agentConnections/requestPolicy.js +76 -0
- package/dist/agentConnections/schema.d.ts +66 -0
- package/dist/agentConnections/schema.d.ts.map +1 -0
- package/dist/agentConnections/schema.js +184 -0
- package/dist/agentConnections/statusResponse.d.ts +10 -0
- package/dist/agentConnections/statusResponse.d.ts.map +1 -0
- package/dist/agentConnections/statusResponse.js +146 -0
- package/dist/agentConnections/store.d.ts +36 -0
- package/dist/agentConnections/store.d.ts.map +1 -0
- package/dist/agentConnections/store.js +275 -0
- package/dist/agentConnections/warehouseStatus.d.ts +30 -0
- package/dist/agentConnections/warehouseStatus.d.ts.map +1 -0
- package/dist/agentConnections/warehouseStatus.js +184 -0
- package/dist/analytics/analytics.d.ts.map +1 -1
- package/dist/analytics/analytics.js +4 -0
- package/dist/config.d.ts +3 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/credentials.d.ts +27 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +161 -0
- package/dist/dbt/models.d.ts.map +1 -1
- package/dist/dbt/models.js +45 -14
- package/dist/handlers/agentAccess.d.ts.map +1 -1
- package/dist/handlers/agentAccess.js +9 -1
- package/dist/handlers/agentConnect.d.ts.map +1 -1
- package/dist/handlers/agentConnect.js +2 -0
- package/dist/handlers/agentConnectionConnect.d.ts +5 -0
- package/dist/handlers/agentConnectionConnect.d.ts.map +1 -0
- package/dist/handlers/agentConnectionConnect.js +371 -0
- package/dist/handlers/agentConnectionStatus.d.ts +7 -0
- package/dist/handlers/agentConnectionStatus.d.ts.map +1 -0
- package/dist/handlers/agentConnectionStatus.js +228 -0
- package/dist/handlers/agentRevoke.d.ts +7 -0
- package/dist/handlers/agentRevoke.d.ts.map +1 -0
- package/dist/handlers/agentRevoke.js +144 -0
- package/dist/handlers/agentStatus.d.ts +4 -1
- package/dist/handlers/agentStatus.d.ts.map +1 -1
- package/dist/handlers/agentStatus.js +7 -0
- package/dist/handlers/apps/createApp.d.ts.map +1 -1
- package/dist/handlers/apps/createApp.js +2 -0
- package/dist/handlers/apps/preview.d.ts.map +1 -1
- package/dist/handlers/apps/preview.js +6 -0
- package/dist/handlers/apps/previewProxy.d.ts.map +1 -1
- package/dist/handlers/apps/previewProxy.js +2 -0
- package/dist/handlers/apps/validate.d.ts.map +1 -1
- package/dist/handlers/apps/validate.js +20 -8
- package/dist/handlers/compile.d.ts.map +1 -1
- package/dist/handlers/compile.js +3 -0
- package/dist/handlers/connectSnowflake.d.ts.map +1 -1
- package/dist/handlers/connectSnowflake.js +2 -0
- package/dist/handlers/createProject.d.ts.map +1 -1
- package/dist/handlers/createProject.js +2 -0
- package/dist/handlers/dbt/apiClient.d.ts +4 -2
- package/dist/handlers/dbt/apiClient.d.ts.map +1 -1
- package/dist/handlers/dbt/apiClient.js +138 -4
- package/dist/handlers/dbt/compile.d.ts.map +1 -1
- package/dist/handlers/dbt/compile.js +13 -2
- package/dist/handlers/dbt/getDbtProfileTargetName.d.ts.map +1 -1
- package/dist/handlers/dbt/getDbtProfileTargetName.js +12 -4
- package/dist/handlers/dbt/getDbtVersion.d.ts.map +1 -1
- package/dist/handlers/dbt/getDbtVersion.js +12 -4
- package/dist/handlers/dbt/getWarehouseClient.d.ts.map +1 -1
- package/dist/handlers/dbt/getWarehouseClient.js +15 -5
- package/dist/handlers/dbt/refresh.d.ts.map +1 -1
- package/dist/handlers/dbt/refresh.js +2 -0
- package/dist/handlers/dbt/run.d.ts.map +1 -1
- package/dist/handlers/dbt/run.js +10 -3
- package/dist/handlers/deploy.d.ts.map +1 -1
- package/dist/handlers/deploy.js +17 -1
- package/dist/handlers/diagnostics.d.ts.map +1 -1
- package/dist/handlers/diagnostics.js +2 -0
- package/dist/handlers/download.d.ts.map +1 -1
- package/dist/handlers/download.js +19 -5
- package/dist/handlers/exportChartImage.d.ts.map +1 -1
- package/dist/handlers/exportChartImage.js +2 -0
- package/dist/handlers/generateExposures.d.ts.map +1 -1
- package/dist/handlers/generateExposures.js +4 -1
- package/dist/handlers/getProject.d.ts.map +1 -1
- package/dist/handlers/getProject.js +25 -1
- package/dist/handlers/listProjects.d.ts.map +1 -1
- package/dist/handlers/listProjects.js +2 -0
- package/dist/handlers/login/connectionBrowser.d.ts +2 -0
- package/dist/handlers/login/connectionBrowser.d.ts.map +1 -0
- package/dist/handlers/login/connectionBrowser.js +22 -0
- package/dist/handlers/login/loopback.d.ts +15 -0
- package/dist/handlers/login/loopback.d.ts.map +1 -0
- package/dist/handlers/login/loopback.js +135 -0
- package/dist/handlers/login/oauth.d.ts.map +1 -1
- package/dist/handlers/login/oauth.js +16 -3
- package/dist/handlers/login/pat.d.ts.map +1 -1
- package/dist/handlers/login/pat.js +2 -0
- package/dist/handlers/login.d.ts.map +1 -1
- package/dist/handlers/login.js +8 -1
- package/dist/handlers/oauthLogin.d.ts.map +1 -1
- package/dist/handlers/oauthLogin.js +2 -0
- package/dist/handlers/ordinary/harness.mocks.d.ts +42 -0
- package/dist/handlers/ordinary/harness.mocks.d.ts.map +1 -0
- package/dist/handlers/ordinary/harness.mocks.js +180 -0
- package/dist/handlers/preAggregateAudit.d.ts.map +1 -1
- package/dist/handlers/preAggregateAudit.js +2 -0
- package/dist/handlers/preview.d.ts.map +1 -1
- package/dist/handlers/preview.js +4 -0
- package/dist/handlers/renameHandler.d.ts.map +1 -1
- package/dist/handlers/renameHandler.js +2 -0
- package/dist/handlers/renameProject.d.ts.map +1 -1
- package/dist/handlers/renameProject.js +2 -0
- package/dist/handlers/resolveProjectFlag.d.ts.map +1 -1
- package/dist/handlers/resolveProjectFlag.js +5 -0
- package/dist/handlers/runChart.d.ts.map +1 -1
- package/dist/handlers/runChart.js +7 -2
- package/dist/handlers/selectProject.d.ts.map +1 -1
- package/dist/handlers/selectProject.js +5 -0
- package/dist/handlers/setProject.d.ts.map +1 -1
- package/dist/handlers/setProject.js +5 -0
- package/dist/handlers/setWarehouse.d.ts.map +1 -1
- package/dist/handlers/setWarehouse.js +2 -0
- package/dist/handlers/slugUpdate.d.ts.map +1 -1
- package/dist/handlers/slugUpdate.js +2 -0
- package/dist/handlers/sourceSelection.d.ts.map +1 -1
- package/dist/handlers/sourceSelection.js +13 -2
- package/dist/handlers/sql.d.ts.map +1 -1
- package/dist/handlers/sql.js +7 -2
- package/dist/handlers/utils.d.ts +3 -0
- package/dist/handlers/utils.d.ts.map +1 -1
- package/dist/handlers/utils.js +39 -0
- package/dist/handlers/validate.d.ts.map +1 -1
- package/dist/handlers/validate.js +2 -0
- package/dist/handlers/warehouseCatalog.d.ts.map +1 -1
- package/dist/handlers/warehouseCatalog.js +7 -2
- package/dist/index.js +43 -3
- package/dist/lightdash-config/index.d.ts.map +1 -1
- package/dist/lightdash-config/index.js +4 -1
- package/package.json +3 -3
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentConnectionConnectHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@lightdash/common");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const uuid_1 = require("uuid");
|
|
8
|
+
const apiUrl_1 = require("../agentConnections/apiUrl");
|
|
9
|
+
const connectInputs_1 = require("../agentConnections/connectInputs");
|
|
10
|
+
const initialToken_1 = require("../agentConnections/initialToken");
|
|
11
|
+
const lifecycle_1 = require("../agentConnections/lifecycle");
|
|
12
|
+
const personalRecovery_1 = require("../agentConnections/personalRecovery");
|
|
13
|
+
const schema_1 = require("../agentConnections/schema");
|
|
14
|
+
const statusResponse_1 = require("../agentConnections/statusResponse");
|
|
15
|
+
const store_1 = require("../agentConnections/store");
|
|
16
|
+
const warehouseStatus_1 = require("../agentConnections/warehouseStatus");
|
|
17
|
+
const config_1 = require("../config");
|
|
18
|
+
const credentials_1 = require("../credentials");
|
|
19
|
+
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
20
|
+
const connectionBrowser_1 = require("./login/connectionBrowser");
|
|
21
|
+
const loopback_1 = require("./login/loopback");
|
|
22
|
+
const couldNotStart = 'Could not start agent connection approval. No credential was saved. Try again when the server is available.';
|
|
23
|
+
const existingConnection = 'The server returned the existing agent connection instead of a new one. No credential was saved.';
|
|
24
|
+
const unchanged = 'No agent connection credential was saved. Your personal CLI login is unchanged.';
|
|
25
|
+
class ApprovalStartError extends Error {
|
|
26
|
+
}
|
|
27
|
+
const requestResults = async (response) => {
|
|
28
|
+
if ([404, 405].includes(response.status))
|
|
29
|
+
throw new ApprovalStartError('Agent connections are not available on this server yet.');
|
|
30
|
+
if (response.status === 401)
|
|
31
|
+
throw new ApprovalStartError('Your personal CLI login was rejected. Run lightdash login for this instance, then try again.');
|
|
32
|
+
let envelope;
|
|
33
|
+
try {
|
|
34
|
+
const value = await response.json();
|
|
35
|
+
envelope = (0, statusResponse_1.responseRecord)(value);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new ApprovalStartError(couldNotStart);
|
|
39
|
+
}
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
const error = typeof envelope.error === 'object' && envelope.error !== null
|
|
42
|
+
? (0, statusResponse_1.responseRecord)(envelope.error)
|
|
43
|
+
: {};
|
|
44
|
+
if (error.code === 'agent_connections_unavailable')
|
|
45
|
+
throw new ApprovalStartError('Agent connections are not available on this server yet.');
|
|
46
|
+
if (error.code === 'agent_identity_disabled')
|
|
47
|
+
throw new ApprovalStartError('Agent connections are disabled on this server. Ask an administrator to enable agent identity.');
|
|
48
|
+
if (response.status === 403)
|
|
49
|
+
throw new ApprovalStartError('The server refused agent connection approval. Check your permissions with an administrator.');
|
|
50
|
+
throw new ApprovalStartError(couldNotStart);
|
|
51
|
+
}
|
|
52
|
+
if (envelope.status !== 'ok')
|
|
53
|
+
throw new ApprovalStartError(couldNotStart);
|
|
54
|
+
return (0, statusResponse_1.responseRecord)(envelope.results);
|
|
55
|
+
};
|
|
56
|
+
const personIdentity = (person) => {
|
|
57
|
+
if (typeof person.userUuid !== 'string' ||
|
|
58
|
+
!(0, uuid_1.validate)(person.userUuid) ||
|
|
59
|
+
typeof person.organizationUuid !== 'string' ||
|
|
60
|
+
!(0, uuid_1.validate)(person.organizationUuid) ||
|
|
61
|
+
person.isServiceAccount === true)
|
|
62
|
+
throw new initialToken_1.ConnectionProofError();
|
|
63
|
+
return {
|
|
64
|
+
personUuid: person.userUuid,
|
|
65
|
+
organizationUuid: person.organizationUuid,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const proofResults = async (response) => {
|
|
69
|
+
if (!response.ok)
|
|
70
|
+
throw new initialToken_1.ConnectionProofError();
|
|
71
|
+
const value = await response.json();
|
|
72
|
+
const envelope = (0, statusResponse_1.responseRecord)(value);
|
|
73
|
+
if (envelope.status !== 'ok')
|
|
74
|
+
throw new initialToken_1.ConnectionProofError();
|
|
75
|
+
return (0, statusResponse_1.responseRecord)(envelope.results);
|
|
76
|
+
};
|
|
77
|
+
const agentConnectionConnectHandler = async (options, dependencies = {
|
|
78
|
+
launchBrowser: connectionBrowser_1.openConnectionBrowser,
|
|
79
|
+
}) => {
|
|
80
|
+
(0, credentials_1.refuseAgentConnection)(true);
|
|
81
|
+
const input = (0, connectInputs_1.connectionConnectInputs)(options);
|
|
82
|
+
const deadline = Date.now() + input.timeout * 1000;
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const timer = setTimeout(() => controller.abort(), input.timeout * 1000);
|
|
85
|
+
const cancel = () => controller.abort();
|
|
86
|
+
process.once('SIGINT', cancel);
|
|
87
|
+
process.once('SIGTERM', cancel);
|
|
88
|
+
let listener = null;
|
|
89
|
+
let spinner = null;
|
|
90
|
+
let countdown = null;
|
|
91
|
+
const stopWaiting = () => {
|
|
92
|
+
spinner?.stop();
|
|
93
|
+
if (countdown !== null)
|
|
94
|
+
clearInterval(countdown);
|
|
95
|
+
spinner = null;
|
|
96
|
+
countdown = null;
|
|
97
|
+
};
|
|
98
|
+
let phase = 'start';
|
|
99
|
+
let instance = null;
|
|
100
|
+
let saved = false;
|
|
101
|
+
const secrets = [];
|
|
102
|
+
const safeText = (value) => (0, statusResponse_1.publicTextIsSafe)(value, secrets) ? value : 'Name unavailable';
|
|
103
|
+
try {
|
|
104
|
+
const config = await (0, config_1.getConfig)({ readOnly: true });
|
|
105
|
+
const bootstrap = (0, personalRecovery_1.capturePersonalBootstrap)(config);
|
|
106
|
+
instance = bootstrap.instanceUrl;
|
|
107
|
+
secrets.push(...bootstrap.publicSecrets);
|
|
108
|
+
const { signal } = controller;
|
|
109
|
+
const person = personIdentity(await requestResults(await bootstrap.fetch({
|
|
110
|
+
method: 'GET',
|
|
111
|
+
url: '/api/v1/user',
|
|
112
|
+
body: undefined,
|
|
113
|
+
signal,
|
|
114
|
+
})));
|
|
115
|
+
const key = { ...person, instanceUrl: instance, name: input.name };
|
|
116
|
+
const store = new store_1.AgentConnectionStore();
|
|
117
|
+
const previous = await store.readSlot(key);
|
|
118
|
+
if (previous)
|
|
119
|
+
console.error(`Replacing agent connection slot ${safeText(input.name)}. The old credential stays until the new approval is verified. The old connection will not be revoked.`);
|
|
120
|
+
const state = (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
121
|
+
const verifier = (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
122
|
+
const codeChallenge = (0, crypto_1.createHash)('sha256')
|
|
123
|
+
.update(verifier)
|
|
124
|
+
.digest('base64url');
|
|
125
|
+
listener = await (0, loopback_1.createConnectionLoopback)({
|
|
126
|
+
port: input.port,
|
|
127
|
+
state,
|
|
128
|
+
issuer: instance,
|
|
129
|
+
signal,
|
|
130
|
+
});
|
|
131
|
+
const approval = await requestResults(await bootstrap.fetch({
|
|
132
|
+
method: 'POST',
|
|
133
|
+
url: '/api/v2/user/agent-connections/requests',
|
|
134
|
+
signal,
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
clientId: 'lightdash-cli',
|
|
137
|
+
name: input.name,
|
|
138
|
+
projects: input.projects,
|
|
139
|
+
capabilities: input.capabilities,
|
|
140
|
+
expiresInSeconds: input.expiresInSeconds,
|
|
141
|
+
redirectUri: listener.redirectUri,
|
|
142
|
+
state,
|
|
143
|
+
codeChallenge,
|
|
144
|
+
codeChallengeMethod: 'S256',
|
|
145
|
+
resource: instance,
|
|
146
|
+
}),
|
|
147
|
+
}));
|
|
148
|
+
if (typeof approval.authorizationUrl !== 'string' ||
|
|
149
|
+
!bootstrap.publicTextIsSafe(approval.authorizationUrl) ||
|
|
150
|
+
!/^https?:\/\//.test(approval.authorizationUrl) ||
|
|
151
|
+
approval.issuer !== instance ||
|
|
152
|
+
approval.resource !== instance ||
|
|
153
|
+
typeof approval.expiresAt !== 'string' ||
|
|
154
|
+
!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(approval.expiresAt) ||
|
|
155
|
+
!Number.isFinite(Date.parse(approval.expiresAt)) ||
|
|
156
|
+
Date.parse(approval.expiresAt) <= Date.now())
|
|
157
|
+
throw new ApprovalStartError(couldNotStart);
|
|
158
|
+
const approvalDeadline = Date.parse(approval.expiresAt);
|
|
159
|
+
const authorizationUrl = (0, apiUrl_1.agentConnectionApiUrl)(instance, approval.authorizationUrl);
|
|
160
|
+
const destination = new URL(authorizationUrl);
|
|
161
|
+
if (!bootstrap.publicTextIsSafe(authorizationUrl) ||
|
|
162
|
+
destination.pathname !==
|
|
163
|
+
new URL(`${instance}/api/v1/oauth/authorize`).pathname ||
|
|
164
|
+
destination.hash)
|
|
165
|
+
throw new ApprovalStartError(couldNotStart);
|
|
166
|
+
console.error(`Open this page to approve the agent connection:\n${authorizationUrl}`);
|
|
167
|
+
console.error('Waiting for browser approval and warehouse setup...');
|
|
168
|
+
phase = 'callback';
|
|
169
|
+
if (process.stderr.isTTY && !globalState_1.default.isNonInteractive()) {
|
|
170
|
+
const waitingText = () => {
|
|
171
|
+
const seconds = Math.max(0, Math.ceil((Math.min(deadline, approvalDeadline) - Date.now()) /
|
|
172
|
+
1000));
|
|
173
|
+
return `Waiting for browser approval… (${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')} remaining)`;
|
|
174
|
+
};
|
|
175
|
+
spinner = globalState_1.default.startSpinner({
|
|
176
|
+
text: waitingText(),
|
|
177
|
+
stream: process.stderr,
|
|
178
|
+
isEnabled: true,
|
|
179
|
+
});
|
|
180
|
+
countdown = setInterval(() => {
|
|
181
|
+
if (spinner !== null)
|
|
182
|
+
spinner.text = waitingText();
|
|
183
|
+
}, 1000);
|
|
184
|
+
}
|
|
185
|
+
if (!globalState_1.default.isNonInteractive())
|
|
186
|
+
await dependencies
|
|
187
|
+
.launchBrowser(authorizationUrl)
|
|
188
|
+
.catch(() => false);
|
|
189
|
+
const callback = await listener.result;
|
|
190
|
+
stopWaiting();
|
|
191
|
+
if (approvalDeadline <= Date.now()) {
|
|
192
|
+
controller.abort();
|
|
193
|
+
throw new Error('The approval transaction expired.');
|
|
194
|
+
}
|
|
195
|
+
phase = 'proof';
|
|
196
|
+
const exchanged = await (0, initialToken_1.exchangeInitialConnectionToken)({
|
|
197
|
+
instanceUrl: instance,
|
|
198
|
+
...callback,
|
|
199
|
+
verifier,
|
|
200
|
+
redirectUri: listener.redirectUri,
|
|
201
|
+
signal,
|
|
202
|
+
proxyAuthorization: bootstrap.proxyAuthorization,
|
|
203
|
+
});
|
|
204
|
+
secrets.push(exchanged.tokens.accessToken, exchanged.tokens.refreshToken);
|
|
205
|
+
if (previous?.grantUuid === exchanged.grantUuid)
|
|
206
|
+
throw new initialToken_1.ConnectionProofError(existingConnection);
|
|
207
|
+
const fetch = (0, apiUrl_1.createAgentConnectionFetch)(instance);
|
|
208
|
+
const headers = {
|
|
209
|
+
Authorization: `Bearer ${exchanged.tokens.accessToken}`,
|
|
210
|
+
...(bootstrap.proxyAuthorization === null
|
|
211
|
+
? {}
|
|
212
|
+
: { 'Proxy-Authorization': bootstrap.proxyAuthorization }),
|
|
213
|
+
};
|
|
214
|
+
const statusResponse = await fetch(`/api/v2/user/agent-connections/${exchanged.grantUuid}/status`, { headers, signal, redirect: 'error' });
|
|
215
|
+
if (!statusResponse.ok)
|
|
216
|
+
throw new initialToken_1.ConnectionProofError();
|
|
217
|
+
const statusValue = await statusResponse.json();
|
|
218
|
+
const statusEnvelope = (0, statusResponse_1.responseRecord)(statusValue);
|
|
219
|
+
const status = (0, statusResponse_1.responseRecord)(statusEnvelope.results);
|
|
220
|
+
const grantDeadline = typeof status.expiresAt === 'string'
|
|
221
|
+
? Date.parse(status.expiresAt)
|
|
222
|
+
: NaN;
|
|
223
|
+
if (!Number.isFinite(grantDeadline))
|
|
224
|
+
throw new initialToken_1.ConnectionProofError();
|
|
225
|
+
const clipDeadline = (value) => new Date(Math.min(Date.parse(value), grantDeadline)).toISOString();
|
|
226
|
+
const slot = (0, schema_1.validateSlot)({
|
|
227
|
+
...key,
|
|
228
|
+
...exchanged,
|
|
229
|
+
tokens: {
|
|
230
|
+
...exchanged.tokens,
|
|
231
|
+
accessTokenExpiresAt: clipDeadline(exchanged.tokens.accessTokenExpiresAt),
|
|
232
|
+
refreshTokenExpiresAt: exchanged.tokens.refreshTokenExpiresAt === null
|
|
233
|
+
? null
|
|
234
|
+
: clipDeadline(exchanged.tokens.refreshTokenExpiresAt),
|
|
235
|
+
},
|
|
236
|
+
clientId: 'lightdash-cli',
|
|
237
|
+
resource: instance,
|
|
238
|
+
approvedAt: status.approvedAt,
|
|
239
|
+
grantExpiresAt: status.expiresAt,
|
|
240
|
+
lastReported: {
|
|
241
|
+
checkedAt: new Date().toISOString(),
|
|
242
|
+
limitsStatus: status.limitsStatus,
|
|
243
|
+
approvedProjects: status.approvedProjects,
|
|
244
|
+
effectiveProjects: status.effectiveProjects,
|
|
245
|
+
status: status.status,
|
|
246
|
+
lastUsedAt: status.lastUsedAt,
|
|
247
|
+
revokedAt: status.revokedAt,
|
|
248
|
+
},
|
|
249
|
+
refreshState: { kind: 'idle' },
|
|
250
|
+
revokeState: { kind: 'none' },
|
|
251
|
+
}, key);
|
|
252
|
+
const verified = (0, statusResponse_1.connectionResponse)(statusValue, slot, true, secrets);
|
|
253
|
+
if (verified.lastReported.status !== 'active' ||
|
|
254
|
+
verified.lastReported.limitsStatus !== 'ok' ||
|
|
255
|
+
verified.lastReported.revokedAt !== null ||
|
|
256
|
+
Date.parse(verified.grantExpiresAt) <= Date.now() ||
|
|
257
|
+
Date.parse(verified.grantExpiresAt) >
|
|
258
|
+
Date.parse(verified.approvedAt) +
|
|
259
|
+
input.expiresInSeconds * 1000 ||
|
|
260
|
+
Date.parse(verified.approvedAt) > Date.now() ||
|
|
261
|
+
verified.lastReported.approvedProjects.some((project) => project.approvedCapabilities.some((capability) => !input.capabilities.includes(capability))))
|
|
262
|
+
throw new initialToken_1.ConnectionProofError();
|
|
263
|
+
if (input.projects !== null) {
|
|
264
|
+
const requestedProjects = input.projects;
|
|
265
|
+
const projectMatches = await Promise.all(verified.lastReported.approvedProjects.map(async (project) => {
|
|
266
|
+
if (requestedProjects.includes(project.projectUuid))
|
|
267
|
+
return project.projectUuid;
|
|
268
|
+
const details = await proofResults(await fetch(`/api/v1/projects/${project.projectUuid}`, {
|
|
269
|
+
headers,
|
|
270
|
+
signal,
|
|
271
|
+
redirect: 'error',
|
|
272
|
+
}));
|
|
273
|
+
if (details.projectUuid !== project.projectUuid ||
|
|
274
|
+
typeof details.slug !== 'string' ||
|
|
275
|
+
!requestedProjects.includes(details.slug))
|
|
276
|
+
throw new initialToken_1.ConnectionProofError();
|
|
277
|
+
return details.slug;
|
|
278
|
+
}));
|
|
279
|
+
if (new Set(projectMatches).size !== projectMatches.length)
|
|
280
|
+
throw new initialToken_1.ConnectionProofError();
|
|
281
|
+
}
|
|
282
|
+
const subject = personIdentity(await proofResults(await fetch('/api/v1/user', {
|
|
283
|
+
headers,
|
|
284
|
+
signal,
|
|
285
|
+
redirect: 'error',
|
|
286
|
+
})));
|
|
287
|
+
if (subject.personUuid !== person.personUuid ||
|
|
288
|
+
subject.organizationUuid !== person.organizationUuid ||
|
|
289
|
+
signal.aborted)
|
|
290
|
+
throw new initialToken_1.ConnectionProofError();
|
|
291
|
+
phase = 'save';
|
|
292
|
+
const outcome = await store.saveSlot(verified, (current) => {
|
|
293
|
+
if (signal.aborted || deadline <= Date.now())
|
|
294
|
+
throw new initialToken_1.ConnectionProofError('Agent connection approval timed out or was cancelled. No credential was saved.');
|
|
295
|
+
if (Date.parse(verified.grantExpiresAt) <= Date.now())
|
|
296
|
+
throw new initialToken_1.ConnectionProofError('The agent connection expired. No credential was saved.');
|
|
297
|
+
if (Date.parse(verified.tokens.accessTokenExpiresAt) <=
|
|
298
|
+
Date.now() ||
|
|
299
|
+
(verified.tokens.refreshTokenExpiresAt !== null &&
|
|
300
|
+
Date.parse(verified.tokens.refreshTokenExpiresAt) <=
|
|
301
|
+
Date.now()))
|
|
302
|
+
throw new initialToken_1.ConnectionProofError('The agent connection token expired before it could be saved. No credential was saved.');
|
|
303
|
+
if (approvalDeadline <= Date.now())
|
|
304
|
+
throw new initialToken_1.ConnectionProofError('The approval transaction expired. No credential was saved.');
|
|
305
|
+
if (current?.grantUuid === verified.grantUuid)
|
|
306
|
+
throw new initialToken_1.ConnectionProofError(existingConnection);
|
|
307
|
+
});
|
|
308
|
+
saved = true;
|
|
309
|
+
console.error(`Agent connection saved: ${safeText(verified.name)}`);
|
|
310
|
+
if (outcome.cleanupIncomplete)
|
|
311
|
+
console.error(`The credential was saved, but local clean-up did not finish. Run lightdash agent status --name ${safeText(verified.name)} to check it.`);
|
|
312
|
+
console.error(`Projects: ${verified.lastReported.approvedProjects.map((project) => safeText(project.name ?? 'Name unavailable')).join(', ') || 'None'}`);
|
|
313
|
+
const actions = [
|
|
314
|
+
...new Set(verified.lastReported.approvedProjects.flatMap((project) => project.approvedCapabilities)),
|
|
315
|
+
];
|
|
316
|
+
console.error(`Approved actions: ${actions.map((capability) => common_1.AGENT_CAPABILITY_DEFINITIONS[capability].name).join(', ') || 'None'}`);
|
|
317
|
+
console.error(`Expires: ${safeText(verified.grantExpiresAt)}`);
|
|
318
|
+
const warehouse = await (0, warehouseStatus_1.readWarehouseStatus)(verified, async ({ url, method, signal: warehouseSignal }) => fetch(url, {
|
|
319
|
+
method,
|
|
320
|
+
headers,
|
|
321
|
+
signal: AbortSignal.any([
|
|
322
|
+
signal,
|
|
323
|
+
warehouseSignal ?? signal,
|
|
324
|
+
]),
|
|
325
|
+
redirect: 'error',
|
|
326
|
+
})).catch(() => null);
|
|
327
|
+
console.error(warehouse !== null && warehouse.state !== 'unknown'
|
|
328
|
+
? `Warehouse sign-ins: ${warehouse.state}`
|
|
329
|
+
: 'Warehouse sign-ins: unknown; review them in the app.');
|
|
330
|
+
if (warehouse?.projects.some((project) => project.source === 'server'))
|
|
331
|
+
console.error('Default warehouse connection checked; review additional connections in the app.');
|
|
332
|
+
console.error(`Use: lightdash --agent-connection ${safeText(verified.name)} <command>`);
|
|
333
|
+
console.error(`Settings: ${safeText((0, lifecycle_1.settingsUrl)(instance))}`);
|
|
334
|
+
process.exitCode = 0;
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
stopWaiting();
|
|
338
|
+
if (saved) {
|
|
339
|
+
process.exitCode = 0;
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
let message = couldNotStart;
|
|
343
|
+
if (error instanceof ApprovalStartError ||
|
|
344
|
+
error instanceof personalRecovery_1.PersonalRecoveryError ||
|
|
345
|
+
error instanceof initialToken_1.ConnectionProofError)
|
|
346
|
+
message = error.message;
|
|
347
|
+
else if (phase === 'proof')
|
|
348
|
+
message = initialToken_1.unboundConnection;
|
|
349
|
+
else if (phase === 'callback')
|
|
350
|
+
message = controller.signal.aborted
|
|
351
|
+
? 'Agent connection approval timed out or was cancelled. No credential was saved.'
|
|
352
|
+
: 'Agent connection approval was denied. No credential was saved.';
|
|
353
|
+
else if (phase === 'save')
|
|
354
|
+
message =
|
|
355
|
+
'Could not save the agent connection credential. Your personal CLI login is unchanged.';
|
|
356
|
+
console.error(safeText(message));
|
|
357
|
+
console.error(unchanged);
|
|
358
|
+
if (instance !== null)
|
|
359
|
+
console.error(`Manage warehouse agent sign-ins in the app:\n${safeText((0, lifecycle_1.settingsUrl)(instance))}`);
|
|
360
|
+
process.exitCode = 1;
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
stopWaiting();
|
|
364
|
+
controller.abort();
|
|
365
|
+
clearTimeout(timer);
|
|
366
|
+
process.removeListener('SIGINT', cancel);
|
|
367
|
+
process.removeListener('SIGTERM', cancel);
|
|
368
|
+
await listener?.close();
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
exports.agentConnectionConnectHandler = agentConnectionConnectHandler;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AgentOptions } from './agentAccess';
|
|
2
|
+
export type ConnectionStatusOptions = AgentOptions & {
|
|
3
|
+
name?: string;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const agentConnectionStatusHandler: (options: ConnectionStatusOptions) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=agentConnectionStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentConnectionStatus.d.ts","sourceRoot":"","sources":["../../src/handlers/agentConnectionStatus.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,MAAM,MAAM,uBAAuB,GAAG,YAAY,GAAG;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAyCF,eAAO,MAAM,4BAA4B,YAC5B,uBAAuB,KACjC,OAAO,CAAC,IAAI,CA6Od,CAAC"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentConnectionStatusHandler = void 0;
|
|
4
|
+
const common_1 = require("@lightdash/common");
|
|
5
|
+
const lifecycle_1 = require("../agentConnections/lifecycle");
|
|
6
|
+
const statusResponse_1 = require("../agentConnections/statusResponse");
|
|
7
|
+
const store_1 = require("../agentConnections/store");
|
|
8
|
+
const warehouseStatus_1 = require("../agentConnections/warehouseStatus");
|
|
9
|
+
const config_1 = require("../config");
|
|
10
|
+
const credentials_1 = require("../credentials");
|
|
11
|
+
const apiClient_1 = require("./dbt/apiClient");
|
|
12
|
+
const localState = (slot) => {
|
|
13
|
+
if (slot.lastReported.status === 'revoked' ||
|
|
14
|
+
slot.lastReported.revokedAt !== null)
|
|
15
|
+
return 'revoked';
|
|
16
|
+
if (Date.parse(slot.grantExpiresAt) <= Date.now() ||
|
|
17
|
+
slot.lastReported.status === 'expired')
|
|
18
|
+
return 'expired';
|
|
19
|
+
if (slot.lastReported.limitsStatus === 'invalid')
|
|
20
|
+
return 'invalid';
|
|
21
|
+
if (slot.refreshState.kind !== 'idle' || slot.revokeState.kind !== 'none')
|
|
22
|
+
return 'reconnect_required';
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
const messages = {
|
|
26
|
+
missing: 'No agent connection credential. Run lightdash agent connect.',
|
|
27
|
+
expired: 'This connection expired.',
|
|
28
|
+
revoked: 'This connection was revoked.',
|
|
29
|
+
invalid: 'Connection limits are invalid. Reconnect before using this slot.',
|
|
30
|
+
mismatched: 'This agent connection does not match this instance, organisation and person. No personal credential was used.',
|
|
31
|
+
reconnect_required: 'The connection refresh or revocation was not confirmed. Reconnect before using this slot.',
|
|
32
|
+
unreachable: 'Could not reach the server. Connection status is unconfirmed.',
|
|
33
|
+
rejected: 'The server rejected this connection credential. Reconnect or check its status in the app.',
|
|
34
|
+
};
|
|
35
|
+
const agentConnectionStatusHandler = async (options) => {
|
|
36
|
+
const selection = (0, lifecycle_1.selectLifecycleSlot)(options.name);
|
|
37
|
+
return (0, credentials_1.runWithAgentConnectionSelection)(selection, async () => {
|
|
38
|
+
const config = await (0, config_1.getConfig)({ readOnly: true });
|
|
39
|
+
const key = (0, lifecycle_1.connectionKey)(config, selection);
|
|
40
|
+
const store = new store_1.AgentConnectionStore();
|
|
41
|
+
let slot = null;
|
|
42
|
+
let state = key === null ? 'mismatched' : 'missing';
|
|
43
|
+
let source = 'local';
|
|
44
|
+
let warehouse = { state: 'unknown', projects: [] };
|
|
45
|
+
let error = null;
|
|
46
|
+
try {
|
|
47
|
+
slot = key === null ? null : await store.readSlot(key);
|
|
48
|
+
if (slot !== null) {
|
|
49
|
+
const local = localState(slot);
|
|
50
|
+
state = local ?? 'active';
|
|
51
|
+
if (local === null) {
|
|
52
|
+
const response = await (0, apiClient_1.lightdashRawApi)({
|
|
53
|
+
method: 'GET',
|
|
54
|
+
url: `/api/v2/user/agent-connections/${slot.grantUuid}/status`,
|
|
55
|
+
body: undefined,
|
|
56
|
+
safeToRetry: true,
|
|
57
|
+
signal: AbortSignal.timeout(15_000),
|
|
58
|
+
lifecycle: {
|
|
59
|
+
kind: 'status',
|
|
60
|
+
grantUuid: slot.grantUuid,
|
|
61
|
+
generation: slot.tokens.generation,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const value = await response.json();
|
|
65
|
+
const requestedSlot = slot;
|
|
66
|
+
const latest = await store.readSlot(slot);
|
|
67
|
+
if (!latest || latest.grantUuid !== slot.grantUuid)
|
|
68
|
+
throw new Error('The local connection changed.');
|
|
69
|
+
const checked = (0, statusResponse_1.connectionResponse)(value, latest, true);
|
|
70
|
+
await store.withLock(async (locked) => {
|
|
71
|
+
const current = await locked.readSlot(checked);
|
|
72
|
+
if (!(0, lifecycle_1.sameGeneration)(current, checked) ||
|
|
73
|
+
current.revokeState.kind !==
|
|
74
|
+
requestedSlot.revokeState.kind)
|
|
75
|
+
throw new Error('The local connection changed.');
|
|
76
|
+
await locked.saveSlot({
|
|
77
|
+
...current,
|
|
78
|
+
lastReported: checked.lastReported,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
slot = checked;
|
|
82
|
+
source = 'server';
|
|
83
|
+
state =
|
|
84
|
+
checked.lastReported.limitsStatus === 'invalid'
|
|
85
|
+
? 'invalid'
|
|
86
|
+
: checked.lastReported.status;
|
|
87
|
+
if (state === 'active')
|
|
88
|
+
warehouse = await (0, warehouseStatus_1.readWarehouseStatus)(checked);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (failure) {
|
|
93
|
+
state = 'invalid';
|
|
94
|
+
if (failure instanceof lifecycle_1.AgentConnectionRequestError)
|
|
95
|
+
state = failure.state;
|
|
96
|
+
else if (failure instanceof statusResponse_1.ConnectionMismatchError)
|
|
97
|
+
state = 'mismatched';
|
|
98
|
+
if (state === 'invalid')
|
|
99
|
+
error =
|
|
100
|
+
slot === null
|
|
101
|
+
? 'Invalid agent connection data. Reconnect before using this slot.'
|
|
102
|
+
: 'The server returned invalid connection data. Connection status is unconfirmed.';
|
|
103
|
+
if (slot !== null) {
|
|
104
|
+
const latest = await store.readSlot(slot).catch(() => null);
|
|
105
|
+
if (latest &&
|
|
106
|
+
(0, lifecycle_1.sameGeneration)(latest, slot) &&
|
|
107
|
+
localState(latest) === 'reconnect_required') {
|
|
108
|
+
slot = latest;
|
|
109
|
+
state = 'reconnect_required';
|
|
110
|
+
error = messages.reconnect_required;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (state !== 'active')
|
|
115
|
+
error ??= messages[state];
|
|
116
|
+
const reported = slot === null ? null : (0, statusResponse_1.publicConnectionReport)(slot);
|
|
117
|
+
let revokeState = null;
|
|
118
|
+
if (slot !== null)
|
|
119
|
+
revokeState =
|
|
120
|
+
slot.revokeState.kind === 'none'
|
|
121
|
+
? { kind: 'none' }
|
|
122
|
+
: {
|
|
123
|
+
kind: 'unconfirmed',
|
|
124
|
+
attemptedAt: slot.revokeState.attemptedAt,
|
|
125
|
+
};
|
|
126
|
+
const result = {
|
|
127
|
+
version: 1,
|
|
128
|
+
slot: selection,
|
|
129
|
+
instanceUrl: slot?.instanceUrl ??
|
|
130
|
+
key?.instanceUrl ??
|
|
131
|
+
config.context?.serverUrl ??
|
|
132
|
+
null,
|
|
133
|
+
personUuid: slot?.personUuid ?? key?.personUuid ?? null,
|
|
134
|
+
organizationUuid: slot?.organizationUuid ?? key?.organizationUuid ?? null,
|
|
135
|
+
grantUuid: slot?.grantUuid ?? null,
|
|
136
|
+
clientId: slot?.clientId ?? null,
|
|
137
|
+
connectionState: state,
|
|
138
|
+
connectionValid: source === 'server' ? state === 'active' : null,
|
|
139
|
+
serverState: source === 'server'
|
|
140
|
+
? (slot?.lastReported.status ?? null)
|
|
141
|
+
: null,
|
|
142
|
+
source,
|
|
143
|
+
checkedAt: source === 'server'
|
|
144
|
+
? (slot?.lastReported.checkedAt ?? null)
|
|
145
|
+
: null,
|
|
146
|
+
lastReportedAt: slot?.lastReported.checkedAt ?? null,
|
|
147
|
+
approvedAt: slot?.approvedAt ?? null,
|
|
148
|
+
grantExpiresAt: slot?.grantExpiresAt ?? null,
|
|
149
|
+
lastUsedAt: slot?.lastReported.lastUsedAt ?? null,
|
|
150
|
+
limitsStatus: slot?.lastReported.limitsStatus ?? null,
|
|
151
|
+
approvedProjects: reported?.approvedProjects ?? [],
|
|
152
|
+
effectiveProjects: reported?.effectiveProjects ?? [],
|
|
153
|
+
warehouse,
|
|
154
|
+
revokeState,
|
|
155
|
+
settingsUrl: config.context?.serverUrl
|
|
156
|
+
? (0, lifecycle_1.settingsUrl)(slot?.instanceUrl ?? config.context.serverUrl)
|
|
157
|
+
: null,
|
|
158
|
+
error,
|
|
159
|
+
};
|
|
160
|
+
if (options.json)
|
|
161
|
+
console.log(JSON.stringify(result));
|
|
162
|
+
else {
|
|
163
|
+
if (error)
|
|
164
|
+
console.error(error);
|
|
165
|
+
if (state === 'missing' && config.context?.apiKey) {
|
|
166
|
+
const credentialLabel = (0, credentials_1.humanCredential)(config.context.apiKey).kind ===
|
|
167
|
+
'service_account'
|
|
168
|
+
? 'Service account credential'
|
|
169
|
+
: 'Personal CLI login';
|
|
170
|
+
console.error(`${credentialLabel}: available (not used). Warehouse status: unknown (not checked).`);
|
|
171
|
+
}
|
|
172
|
+
let label = state.charAt(0).toUpperCase() + state.slice(1);
|
|
173
|
+
if (state === 'expired' && source === 'local')
|
|
174
|
+
label = 'Expired (local)';
|
|
175
|
+
else if (state === 'revoked' && source === 'local')
|
|
176
|
+
label = 'Revoked (last reported)';
|
|
177
|
+
console.error(`Connection: ${selection} | ${label} | CLI`);
|
|
178
|
+
console.error(`Instance: ${result.instanceUrl ?? 'unknown'}`);
|
|
179
|
+
console.error(`Person: ${result.personUuid ?? 'unknown'} | Organisation: ${result.organizationUuid ?? 'unknown'}`);
|
|
180
|
+
if (source === 'local' && slot) {
|
|
181
|
+
console.error(`Last reported ${slot.lastReported.status} at ${slot.lastReported.checkedAt}`);
|
|
182
|
+
console.error('Approved projects and actions below are cached.');
|
|
183
|
+
}
|
|
184
|
+
console.error(`Approved: ${result.approvedAt ?? 'unknown'}`);
|
|
185
|
+
for (const project of result.approvedProjects) {
|
|
186
|
+
console.error(`Project: ${project.name ?? `${project.projectUuid} (Name unavailable)`}`);
|
|
187
|
+
console.error(`Approved actions: ${project.approvedCapabilities.map(common_1.getAgentCapabilityName).join(', ') || 'none'}`);
|
|
188
|
+
for (const capability of result.effectiveProjects.find((p) => p.projectUuid === project.projectUuid)?.capabilities ?? [])
|
|
189
|
+
console.error(`${(0, common_1.getAgentCapabilityName)(capability.capability)}: ${(0, statusResponse_1.statusReasonMessage)(capability.reason) ?? 'Allowed'}`);
|
|
190
|
+
}
|
|
191
|
+
console.error('Allowed actions can still have resource, destination or recurring-refresh restrictions.');
|
|
192
|
+
console.error(`Expires: ${result.grantExpiresAt ?? 'unknown'}`);
|
|
193
|
+
console.error(`Last used: ${result.lastUsedAt ?? 'not reported'}`);
|
|
194
|
+
if (warehouse.projects.length > 0 &&
|
|
195
|
+
warehouse.projects.every((project) => project.reason ===
|
|
196
|
+
'This connection cannot read warehouse readiness.'))
|
|
197
|
+
console.error('Warehouse sign-ins: unknown (this connection cannot read warehouse readiness).');
|
|
198
|
+
else
|
|
199
|
+
console.error(`Warehouse sign-ins: ${warehouse.state}`);
|
|
200
|
+
for (const project of warehouse.projects) {
|
|
201
|
+
console.error(`Warehouse sign-ins: ${project.projectUuid} ${project.state}${project.reason ? ` (${project.reason})` : ''}`);
|
|
202
|
+
if (project.settingsUrl && project.state !== 'ready')
|
|
203
|
+
console.error(project.settingsUrl);
|
|
204
|
+
}
|
|
205
|
+
if (warehouse.projects.some((project) => project.source === 'server'))
|
|
206
|
+
console.error('Default warehouse connection checked; review additional connections in the app.');
|
|
207
|
+
else
|
|
208
|
+
console.error('Default warehouse connection readiness could not be checked; review connections in the app.');
|
|
209
|
+
if (result.settingsUrl)
|
|
210
|
+
console.error(`Settings: ${result.settingsUrl}`);
|
|
211
|
+
}
|
|
212
|
+
const dataCapabilities = [
|
|
213
|
+
common_1.AgentCapability.ReadDiscover,
|
|
214
|
+
common_1.AgentCapability.Query,
|
|
215
|
+
common_1.AgentCapability.RawSql,
|
|
216
|
+
common_1.AgentCapability.Export,
|
|
217
|
+
];
|
|
218
|
+
const approvedData = result.approvedProjects.some((p) => p.approvedCapabilities.some((c) => dataCapabilities.includes(c)));
|
|
219
|
+
const effectiveData = result.effectiveProjects.some((p) => p.capabilities.some((c) => c.allowed && dataCapabilities.includes(c.capability)));
|
|
220
|
+
process.exitCode =
|
|
221
|
+
state === 'active' &&
|
|
222
|
+
warehouse.state === 'ready' &&
|
|
223
|
+
(!approvedData || effectiveData)
|
|
224
|
+
? 0
|
|
225
|
+
: 1;
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
exports.agentConnectionStatusHandler = agentConnectionStatusHandler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentRevoke.d.ts","sourceRoot":"","sources":["../../src/handlers/agentRevoke.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,kBAAkB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AACF,eAAO,MAAM,kBAAkB,YAClB,kBAAkB,KAC5B,OAAO,CAAC,IAAI,CAqJd,CAAC"}
|