@onklave/agent-cli 0.1.30 → 0.1.32
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/main.js +327 -25
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -712,8 +712,8 @@ var PlatformClient = class {
|
|
|
712
712
|
/*
|
|
713
713
|
* Make an authenticated HTTP request to the platform.
|
|
714
714
|
*/
|
|
715
|
-
async request(method,
|
|
716
|
-
const url = `${this.baseUrl}${
|
|
715
|
+
async request(method, path8, body, extraHeaders) {
|
|
716
|
+
const url = `${this.baseUrl}${path8}`;
|
|
717
717
|
const headers = {
|
|
718
718
|
Authorization: `Bearer ${this.token}`,
|
|
719
719
|
"Content-Type": "application/json",
|
|
@@ -2980,7 +2980,7 @@ async function logsCommand(args) {
|
|
|
2980
2980
|
}
|
|
2981
2981
|
|
|
2982
2982
|
// _apps/@onklave/agent-cli/src/commands/daemon.command.ts
|
|
2983
|
-
import * as
|
|
2983
|
+
import * as fs7 from "fs";
|
|
2984
2984
|
|
|
2985
2985
|
// _apps/@onklave/agent-cli/src/services/daemon-comms.service.ts
|
|
2986
2986
|
var DaemonCommsService = class {
|
|
@@ -3070,6 +3070,8 @@ var DaemonClaimHandler = class {
|
|
|
3070
3070
|
this.resourceSampler = opts.resourceSampler;
|
|
3071
3071
|
this.maxCpuPercent = opts.maxCpuPercent ?? 80;
|
|
3072
3072
|
this.maxMemoryBytes = opts.maxMemoryBytes;
|
|
3073
|
+
this.brokerClient = opts.brokerClient;
|
|
3074
|
+
this.workItemRunner = opts.workItemRunner;
|
|
3073
3075
|
}
|
|
3074
3076
|
/**
|
|
3075
3077
|
* Wire up the inbound event subscriptions. Call after the socket has
|
|
@@ -3203,6 +3205,10 @@ var DaemonClaimHandler = class {
|
|
|
3203
3205
|
);
|
|
3204
3206
|
return;
|
|
3205
3207
|
}
|
|
3208
|
+
if (this.brokerClient && this.workItemRunner) {
|
|
3209
|
+
await this.runLocalPickup(event);
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3206
3212
|
if (!this.platformClient || !this.deviceToken) {
|
|
3207
3213
|
console.log(
|
|
3208
3214
|
`[daemon] assignment:claim-available assignmentId=${event.assignmentId} (platformClient not wired \u2014 Phase 5b config missing)`
|
|
@@ -3242,6 +3248,89 @@ var DaemonClaimHandler = class {
|
|
|
3242
3248
|
});
|
|
3243
3249
|
}
|
|
3244
3250
|
}
|
|
3251
|
+
/**
|
|
3252
|
+
* Phase 2 "Slice B" — the full local-execution path for one assignment:
|
|
3253
|
+
* broker.accept → clone + run Claude Code locally → broker.report-result.
|
|
3254
|
+
* The daemon authenticates to the broker with its DEVICE token only; the
|
|
3255
|
+
* broker is the only thing that ever touches teams' internal (S2S) endpoints.
|
|
3256
|
+
* Holds an active-session slot for the lifetime of the run so the capacity
|
|
3257
|
+
* gate stays accurate. Best-effort: failures are audited and, where a session
|
|
3258
|
+
* already exists, reported back as `failed`.
|
|
3259
|
+
*/
|
|
3260
|
+
async runLocalPickup(event) {
|
|
3261
|
+
const broker = this.brokerClient;
|
|
3262
|
+
const runner = this.workItemRunner;
|
|
3263
|
+
this.activeSessions += 1;
|
|
3264
|
+
let sessionId;
|
|
3265
|
+
try {
|
|
3266
|
+
const accepted = await broker.accept({
|
|
3267
|
+
assignmentId: event.assignmentId,
|
|
3268
|
+
workItemId: event.workItemId,
|
|
3269
|
+
orgId: event.orgId,
|
|
3270
|
+
machineId: this.machineId
|
|
3271
|
+
});
|
|
3272
|
+
sessionId = accepted.sessionId;
|
|
3273
|
+
this.audit.record({
|
|
3274
|
+
sessionId,
|
|
3275
|
+
type: "daemon_lifecycle" /* DAEMON_LIFECYCLE */,
|
|
3276
|
+
action: DAEMON_AUDIT_ACTIONS.SESSION_CLAIMED,
|
|
3277
|
+
details: {
|
|
3278
|
+
machineId: this.machineId,
|
|
3279
|
+
orgId: event.orgId,
|
|
3280
|
+
workItemId: event.workItemId,
|
|
3281
|
+
assignmentId: event.assignmentId,
|
|
3282
|
+
via: "assignment:claim-available"
|
|
3283
|
+
},
|
|
3284
|
+
outcome: "success"
|
|
3285
|
+
});
|
|
3286
|
+
const result = await runner.run(sessionId, accepted.workItem);
|
|
3287
|
+
await broker.reportResult({
|
|
3288
|
+
sessionId,
|
|
3289
|
+
status: result.status,
|
|
3290
|
+
summary: result.summary,
|
|
3291
|
+
branchName: result.branchName,
|
|
3292
|
+
assignmentId: event.assignmentId,
|
|
3293
|
+
machineId: this.machineId
|
|
3294
|
+
});
|
|
3295
|
+
this.audit.record({
|
|
3296
|
+
sessionId,
|
|
3297
|
+
type: "daemon_lifecycle" /* DAEMON_LIFECYCLE */,
|
|
3298
|
+
action: result.status === "failed" ? DAEMON_AUDIT_ACTIONS.SESSION_FAILED : DAEMON_AUDIT_ACTIONS.SESSION_COMPLETED,
|
|
3299
|
+
details: {
|
|
3300
|
+
machineId: this.machineId,
|
|
3301
|
+
status: result.status,
|
|
3302
|
+
...result.branchName ? { branchName: result.branchName } : {}
|
|
3303
|
+
},
|
|
3304
|
+
outcome: result.status === "failed" ? "failure" : "success"
|
|
3305
|
+
});
|
|
3306
|
+
} catch (err) {
|
|
3307
|
+
const error = err.message;
|
|
3308
|
+
this.audit.record({
|
|
3309
|
+
sessionId: sessionId ?? event.assignmentId,
|
|
3310
|
+
type: "daemon_lifecycle" /* DAEMON_LIFECYCLE */,
|
|
3311
|
+
action: DAEMON_AUDIT_ACTIONS.SESSION_FAILED,
|
|
3312
|
+
details: { machineId: this.machineId, phase: "pickup", error },
|
|
3313
|
+
outcome: "failure"
|
|
3314
|
+
});
|
|
3315
|
+
if (sessionId) {
|
|
3316
|
+
try {
|
|
3317
|
+
await broker.reportResult({
|
|
3318
|
+
sessionId,
|
|
3319
|
+
status: "failed",
|
|
3320
|
+
summary: error,
|
|
3321
|
+
assignmentId: event.assignmentId,
|
|
3322
|
+
machineId: this.machineId
|
|
3323
|
+
});
|
|
3324
|
+
} catch (reportErr) {
|
|
3325
|
+
console.warn(
|
|
3326
|
+
`[daemon] failed to report pickup failure for ${sessionId}: ${reportErr.message}`
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
} finally {
|
|
3331
|
+
this.activeSessions -= 1;
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3245
3334
|
/**
|
|
3246
3335
|
* Phase 5c — release a previously claimed assignment when the
|
|
3247
3336
|
* daemon cannot proceed (e.g. drain interrupted, capacity check
|
|
@@ -3292,8 +3381,217 @@ function truncate2(s, n) {
|
|
|
3292
3381
|
return s.length <= n ? s : `${s.slice(0, n - 1)}\u2026`;
|
|
3293
3382
|
}
|
|
3294
3383
|
|
|
3295
|
-
// _apps/@onklave/agent-cli/src/services/
|
|
3384
|
+
// _apps/@onklave/agent-cli/src/services/platform-broker-client.ts
|
|
3385
|
+
var PlatformBrokerClient = class {
|
|
3386
|
+
constructor(baseUrl, deviceToken) {
|
|
3387
|
+
this.baseUrl = baseUrl;
|
|
3388
|
+
this.deviceToken = deviceToken;
|
|
3389
|
+
}
|
|
3390
|
+
/**
|
|
3391
|
+
* Accept an assignment as this registered machine. The broker accepts on
|
|
3392
|
+
* teams (→ in_progress), persists a tracking session, and returns the
|
|
3393
|
+
* sessionId + work context.
|
|
3394
|
+
*/
|
|
3395
|
+
async accept(params) {
|
|
3396
|
+
return this.request(
|
|
3397
|
+
"POST",
|
|
3398
|
+
"/api/v1/work-item-pickup/accept",
|
|
3399
|
+
params
|
|
3400
|
+
);
|
|
3401
|
+
}
|
|
3402
|
+
/** Report a finished local run back to the broker (→ WorkItem). */
|
|
3403
|
+
async reportResult(params) {
|
|
3404
|
+
await this.request(
|
|
3405
|
+
"POST",
|
|
3406
|
+
"/api/v1/work-item-pickup/report-result",
|
|
3407
|
+
params
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
async request(method, path8, body) {
|
|
3411
|
+
const url = `${this.baseUrl}${path8}`;
|
|
3412
|
+
const response = await fetch(url, {
|
|
3413
|
+
method,
|
|
3414
|
+
headers: {
|
|
3415
|
+
"x-device-token": this.deviceToken,
|
|
3416
|
+
"Content-Type": "application/json",
|
|
3417
|
+
Accept: "application/json"
|
|
3418
|
+
},
|
|
3419
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
3420
|
+
signal: AbortSignal.timeout(3e4)
|
|
3421
|
+
});
|
|
3422
|
+
if (!response.ok) {
|
|
3423
|
+
let errorMessage = `Broker API error: ${response.status} ${response.statusText}`;
|
|
3424
|
+
try {
|
|
3425
|
+
const errorBody = await response.json();
|
|
3426
|
+
if (errorBody && typeof errorBody === "object" && "message" in errorBody) {
|
|
3427
|
+
errorMessage = `Broker API error: ${errorBody.message}`;
|
|
3428
|
+
}
|
|
3429
|
+
} catch {
|
|
3430
|
+
}
|
|
3431
|
+
throw new Error(errorMessage);
|
|
3432
|
+
}
|
|
3433
|
+
const contentType = response.headers.get("content-type");
|
|
3434
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
3435
|
+
return void 0;
|
|
3436
|
+
}
|
|
3437
|
+
const text = await response.text();
|
|
3438
|
+
if (!text) {
|
|
3439
|
+
return void 0;
|
|
3440
|
+
}
|
|
3441
|
+
return JSON.parse(text);
|
|
3442
|
+
}
|
|
3443
|
+
};
|
|
3444
|
+
|
|
3445
|
+
// _apps/@onklave/agent-cli/src/services/work-item-runner.service.ts
|
|
3446
|
+
import { spawn as spawn2 } from "child_process";
|
|
3447
|
+
import * as fs5 from "fs";
|
|
3296
3448
|
import * as os4 from "os";
|
|
3449
|
+
import * as path6 from "path";
|
|
3450
|
+
var WorkItemRunner = class {
|
|
3451
|
+
constructor(opts = {}) {
|
|
3452
|
+
this.sessionManager = opts.sessionManager ?? new SessionManager();
|
|
3453
|
+
this.git = opts.git ?? defaultGit;
|
|
3454
|
+
this.model = opts.model ?? "claude-sonnet-4-6";
|
|
3455
|
+
this.timeoutSeconds = opts.timeoutSeconds ?? 1800;
|
|
3456
|
+
}
|
|
3457
|
+
/**
|
|
3458
|
+
* Clone → branch → run Claude Code. Returns the run outcome; never throws —
|
|
3459
|
+
* failures are mapped to a `failed` result with an error summary so the
|
|
3460
|
+
* caller can always report a status back to the broker.
|
|
3461
|
+
*/
|
|
3462
|
+
async run(sessionId, workItem) {
|
|
3463
|
+
const repo = workItem.project?.repos?.[0];
|
|
3464
|
+
if (!repo?.url) {
|
|
3465
|
+
return {
|
|
3466
|
+
status: "failed",
|
|
3467
|
+
summary: `Work item ${workItem.id} has no clonable repo (project.repos[0].url missing).`
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3470
|
+
const workDir = fs5.mkdtempSync(
|
|
3471
|
+
path6.join(os4.tmpdir(), `onklave-pickup-${sessionId}-`)
|
|
3472
|
+
);
|
|
3473
|
+
const repoDir = path6.join(workDir, sanitizeName(repo.name) || "repo");
|
|
3474
|
+
const branchName = `onklave/work-item/${workItem.id}`;
|
|
3475
|
+
try {
|
|
3476
|
+
await this.git(["clone", repo.url, repoDir], workDir);
|
|
3477
|
+
await this.git(["checkout", "-b", branchName], repoDir);
|
|
3478
|
+
} catch (err) {
|
|
3479
|
+
cleanup(workDir);
|
|
3480
|
+
return {
|
|
3481
|
+
status: "failed",
|
|
3482
|
+
summary: `Failed to prepare workspace for ${repo.url}: ${err.message}`,
|
|
3483
|
+
branchName
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
const guardrails = new GuardrailEnforcer({
|
|
3487
|
+
rules: [],
|
|
3488
|
+
allowedTools: [],
|
|
3489
|
+
deniedTools: [],
|
|
3490
|
+
readablePaths: [repoDir],
|
|
3491
|
+
writablePaths: [repoDir]
|
|
3492
|
+
});
|
|
3493
|
+
const writeCheck = guardrails.checkPathAccess(repoDir, "write");
|
|
3494
|
+
if (!writeCheck.allowed) {
|
|
3495
|
+
cleanup(workDir);
|
|
3496
|
+
return {
|
|
3497
|
+
status: "failed",
|
|
3498
|
+
summary: `Guardrails blocked the workspace: ${writeCheck.reason}`,
|
|
3499
|
+
branchName
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
const addDirs = [repoDir];
|
|
3503
|
+
const task = buildTask(workItem);
|
|
3504
|
+
const config = {
|
|
3505
|
+
task,
|
|
3506
|
+
context: repoDir,
|
|
3507
|
+
persona: null,
|
|
3508
|
+
workflow: null,
|
|
3509
|
+
model: this.model,
|
|
3510
|
+
timeout: this.timeoutSeconds,
|
|
3511
|
+
guardrails: [],
|
|
3512
|
+
allowedTools: [],
|
|
3513
|
+
deniedTools: [],
|
|
3514
|
+
addDirs,
|
|
3515
|
+
apiKey: null,
|
|
3516
|
+
headless: true,
|
|
3517
|
+
platformUrl: "",
|
|
3518
|
+
orgId: null,
|
|
3519
|
+
systemPromptAppend: null
|
|
3520
|
+
};
|
|
3521
|
+
let output = "";
|
|
3522
|
+
const exitCode = await new Promise((resolve3) => {
|
|
3523
|
+
const timeout = setTimeout(() => {
|
|
3524
|
+
void this.sessionManager.stopSession(sessionId).catch(() => void 0);
|
|
3525
|
+
}, this.timeoutSeconds * 1e3);
|
|
3526
|
+
void this.sessionManager.spawnSession(sessionId, config, {
|
|
3527
|
+
onStdout: (data) => {
|
|
3528
|
+
output += data;
|
|
3529
|
+
},
|
|
3530
|
+
onStderr: (data) => {
|
|
3531
|
+
output += data;
|
|
3532
|
+
},
|
|
3533
|
+
onExit: (code) => {
|
|
3534
|
+
clearTimeout(timeout);
|
|
3535
|
+
resolve3(code);
|
|
3536
|
+
}
|
|
3537
|
+
});
|
|
3538
|
+
});
|
|
3539
|
+
cleanup(workDir);
|
|
3540
|
+
if (exitCode === 0) {
|
|
3541
|
+
return {
|
|
3542
|
+
status: "in_review",
|
|
3543
|
+
summary: output.trim().slice(-2e3) || "Run completed (no output).",
|
|
3544
|
+
branchName
|
|
3545
|
+
};
|
|
3546
|
+
}
|
|
3547
|
+
return {
|
|
3548
|
+
status: "failed",
|
|
3549
|
+
summary: `Claude Code exited with code ${exitCode}.
|
|
3550
|
+
` + output.trim().slice(-2e3),
|
|
3551
|
+
branchName
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
};
|
|
3555
|
+
function buildTask(workItem) {
|
|
3556
|
+
const parts = [workItem.title];
|
|
3557
|
+
if (workItem.description) {
|
|
3558
|
+
parts.push("", workItem.description);
|
|
3559
|
+
}
|
|
3560
|
+
return parts.join("\n");
|
|
3561
|
+
}
|
|
3562
|
+
function sanitizeName(name) {
|
|
3563
|
+
if (!name) return "";
|
|
3564
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
3565
|
+
}
|
|
3566
|
+
function cleanup(dir) {
|
|
3567
|
+
try {
|
|
3568
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
3569
|
+
} catch {
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
function defaultGit(args, cwd) {
|
|
3573
|
+
return new Promise((resolve3, reject) => {
|
|
3574
|
+
const child = spawn2("git", args, {
|
|
3575
|
+
cwd,
|
|
3576
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3577
|
+
});
|
|
3578
|
+
let stderr = "";
|
|
3579
|
+
child.stderr?.on("data", (d) => {
|
|
3580
|
+
stderr += d.toString();
|
|
3581
|
+
});
|
|
3582
|
+
child.on("error", (err) => reject(err));
|
|
3583
|
+
child.on("exit", (code) => {
|
|
3584
|
+
if (code === 0) {
|
|
3585
|
+
resolve3();
|
|
3586
|
+
} else {
|
|
3587
|
+
reject(new Error(`git ${args[0]} exited ${code}: ${stderr.trim()}`));
|
|
3588
|
+
}
|
|
3589
|
+
});
|
|
3590
|
+
});
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
// _apps/@onklave/agent-cli/src/services/daemon-resource-sampler.service.ts
|
|
3594
|
+
import * as os5 from "os";
|
|
3297
3595
|
var DEFAULT_SAMPLE_INTERVAL_MS = 5e3;
|
|
3298
3596
|
var DEFAULT_WINDOW_MS = 6e4;
|
|
3299
3597
|
var DaemonResourceSampler = class {
|
|
@@ -3361,8 +3659,8 @@ var DaemonResourceSampler = class {
|
|
|
3361
3659
|
}
|
|
3362
3660
|
};
|
|
3363
3661
|
function defaultCpuPercent() {
|
|
3364
|
-
const cpus2 =
|
|
3365
|
-
const load1m =
|
|
3662
|
+
const cpus2 = os5.cpus().length || 1;
|
|
3663
|
+
const load1m = os5.loadavg()[0];
|
|
3366
3664
|
return load1m / cpus2 * 100;
|
|
3367
3665
|
}
|
|
3368
3666
|
function defaultMemoryRss() {
|
|
@@ -3639,9 +3937,9 @@ function parseSemverNumeric(v) {
|
|
|
3639
3937
|
}
|
|
3640
3938
|
|
|
3641
3939
|
// _apps/@onklave/agent-cli/src/services/daemon-state.service.ts
|
|
3642
|
-
import * as
|
|
3643
|
-
import * as
|
|
3644
|
-
import * as
|
|
3940
|
+
import * as fs6 from "fs";
|
|
3941
|
+
import * as path7 from "path";
|
|
3942
|
+
import * as os6 from "os";
|
|
3645
3943
|
var VALID_TRANSITIONS = {
|
|
3646
3944
|
installing: ["registered"],
|
|
3647
3945
|
registered: ["starting"],
|
|
@@ -3652,10 +3950,10 @@ var VALID_TRANSITIONS = {
|
|
|
3652
3950
|
stopped: ["starting"]
|
|
3653
3951
|
};
|
|
3654
3952
|
function defaultStateFilePath() {
|
|
3655
|
-
return
|
|
3953
|
+
return path7.join(os6.homedir(), ".config", "onklave", "daemon.state.json");
|
|
3656
3954
|
}
|
|
3657
3955
|
function defaultPidFilePath() {
|
|
3658
|
-
return
|
|
3956
|
+
return path7.join(os6.homedir(), ".config", "onklave", "daemon.pid");
|
|
3659
3957
|
}
|
|
3660
3958
|
var DaemonStateError = class extends Error {
|
|
3661
3959
|
constructor(message) {
|
|
@@ -3679,8 +3977,8 @@ var DaemonStateService = class {
|
|
|
3679
3977
|
*/
|
|
3680
3978
|
static readPersisted(stateFile = defaultStateFilePath()) {
|
|
3681
3979
|
try {
|
|
3682
|
-
if (!
|
|
3683
|
-
const raw =
|
|
3980
|
+
if (!fs6.existsSync(stateFile)) return null;
|
|
3981
|
+
const raw = fs6.readFileSync(stateFile, "utf8");
|
|
3684
3982
|
const parsed = JSON.parse(raw);
|
|
3685
3983
|
if (!parsed.state || !parsed.enteredAt) return null;
|
|
3686
3984
|
return parsed;
|
|
@@ -3730,8 +4028,8 @@ var DaemonStateService = class {
|
|
|
3730
4028
|
}
|
|
3731
4029
|
}
|
|
3732
4030
|
persist(reason) {
|
|
3733
|
-
const dir =
|
|
3734
|
-
|
|
4031
|
+
const dir = path7.dirname(this.stateFile);
|
|
4032
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
3735
4033
|
const payload = {
|
|
3736
4034
|
state: this.current,
|
|
3737
4035
|
enteredAt: this.enteredAt.toISOString(),
|
|
@@ -3741,8 +4039,8 @@ var DaemonStateService = class {
|
|
|
3741
4039
|
...this.latestRuntime ? { runtime: this.latestRuntime } : {}
|
|
3742
4040
|
};
|
|
3743
4041
|
const tmp = `${this.stateFile}.tmp`;
|
|
3744
|
-
|
|
3745
|
-
|
|
4042
|
+
fs6.writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
4043
|
+
fs6.renameSync(tmp, this.stateFile);
|
|
3746
4044
|
}
|
|
3747
4045
|
/**
|
|
3748
4046
|
* Publish the latest runtime snapshot. Persists to the state file
|
|
@@ -3760,7 +4058,7 @@ var DaemonStateService = class {
|
|
|
3760
4058
|
*/
|
|
3761
4059
|
clearPersisted() {
|
|
3762
4060
|
try {
|
|
3763
|
-
|
|
4061
|
+
fs6.unlinkSync(this.stateFile);
|
|
3764
4062
|
} catch {
|
|
3765
4063
|
}
|
|
3766
4064
|
}
|
|
@@ -3946,6 +4244,8 @@ async function daemonStart() {
|
|
|
3946
4244
|
const spawner = new DaemonSpawner({ platformUrl });
|
|
3947
4245
|
const platformClient = new PlatformClient(platformUrl, creds.token);
|
|
3948
4246
|
const resourceSampler = new DaemonResourceSampler();
|
|
4247
|
+
const brokerClient = new PlatformBrokerClient(platformUrl, creds.deviceToken);
|
|
4248
|
+
const workItemRunner = new WorkItemRunner();
|
|
3949
4249
|
const claimHandler = new DaemonClaimHandler({
|
|
3950
4250
|
machineId: creds.machineId,
|
|
3951
4251
|
audit: auditStreamer,
|
|
@@ -3953,8 +4253,10 @@ async function daemonStart() {
|
|
|
3953
4253
|
platformClient,
|
|
3954
4254
|
deviceToken: creds.deviceToken,
|
|
3955
4255
|
resourceSampler,
|
|
3956
|
-
maxCpuPercent: 80
|
|
4256
|
+
maxCpuPercent: 80,
|
|
3957
4257
|
// spec §8.1 default
|
|
4258
|
+
brokerClient,
|
|
4259
|
+
workItemRunner
|
|
3958
4260
|
});
|
|
3959
4261
|
const tokenObserver = new DaemonTokenObserver({
|
|
3960
4262
|
machineId: creds.machineId,
|
|
@@ -4211,7 +4513,7 @@ function transitionToAction(next) {
|
|
|
4211
4513
|
}
|
|
4212
4514
|
function readPid(pidFile) {
|
|
4213
4515
|
try {
|
|
4214
|
-
const raw =
|
|
4516
|
+
const raw = fs7.readFileSync(pidFile, "utf8").trim();
|
|
4215
4517
|
const parsed = Number.parseInt(raw, 10);
|
|
4216
4518
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
4217
4519
|
} catch {
|
|
@@ -4220,12 +4522,12 @@ function readPid(pidFile) {
|
|
|
4220
4522
|
}
|
|
4221
4523
|
function writePid(pidFile) {
|
|
4222
4524
|
const dir = pidFile.replace(/\/[^/]+$/, "");
|
|
4223
|
-
|
|
4224
|
-
|
|
4525
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
4526
|
+
fs7.writeFileSync(pidFile, String(process.pid), { mode: 384 });
|
|
4225
4527
|
}
|
|
4226
4528
|
function removePid(pidFile) {
|
|
4227
4529
|
try {
|
|
4228
|
-
|
|
4530
|
+
fs7.unlinkSync(pidFile);
|
|
4229
4531
|
} catch {
|
|
4230
4532
|
}
|
|
4231
4533
|
}
|
|
@@ -4243,8 +4545,8 @@ function readPackageVersion() {
|
|
|
4243
4545
|
`${__dirname}/../../../package.json`
|
|
4244
4546
|
];
|
|
4245
4547
|
for (const p of candidates) {
|
|
4246
|
-
if (
|
|
4247
|
-
const pkg = JSON.parse(
|
|
4548
|
+
if (fs7.existsSync(p)) {
|
|
4549
|
+
const pkg = JSON.parse(fs7.readFileSync(p, "utf8"));
|
|
4248
4550
|
if (pkg.name === "@onklave/agent-cli" && pkg.version)
|
|
4249
4551
|
return pkg.version;
|
|
4250
4552
|
}
|