agentlife 1.4.1 → 1.5.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/index.js +127 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5289,9 +5289,12 @@ import * as fs7 from "node:fs";
|
|
|
5289
5289
|
import * as os5 from "node:os";
|
|
5290
5290
|
import * as path9 from "node:path";
|
|
5291
5291
|
var AGENTLIFE_DIR = path9.join(os5.homedir(), ".openclaw", "agentlife");
|
|
5292
|
-
var DEVICE_FILE = path9.join(AGENTLIFE_DIR, "device.json");
|
|
5293
5292
|
var TUNNEL_FILE = path9.join(AGENTLIFE_DIR, "tunnel.json");
|
|
5294
5293
|
var BIN_DIR = path9.join(AGENTLIFE_DIR, "bin");
|
|
5294
|
+
var PAIR_REQUEST_MARKER = path9.join(AGENTLIFE_DIR, "pair-requested");
|
|
5295
|
+
var IDENTITY_DIR = path9.join(os5.homedir(), ".agentlife");
|
|
5296
|
+
var DEVICE_FILE = path9.join(IDENTITY_DIR, "device.json");
|
|
5297
|
+
var LEGACY_DEVICE_FILE = path9.join(AGENTLIFE_DIR, "device.json");
|
|
5295
5298
|
var CLOUDFLARED_BIN = os5.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
5296
5299
|
var CLOUDFLARED_PATH = path9.join(BIN_DIR, CLOUDFLARED_BIN);
|
|
5297
5300
|
var API_BASE = process.env.AGENTLIFE_API_BASE || "https://api.agentlife.app";
|
|
@@ -5306,9 +5309,12 @@ function createInitialState() {
|
|
|
5306
5309
|
tunnelInfo: null,
|
|
5307
5310
|
process: null,
|
|
5308
5311
|
restartTimer: null,
|
|
5312
|
+
provisionRetryTimer: null,
|
|
5309
5313
|
stopped: false,
|
|
5310
5314
|
readyPromise: promise,
|
|
5311
|
-
readyResolve: resolver
|
|
5315
|
+
readyResolve: resolver,
|
|
5316
|
+
inFlight: null,
|
|
5317
|
+
binPath: null
|
|
5312
5318
|
};
|
|
5313
5319
|
}
|
|
5314
5320
|
var state = createInitialState();
|
|
@@ -5336,6 +5342,10 @@ function stopCloudflaredSupervisor() {
|
|
|
5336
5342
|
clearTimeout(state.restartTimer);
|
|
5337
5343
|
state.restartTimer = null;
|
|
5338
5344
|
}
|
|
5345
|
+
if (state.provisionRetryTimer) {
|
|
5346
|
+
clearTimeout(state.provisionRetryTimer);
|
|
5347
|
+
state.provisionRetryTimer = null;
|
|
5348
|
+
}
|
|
5339
5349
|
if (state.process) {
|
|
5340
5350
|
try {
|
|
5341
5351
|
state.process.kill("SIGTERM");
|
|
@@ -5344,10 +5354,30 @@ function stopCloudflaredSupervisor() {
|
|
|
5344
5354
|
}
|
|
5345
5355
|
}
|
|
5346
5356
|
async function bootstrap() {
|
|
5347
|
-
const result = await
|
|
5348
|
-
state.tunnelInfo = result;
|
|
5357
|
+
const result = await runBootstrap();
|
|
5349
5358
|
state.readyResolve(result);
|
|
5350
5359
|
}
|
|
5360
|
+
async function runBootstrap() {
|
|
5361
|
+
if (state.inFlight)
|
|
5362
|
+
return state.inFlight;
|
|
5363
|
+
const promise = doBootstrap().then((result) => {
|
|
5364
|
+
if (result)
|
|
5365
|
+
state.tunnelInfo = result;
|
|
5366
|
+
return result;
|
|
5367
|
+
}).finally(() => {
|
|
5368
|
+
state.inFlight = null;
|
|
5369
|
+
});
|
|
5370
|
+
state.inFlight = promise;
|
|
5371
|
+
return promise;
|
|
5372
|
+
}
|
|
5373
|
+
function triggerProvision() {
|
|
5374
|
+
if (state.stopped)
|
|
5375
|
+
return;
|
|
5376
|
+
requestPair();
|
|
5377
|
+
runBootstrap().catch((err) => {
|
|
5378
|
+
console.warn("[cloudflared-supervisor] triggered provision failed:", err?.message ?? err);
|
|
5379
|
+
});
|
|
5380
|
+
}
|
|
5351
5381
|
async function doBootstrap() {
|
|
5352
5382
|
ensureDirs();
|
|
5353
5383
|
const identity = loadOrCreateDeviceIdentity();
|
|
@@ -5357,29 +5387,84 @@ async function doBootstrap() {
|
|
|
5357
5387
|
return null;
|
|
5358
5388
|
}
|
|
5359
5389
|
let tunnelInfo = loadCachedTunnel();
|
|
5360
|
-
if (!tunnelInfo
|
|
5390
|
+
if (!tunnelInfo) {
|
|
5391
|
+
if (!isPairRequested()) {
|
|
5392
|
+
console.log("[cloudflared-supervisor] no tunnel cached and no pair request — staying idle (LAN-only)");
|
|
5393
|
+
return null;
|
|
5394
|
+
}
|
|
5395
|
+
console.log("[cloudflared-supervisor] pair requested — provisioning fresh tunnel");
|
|
5361
5396
|
tunnelInfo = await provisionTunnel(identity);
|
|
5362
5397
|
if (!tunnelInfo) {
|
|
5363
|
-
console.
|
|
5398
|
+
console.log("[cloudflared-supervisor] provision attempt did not yield a tunnel — will retry on next pair request");
|
|
5364
5399
|
return null;
|
|
5365
5400
|
}
|
|
5366
5401
|
persistTunnel(tunnelInfo);
|
|
5402
|
+
clearPairRequest();
|
|
5403
|
+
} else if (isPairRequested()) {
|
|
5404
|
+
console.log("[cloudflared-supervisor] pair requested with cache present — re-provisioning");
|
|
5405
|
+
const refreshed = await provisionTunnel(identity);
|
|
5406
|
+
if (refreshed) {
|
|
5407
|
+
tunnelInfo = refreshed;
|
|
5408
|
+
persistTunnel(tunnelInfo);
|
|
5409
|
+
} else {
|
|
5410
|
+
console.warn("[cloudflared-supervisor] re-provision failed — keeping cached tunnel");
|
|
5411
|
+
}
|
|
5412
|
+
clearPairRequest();
|
|
5367
5413
|
}
|
|
5368
5414
|
console.log(`[cloudflared-supervisor] tunnel ready: ${tunnelInfo.tunnelUrl}`);
|
|
5369
5415
|
startCloudflaredProcess(binPath, tunnelInfo.tunnelToken);
|
|
5370
5416
|
return tunnelInfo;
|
|
5371
5417
|
}
|
|
5418
|
+
function isPairRequested() {
|
|
5419
|
+
return fs7.existsSync(PAIR_REQUEST_MARKER);
|
|
5420
|
+
}
|
|
5421
|
+
function requestPair() {
|
|
5422
|
+
try {
|
|
5423
|
+
if (!fs7.existsSync(AGENTLIFE_DIR))
|
|
5424
|
+
fs7.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
|
|
5425
|
+
fs7.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
|
|
5426
|
+
} catch (err) {
|
|
5427
|
+
console.warn(`[cloudflared-supervisor] failed to write pair-request marker: ${err?.message ?? err}`);
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
function clearPairRequest() {
|
|
5431
|
+
try {
|
|
5432
|
+
fs7.unlinkSync(PAIR_REQUEST_MARKER);
|
|
5433
|
+
} catch {}
|
|
5434
|
+
}
|
|
5435
|
+
function parseRetryAfter(value) {
|
|
5436
|
+
const n = value == null ? NaN : parseInt(value.trim(), 10);
|
|
5437
|
+
if (!Number.isFinite(n) || n < 1)
|
|
5438
|
+
return 60;
|
|
5439
|
+
return Math.min(n, 3600);
|
|
5440
|
+
}
|
|
5441
|
+
function scheduleProvisionRetry(delayMs) {
|
|
5442
|
+
if (state.stopped)
|
|
5443
|
+
return;
|
|
5444
|
+
if (state.provisionRetryTimer) {
|
|
5445
|
+
clearTimeout(state.provisionRetryTimer);
|
|
5446
|
+
state.provisionRetryTimer = null;
|
|
5447
|
+
}
|
|
5448
|
+
state.provisionRetryTimer = setTimeout(() => {
|
|
5449
|
+
state.provisionRetryTimer = null;
|
|
5450
|
+
if (state.stopped || state.tunnelInfo)
|
|
5451
|
+
return;
|
|
5452
|
+
triggerProvision();
|
|
5453
|
+
}, delayMs);
|
|
5454
|
+
}
|
|
5372
5455
|
function ensureDirs() {
|
|
5373
5456
|
if (!fs7.existsSync(AGENTLIFE_DIR))
|
|
5374
5457
|
fs7.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
|
|
5375
5458
|
if (!fs7.existsSync(BIN_DIR))
|
|
5376
5459
|
fs7.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
|
|
5460
|
+
if (!fs7.existsSync(IDENTITY_DIR))
|
|
5461
|
+
fs7.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
|
|
5377
5462
|
}
|
|
5378
|
-
function
|
|
5379
|
-
if (!fs7.existsSync(
|
|
5463
|
+
function readIdentityFile(filePath) {
|
|
5464
|
+
if (!fs7.existsSync(filePath))
|
|
5380
5465
|
return null;
|
|
5381
5466
|
try {
|
|
5382
|
-
const raw = fs7.readFileSync(
|
|
5467
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
5383
5468
|
const parsed = JSON.parse(raw);
|
|
5384
5469
|
if (typeof parsed.deviceId === "string" && typeof parsed.deviceSecret === "string") {
|
|
5385
5470
|
return { deviceId: parsed.deviceId, deviceSecret: parsed.deviceSecret };
|
|
@@ -5389,10 +5474,35 @@ function loadDeviceIdentity() {
|
|
|
5389
5474
|
return null;
|
|
5390
5475
|
}
|
|
5391
5476
|
}
|
|
5477
|
+
function loadDeviceIdentity() {
|
|
5478
|
+
const current = readIdentityFile(DEVICE_FILE);
|
|
5479
|
+
if (current)
|
|
5480
|
+
return current;
|
|
5481
|
+
const legacy = readIdentityFile(LEGACY_DEVICE_FILE);
|
|
5482
|
+
if (!legacy)
|
|
5483
|
+
return null;
|
|
5484
|
+
try {
|
|
5485
|
+
if (!fs7.existsSync(IDENTITY_DIR))
|
|
5486
|
+
fs7.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
|
|
5487
|
+
fs7.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
|
|
5488
|
+
try {
|
|
5489
|
+
fs7.chmodSync(DEVICE_FILE, 384);
|
|
5490
|
+
} catch {}
|
|
5491
|
+
try {
|
|
5492
|
+
fs7.unlinkSync(LEGACY_DEVICE_FILE);
|
|
5493
|
+
} catch {}
|
|
5494
|
+
console.log(`[cloudflared-supervisor] migrated device.json to ${DEVICE_FILE}`);
|
|
5495
|
+
} catch (err) {
|
|
5496
|
+
console.warn(`[cloudflared-supervisor] device.json migration failed: ${err?.message ?? err}`);
|
|
5497
|
+
}
|
|
5498
|
+
return legacy;
|
|
5499
|
+
}
|
|
5392
5500
|
function loadOrCreateDeviceIdentity() {
|
|
5393
5501
|
const existing = loadDeviceIdentity();
|
|
5394
5502
|
if (existing)
|
|
5395
5503
|
return existing;
|
|
5504
|
+
if (!fs7.existsSync(IDENTITY_DIR))
|
|
5505
|
+
fs7.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
|
|
5396
5506
|
const identity = {
|
|
5397
5507
|
deviceId: crypto2.randomUUID(),
|
|
5398
5508
|
deviceSecret: crypto2.randomBytes(32).toString("base64url")
|
|
@@ -5436,6 +5546,12 @@ async function provisionTunnel(identity) {
|
|
|
5436
5546
|
});
|
|
5437
5547
|
if (!response.ok) {
|
|
5438
5548
|
const body = await response.text();
|
|
5549
|
+
if (response.status === 503) {
|
|
5550
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
5551
|
+
console.warn(`[cloudflared-supervisor] provision rate-limited by server (HTTP 503); ` + `scheduling retry in ${retryAfter}s`);
|
|
5552
|
+
scheduleProvisionRetry(retryAfter * 1000);
|
|
5553
|
+
return null;
|
|
5554
|
+
}
|
|
5439
5555
|
console.warn(`[cloudflared-supervisor] provision HTTP ${response.status}: ${body.slice(0, 200)}`);
|
|
5440
5556
|
return null;
|
|
5441
5557
|
}
|
|
@@ -5718,6 +5834,8 @@ function registerWebApp(api) {
|
|
|
5718
5834
|
return true;
|
|
5719
5835
|
const bootstrapToken = mintBootstrapToken();
|
|
5720
5836
|
const tunnelInfo = getTunnelInfo();
|
|
5837
|
+
if (!tunnelInfo?.tunnelUrl)
|
|
5838
|
+
triggerProvision();
|
|
5721
5839
|
const payload = { bootstrapToken };
|
|
5722
5840
|
if (tunnelInfo?.tunnelUrl)
|
|
5723
5841
|
payload.tunnelUrl = tunnelInfo.tunnelUrl;
|