@sipher.dev/agents 0.1.0 → 0.1.2
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/connector-platforms.json +14 -0
- package/connector-runtime.json +4 -0
- package/dist/index.js +319 -3400
- package/dist/local-control-DKnrc-X8.d.ts +27 -0
- package/dist/local-control-Dm6FcXAi.js +429 -0
- package/dist/local-control-server.d.ts +18 -0
- package/dist/local-control-server.js +149 -0
- package/dist/local-control.d.ts +355 -0
- package/dist/local-control.js +2 -0
- package/dist/local-machine-controller-CjOyllJj.js +453 -0
- package/dist/release-resolver-PnPDodU9.js +5828 -0
- package/dist/src-ghbvDJyd.js +731 -0
- package/package.json +32 -23
- package/release-trust-anchor.json +11 -0
- package/dist/local-machine-controller-Dtiash-C.js +0 -2129
- package/dist/local-machine.d.ts +0 -177
- package/dist/local-machine.js +0 -2
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { A as insecureTlsForServer, C as MachineClaimError, D as discoverDeployment, a as installDeploymentConnector, b as resolveConnectorProfileContext, f as normalizeServerUrl, g as runServiceCommand, j as tlsFailureHint, k as fetchForConfiguredServer, u as removeConnectorRegistry, w as MachineClient } from "./release-resolver-PnPDodU9.js";
|
|
2
|
+
import { hostname } from "node:os";
|
|
3
|
+
import { rm } from "node:fs/promises";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
import { setTimeout } from "node:timers/promises";
|
|
6
|
+
//#region src/auth-client.ts
|
|
7
|
+
const REQUEST_TIMEOUT_MS = 3e4;
|
|
8
|
+
var CliAuthClient = class {
|
|
9
|
+
serverUrl;
|
|
10
|
+
tlsOptions;
|
|
11
|
+
constructor(serverUrl, tlsOptions = {}) {
|
|
12
|
+
this.serverUrl = serverUrl;
|
|
13
|
+
this.tlsOptions = tlsOptions;
|
|
14
|
+
}
|
|
15
|
+
async startDeviceAuthorization(input) {
|
|
16
|
+
const body = {
|
|
17
|
+
deployment_id: input.deploymentId,
|
|
18
|
+
connector_install_id: input.connectorInstallId,
|
|
19
|
+
service_key: input.serviceKey,
|
|
20
|
+
challenge: input.challenge,
|
|
21
|
+
...input.expectedMachineId ? { expected_machine_id: input.expectedMachineId } : {},
|
|
22
|
+
...input.deviceLabel ? { device_label: input.deviceLabel } : {},
|
|
23
|
+
...input.bootstrap ? {
|
|
24
|
+
policy_digest: input.bootstrap.policyDigest,
|
|
25
|
+
release_tuple: input.bootstrap.releaseTuple
|
|
26
|
+
} : {}
|
|
27
|
+
};
|
|
28
|
+
const response = await postJson(new URL("/api/cli/oauth/device", this.serverUrl), body, this.tlsOptions);
|
|
29
|
+
return {
|
|
30
|
+
deviceCode: readString(response, "device_code"),
|
|
31
|
+
userCode: readString(response, "user_code"),
|
|
32
|
+
verificationUri: readString(response, "verification_uri"),
|
|
33
|
+
verificationUriComplete: readString(response, "verification_uri_complete"),
|
|
34
|
+
expiresIn: readNumber(response, "expires_in"),
|
|
35
|
+
interval: readNumber(response, "interval")
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async pollDeviceToken(deviceCode) {
|
|
39
|
+
return parseTokenResponse(await postForm(new URL("/api/cli/oauth/token", this.serverUrl), {
|
|
40
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
41
|
+
device_code: deviceCode
|
|
42
|
+
}, this.tlsOptions));
|
|
43
|
+
}
|
|
44
|
+
async refresh(refreshToken) {
|
|
45
|
+
return parseTokenResponse(await postForm(new URL("/api/cli/oauth/token", this.serverUrl), {
|
|
46
|
+
grant_type: "refresh_token",
|
|
47
|
+
refresh_token: refreshToken
|
|
48
|
+
}, this.tlsOptions));
|
|
49
|
+
}
|
|
50
|
+
async revoke(refreshToken) {
|
|
51
|
+
await postForm(new URL("/api/cli/oauth/revoke", this.serverUrl), { refresh_token: refreshToken }, this.tlsOptions);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
async function postJson(url, body, tlsOptions) {
|
|
55
|
+
const signal = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
56
|
+
let response;
|
|
57
|
+
try {
|
|
58
|
+
response = await fetchForConfiguredServer(url, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
redirect: "manual",
|
|
61
|
+
headers: { "content-type": "application/json" },
|
|
62
|
+
body: JSON.stringify(body),
|
|
63
|
+
signal
|
|
64
|
+
}, tlsOptions);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
throw new Error(tlsFailureHint(error) ?? "CLI auth request failed: fetch failed.");
|
|
67
|
+
}
|
|
68
|
+
return readResponse(response);
|
|
69
|
+
}
|
|
70
|
+
async function postForm(url, body, tlsOptions) {
|
|
71
|
+
const signal = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
72
|
+
let response;
|
|
73
|
+
try {
|
|
74
|
+
response = await fetchForConfiguredServer(url, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
redirect: "manual",
|
|
77
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
78
|
+
body: new URLSearchParams(body),
|
|
79
|
+
signal
|
|
80
|
+
}, tlsOptions);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw new Error(tlsFailureHint(error) ?? "CLI auth request failed: fetch failed.");
|
|
83
|
+
}
|
|
84
|
+
return readResponse(response);
|
|
85
|
+
}
|
|
86
|
+
async function readResponse(response) {
|
|
87
|
+
const value = await response.json().catch(() => ({}));
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const error = isRecord(value) && typeof value.error === "string" ? value.error : "request_failed";
|
|
90
|
+
throw new Error(`CLI auth request failed: ${error}`);
|
|
91
|
+
}
|
|
92
|
+
if (!isRecord(value)) throw new Error("CLI auth response was invalid.");
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
function parseTokenResponse(value) {
|
|
96
|
+
const tokenType = readString(value, "token_type");
|
|
97
|
+
if (tokenType !== "Bearer") throw new Error("CLI auth token response was invalid.");
|
|
98
|
+
return {
|
|
99
|
+
accessToken: readString(value, "access_token"),
|
|
100
|
+
refreshToken: readString(value, "refresh_token"),
|
|
101
|
+
expiresIn: readNumber(value, "expires_in"),
|
|
102
|
+
tokenType,
|
|
103
|
+
deploymentId: readString(value, "deployment_id")
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function readString(value, key) {
|
|
107
|
+
const field = value[key];
|
|
108
|
+
if (typeof field !== "string" || !field.trim()) throw new Error("CLI auth response was invalid.");
|
|
109
|
+
return field;
|
|
110
|
+
}
|
|
111
|
+
function readNumber(value, key) {
|
|
112
|
+
const field = value[key];
|
|
113
|
+
if (typeof field !== "number" || !Number.isFinite(field)) throw new Error("CLI auth response was invalid.");
|
|
114
|
+
return field;
|
|
115
|
+
}
|
|
116
|
+
function isRecord(value) {
|
|
117
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/local-machine-controller.ts
|
|
121
|
+
const ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
122
|
+
async function discoverDeploymentForStore(options) {
|
|
123
|
+
const knownByUrl = await options.store.findProfileForServerUrl(options.serverUrl);
|
|
124
|
+
const allowInsecureTls = options.allowInsecureTls ?? (knownByUrl ? insecureTlsForServer(knownByUrl.canonicalOrigin, options.serverUrl, knownByUrl.allowInsecureTls) : void 0);
|
|
125
|
+
const discovered = await discoverDeployment(options.serverUrl, {
|
|
126
|
+
allowInsecureTls,
|
|
127
|
+
...knownByUrl ? {
|
|
128
|
+
trustedCanonicalOrigin: knownByUrl.canonicalOrigin,
|
|
129
|
+
expectedDeploymentId: knownByUrl.deploymentId
|
|
130
|
+
} : {}
|
|
131
|
+
});
|
|
132
|
+
if (knownByUrl) return discovered;
|
|
133
|
+
const knownById = await options.store.getProfile(discovered.deploymentId);
|
|
134
|
+
if (!knownById) return discovered;
|
|
135
|
+
return discoverDeployment(options.serverUrl, {
|
|
136
|
+
allowInsecureTls: options.allowInsecureTls ?? insecureTlsForServer(knownById.canonicalOrigin, options.serverUrl, knownById.allowInsecureTls),
|
|
137
|
+
trustedCanonicalOrigin: knownById.canonicalOrigin,
|
|
138
|
+
expectedDeploymentId: knownById.deploymentId
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function setupConnectorProfile(options) {
|
|
142
|
+
const deployment = await discoverDeploymentForStore({
|
|
143
|
+
serverUrl: options.serverUrl,
|
|
144
|
+
store: options.store,
|
|
145
|
+
allowInsecureTls: options.allowInsecureTls
|
|
146
|
+
});
|
|
147
|
+
const claimed = await claimLocalMachine({
|
|
148
|
+
deployment,
|
|
149
|
+
store: options.store,
|
|
150
|
+
name: options.name,
|
|
151
|
+
credentialLane: "pending",
|
|
152
|
+
allowInsecureTls: insecureTlsForServer(options.serverUrl, deployment.serverUrl, options.allowInsecureTls),
|
|
153
|
+
...options.bootstrap ? { bootstrap: {
|
|
154
|
+
policyDigest: options.bootstrap.policyDigest,
|
|
155
|
+
releaseTuple: options.bootstrap.releaseTuple
|
|
156
|
+
} } : {}
|
|
157
|
+
});
|
|
158
|
+
const { machine, serverUrl, clientAccessToken } = claimed;
|
|
159
|
+
let profile = claimed.profile;
|
|
160
|
+
if (options.bootstrap) {
|
|
161
|
+
if (!machine.bootstrap) throw new Error("Server did not issue connector bootstrap credentials.");
|
|
162
|
+
profile = await options.store.updateProfile(deployment.deploymentId, (current) => ({
|
|
163
|
+
...current ?? profile,
|
|
164
|
+
bootstrapReceipt: {
|
|
165
|
+
receiptId: machine.bootstrap.receiptId,
|
|
166
|
+
policyDigest: options.bootstrap.policyDigest,
|
|
167
|
+
snapshot: options.bootstrap.release.snapshot
|
|
168
|
+
}
|
|
169
|
+
}));
|
|
170
|
+
const credentials = {
|
|
171
|
+
...machine.bootstrap,
|
|
172
|
+
machineId: machine.machineId
|
|
173
|
+
};
|
|
174
|
+
await installDeploymentConnector({
|
|
175
|
+
deployment,
|
|
176
|
+
release: options.bootstrap.release,
|
|
177
|
+
credentials,
|
|
178
|
+
connectorInstallId: profile.connectorInstallId,
|
|
179
|
+
challenge: profile.challenge,
|
|
180
|
+
...profile.allowInsecureTls ? { allowInsecureTls: true } : {},
|
|
181
|
+
acknowledge: (receiptId) => new MachineClient(serverUrl, { allowInsecureTls: profile.allowInsecureTls }).acknowledgeBootstrap({
|
|
182
|
+
accessToken: clientAccessToken,
|
|
183
|
+
receiptId
|
|
184
|
+
}),
|
|
185
|
+
...options.reconciliationLockHeld ? { reconciliationLockHeld: true } : {}
|
|
186
|
+
});
|
|
187
|
+
await options.store.clearBootstrapReceipt(deployment.deploymentId);
|
|
188
|
+
}
|
|
189
|
+
if (options.installService) {
|
|
190
|
+
if (!options.serviceInstaller) throw new Error("Connector service install is not available in this runtime.");
|
|
191
|
+
await options.serviceInstaller({
|
|
192
|
+
serverUrl,
|
|
193
|
+
deploymentId: profile.deploymentId,
|
|
194
|
+
serviceKey: profile.serviceKey,
|
|
195
|
+
allowInsecureTls: profile.allowInsecureTls
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return machine;
|
|
199
|
+
}
|
|
200
|
+
async function resumeConnectorBootstrap(options) {
|
|
201
|
+
const stored = await options.store.getProfile(options.deployment.deploymentId);
|
|
202
|
+
const receipt = stored?.bootstrapReceipt;
|
|
203
|
+
if (!stored?.refreshToken || !receipt) throw new Error("Recoverable connector bootstrap receipt was not found.");
|
|
204
|
+
const claimed = await claimLocalMachine({
|
|
205
|
+
deployment: options.deployment,
|
|
206
|
+
store: options.store,
|
|
207
|
+
credentialLane: "active",
|
|
208
|
+
allowInsecureTls: options.allowInsecureTls,
|
|
209
|
+
bootstrap: {
|
|
210
|
+
policyDigest: receipt.policyDigest,
|
|
211
|
+
releaseTuple: receipt.snapshot.tuple,
|
|
212
|
+
receiptId: receipt.receiptId
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
if (!claimed.machine.bootstrap) throw new Error("Server did not recover connector bootstrap credentials.");
|
|
216
|
+
const nextReceipt = claimed.machine.bootstrap.receiptId;
|
|
217
|
+
await options.store.updateProfile(options.deployment.deploymentId, (current) => ({
|
|
218
|
+
...current ?? claimed.profile,
|
|
219
|
+
bootstrapReceipt: {
|
|
220
|
+
receiptId: nextReceipt,
|
|
221
|
+
policyDigest: receipt.policyDigest,
|
|
222
|
+
snapshot: receipt.snapshot
|
|
223
|
+
}
|
|
224
|
+
}));
|
|
225
|
+
await installDeploymentConnector({
|
|
226
|
+
deployment: options.deployment,
|
|
227
|
+
release: options.release,
|
|
228
|
+
credentials: {
|
|
229
|
+
...claimed.machine.bootstrap,
|
|
230
|
+
machineId: claimed.machine.machineId
|
|
231
|
+
},
|
|
232
|
+
connectorInstallId: claimed.profile.connectorInstallId,
|
|
233
|
+
challenge: claimed.profile.challenge,
|
|
234
|
+
...claimed.profile.allowInsecureTls ? { allowInsecureTls: true } : {},
|
|
235
|
+
acknowledge: (receiptId) => new MachineClient(options.deployment.serverUrl, { allowInsecureTls: claimed.profile.allowInsecureTls }).acknowledgeBootstrap({
|
|
236
|
+
accessToken: claimed.clientAccessToken,
|
|
237
|
+
receiptId
|
|
238
|
+
}),
|
|
239
|
+
...options.reconciliationLockHeld ? { reconciliationLockHeld: true } : {}
|
|
240
|
+
});
|
|
241
|
+
await options.store.clearBootstrapReceipt(options.deployment.deploymentId);
|
|
242
|
+
return claimed.machine;
|
|
243
|
+
}
|
|
244
|
+
async function repairConnectorProfile(options) {
|
|
245
|
+
const deployment = options.deployment ?? await discoverDeploymentForStore({
|
|
246
|
+
serverUrl: options.serverUrl,
|
|
247
|
+
store: options.store,
|
|
248
|
+
allowInsecureTls: options.allowInsecureTls
|
|
249
|
+
});
|
|
250
|
+
const storedProfile = await options.store.getProfile(deployment.deploymentId);
|
|
251
|
+
return claimLocalMachine({
|
|
252
|
+
deployment,
|
|
253
|
+
store: options.store,
|
|
254
|
+
name: options.name,
|
|
255
|
+
credentialLane: "active",
|
|
256
|
+
allowInsecureTls: insecureTlsForServer(options.serverUrl, deployment.serverUrl, options.allowInsecureTls ?? storedProfile?.allowInsecureTls)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async function startConnectorDeviceAuthorization(options) {
|
|
260
|
+
const deployment = await discoverDeploymentForStore({
|
|
261
|
+
serverUrl: options.serverUrl,
|
|
262
|
+
store: options.store,
|
|
263
|
+
allowInsecureTls: options.allowInsecureTls
|
|
264
|
+
});
|
|
265
|
+
const allowInsecureTls = insecureTlsForServer(options.serverUrl, deployment.serverUrl, options.allowInsecureTls);
|
|
266
|
+
const profile = await options.store.prepareDeployment(deployment, { allowInsecureTls });
|
|
267
|
+
return {
|
|
268
|
+
...await new CliAuthClient(deployment.serverUrl, { allowInsecureTls }).startDeviceAuthorization({
|
|
269
|
+
deploymentId: deployment.deploymentId,
|
|
270
|
+
connectorInstallId: profile.connectorInstallId,
|
|
271
|
+
serviceKey: deployment.serviceKey,
|
|
272
|
+
challenge: profile.challenge,
|
|
273
|
+
expectedMachineId: profile.machineId,
|
|
274
|
+
deviceLabel: options.deviceLabel ?? hostname(),
|
|
275
|
+
...options.bootstrap ? { bootstrap: options.bootstrap } : {}
|
|
276
|
+
}),
|
|
277
|
+
serverUrl: deployment.serverUrl,
|
|
278
|
+
deploymentId: deployment.deploymentId
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async function pollConnectorDeviceToken(options) {
|
|
282
|
+
const token = await pollDeviceToken(new CliAuthClient(normalizeServerUrl(options.serverUrl), { allowInsecureTls: options.allowInsecureTls }), {
|
|
283
|
+
deviceCode: options.deviceCode,
|
|
284
|
+
intervalSeconds: options.intervalSeconds,
|
|
285
|
+
expiresInSeconds: options.expiresInSeconds
|
|
286
|
+
});
|
|
287
|
+
if (token.deploymentId !== options.deploymentId) throw new Error("Authenticated token grant belongs to a different deployment.");
|
|
288
|
+
return options.store.savePendingLogin({
|
|
289
|
+
deploymentId: options.deploymentId,
|
|
290
|
+
refreshToken: token.refreshToken,
|
|
291
|
+
accessToken: token.accessToken,
|
|
292
|
+
expiresInSeconds: token.expiresIn
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
async function ensureAccessToken(input) {
|
|
296
|
+
const serverUrl = normalizeServerUrl(input.serverUrl);
|
|
297
|
+
return input.store.withLockedData(async (data) => {
|
|
298
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
299
|
+
const pendingProfile = data.pendingProfiles[input.deploymentId];
|
|
300
|
+
const activeProfile = data.profiles[input.deploymentId];
|
|
301
|
+
const pending = data.pendingLogins[input.deploymentId];
|
|
302
|
+
const profile = input.usePendingCredentials ? pendingProfile : activeProfile;
|
|
303
|
+
const credentials = input.usePendingCredentials ? pending : activeProfile;
|
|
304
|
+
if (!profile) throw new Error(`Not logged in to ${serverUrl}. Run agents login.`);
|
|
305
|
+
if (!credentials?.refreshToken) throw new Error(`Not logged in to ${serverUrl}. Run agents login.`);
|
|
306
|
+
const auth = input.authClient ?? new CliAuthClient(serverUrl, { allowInsecureTls: input.allowInsecureTls });
|
|
307
|
+
if (!input.forceRefresh && credentials.accessToken && tokenIsFresh(credentials.accessTokenExpiresAt, now)) return credentials.accessToken;
|
|
308
|
+
const refreshed = await auth.refresh(credentials.refreshToken);
|
|
309
|
+
if (refreshed.deploymentId !== profile.deploymentId) throw new Error("Authenticated token grant belongs to a different deployment.");
|
|
310
|
+
const updated = {
|
|
311
|
+
...credentials,
|
|
312
|
+
refreshToken: refreshed.refreshToken,
|
|
313
|
+
accessToken: refreshed.accessToken,
|
|
314
|
+
accessTokenExpiresAt: expiresAt(refreshed.expiresIn, now)
|
|
315
|
+
};
|
|
316
|
+
if (input.usePendingCredentials) data.pendingLogins[input.deploymentId] = updated;
|
|
317
|
+
else data.profiles[input.deploymentId] = {
|
|
318
|
+
...activeProfile,
|
|
319
|
+
...updated
|
|
320
|
+
};
|
|
321
|
+
return refreshed.accessToken;
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function createAccessTokenProvider(input) {
|
|
325
|
+
return async (options) => {
|
|
326
|
+
const deployment = await discoverDeploymentForStore({
|
|
327
|
+
serverUrl: input.serverUrl,
|
|
328
|
+
store: input.store,
|
|
329
|
+
allowInsecureTls: input.allowInsecureTls
|
|
330
|
+
});
|
|
331
|
+
if (deployment.deploymentId !== input.deploymentId) throw new Error("Discovered deployment identity changed before connector authentication.");
|
|
332
|
+
const allowInsecureTls = insecureTlsForServer(input.serverUrl, deployment.serverUrl, input.allowInsecureTls);
|
|
333
|
+
const accessToken = await ensureAccessToken({
|
|
334
|
+
...input,
|
|
335
|
+
serverUrl: deployment.serverUrl,
|
|
336
|
+
forceRefresh: options?.forceRefresh,
|
|
337
|
+
allowInsecureTls
|
|
338
|
+
});
|
|
339
|
+
return {
|
|
340
|
+
serverUrl: deployment.serverUrl,
|
|
341
|
+
accessToken,
|
|
342
|
+
...allowInsecureTls ? { allowInsecureTls: true } : {}
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
async function claimLocalMachine(options) {
|
|
347
|
+
const deployment = options.deployment;
|
|
348
|
+
const serverUrl = deployment.serverUrl;
|
|
349
|
+
const prepared = options.credentialLane === "pending" ? await options.store.prepareDeployment(deployment, { allowInsecureTls: options.allowInsecureTls }) : await options.store.getProfile(deployment.deploymentId);
|
|
350
|
+
if (!prepared || options.credentialLane === "active" && !prepared.refreshToken) throw new Error(`Not logged in to ${serverUrl}. Run agents login.`);
|
|
351
|
+
const accessToken = await ensureAccessToken({
|
|
352
|
+
serverUrl,
|
|
353
|
+
deploymentId: deployment.deploymentId,
|
|
354
|
+
store: options.store,
|
|
355
|
+
usePendingCredentials: options.credentialLane === "pending",
|
|
356
|
+
allowInsecureTls: options.allowInsecureTls
|
|
357
|
+
}).catch((error) => {
|
|
358
|
+
throw markRefreshError(error);
|
|
359
|
+
});
|
|
360
|
+
const machineClient = new MachineClient(serverUrl, { allowInsecureTls: options.allowInsecureTls });
|
|
361
|
+
const claimInput = {
|
|
362
|
+
accessToken,
|
|
363
|
+
deploymentId: deployment.deploymentId,
|
|
364
|
+
serviceKey: deployment.serviceKey,
|
|
365
|
+
challenge: prepared.challenge,
|
|
366
|
+
connectorInstallId: prepared.connectorInstallId,
|
|
367
|
+
name: options.name,
|
|
368
|
+
...options.bootstrap ? { bootstrap: options.bootstrap } : {}
|
|
369
|
+
};
|
|
370
|
+
let machine;
|
|
371
|
+
try {
|
|
372
|
+
machine = await machineClient.claim(claimInput);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
if (error instanceof MachineClaimError && error.code === "machine_retirement_pending") {
|
|
375
|
+
const retirement = await machineClient.readRetirement(accessToken);
|
|
376
|
+
if (!retirement || retirement.deploymentId !== deployment.deploymentId || retirement.connectorInstallId !== prepared.connectorInstallId) throw new Error("Machine retirement did not match the local deployment binding.");
|
|
377
|
+
await clearRetiredLocalBinding(deployment.deploymentId, deployment.serviceKey);
|
|
378
|
+
if (!retirement.acknowledged) await machineClient.acknowledgeRetirement({
|
|
379
|
+
accessToken,
|
|
380
|
+
retirement
|
|
381
|
+
});
|
|
382
|
+
machine = await machineClient.claim(claimInput);
|
|
383
|
+
} else {
|
|
384
|
+
if (!options.bootstrap || error instanceof MachineClaimError && error.status < 500) throw error;
|
|
385
|
+
machine = await machineClient.claim(claimInput);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const profile = options.credentialLane === "pending" ? await options.store.commitPendingDeployment(deployment.deploymentId, machine.machineId) : await options.store.updateProfile(deployment.deploymentId, (current) => {
|
|
389
|
+
if (!current?.refreshToken) throw new Error(`Not logged in to ${serverUrl}. Run agents login.`);
|
|
390
|
+
const updated = {
|
|
391
|
+
...current,
|
|
392
|
+
serverUrl,
|
|
393
|
+
canonicalOrigin: deployment.canonicalOrigin,
|
|
394
|
+
aliases: deployment.aliases,
|
|
395
|
+
policyDigest: deployment.policyDigest,
|
|
396
|
+
serviceKey: deployment.serviceKey,
|
|
397
|
+
machineId: machine.machineId
|
|
398
|
+
};
|
|
399
|
+
delete updated.allowInsecureTls;
|
|
400
|
+
if (options.allowInsecureTls) updated.allowInsecureTls = true;
|
|
401
|
+
return updated;
|
|
402
|
+
});
|
|
403
|
+
return {
|
|
404
|
+
machine,
|
|
405
|
+
serverUrl,
|
|
406
|
+
profile,
|
|
407
|
+
clientAccessToken: accessToken
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
async function clearRetiredLocalBinding(deploymentId, serviceKey) {
|
|
411
|
+
const context = resolveConnectorProfileContext(serviceKey);
|
|
412
|
+
await runServiceCommand("uninstall", {
|
|
413
|
+
deploymentId,
|
|
414
|
+
serviceKey
|
|
415
|
+
});
|
|
416
|
+
await Promise.all([
|
|
417
|
+
removeConnectorRegistry(context),
|
|
418
|
+
rm(dirname(context.configPath), {
|
|
419
|
+
recursive: true,
|
|
420
|
+
force: true
|
|
421
|
+
}),
|
|
422
|
+
rm(context.statePath, {
|
|
423
|
+
recursive: true,
|
|
424
|
+
force: true
|
|
425
|
+
})
|
|
426
|
+
]);
|
|
427
|
+
}
|
|
428
|
+
async function pollDeviceToken(auth, input) {
|
|
429
|
+
const expiresAt = Date.now() + input.expiresInSeconds * 1e3;
|
|
430
|
+
let intervalMs = Math.max(1, input.intervalSeconds) * 1e3;
|
|
431
|
+
while (Date.now() < expiresAt) try {
|
|
432
|
+
return await auth.pollDeviceToken(input.deviceCode);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
const message = error instanceof Error ? error.message : "";
|
|
435
|
+
if (message.includes("slow_down")) intervalMs += 5e3;
|
|
436
|
+
if (!message.includes("authorization_pending") && !message.includes("slow_down")) throw error;
|
|
437
|
+
await setTimeout(intervalMs);
|
|
438
|
+
}
|
|
439
|
+
throw new Error("CLI login expired before authorization completed.");
|
|
440
|
+
}
|
|
441
|
+
function tokenIsFresh(expiresAtValue, now) {
|
|
442
|
+
if (!expiresAtValue) return false;
|
|
443
|
+
return new Date(expiresAtValue).getTime() - now.getTime() > ACCESS_TOKEN_REFRESH_SKEW_MS;
|
|
444
|
+
}
|
|
445
|
+
function expiresAt(expiresInSeconds, now) {
|
|
446
|
+
return new Date(now.getTime() + Math.max(0, expiresInSeconds) * 1e3).toISOString();
|
|
447
|
+
}
|
|
448
|
+
function markRefreshError(error) {
|
|
449
|
+
if (error instanceof Error) return Object.assign(error, { localMachineStage: "refresh" });
|
|
450
|
+
return Object.assign(/* @__PURE__ */ new Error("Failed to refresh connector access token."), { localMachineStage: "refresh" });
|
|
451
|
+
}
|
|
452
|
+
//#endregion
|
|
453
|
+
export { repairConnectorProfile as a, startConnectorDeviceAuthorization as c, pollConnectorDeviceToken as i, CliAuthClient as l, discoverDeploymentForStore as n, resumeConnectorBootstrap as o, ensureAccessToken as r, setupConnectorProfile as s, createAccessTokenProvider as t };
|