@sekuire/sdk 0.1.13 → 0.1.15
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/beacon.d.ts +78 -7
- package/dist/index.d.ts +206 -17
- package/dist/index.esm.js +540 -68
- package/dist/index.js +540 -67
- package/dist/runtime-credentials.d.ts +24 -0
- package/dist/sdk.d.ts +65 -4
- package/dist/types/a2a-types.d.ts +2 -2
- package/dist/worker.d.ts +35 -3
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -95,23 +95,48 @@ class Beacon {
|
|
|
95
95
|
this.lastHeartbeat = null;
|
|
96
96
|
this.failedHeartbeats = 0;
|
|
97
97
|
this.degradedMode = false;
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
this.recoveredFromEnv = false;
|
|
99
|
+
this.expiresAt = null;
|
|
100
|
+
const storeSnapshot = config.credentialsStore?.getAll() || {};
|
|
101
|
+
const resolvedApiKey = config.apiKey || this.getEnvVar('SEKUIRE_API_KEY') || '';
|
|
102
|
+
const resolvedInstallToken = config.installToken || this.getEnvVar('SEKUIRE_INSTALL_TOKEN');
|
|
103
|
+
const resolvedInstallationId = config.installationId || storeSnapshot.installationId || this.getEnvVar('SEKUIRE_INSTALLATION_ID');
|
|
104
|
+
const resolvedRefreshToken = config.refreshToken || storeSnapshot.refreshToken || this.getEnvVar('SEKUIRE_REFRESH_TOKEN');
|
|
105
|
+
const resolvedRuntimeToken = config.runtimeToken || storeSnapshot.runtimeToken || this.getEnvVar('SEKUIRE_RUNTIME_TOKEN');
|
|
100
106
|
this.config = {
|
|
101
107
|
...config,
|
|
102
108
|
heartbeatIntervalSeconds: config.heartbeatIntervalSeconds ?? 60,
|
|
103
109
|
apiKey: resolvedApiKey,
|
|
104
110
|
installToken: resolvedInstallToken,
|
|
111
|
+
installationId: resolvedInstallationId,
|
|
112
|
+
refreshToken: resolvedRefreshToken,
|
|
113
|
+
runtimeToken: resolvedRuntimeToken,
|
|
105
114
|
capabilities: config.capabilities ?? [],
|
|
106
115
|
};
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
this.credentialsStore = config.credentialsStore;
|
|
117
|
+
if (this.credentialsStore) {
|
|
118
|
+
this.credentialsStore.update({
|
|
119
|
+
installationId: resolvedInstallationId,
|
|
120
|
+
refreshToken: resolvedRefreshToken,
|
|
121
|
+
runtimeToken: resolvedRuntimeToken,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const hasRecoveryCredentials = resolvedInstallationId && resolvedRefreshToken;
|
|
125
|
+
if (!this.config.installToken && !this.config.apiKey && !hasRecoveryCredentials) {
|
|
126
|
+
console.warn('[Beacon] No credentials provided. Set one of:');
|
|
127
|
+
console.warn('[Beacon] - SEKUIRE_INSTALL_TOKEN (for initial bootstrap)');
|
|
128
|
+
console.warn('[Beacon] - SEKUIRE_INSTALLATION_ID + SEKUIRE_REFRESH_TOKEN (for recovery)');
|
|
109
129
|
}
|
|
110
130
|
}
|
|
111
131
|
/**
|
|
112
132
|
* Start the beacon - registers with Sekuire and begins heartbeat loop
|
|
113
133
|
*
|
|
114
|
-
*
|
|
134
|
+
* Authentication priority:
|
|
135
|
+
* 1. If SEKUIRE_INSTALLATION_ID + SEKUIRE_REFRESH_TOKEN are set, recover credentials
|
|
136
|
+
* 2. If SEKUIRE_RUNTIME_TOKEN is also set and valid, use it directly
|
|
137
|
+
* 3. Fall back to SEKUIRE_INSTALL_TOKEN for fresh bootstrap
|
|
138
|
+
*
|
|
139
|
+
* If all authentication methods fail, the agent continues running in degraded mode
|
|
115
140
|
* without Sekuire features. This prevents auth failures from crashing agents.
|
|
116
141
|
*/
|
|
117
142
|
async start() {
|
|
@@ -124,10 +149,21 @@ class Beacon {
|
|
|
124
149
|
if (deploymentUrl) {
|
|
125
150
|
console.log(`[Beacon] Deployment URL: ${deploymentUrl}`);
|
|
126
151
|
}
|
|
127
|
-
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
let authenticated = false;
|
|
153
|
+
// Priority 1: Try credential recovery from env vars
|
|
154
|
+
if (this.config.installationId && this.config.refreshToken) {
|
|
155
|
+
console.log('[Beacon] Found recovery credentials in environment');
|
|
156
|
+
authenticated = await this.tryCredentialRecovery();
|
|
157
|
+
if (authenticated) {
|
|
158
|
+
this.recoveredFromEnv = true;
|
|
159
|
+
console.log('[Beacon] Successfully recovered credentials from environment');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Priority 2: Fall back to fresh bootstrap with install token
|
|
163
|
+
if (!authenticated) {
|
|
164
|
+
authenticated = await this.bootstrapWithRetry(deploymentUrl);
|
|
165
|
+
}
|
|
166
|
+
if (!authenticated) {
|
|
131
167
|
this.degradedMode = true;
|
|
132
168
|
console.warn('[Beacon] Running in degraded mode - Sekuire features disabled');
|
|
133
169
|
console.warn('[Beacon] To re-enable: generate a new install token from the dashboard');
|
|
@@ -135,6 +171,9 @@ class Beacon {
|
|
|
135
171
|
return;
|
|
136
172
|
}
|
|
137
173
|
console.log(`[Beacon] Bootstrap successful, installation ID: ${this.installationId}`);
|
|
174
|
+
if (this.recoveredFromEnv) {
|
|
175
|
+
console.log('[Beacon] Credentials recovered via refresh token (no install token consumed)');
|
|
176
|
+
}
|
|
138
177
|
// Start heartbeat loop
|
|
139
178
|
const intervalMs = this.config.heartbeatIntervalSeconds * 1000;
|
|
140
179
|
this.intervalId = setInterval(() => {
|
|
@@ -162,12 +201,13 @@ class Beacon {
|
|
|
162
201
|
getStatus() {
|
|
163
202
|
return {
|
|
164
203
|
isRunning: this.intervalId !== null || this.degradedMode,
|
|
165
|
-
installationId: this.
|
|
166
|
-
runtimeToken: this.
|
|
204
|
+
installationId: this.getInstallationId() || undefined,
|
|
205
|
+
runtimeToken: this.getRuntimeToken() ? '***' + this.getRuntimeToken().slice(-4) : undefined,
|
|
167
206
|
deploymentUrl: this.config.deploymentUrl || detectDeploymentUrl(),
|
|
168
207
|
lastHeartbeat: this.lastHeartbeat || undefined,
|
|
169
208
|
failedHeartbeats: this.failedHeartbeats,
|
|
170
209
|
degradedMode: this.degradedMode,
|
|
210
|
+
recoveredFromEnv: this.recoveredFromEnv,
|
|
171
211
|
};
|
|
172
212
|
}
|
|
173
213
|
/**
|
|
@@ -179,14 +219,140 @@ class Beacon {
|
|
|
179
219
|
* @returns Runtime credentials or null if not bootstrapped
|
|
180
220
|
*/
|
|
181
221
|
getRuntimeCredentials() {
|
|
182
|
-
|
|
222
|
+
const installationId = this.getInstallationId();
|
|
223
|
+
const runtimeToken = this.getRuntimeToken();
|
|
224
|
+
if (!installationId || !runtimeToken) {
|
|
183
225
|
return null;
|
|
184
226
|
}
|
|
185
227
|
return {
|
|
186
|
-
installationId
|
|
187
|
-
runtimeToken
|
|
228
|
+
installationId,
|
|
229
|
+
runtimeToken,
|
|
188
230
|
};
|
|
189
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Get full installation credentials for persistence.
|
|
234
|
+
*
|
|
235
|
+
* Use this after successful bootstrap to extract credentials that can be
|
|
236
|
+
* saved as environment variables or secrets for future container restarts.
|
|
237
|
+
* These credentials allow recovery without consuming a new install token.
|
|
238
|
+
*
|
|
239
|
+
* Example usage:
|
|
240
|
+
* ```ts
|
|
241
|
+
* const creds = beacon.getInstallationCredentials();
|
|
242
|
+
* if (creds) {
|
|
243
|
+
* // Save to your secrets manager (AWS Secrets Manager, K8s Secret, etc.)
|
|
244
|
+
* // SEKUIRE_INSTALLATION_ID = creds.installationId
|
|
245
|
+
* // SEKUIRE_REFRESH_TOKEN = creds.refreshToken
|
|
246
|
+
* // SEKUIRE_RUNTIME_TOKEN = creds.runtimeToken (optional, has expiry)
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
*
|
|
250
|
+
* @returns Full installation credentials or null if not bootstrapped
|
|
251
|
+
*/
|
|
252
|
+
getInstallationCredentials() {
|
|
253
|
+
const installationId = this.getInstallationId();
|
|
254
|
+
const runtimeToken = this.getRuntimeToken();
|
|
255
|
+
const refreshToken = this.getRefreshToken();
|
|
256
|
+
if (!installationId || !runtimeToken || !refreshToken) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
installationId,
|
|
261
|
+
runtimeToken,
|
|
262
|
+
refreshToken,
|
|
263
|
+
expiresAt: this.expiresAt || undefined,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Try to recover credentials using installation ID and refresh token from env.
|
|
268
|
+
*
|
|
269
|
+
* This allows agents to survive container restarts without needing a new
|
|
270
|
+
* install token, which is especially important for ephemeral compute
|
|
271
|
+
* environments like Cloud Run, Kubernetes, or serverless functions.
|
|
272
|
+
*
|
|
273
|
+
* @returns true if recovery succeeded, false otherwise
|
|
274
|
+
*/
|
|
275
|
+
async tryCredentialRecovery() {
|
|
276
|
+
const installationId = this.getInstallationId() || this.config.installationId;
|
|
277
|
+
const refreshToken = this.getRefreshToken() || this.config.refreshToken;
|
|
278
|
+
const existingRuntimeToken = this.getRuntimeToken() || this.config.runtimeToken;
|
|
279
|
+
if (!installationId || !refreshToken) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
// If we have an existing runtime token, try to use it first
|
|
283
|
+
if (existingRuntimeToken) {
|
|
284
|
+
console.log('[Beacon] Found existing runtime token, validating...');
|
|
285
|
+
const isValid = await this.validateRuntimeToken(installationId, existingRuntimeToken);
|
|
286
|
+
if (isValid) {
|
|
287
|
+
this.installationId = installationId;
|
|
288
|
+
this.runtimeToken = existingRuntimeToken;
|
|
289
|
+
this.refreshToken = refreshToken;
|
|
290
|
+
this.credentialsStore?.update({
|
|
291
|
+
installationId,
|
|
292
|
+
runtimeToken: existingRuntimeToken,
|
|
293
|
+
refreshToken,
|
|
294
|
+
});
|
|
295
|
+
console.log('[Beacon] Existing runtime token is valid');
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
console.log('[Beacon] Existing runtime token is invalid or expired, refreshing...');
|
|
299
|
+
}
|
|
300
|
+
// Refresh to get a new runtime token
|
|
301
|
+
try {
|
|
302
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/refresh`, {
|
|
303
|
+
method: 'POST',
|
|
304
|
+
headers: {
|
|
305
|
+
'Content-Type': 'application/json',
|
|
306
|
+
},
|
|
307
|
+
body: JSON.stringify({
|
|
308
|
+
refresh_token: refreshToken,
|
|
309
|
+
}),
|
|
310
|
+
});
|
|
311
|
+
if (!response.ok) {
|
|
312
|
+
const body = await response.text();
|
|
313
|
+
console.error(`[Beacon] Credential recovery failed: ${response.status} - ${body}`);
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
const data = await response.json();
|
|
317
|
+
this.installationId = installationId;
|
|
318
|
+
this.runtimeToken = data.runtime_token;
|
|
319
|
+
this.refreshToken = refreshToken; // Refresh token is stable
|
|
320
|
+
this.expiresAt = data.expires_at || null;
|
|
321
|
+
this.credentialsStore?.update({
|
|
322
|
+
installationId,
|
|
323
|
+
runtimeToken: data.runtime_token,
|
|
324
|
+
refreshToken,
|
|
325
|
+
expiresAt: data.expires_at,
|
|
326
|
+
});
|
|
327
|
+
console.log('[Beacon] Credentials recovered via refresh token');
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
console.error('[Beacon] Credential recovery error:', error);
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Validate a runtime token by making a lightweight API call.
|
|
337
|
+
*/
|
|
338
|
+
async validateRuntimeToken(installationId, token) {
|
|
339
|
+
try {
|
|
340
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/lease`, {
|
|
341
|
+
method: 'POST',
|
|
342
|
+
headers: {
|
|
343
|
+
'Content-Type': 'application/json',
|
|
344
|
+
'Authorization': `Bearer ${token}`,
|
|
345
|
+
},
|
|
346
|
+
body: JSON.stringify({
|
|
347
|
+
status: 'running',
|
|
348
|
+
}),
|
|
349
|
+
});
|
|
350
|
+
return response.ok;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
190
356
|
/**
|
|
191
357
|
* Bootstrap with retry and exponential backoff
|
|
192
358
|
*
|
|
@@ -309,6 +475,13 @@ class Beacon {
|
|
|
309
475
|
this.installationId = data.installation_id;
|
|
310
476
|
this.runtimeToken = data.runtime_token;
|
|
311
477
|
this.refreshToken = data.refresh_token;
|
|
478
|
+
this.expiresAt = data.expires_at || null;
|
|
479
|
+
this.credentialsStore?.update({
|
|
480
|
+
installationId: data.installation_id,
|
|
481
|
+
runtimeToken: data.runtime_token,
|
|
482
|
+
refreshToken: data.refresh_token,
|
|
483
|
+
expiresAt: data.expires_at,
|
|
484
|
+
});
|
|
312
485
|
}
|
|
313
486
|
/**
|
|
314
487
|
* Send heartbeat (lease renewal) to Sekuire
|
|
@@ -316,18 +489,20 @@ class Beacon {
|
|
|
316
489
|
* Uses the installation-based lease endpoint with runtime token authentication.
|
|
317
490
|
*/
|
|
318
491
|
async heartbeat() {
|
|
319
|
-
|
|
492
|
+
const installationId = this.getInstallationId();
|
|
493
|
+
const runtimeToken = this.getRuntimeToken();
|
|
494
|
+
if (!installationId || !runtimeToken) {
|
|
320
495
|
console.warn('[Beacon] Cannot send heartbeat - not bootstrapped');
|
|
321
496
|
return;
|
|
322
497
|
}
|
|
323
498
|
const deploymentUrl = this.config.deploymentUrl || detectDeploymentUrl();
|
|
324
499
|
const prevFailed = this.failedHeartbeats;
|
|
325
500
|
try {
|
|
326
|
-
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${
|
|
501
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/lease`, {
|
|
327
502
|
method: 'POST',
|
|
328
503
|
headers: {
|
|
329
504
|
'Content-Type': 'application/json',
|
|
330
|
-
'Authorization': `Bearer ${
|
|
505
|
+
'Authorization': `Bearer ${runtimeToken}`,
|
|
331
506
|
},
|
|
332
507
|
body: JSON.stringify({
|
|
333
508
|
status: 'running',
|
|
@@ -350,6 +525,7 @@ class Beacon {
|
|
|
350
525
|
const data = await response.json();
|
|
351
526
|
if (data.refreshed_token) {
|
|
352
527
|
this.runtimeToken = data.refreshed_token.runtime_token;
|
|
528
|
+
this.credentialsStore?.setRuntimeToken(data.refreshed_token.runtime_token, data.refreshed_token.expires_at);
|
|
353
529
|
console.log(`[Beacon] Token auto-refreshed by server (version ${data.refreshed_token.token_version})`);
|
|
354
530
|
}
|
|
355
531
|
this.lastHeartbeat = new Date();
|
|
@@ -368,16 +544,18 @@ class Beacon {
|
|
|
368
544
|
* Refresh the runtime token using the refresh token
|
|
369
545
|
*/
|
|
370
546
|
async refreshRuntimeToken() {
|
|
371
|
-
|
|
547
|
+
const installationId = this.getInstallationId();
|
|
548
|
+
const refreshToken = this.getRefreshToken();
|
|
549
|
+
if (!installationId || !refreshToken) {
|
|
372
550
|
throw new Error('Cannot refresh token - missing installation ID or refresh token');
|
|
373
551
|
}
|
|
374
|
-
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${
|
|
552
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/refresh`, {
|
|
375
553
|
method: 'POST',
|
|
376
554
|
headers: {
|
|
377
555
|
'Content-Type': 'application/json',
|
|
378
556
|
},
|
|
379
557
|
body: JSON.stringify({
|
|
380
|
-
refresh_token:
|
|
558
|
+
refresh_token: refreshToken,
|
|
381
559
|
}),
|
|
382
560
|
});
|
|
383
561
|
if (!response.ok) {
|
|
@@ -386,6 +564,7 @@ class Beacon {
|
|
|
386
564
|
}
|
|
387
565
|
const data = await response.json();
|
|
388
566
|
this.runtimeToken = data.runtime_token;
|
|
567
|
+
this.credentialsStore?.setRuntimeToken(data.runtime_token, data.expires_at);
|
|
389
568
|
console.log('[Beacon] Runtime token refreshed successfully');
|
|
390
569
|
}
|
|
391
570
|
/**
|
|
@@ -397,22 +576,37 @@ class Beacon {
|
|
|
397
576
|
}
|
|
398
577
|
}
|
|
399
578
|
/**
|
|
400
|
-
* Get
|
|
579
|
+
* Get environment variable value (works in Node.js and browser)
|
|
401
580
|
*/
|
|
402
|
-
|
|
581
|
+
getEnvVar(name) {
|
|
403
582
|
if (typeof process !== 'undefined' && process.env) {
|
|
404
|
-
return process.env
|
|
583
|
+
return process.env[name];
|
|
405
584
|
}
|
|
406
585
|
return undefined;
|
|
407
586
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
return process.env.SEKUIRE_INSTALL_TOKEN;
|
|
587
|
+
getInstallationId() {
|
|
588
|
+
const storeValue = this.credentialsStore?.getInstallationId();
|
|
589
|
+
if (storeValue) {
|
|
590
|
+
this.installationId = storeValue;
|
|
591
|
+
return storeValue;
|
|
414
592
|
}
|
|
415
|
-
return
|
|
593
|
+
return this.installationId;
|
|
594
|
+
}
|
|
595
|
+
getRuntimeToken() {
|
|
596
|
+
const storeValue = this.credentialsStore?.getRuntimeToken();
|
|
597
|
+
if (storeValue) {
|
|
598
|
+
this.runtimeToken = storeValue;
|
|
599
|
+
return storeValue;
|
|
600
|
+
}
|
|
601
|
+
return this.runtimeToken;
|
|
602
|
+
}
|
|
603
|
+
getRefreshToken() {
|
|
604
|
+
const storeValue = this.credentialsStore?.getRefreshToken();
|
|
605
|
+
if (storeValue) {
|
|
606
|
+
this.refreshToken = storeValue;
|
|
607
|
+
return storeValue;
|
|
608
|
+
}
|
|
609
|
+
return this.refreshToken;
|
|
416
610
|
}
|
|
417
611
|
}
|
|
418
612
|
// ============================================================================
|
|
@@ -904,6 +1098,66 @@ class SekuireLogger {
|
|
|
904
1098
|
}
|
|
905
1099
|
}
|
|
906
1100
|
|
|
1101
|
+
class RuntimeCredentialsStore {
|
|
1102
|
+
constructor(initial) {
|
|
1103
|
+
if (initial) {
|
|
1104
|
+
this.update(initial);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
update(partial) {
|
|
1108
|
+
if (partial.installationId) {
|
|
1109
|
+
this.installationId = partial.installationId;
|
|
1110
|
+
}
|
|
1111
|
+
if (partial.runtimeToken) {
|
|
1112
|
+
this.runtimeToken = partial.runtimeToken;
|
|
1113
|
+
}
|
|
1114
|
+
if (partial.refreshToken) {
|
|
1115
|
+
this.refreshToken = partial.refreshToken;
|
|
1116
|
+
}
|
|
1117
|
+
if (partial.expiresAt !== undefined) {
|
|
1118
|
+
this.expiresAt = partial.expiresAt;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
setRuntimeToken(runtimeToken, expiresAt) {
|
|
1122
|
+
this.runtimeToken = runtimeToken;
|
|
1123
|
+
if (expiresAt !== undefined) {
|
|
1124
|
+
this.expiresAt = expiresAt;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
setInstallationId(installationId) {
|
|
1128
|
+
this.installationId = installationId;
|
|
1129
|
+
}
|
|
1130
|
+
setRefreshToken(refreshToken) {
|
|
1131
|
+
this.refreshToken = refreshToken;
|
|
1132
|
+
}
|
|
1133
|
+
getInstallationId() {
|
|
1134
|
+
return this.installationId;
|
|
1135
|
+
}
|
|
1136
|
+
getRuntimeToken() {
|
|
1137
|
+
return this.runtimeToken;
|
|
1138
|
+
}
|
|
1139
|
+
getRefreshToken() {
|
|
1140
|
+
return this.refreshToken;
|
|
1141
|
+
}
|
|
1142
|
+
getExpiresAt() {
|
|
1143
|
+
return this.expiresAt;
|
|
1144
|
+
}
|
|
1145
|
+
getAll() {
|
|
1146
|
+
return {
|
|
1147
|
+
installationId: this.installationId,
|
|
1148
|
+
runtimeToken: this.runtimeToken,
|
|
1149
|
+
refreshToken: this.refreshToken,
|
|
1150
|
+
expiresAt: this.expiresAt,
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
hasRecoveryCredentials() {
|
|
1154
|
+
return !!(this.installationId && this.refreshToken);
|
|
1155
|
+
}
|
|
1156
|
+
hasRuntimeToken() {
|
|
1157
|
+
return !!this.runtimeToken;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
907
1161
|
function getDefaultExportFromCjs (x) {
|
|
908
1162
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
909
1163
|
}
|
|
@@ -1426,8 +1680,16 @@ class TaskWorker {
|
|
|
1426
1680
|
this.runtimeToken = null;
|
|
1427
1681
|
this.refreshToken = null;
|
|
1428
1682
|
this.expiresAt = null;
|
|
1429
|
-
|
|
1430
|
-
|
|
1683
|
+
this.recoveredFromEnv = false;
|
|
1684
|
+
const storeSnapshot = config.credentialsStore?.getAll() || {};
|
|
1685
|
+
const resolvedInstallationId = config.installationId || storeSnapshot.installationId || this.getEnvVar('SEKUIRE_INSTALLATION_ID');
|
|
1686
|
+
const resolvedRefreshToken = config.refreshToken || storeSnapshot.refreshToken || this.getEnvVar('SEKUIRE_REFRESH_TOKEN');
|
|
1687
|
+
const resolvedRuntimeToken = config.runtimeToken || storeSnapshot.runtimeToken || this.getEnvVar('SEKUIRE_RUNTIME_TOKEN');
|
|
1688
|
+
const resolvedInstallToken = config.installToken || this.getEnvVar('SEKUIRE_INSTALL_TOKEN');
|
|
1689
|
+
const hasRecoveryCredentials = resolvedInstallationId && resolvedRefreshToken;
|
|
1690
|
+
if (!resolvedInstallToken && !resolvedRuntimeToken && !hasRecoveryCredentials) {
|
|
1691
|
+
throw new Error("WorkerConfig requires one of: " +
|
|
1692
|
+
"installToken, runtimeToken, or (installationId + refreshToken)");
|
|
1431
1693
|
}
|
|
1432
1694
|
this.config = {
|
|
1433
1695
|
heartbeatIntervalMs: 10000, // 10 seconds
|
|
@@ -1435,9 +1697,30 @@ class TaskWorker {
|
|
|
1435
1697
|
maxReconnectDelayMs: 30000, // 30 seconds max
|
|
1436
1698
|
capabilities: [],
|
|
1437
1699
|
...config,
|
|
1700
|
+
installToken: resolvedInstallToken,
|
|
1701
|
+
runtimeToken: resolvedRuntimeToken,
|
|
1702
|
+
installationId: resolvedInstallationId,
|
|
1703
|
+
refreshToken: resolvedRefreshToken,
|
|
1438
1704
|
};
|
|
1705
|
+
this.credentialsStore = config.credentialsStore;
|
|
1706
|
+
this.tokenProvider = config.tokenProvider;
|
|
1439
1707
|
this.reconnectDelay = this.config.reconnectDelayMs;
|
|
1440
|
-
this.runtimeToken =
|
|
1708
|
+
this.runtimeToken = resolvedRuntimeToken || null;
|
|
1709
|
+
this.installationId = resolvedInstallationId || null;
|
|
1710
|
+
this.refreshToken = resolvedRefreshToken || null;
|
|
1711
|
+
if (this.credentialsStore) {
|
|
1712
|
+
this.credentialsStore.update({
|
|
1713
|
+
installationId: resolvedInstallationId,
|
|
1714
|
+
refreshToken: resolvedRefreshToken,
|
|
1715
|
+
runtimeToken: resolvedRuntimeToken,
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
getEnvVar(name) {
|
|
1720
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
1721
|
+
return process.env[name];
|
|
1722
|
+
}
|
|
1723
|
+
return undefined;
|
|
1441
1724
|
}
|
|
1442
1725
|
/**
|
|
1443
1726
|
* Register a handler for a specific capability
|
|
@@ -1449,13 +1732,88 @@ class TaskWorker {
|
|
|
1449
1732
|
}
|
|
1450
1733
|
/**
|
|
1451
1734
|
* Start the worker (connects to SSE stream and starts heartbeat)
|
|
1735
|
+
*
|
|
1736
|
+
* Authentication priority:
|
|
1737
|
+
* 1. If runtimeToken is provided, use it directly
|
|
1738
|
+
* 2. If installationId + refreshToken are set, recover credentials
|
|
1739
|
+
* 3. Fall back to installToken for fresh bootstrap
|
|
1452
1740
|
*/
|
|
1453
1741
|
async start() {
|
|
1454
1742
|
console.log("[Worker] Starting task worker...");
|
|
1455
|
-
|
|
1743
|
+
let authenticated = false;
|
|
1744
|
+
// Priority 1: Already have runtime token
|
|
1745
|
+
const tokenFromProvider = this.tokenProvider?.();
|
|
1746
|
+
const tokenFromStore = this.credentialsStore?.getRuntimeToken();
|
|
1747
|
+
if (tokenFromProvider || tokenFromStore || this.runtimeToken) {
|
|
1748
|
+
if (tokenFromProvider) {
|
|
1749
|
+
this.runtimeToken = tokenFromProvider;
|
|
1750
|
+
}
|
|
1751
|
+
else if (tokenFromStore) {
|
|
1752
|
+
this.runtimeToken = tokenFromStore;
|
|
1753
|
+
}
|
|
1754
|
+
console.log("[Worker] Using provided runtime token");
|
|
1755
|
+
authenticated = true;
|
|
1756
|
+
}
|
|
1757
|
+
// Priority 2: Try credential recovery from env vars
|
|
1758
|
+
if (!authenticated && this.config.installationId && this.config.refreshToken) {
|
|
1759
|
+
console.log("[Worker] Attempting credential recovery from environment...");
|
|
1760
|
+
authenticated = await this.tryCredentialRecovery();
|
|
1761
|
+
if (authenticated) {
|
|
1762
|
+
this.recoveredFromEnv = true;
|
|
1763
|
+
console.log("[Worker] Successfully recovered credentials from environment");
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
// Priority 3: Fall back to fresh bootstrap
|
|
1767
|
+
if (!authenticated) {
|
|
1768
|
+
await this.bootstrap();
|
|
1769
|
+
authenticated = true;
|
|
1770
|
+
}
|
|
1456
1771
|
this.connect();
|
|
1457
1772
|
this.startHeartbeat();
|
|
1458
1773
|
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Try to recover credentials using installation ID and refresh token.
|
|
1776
|
+
*/
|
|
1777
|
+
async tryCredentialRecovery() {
|
|
1778
|
+
const installationId = this.getInstallationId() || this.config.installationId;
|
|
1779
|
+
const refreshToken = this.getRefreshToken() || this.config.refreshToken;
|
|
1780
|
+
if (!installationId || !refreshToken) {
|
|
1781
|
+
return false;
|
|
1782
|
+
}
|
|
1783
|
+
try {
|
|
1784
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/refresh`, {
|
|
1785
|
+
method: "POST",
|
|
1786
|
+
headers: {
|
|
1787
|
+
"Content-Type": "application/json",
|
|
1788
|
+
},
|
|
1789
|
+
body: JSON.stringify({
|
|
1790
|
+
refresh_token: refreshToken,
|
|
1791
|
+
}),
|
|
1792
|
+
});
|
|
1793
|
+
if (!response.ok) {
|
|
1794
|
+
const body = await response.text();
|
|
1795
|
+
console.error(`[Worker] Credential recovery failed: ${response.status} - ${body}`);
|
|
1796
|
+
return false;
|
|
1797
|
+
}
|
|
1798
|
+
const data = await response.json();
|
|
1799
|
+
this.installationId = installationId;
|
|
1800
|
+
this.runtimeToken = data.runtime_token;
|
|
1801
|
+
this.refreshToken = refreshToken;
|
|
1802
|
+
this.expiresAt = data.expires_at ? new Date(data.expires_at).getTime() : null;
|
|
1803
|
+
this.credentialsStore?.update({
|
|
1804
|
+
installationId,
|
|
1805
|
+
runtimeToken: data.runtime_token,
|
|
1806
|
+
refreshToken,
|
|
1807
|
+
expiresAt: data.expires_at,
|
|
1808
|
+
});
|
|
1809
|
+
console.log(`[Worker] Credentials recovered for installation: ${installationId}`);
|
|
1810
|
+
return true;
|
|
1811
|
+
}
|
|
1812
|
+
catch (error) {
|
|
1813
|
+
console.error("[Worker] Credential recovery error:", error);
|
|
1814
|
+
return false;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1459
1817
|
/**
|
|
1460
1818
|
* Stop the worker gracefully
|
|
1461
1819
|
*/
|
|
@@ -1518,6 +1876,16 @@ class TaskWorker {
|
|
|
1518
1876
|
* Get runtime token for API calls
|
|
1519
1877
|
*/
|
|
1520
1878
|
getToken() {
|
|
1879
|
+
const tokenFromProvider = this.tokenProvider?.();
|
|
1880
|
+
if (tokenFromProvider) {
|
|
1881
|
+
this.runtimeToken = tokenFromProvider;
|
|
1882
|
+
return tokenFromProvider;
|
|
1883
|
+
}
|
|
1884
|
+
const tokenFromStore = this.credentialsStore?.getRuntimeToken();
|
|
1885
|
+
if (tokenFromStore) {
|
|
1886
|
+
this.runtimeToken = tokenFromStore;
|
|
1887
|
+
return tokenFromStore;
|
|
1888
|
+
}
|
|
1521
1889
|
if (!this.runtimeToken) {
|
|
1522
1890
|
throw new Error("No runtime token available - call bootstrap first");
|
|
1523
1891
|
}
|
|
@@ -1578,23 +1946,23 @@ class TaskWorker {
|
|
|
1578
1946
|
async handleTask(task) {
|
|
1579
1947
|
// Kill Switch: Block task execution if paused
|
|
1580
1948
|
if (this.isPaused) {
|
|
1581
|
-
console.log(`[Worker] Task ${task.
|
|
1582
|
-
await this.completeTask(task.
|
|
1949
|
+
console.log(`[Worker] Task ${task.id} rejected - agent is paused`);
|
|
1950
|
+
await this.completeTask(task.id, "failed", null, { message: "Agent has been paused by administrator." });
|
|
1583
1951
|
return;
|
|
1584
1952
|
}
|
|
1585
|
-
console.log(`[Worker] Received task: ${task.
|
|
1953
|
+
console.log(`[Worker] Received task: ${task.id}`);
|
|
1586
1954
|
// Find handler by capability or tool
|
|
1587
|
-
const capability = task.
|
|
1955
|
+
const capability = task.target_capability || task.target_tool || "default";
|
|
1588
1956
|
const handler = this.handlers.get(capability) || this.handlers.get("default");
|
|
1589
1957
|
if (!handler) {
|
|
1590
1958
|
console.warn(`[Worker] No handler for capability: ${capability}`);
|
|
1591
|
-
await this.completeTask(task.
|
|
1959
|
+
await this.completeTask(task.id, "failed", null, {
|
|
1592
1960
|
error: `No handler registered for capability: ${capability}`,
|
|
1593
1961
|
});
|
|
1594
1962
|
return;
|
|
1595
1963
|
}
|
|
1596
1964
|
const ctx = {
|
|
1597
|
-
taskId: task.
|
|
1965
|
+
taskId: task.id,
|
|
1598
1966
|
workspaceId: task.workspace_id,
|
|
1599
1967
|
requesterId: task.requester_agent_id,
|
|
1600
1968
|
parentTaskId: task.parent_task_id,
|
|
@@ -1604,12 +1972,12 @@ class TaskWorker {
|
|
|
1604
1972
|
try {
|
|
1605
1973
|
console.log(`[Worker] Executing handler for: ${capability}`);
|
|
1606
1974
|
const result = await handler(ctx, task.input);
|
|
1607
|
-
await this.completeTask(task.
|
|
1608
|
-
console.log(`[Worker] Task ${task.
|
|
1975
|
+
await this.completeTask(task.id, "completed", result, null);
|
|
1976
|
+
console.log(`[Worker] Task ${task.id} completed successfully`);
|
|
1609
1977
|
}
|
|
1610
1978
|
catch (error) {
|
|
1611
|
-
console.error(`[Worker] Task ${task.
|
|
1612
|
-
await this.completeTask(task.
|
|
1979
|
+
console.error(`[Worker] Task ${task.id} failed:`, error);
|
|
1980
|
+
await this.completeTask(task.id, "failed", null, {
|
|
1613
1981
|
error: error instanceof Error ? error.message : String(error),
|
|
1614
1982
|
});
|
|
1615
1983
|
}
|
|
@@ -1670,6 +2038,12 @@ class TaskWorker {
|
|
|
1670
2038
|
this.expiresAt = data.expires_at
|
|
1671
2039
|
? new Date(data.expires_at).getTime()
|
|
1672
2040
|
: null;
|
|
2041
|
+
this.credentialsStore?.update({
|
|
2042
|
+
installationId: data.installation_id,
|
|
2043
|
+
runtimeToken: data.runtime_token,
|
|
2044
|
+
refreshToken: data.refresh_token,
|
|
2045
|
+
expiresAt: data.expires_at,
|
|
2046
|
+
});
|
|
1673
2047
|
console.log(`[Worker] Bootstrapped installation: ${this.installationId}`);
|
|
1674
2048
|
}
|
|
1675
2049
|
catch (e) {
|
|
@@ -1682,12 +2056,13 @@ class TaskWorker {
|
|
|
1682
2056
|
*/
|
|
1683
2057
|
startHeartbeat() {
|
|
1684
2058
|
this.heartbeatInterval = setInterval(async () => {
|
|
1685
|
-
|
|
2059
|
+
const installationId = this.getInstallationId();
|
|
2060
|
+
if (!installationId) {
|
|
1686
2061
|
return;
|
|
1687
2062
|
}
|
|
1688
2063
|
try {
|
|
1689
2064
|
const deploymentUrl = this.config.deploymentUrl || detectDeploymentUrl();
|
|
1690
|
-
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${
|
|
2065
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/lease`, {
|
|
1691
2066
|
method: "POST",
|
|
1692
2067
|
headers: {
|
|
1693
2068
|
"Content-Type": "application/json",
|
|
@@ -1713,6 +2088,15 @@ class TaskWorker {
|
|
|
1713
2088
|
}
|
|
1714
2089
|
if (response.ok) {
|
|
1715
2090
|
const data = await response.json();
|
|
2091
|
+
// Handle server-side token auto-refresh (sliding expiration)
|
|
2092
|
+
if (data.refreshed_token) {
|
|
2093
|
+
this.runtimeToken = data.refreshed_token.runtime_token;
|
|
2094
|
+
if (data.refreshed_token.expires_at) {
|
|
2095
|
+
this.expiresAt = new Date(data.refreshed_token.expires_at).getTime();
|
|
2096
|
+
}
|
|
2097
|
+
this.credentialsStore?.setRuntimeToken(data.refreshed_token.runtime_token, data.refreshed_token.expires_at);
|
|
2098
|
+
console.log(`[Worker] Token auto-refreshed by server (version ${data.refreshed_token.token_version})`);
|
|
2099
|
+
}
|
|
1716
2100
|
if (data.command) {
|
|
1717
2101
|
this.handleCommand(data.command);
|
|
1718
2102
|
}
|
|
@@ -1727,16 +2111,18 @@ class TaskWorker {
|
|
|
1727
2111
|
* Refresh the runtime token using the refresh token
|
|
1728
2112
|
*/
|
|
1729
2113
|
async refreshRuntimeToken() {
|
|
1730
|
-
|
|
2114
|
+
const installationId = this.getInstallationId();
|
|
2115
|
+
const refreshToken = this.getRefreshToken();
|
|
2116
|
+
if (!installationId || !refreshToken) {
|
|
1731
2117
|
throw new Error("Cannot refresh token - missing installation ID or refresh token");
|
|
1732
2118
|
}
|
|
1733
|
-
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${
|
|
2119
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/v1/installations/${installationId}/refresh`, {
|
|
1734
2120
|
method: "POST",
|
|
1735
2121
|
headers: {
|
|
1736
2122
|
"Content-Type": "application/json",
|
|
1737
2123
|
},
|
|
1738
2124
|
body: JSON.stringify({
|
|
1739
|
-
refresh_token:
|
|
2125
|
+
refresh_token: refreshToken,
|
|
1740
2126
|
}),
|
|
1741
2127
|
});
|
|
1742
2128
|
if (!response.ok) {
|
|
@@ -1745,8 +2131,25 @@ class TaskWorker {
|
|
|
1745
2131
|
}
|
|
1746
2132
|
const data = await response.json();
|
|
1747
2133
|
this.runtimeToken = data.runtime_token;
|
|
2134
|
+
this.credentialsStore?.setRuntimeToken(data.runtime_token, data.expires_at);
|
|
1748
2135
|
console.log("[Worker] Runtime token refreshed successfully");
|
|
1749
2136
|
}
|
|
2137
|
+
getInstallationId() {
|
|
2138
|
+
const storeValue = this.credentialsStore?.getInstallationId();
|
|
2139
|
+
if (storeValue) {
|
|
2140
|
+
this.installationId = storeValue;
|
|
2141
|
+
return storeValue;
|
|
2142
|
+
}
|
|
2143
|
+
return this.installationId;
|
|
2144
|
+
}
|
|
2145
|
+
getRefreshToken() {
|
|
2146
|
+
const storeValue = this.credentialsStore?.getRefreshToken();
|
|
2147
|
+
if (storeValue) {
|
|
2148
|
+
this.refreshToken = storeValue;
|
|
2149
|
+
return storeValue;
|
|
2150
|
+
}
|
|
2151
|
+
return this.refreshToken;
|
|
2152
|
+
}
|
|
1750
2153
|
}
|
|
1751
2154
|
// ============================================================================
|
|
1752
2155
|
// Convenience Export
|
|
@@ -1769,10 +2172,16 @@ class SekuireSDK {
|
|
|
1769
2172
|
}
|
|
1770
2173
|
const apiKey = config.apiKey || process.env.SEKUIRE_API_KEY;
|
|
1771
2174
|
const installToken = config.installToken || process.env.SEKUIRE_INSTALL_TOKEN;
|
|
2175
|
+
const installationId = config.installationId || process.env.SEKUIRE_INSTALLATION_ID;
|
|
2176
|
+
const refreshToken = config.refreshToken || process.env.SEKUIRE_REFRESH_TOKEN;
|
|
2177
|
+
const runtimeToken = config.runtimeToken || process.env.SEKUIRE_RUNTIME_TOKEN;
|
|
1772
2178
|
this.config = {
|
|
1773
2179
|
privateKey: config.privateKey,
|
|
1774
2180
|
apiKey,
|
|
1775
2181
|
installToken,
|
|
2182
|
+
installationId,
|
|
2183
|
+
refreshToken,
|
|
2184
|
+
runtimeToken,
|
|
1776
2185
|
agentId: config.agentId,
|
|
1777
2186
|
agentName: config.agentName ?? "Unknown",
|
|
1778
2187
|
apiUrl: (config.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, ""),
|
|
@@ -1783,6 +2192,11 @@ class SekuireSDK {
|
|
|
1783
2192
|
loggingEnabled: config.loggingEnabled !== false,
|
|
1784
2193
|
capabilities: config.capabilities ?? [],
|
|
1785
2194
|
};
|
|
2195
|
+
this.credentialsStore = new RuntimeCredentialsStore({
|
|
2196
|
+
installationId: this.config.installationId,
|
|
2197
|
+
refreshToken: this.config.refreshToken,
|
|
2198
|
+
runtimeToken: this.config.runtimeToken,
|
|
2199
|
+
});
|
|
1786
2200
|
this.identity = new AgentIdentity(this.config.agentName, this.config.agentId, this.config.privateKey);
|
|
1787
2201
|
const loggerConfig = {
|
|
1788
2202
|
sekuireId: this.config.agentId,
|
|
@@ -1806,6 +2220,10 @@ class SekuireSDK {
|
|
|
1806
2220
|
workspaceId: this.config.workspaceId || undefined,
|
|
1807
2221
|
heartbeatIntervalSeconds: this.config.heartbeatIntervalSeconds,
|
|
1808
2222
|
capabilities: this.config.capabilities,
|
|
2223
|
+
installationId: this.config.installationId,
|
|
2224
|
+
refreshToken: this.config.refreshToken,
|
|
2225
|
+
runtimeToken: this.config.runtimeToken,
|
|
2226
|
+
credentialsStore: this.credentialsStore,
|
|
1809
2227
|
});
|
|
1810
2228
|
}
|
|
1811
2229
|
/**
|
|
@@ -1813,7 +2231,10 @@ class SekuireSDK {
|
|
|
1813
2231
|
*
|
|
1814
2232
|
* Required env vars:
|
|
1815
2233
|
* SEKUIRE_AGENT_ID - The agent's sekuire_id
|
|
1816
|
-
*
|
|
2234
|
+
*
|
|
2235
|
+
* Authentication (one of the following):
|
|
2236
|
+
* SEKUIRE_INSTALL_TOKEN - Install token from dashboard (for initial bootstrap)
|
|
2237
|
+
* SEKUIRE_INSTALLATION_ID + SEKUIRE_REFRESH_TOKEN - For credential recovery
|
|
1817
2238
|
*
|
|
1818
2239
|
* Optional env vars:
|
|
1819
2240
|
* SEKUIRE_API_KEY - API key for authentication (X-API-Key header)
|
|
@@ -1822,6 +2243,7 @@ class SekuireSDK {
|
|
|
1822
2243
|
* SEKUIRE_API_URL - API base URL (default: https://api.sekuire.ai)
|
|
1823
2244
|
* SEKUIRE_WORKSPACE_ID - Workspace ID for policy enforcement
|
|
1824
2245
|
* SEKUIRE_ENVIRONMENT - Environment name (default: production)
|
|
2246
|
+
* SEKUIRE_RUNTIME_TOKEN - Pre-existing runtime token (skips refresh if valid)
|
|
1825
2247
|
*/
|
|
1826
2248
|
static fromEnv(options) {
|
|
1827
2249
|
const agentId = process.env.SEKUIRE_AGENT_ID;
|
|
@@ -1832,6 +2254,9 @@ class SekuireSDK {
|
|
|
1832
2254
|
privateKey: process.env.SEKUIRE_PRIVATE_KEY,
|
|
1833
2255
|
apiKey: process.env.SEKUIRE_API_KEY,
|
|
1834
2256
|
installToken: process.env.SEKUIRE_INSTALL_TOKEN,
|
|
2257
|
+
installationId: process.env.SEKUIRE_INSTALLATION_ID,
|
|
2258
|
+
refreshToken: process.env.SEKUIRE_REFRESH_TOKEN,
|
|
2259
|
+
runtimeToken: process.env.SEKUIRE_RUNTIME_TOKEN,
|
|
1835
2260
|
agentId,
|
|
1836
2261
|
agentName: process.env.SEKUIRE_AGENT_NAME,
|
|
1837
2262
|
apiUrl: process.env.SEKUIRE_API_URL ?? DEFAULT_API_URL,
|
|
@@ -1844,22 +2269,27 @@ class SekuireSDK {
|
|
|
1844
2269
|
/**
|
|
1845
2270
|
* Start the SDK.
|
|
1846
2271
|
*
|
|
1847
|
-
* - Bootstraps with Sekuire using
|
|
2272
|
+
* - Bootstraps with Sekuire using install token or recovery credentials
|
|
1848
2273
|
* - Starts auto-heartbeat loop if enabled
|
|
1849
2274
|
*
|
|
1850
|
-
*
|
|
2275
|
+
* Authentication (one of):
|
|
2276
|
+
* - SEKUIRE_INSTALL_TOKEN for initial bootstrap
|
|
2277
|
+
* - SEKUIRE_INSTALLATION_ID + SEKUIRE_REFRESH_TOKEN for recovery
|
|
1851
2278
|
*/
|
|
1852
2279
|
async start() {
|
|
1853
2280
|
if (this.isRunning) {
|
|
1854
2281
|
return;
|
|
1855
2282
|
}
|
|
1856
2283
|
this.isRunning = true;
|
|
1857
|
-
|
|
2284
|
+
const hasInstallToken = !!this.config.installToken;
|
|
2285
|
+
const hasRecoveryCredentials = !!(this.config.installationId && this.config.refreshToken);
|
|
2286
|
+
if (this.config.autoHeartbeat && (hasInstallToken || hasRecoveryCredentials)) {
|
|
1858
2287
|
await this.beacon.start();
|
|
1859
2288
|
}
|
|
1860
|
-
else if (this.config.autoHeartbeat && !
|
|
1861
|
-
console.warn("[SekuireSDK] Auto-heartbeat enabled but no
|
|
1862
|
-
"
|
|
2289
|
+
else if (this.config.autoHeartbeat && !hasInstallToken && !hasRecoveryCredentials) {
|
|
2290
|
+
console.warn("[SekuireSDK] Auto-heartbeat enabled but no credentials provided. Set one of:" +
|
|
2291
|
+
"\n - SEKUIRE_INSTALL_TOKEN (for initial bootstrap)" +
|
|
2292
|
+
"\n - SEKUIRE_INSTALLATION_ID + SEKUIRE_REFRESH_TOKEN (for recovery)");
|
|
1863
2293
|
}
|
|
1864
2294
|
}
|
|
1865
2295
|
/**
|
|
@@ -1934,6 +2364,41 @@ class SekuireSDK {
|
|
|
1934
2364
|
getRuntimeCredentials() {
|
|
1935
2365
|
return this.beacon.getRuntimeCredentials();
|
|
1936
2366
|
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Get full installation credentials for persistence.
|
|
2369
|
+
*
|
|
2370
|
+
* Use this after successful SDK start to extract credentials that can be
|
|
2371
|
+
* saved as environment variables or secrets for future container restarts.
|
|
2372
|
+
* These credentials allow recovery without consuming a new install token.
|
|
2373
|
+
*
|
|
2374
|
+
* Recommended workflow for ephemeral compute (Cloud Run, K8s, etc.):
|
|
2375
|
+
* 1. First deploy: use SEKUIRE_INSTALL_TOKEN for initial bootstrap
|
|
2376
|
+
* 2. After start: call getInstallationCredentials() to extract creds
|
|
2377
|
+
* 3. Save to secrets manager (AWS Secrets Manager, K8s Secret, etc.)
|
|
2378
|
+
* 4. Update deployment with:
|
|
2379
|
+
* - SEKUIRE_INSTALLATION_ID
|
|
2380
|
+
* - SEKUIRE_REFRESH_TOKEN
|
|
2381
|
+
* - (optionally) SEKUIRE_RUNTIME_TOKEN
|
|
2382
|
+
* 5. Future restarts: SDK auto-recovers using refresh token
|
|
2383
|
+
*
|
|
2384
|
+
* @example
|
|
2385
|
+
* ```ts
|
|
2386
|
+
* const sdk = SekuireSDK.fromEnv();
|
|
2387
|
+
* await sdk.start();
|
|
2388
|
+
*
|
|
2389
|
+
* const creds = sdk.getInstallationCredentials();
|
|
2390
|
+
* if (creds) {
|
|
2391
|
+
* console.log('Save these to your secrets manager:');
|
|
2392
|
+
* console.log(`SEKUIRE_INSTALLATION_ID=${creds.installationId}`);
|
|
2393
|
+
* console.log(`SEKUIRE_REFRESH_TOKEN=${creds.refreshToken}`);
|
|
2394
|
+
* }
|
|
2395
|
+
* ```
|
|
2396
|
+
*
|
|
2397
|
+
* @returns Full installation credentials or null if not bootstrapped
|
|
2398
|
+
*/
|
|
2399
|
+
getInstallationCredentials() {
|
|
2400
|
+
return this.beacon.getInstallationCredentials();
|
|
2401
|
+
}
|
|
1937
2402
|
/**
|
|
1938
2403
|
* Create a TaskWorker that shares credentials with this SDK instance.
|
|
1939
2404
|
*
|
|
@@ -1941,6 +2406,9 @@ class SekuireSDK {
|
|
|
1941
2406
|
* the runtime credentials from the SDK's beacon. The SDK must be started
|
|
1942
2407
|
* before calling this method.
|
|
1943
2408
|
*
|
|
2409
|
+
* The Worker receives full credentials (installationId, runtimeToken, refreshToken)
|
|
2410
|
+
* so it can independently refresh tokens when they expire.
|
|
2411
|
+
*
|
|
1944
2412
|
* @param options Optional worker configuration overrides
|
|
1945
2413
|
* @returns TaskWorker instance ready to be started
|
|
1946
2414
|
* @throws Error if SDK hasn't been started or bootstrap failed
|
|
@@ -1958,7 +2426,7 @@ class SekuireSDK {
|
|
|
1958
2426
|
* ```
|
|
1959
2427
|
*/
|
|
1960
2428
|
createTaskWorker(options) {
|
|
1961
|
-
const credentials = this.
|
|
2429
|
+
const credentials = this.getInstallationCredentials();
|
|
1962
2430
|
if (!credentials) {
|
|
1963
2431
|
throw new Error("SDK must be started before creating a TaskWorker. " +
|
|
1964
2432
|
"Call sdk.start() first and ensure bootstrap succeeded.");
|
|
@@ -1967,6 +2435,10 @@ class SekuireSDK {
|
|
|
1967
2435
|
apiBaseUrl: this.config.apiUrl,
|
|
1968
2436
|
agentId: this.config.agentId,
|
|
1969
2437
|
runtimeToken: credentials.runtimeToken,
|
|
2438
|
+
installationId: credentials.installationId,
|
|
2439
|
+
refreshToken: credentials.refreshToken,
|
|
2440
|
+
credentialsStore: this.credentialsStore,
|
|
2441
|
+
tokenProvider: () => this.credentialsStore.getRuntimeToken(),
|
|
1970
2442
|
deploymentUrl: detectDeploymentUrl(),
|
|
1971
2443
|
capabilities: options?.capabilities ?? this.config.capabilities,
|
|
1972
2444
|
heartbeatIntervalMs: options?.heartbeatIntervalMs,
|
|
@@ -16703,7 +17175,7 @@ class A2AClient {
|
|
|
16703
17175
|
const event = JSON.parse(data);
|
|
16704
17176
|
yield event;
|
|
16705
17177
|
// Stop on terminal states
|
|
16706
|
-
if (["completed", "failed", "
|
|
17178
|
+
if (["completed", "failed", "canceled"].includes(event.status)) {
|
|
16707
17179
|
return;
|
|
16708
17180
|
}
|
|
16709
17181
|
}
|
|
@@ -16883,7 +17355,7 @@ class A2AServer {
|
|
|
16883
17355
|
task = {
|
|
16884
17356
|
id: taskId,
|
|
16885
17357
|
contextId: undefined,
|
|
16886
|
-
status: "
|
|
17358
|
+
status: "submitted",
|
|
16887
17359
|
history: [],
|
|
16888
17360
|
artifacts: [],
|
|
16889
17361
|
createdAt: new Date(),
|
|
@@ -16996,7 +17468,7 @@ class A2AServer {
|
|
|
16996
17468
|
const self = this;
|
|
16997
17469
|
return (async function* () {
|
|
16998
17470
|
yield self.createUpdateEvent(task);
|
|
16999
|
-
if (["completed", "failed", "
|
|
17471
|
+
if (["completed", "failed", "canceled"].includes(task.status)) {
|
|
17000
17472
|
return;
|
|
17001
17473
|
}
|
|
17002
17474
|
const queue = [];
|
|
@@ -17014,7 +17486,7 @@ class A2AServer {
|
|
|
17014
17486
|
while (queue.length > 0) {
|
|
17015
17487
|
const event = queue.shift();
|
|
17016
17488
|
yield event;
|
|
17017
|
-
if (["completed", "failed", "
|
|
17489
|
+
if (["completed", "failed", "canceled"].includes(event.status)) {
|
|
17018
17490
|
return;
|
|
17019
17491
|
}
|
|
17020
17492
|
}
|
|
@@ -17051,7 +17523,7 @@ class A2AServer {
|
|
|
17051
17523
|
task = {
|
|
17052
17524
|
id: taskId,
|
|
17053
17525
|
contextId: params.contextId,
|
|
17054
|
-
status: "
|
|
17526
|
+
status: "submitted",
|
|
17055
17527
|
history: [],
|
|
17056
17528
|
artifacts: [],
|
|
17057
17529
|
createdAt: new Date(),
|
|
@@ -17120,10 +17592,10 @@ class A2AServer {
|
|
|
17120
17592
|
if (!task) {
|
|
17121
17593
|
return this.error(request.id, -32001, "Task not found");
|
|
17122
17594
|
}
|
|
17123
|
-
if (["completed", "failed", "
|
|
17595
|
+
if (["completed", "failed", "canceled"].includes(task.status)) {
|
|
17124
17596
|
return this.error(request.id, -32001, "Task already completed");
|
|
17125
17597
|
}
|
|
17126
|
-
task.status = "
|
|
17598
|
+
task.status = "canceled";
|
|
17127
17599
|
task.updatedAt = new Date();
|
|
17128
17600
|
this.notifySubscribers(task.id);
|
|
17129
17601
|
return this.success(request.id, this.taskToResponse(task));
|
|
@@ -17312,7 +17784,7 @@ class A2ATaskDelegator {
|
|
|
17312
17784
|
try {
|
|
17313
17785
|
for await (const event of this.client.subscribe(taskId)) {
|
|
17314
17786
|
yield event;
|
|
17315
|
-
if (["completed", "failed", "
|
|
17787
|
+
if (["completed", "failed", "canceled"].includes(event.status)) {
|
|
17316
17788
|
finalTask = await this.client.getTask(taskId);
|
|
17317
17789
|
break;
|
|
17318
17790
|
}
|
|
@@ -17376,7 +17848,7 @@ class A2ATaskDelegator {
|
|
|
17376
17848
|
if (onProgress) {
|
|
17377
17849
|
onProgress(event);
|
|
17378
17850
|
}
|
|
17379
|
-
if (["completed", "failed", "
|
|
17851
|
+
if (["completed", "failed", "canceled"].includes(event.status)) {
|
|
17380
17852
|
return this.client.getTask(taskId);
|
|
17381
17853
|
}
|
|
17382
17854
|
if (Date.now() > deadline) {
|
|
@@ -17407,7 +17879,7 @@ class A2ATaskDelegator {
|
|
|
17407
17879
|
artifacts: task.artifacts,
|
|
17408
17880
|
});
|
|
17409
17881
|
}
|
|
17410
|
-
if (["completed", "failed", "
|
|
17882
|
+
if (["completed", "failed", "canceled"].includes(task.status.state)) {
|
|
17411
17883
|
return task;
|
|
17412
17884
|
}
|
|
17413
17885
|
await this.sleep(this.config.pollInterval);
|
|
@@ -17422,4 +17894,4 @@ function createDelegator(config) {
|
|
|
17422
17894
|
return new A2ATaskDelegator(config);
|
|
17423
17895
|
}
|
|
17424
17896
|
|
|
17425
|
-
export { A2AClient, A2AError, A2AServer, A2ATaskDelegator, SekuireAgent as Agent, AgentIdentity, AnthropicProvider, BaseMemoryStorage, Beacon, CONVEX_FUNCTIONS_TEMPLATE, CONVEX_SCHEMA_TEMPLATE, CloudflareD1Storage, CloudflareKVStorage, ComplianceError, ComplianceMonitor, ContentPolicyError, ConvexStorage, CryptoError, DEFAULT_API_URL, DynamoDBStorage, FileAccessError, GoogleProvider, InMemoryStorage, NetworkComplianceError, NetworkError, OllamaProvider, OpenAIProvider, PolicyClient, PolicyEnforcer, PolicyViolationError, PostgresStorage, ProtocolError, RedisStorage, SQLiteStorage, SekuireAgent$1 as SekuireAgent, SekuireAgentBuilder, SekuireClient, SekuireCrypto, SekuireError, SekuireLogger, SekuireRegistryClient, SekuireSDK, SekuireServer, SekuireSpanExporter, TaskWorker, ToolPatternParser, ToolRegistry, ToolUsageError, TursoStorage, UpstashStorage, builtInTools, calculateSekuireId, createAgent, createBeacon, createDefaultToolRegistry, createDelegationTool, createDelegator, createDiscoveryTool, createLLMProvider, createMemoryStorage, createRegistryClient, createSekuireClient, createSekuireExpressMiddleware, createSekuireFastifyPlugin, createWorker, detectDeploymentUrl, generateKeyPair, getAgent, getAgentConfig, getAgents, getTools$1 as getLegacyTools, getStorageInfo, getSystemPrompt, getTools, getTracer, hasStorage, initTelemetry, listStorageTypes, llm, loadConfig, loadSystemPrompt, loadTools, registerStorage, shutdownTelemetry, tool, tools };
|
|
17897
|
+
export { A2AClient, A2AError, A2AServer, A2ATaskDelegator, SekuireAgent as Agent, AgentIdentity, AnthropicProvider, BaseMemoryStorage, Beacon, CONVEX_FUNCTIONS_TEMPLATE, CONVEX_SCHEMA_TEMPLATE, CloudflareD1Storage, CloudflareKVStorage, ComplianceError, ComplianceMonitor, ContentPolicyError, ConvexStorage, CryptoError, DEFAULT_API_URL, DynamoDBStorage, FileAccessError, GoogleProvider, InMemoryStorage, NetworkComplianceError, NetworkError, OllamaProvider, OpenAIProvider, PolicyClient, PolicyEnforcer, PolicyViolationError, PostgresStorage, ProtocolError, RedisStorage, RuntimeCredentialsStore, SQLiteStorage, SekuireAgent$1 as SekuireAgent, SekuireAgentBuilder, SekuireClient, SekuireCrypto, SekuireError, SekuireLogger, SekuireRegistryClient, SekuireSDK, SekuireServer, SekuireSpanExporter, TaskWorker, ToolPatternParser, ToolRegistry, ToolUsageError, TursoStorage, UpstashStorage, builtInTools, calculateSekuireId, createAgent, createBeacon, createDefaultToolRegistry, createDelegationTool, createDelegator, createDiscoveryTool, createLLMProvider, createMemoryStorage, createRegistryClient, createSekuireClient, createSekuireExpressMiddleware, createSekuireFastifyPlugin, createWorker, detectDeploymentUrl, generateKeyPair, getAgent, getAgentConfig, getAgents, getTools$1 as getLegacyTools, getStorageInfo, getSystemPrompt, getTools, getTracer, hasStorage, initTelemetry, listStorageTypes, llm, loadConfig, loadSystemPrompt, loadTools, registerStorage, shutdownTelemetry, tool, tools };
|