@amaster.ai/employee-runtime-connector 0.1.1-beta.7 → 0.1.1-beta.8
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/amaster-runtime-daemon.mjs +21 -4
- package/dist/amaster-runtime.mjs +92 -12
- package/package.json +1 -1
|
@@ -7277,6 +7277,14 @@ function runtimeStatusOutboxDir(config) {
|
|
|
7277
7277
|
AMASTER_DAEMON_STATE_FILE: statePath
|
|
7278
7278
|
})), "result-outbox");
|
|
7279
7279
|
}
|
|
7280
|
+
function runtimeStatusRunCompletionDir(config) {
|
|
7281
|
+
const explicit = config.AMASTER_RUN_COMPLETION_STATE_DIR ?? config.runCompletionStateDir;
|
|
7282
|
+
if (typeof explicit === "string" && explicit.trim()) return resolve6(explicit);
|
|
7283
|
+
const statePath = config.AMASTER_DAEMON_STATE_FILE ?? config.daemonStateFile;
|
|
7284
|
+
return join10(dirname6(stateFilePath({
|
|
7285
|
+
AMASTER_DAEMON_STATE_FILE: statePath
|
|
7286
|
+
})), "run-completion-state");
|
|
7287
|
+
}
|
|
7280
7288
|
function countRuntimeStatusJsonEntries(dir) {
|
|
7281
7289
|
if (!existsSync9(dir)) return 0;
|
|
7282
7290
|
return runtimeStatusDirectoryEntries(dir).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).length;
|
|
@@ -7311,6 +7319,8 @@ function summarizeRuntimeLocalState(input) {
|
|
|
7311
7319
|
}
|
|
7312
7320
|
const pendingOutboxDir = runtimeStatusOutboxDir(config);
|
|
7313
7321
|
const invalidOutboxDir = join10(pendingOutboxDir, "invalid");
|
|
7322
|
+
const pendingRunCompletionDir = runtimeStatusRunCompletionDir(config);
|
|
7323
|
+
const invalidRunCompletionDir = join10(pendingRunCompletionDir, "invalid");
|
|
7314
7324
|
return {
|
|
7315
7325
|
managedWorkspacesRoot: root,
|
|
7316
7326
|
managedWorkdirCount: workdirs.length,
|
|
@@ -7320,7 +7330,9 @@ function summarizeRuntimeLocalState(input) {
|
|
|
7320
7330
|
newestTouchedAt: newestTouchedTime === null ? null : new Date(newestTouchedTime).toISOString(),
|
|
7321
7331
|
recentManagedRuns: recentManagedRuns.sort((left, right) => right.sortTime - left.sortTime || left.manifestPath.localeCompare(right.manifestPath)).slice(0, input.recentManagedRunLimit ?? 5).map(({ sortTime: _sortTime, ...entry }) => entry),
|
|
7322
7332
|
resultOutboxPending: countRuntimeStatusJsonEntries(pendingOutboxDir),
|
|
7323
|
-
resultOutboxInvalid: countRuntimeStatusJsonEntries(invalidOutboxDir)
|
|
7333
|
+
resultOutboxInvalid: countRuntimeStatusJsonEntries(invalidOutboxDir),
|
|
7334
|
+
runCompletionPending: countRuntimeStatusJsonEntries(pendingRunCompletionDir),
|
|
7335
|
+
runCompletionInvalid: countRuntimeStatusJsonEntries(invalidRunCompletionDir)
|
|
7324
7336
|
};
|
|
7325
7337
|
}
|
|
7326
7338
|
|
|
@@ -9197,7 +9209,7 @@ function assertSourceAcquisitionRuntimeAuthority({
|
|
|
9197
9209
|
}
|
|
9198
9210
|
|
|
9199
9211
|
// src/amaster-runtime-daemon.mjs
|
|
9200
|
-
var CONNECTOR_VERSION = "0.1.1-beta.
|
|
9212
|
+
var CONNECTOR_VERSION = "0.1.1-beta.8";
|
|
9201
9213
|
var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
|
|
9202
9214
|
var SOURCE_ACQUISITION_CAPABILITY = "source_acquisition_v1";
|
|
9203
9215
|
var SOURCE_ACQUISITION_PROFILE_VERSION = "source_acquisition_v1";
|
|
@@ -9433,6 +9445,9 @@ function resultOutboxFailedRunCommands(config) {
|
|
|
9433
9445
|
}
|
|
9434
9446
|
return entries;
|
|
9435
9447
|
}
|
|
9448
|
+
function activeRunCommandCountsAsActive(entry) {
|
|
9449
|
+
return readString(entry?.phase) !== "result_outbox_failed" && readString(entry?.evidenceDeliveryStatus) !== "failed";
|
|
9450
|
+
}
|
|
9436
9451
|
function clearDeliveredResultOutboxCommand(config, entry) {
|
|
9437
9452
|
const commandId = readString(entry?.commandId);
|
|
9438
9453
|
if (commandId) pendingResultOutboxRunCommands.delete(commandId);
|
|
@@ -9860,6 +9875,7 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
9860
9875
|
activeRunCommandById.set(commandId ?? `pending-${activeRunCommandById.size}`, entry);
|
|
9861
9876
|
}
|
|
9862
9877
|
const activeRunCommandList = Array.from(activeRunCommandById.values()).map((entry) => buildActiveRunCommandStatus(config, entry));
|
|
9878
|
+
const activeRunCount = activeRunCommandList.filter(activeRunCommandCountsAsActive).length;
|
|
9863
9879
|
const executorReadiness = buildExecutorReadiness(config);
|
|
9864
9880
|
const platformCredential = piAgentLocalPlatformCredentialStatus(config);
|
|
9865
9881
|
const headedBrowserAuth = browserCapabilityStatus(config, "headed_browser_auth");
|
|
@@ -9882,7 +9898,7 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
9882
9898
|
...heartbeatMode === "diagnostic" ? { heartbeatMode: "diagnostic" } : {},
|
|
9883
9899
|
...diagnosticSource ? { diagnosticSource } : {},
|
|
9884
9900
|
...orphanCheck === "startup" ? { orphanCheck: "startup" } : {},
|
|
9885
|
-
activeRuns:
|
|
9901
|
+
activeRuns: activeRunCount,
|
|
9886
9902
|
...activeRunCommandList.length > 0 ? { activeRunCommands: activeRunCommandList } : {},
|
|
9887
9903
|
pollIntervalSeconds: config.pollIntervalSeconds,
|
|
9888
9904
|
orphanReaperIntervalSeconds: config.orphanReaperIntervalSeconds,
|
|
@@ -14594,7 +14610,8 @@ function statusSummary(config) {
|
|
|
14594
14610
|
process.stdout.write(`${JSON.stringify(summarizeRuntimeLocalState({
|
|
14595
14611
|
config: {
|
|
14596
14612
|
...config,
|
|
14597
|
-
AMASTER_DAEMON_STATE_FILE: stateFilePath(process.env)
|
|
14613
|
+
AMASTER_DAEMON_STATE_FILE: stateFilePath(process.env),
|
|
14614
|
+
AMASTER_RUN_COMPLETION_STATE_DIR: process.env.AMASTER_RUN_COMPLETION_STATE_DIR
|
|
14598
14615
|
}
|
|
14599
14616
|
}), null, 2)}
|
|
14600
14617
|
`);
|
package/dist/amaster-runtime.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
4
|
-
import {
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
6
|
import { homedir, hostname } from "node:os";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
7
8
|
|
|
8
|
-
const CONNECTOR_VERSION = "0.1.1-beta.
|
|
9
|
+
const CONNECTOR_VERSION = "0.1.1-beta.8";
|
|
9
10
|
|
|
10
11
|
const CAPABILITIES = [
|
|
11
12
|
"remote_registration",
|
|
@@ -364,6 +365,58 @@ function runningStatePid(state, config) {
|
|
|
364
365
|
return pidRunning(pid) ? pid : null;
|
|
365
366
|
}
|
|
366
367
|
|
|
368
|
+
function processCommandLine(pid) {
|
|
369
|
+
try {
|
|
370
|
+
if (process.platform === "linux") {
|
|
371
|
+
return readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\0", " ").trim() || null;
|
|
372
|
+
}
|
|
373
|
+
if (process.platform === "win32") {
|
|
374
|
+
const result = spawnSync(
|
|
375
|
+
"powershell.exe",
|
|
376
|
+
[
|
|
377
|
+
"-NoProfile",
|
|
378
|
+
"-NonInteractive",
|
|
379
|
+
"-Command",
|
|
380
|
+
`(Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}').CommandLine`,
|
|
381
|
+
],
|
|
382
|
+
{ encoding: "utf8", windowsHide: true, timeout: 2_000 },
|
|
383
|
+
);
|
|
384
|
+
return result.status === 0 ? result.stdout.trim() || null : null;
|
|
385
|
+
}
|
|
386
|
+
const result = spawnSync("ps", ["-ww", "-p", String(pid), "-o", "command="], {
|
|
387
|
+
encoding: "utf8",
|
|
388
|
+
timeout: 2_000,
|
|
389
|
+
});
|
|
390
|
+
return result.status === 0 ? result.stdout.trim() || null : null;
|
|
391
|
+
} catch {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function recordedDaemonIdentity(pidInfo, state, config) {
|
|
397
|
+
const pid = Number(pidInfo?.pid);
|
|
398
|
+
if (!pidRunning(pid)) return { status: "not_running", pid };
|
|
399
|
+
const commandLine = processCommandLine(pid);
|
|
400
|
+
// An unavailable OS probe is not proof that the PID belongs to another
|
|
401
|
+
// process. Keep the PID file intact and refuse start/stop so the CLI cannot
|
|
402
|
+
// trade signal safety for a duplicate daemon.
|
|
403
|
+
if (!commandLine) return { status: "unverifiable", pid };
|
|
404
|
+
const processToken = typeof pidInfo?.processToken === "string" ? pidInfo.processToken.trim() : "";
|
|
405
|
+
const expectedScriptPath = resolve(daemonScriptPath());
|
|
406
|
+
if (processToken) {
|
|
407
|
+
// The random token is the primary identity across dev/installed/symlink
|
|
408
|
+
// layouts. The daemon basename and token flag keep an unrelated process
|
|
409
|
+
// that merely contains the token text from matching.
|
|
410
|
+
const matchesTokenIdentity = commandLine.includes("--runtime-process-token")
|
|
411
|
+
&& commandLine.includes(processToken)
|
|
412
|
+
&& (commandLine.includes(expectedScriptPath) || commandLine.includes(basename(expectedScriptPath)));
|
|
413
|
+
return { status: matchesTokenIdentity ? "matched" : "mismatched", pid };
|
|
414
|
+
}
|
|
415
|
+
const matchesLegacyIdentity = commandLine.includes(expectedScriptPath)
|
|
416
|
+
&& runningStatePid(state, config) === pid;
|
|
417
|
+
return { status: matchesLegacyIdentity ? "matched" : "mismatched", pid };
|
|
418
|
+
}
|
|
419
|
+
|
|
367
420
|
function daemonScriptPath() {
|
|
368
421
|
const candidates = [
|
|
369
422
|
join(scriptDir, "amaster-runtime-daemon.mjs"),
|
|
@@ -875,10 +928,18 @@ function commandDaemon(argv) {
|
|
|
875
928
|
if (action === "restart") commandStop({ quiet: true });
|
|
876
929
|
|
|
877
930
|
const current = readPidInfo();
|
|
878
|
-
|
|
879
|
-
|
|
931
|
+
const currentIdentity = recordedDaemonIdentity(current, readState(config), withDefaults(config));
|
|
932
|
+
if (currentIdentity.status === "unverifiable") {
|
|
933
|
+
throw new Error(
|
|
934
|
+
`Cannot safely start AMaster runtime daemon: pid ${currentIdentity.pid} is live but the process identity probe was unavailable. `
|
|
935
|
+
+ "The PID file was retained; restore the platform process probe or stop the supervised process explicitly before retrying.",
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
if (currentIdentity.status === "matched" && flags.force !== "true") {
|
|
939
|
+
process.stdout.write(`AMaster runtime daemon already running pid=${currentIdentity.pid}\n`);
|
|
880
940
|
return;
|
|
881
941
|
}
|
|
942
|
+
if (currentIdentity.status === "matched") commandStop({ quiet: true });
|
|
882
943
|
|
|
883
944
|
if (flags.foreground === "true") {
|
|
884
945
|
spawnDaemonForeground(["start"]);
|
|
@@ -887,7 +948,13 @@ function commandDaemon(argv) {
|
|
|
887
948
|
|
|
888
949
|
mkdirSync(configDir, { recursive: true });
|
|
889
950
|
const fd = openSync(logFile, "a");
|
|
890
|
-
const
|
|
951
|
+
const processToken = randomUUID();
|
|
952
|
+
const child = spawn(process.execPath, [
|
|
953
|
+
daemonScriptPath(),
|
|
954
|
+
"start",
|
|
955
|
+
"--runtime-process-token",
|
|
956
|
+
processToken,
|
|
957
|
+
], {
|
|
891
958
|
cwd: scriptDir,
|
|
892
959
|
detached: true,
|
|
893
960
|
env: {
|
|
@@ -900,6 +967,7 @@ function commandDaemon(argv) {
|
|
|
900
967
|
closeSync(fd);
|
|
901
968
|
writePidInfo({
|
|
902
969
|
pid: child.pid,
|
|
970
|
+
processToken,
|
|
903
971
|
startedAt: new Date().toISOString(),
|
|
904
972
|
serverUrl: withDefaults(config).AMASTER_EMPLOYEE_SERVER_URL,
|
|
905
973
|
logFile,
|
|
@@ -912,12 +980,14 @@ function commandStatus() {
|
|
|
912
980
|
const config = withDefaults(readConfig());
|
|
913
981
|
const pidInfo = readPidInfo();
|
|
914
982
|
const state = readState(config);
|
|
915
|
-
const foregroundPid = runningStatePid(state, config);
|
|
916
|
-
const
|
|
983
|
+
const foregroundPid = pidInfo?.pid ? null : runningStatePid(state, config);
|
|
984
|
+
const recordedIdentity = recordedDaemonIdentity(pidInfo, state, config);
|
|
985
|
+
const runningPid = recordedIdentity.status === "matched" ? recordedIdentity.pid : foregroundPid;
|
|
917
986
|
process.stdout.write("AMaster runtime daemon\n");
|
|
918
|
-
process.stdout.write(`status: ${runningPid ? "running" : "stopped"}\n`);
|
|
987
|
+
process.stdout.write(`status: ${runningPid ? "running" : recordedIdentity.status === "unverifiable" ? "unknown" : "stopped"}\n`);
|
|
919
988
|
if (runningPid) process.stdout.write(`pid: ${runningPid}\n`);
|
|
920
|
-
else if (
|
|
989
|
+
else if (recordedIdentity.status === "unverifiable") process.stdout.write(`unverified_pid: ${recordedIdentity.pid}\n`);
|
|
990
|
+
else if (pidInfo?.pid) process.stdout.write(`stale_pid: ${pidInfo.pid}\n`);
|
|
921
991
|
process.stdout.write(`server_url: ${config.AMASTER_EMPLOYEE_SERVER_URL}\n`);
|
|
922
992
|
process.stdout.write(`device_name: ${config.AMASTER_CONNECTOR_NAME}\n`);
|
|
923
993
|
process.stdout.write(`workspace_bindings: ${config.AMASTER_WORKSPACE_ALLOWLIST}\n`);
|
|
@@ -934,6 +1004,8 @@ function commandStatus() {
|
|
|
934
1004
|
process.stdout.write(`stale_managed_workdirs: ${localSummary.staleManagedWorkdirCount ?? 0}\n`);
|
|
935
1005
|
process.stdout.write(`result_outbox_pending: ${localSummary.resultOutboxPending ?? 0}\n`);
|
|
936
1006
|
process.stdout.write(`result_outbox_invalid: ${localSummary.resultOutboxInvalid ?? 0}\n`);
|
|
1007
|
+
process.stdout.write(`run_completion_pending: ${localSummary.runCompletionPending ?? 0}\n`);
|
|
1008
|
+
process.stdout.write(`run_completion_invalid: ${localSummary.runCompletionInvalid ?? 0}\n`);
|
|
937
1009
|
const recentManagedRuns = Array.isArray(localSummary.recentManagedRuns)
|
|
938
1010
|
? localSummary.recentManagedRuns
|
|
939
1011
|
: [];
|
|
@@ -957,19 +1029,27 @@ function commandStatus() {
|
|
|
957
1029
|
}
|
|
958
1030
|
|
|
959
1031
|
function commandStop(opts = {}) {
|
|
1032
|
+
const config = withDefaults(readConfig());
|
|
960
1033
|
const pidInfo = readPidInfo();
|
|
961
|
-
|
|
1034
|
+
const recordedIdentity = recordedDaemonIdentity(pidInfo, readState(config), config);
|
|
1035
|
+
if (recordedIdentity.status === "unverifiable") {
|
|
1036
|
+
throw new Error(
|
|
1037
|
+
`Cannot safely stop AMaster runtime daemon: pid ${recordedIdentity.pid} is live but the process identity probe was unavailable. `
|
|
1038
|
+
+ "The PID file was retained and no signal was sent.",
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
if (recordedIdentity.status !== "matched") {
|
|
962
1042
|
if (!opts.quiet) process.stdout.write("AMaster runtime daemon is not running\n");
|
|
963
1043
|
rmSync(pidFile, { force: true });
|
|
964
1044
|
return;
|
|
965
1045
|
}
|
|
966
1046
|
try {
|
|
967
|
-
process.kill(
|
|
1047
|
+
process.kill(recordedIdentity.pid, "SIGTERM");
|
|
968
1048
|
} catch {
|
|
969
1049
|
// Process may exit between the status check and SIGTERM.
|
|
970
1050
|
}
|
|
971
1051
|
rmSync(pidFile, { force: true });
|
|
972
|
-
if (!opts.quiet) process.stdout.write(`AMaster runtime daemon stopped pid=${
|
|
1052
|
+
if (!opts.quiet) process.stdout.write(`AMaster runtime daemon stopped pid=${recordedIdentity.pid}\n`);
|
|
973
1053
|
}
|
|
974
1054
|
|
|
975
1055
|
function passthroughDaemon(command, argv = []) {
|