pi-radar 0.9.0 → 0.10.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/README.md +3 -1
- package/dist/extension/baseline.js +224 -117
- package/dist/extension/radar-children.js +105 -1
- package/dist/extension/radar-client.js +12 -3
- package/dist/extension/radar-launch.js +25 -94
- package/dist/extension/radar-runtime.js +1 -1
- package/dist/index.js +190 -216
- package/extension/generated-contracts.ts +10 -2
- package/extension/radar-children.ts +50 -4
- package/extension/radar-client.ts +17 -3
- package/extension/radar-launch.ts +54 -216
- package/extension/radar-runtime.ts +1 -1
- package/package.json +1 -1
- package/schema/public/v1/observation-get-response.deserialize.schema.json +0 -12
- package/schema/public/v1/observation-get-response.serialize.schema.json +2 -16
- package/schema/public/v1/observation-snapshot.deserialize.schema.json +0 -12
- package/schema/public/v1/observation-snapshot.serialize.schema.json +2 -16
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var radar_children_exports = {};
|
|
|
9
9
|
__export(radar_children_exports, {
|
|
10
10
|
createRadarChildren: () => createRadarChildren
|
|
11
11
|
});
|
|
12
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12
13
|
|
|
13
14
|
// extension/radar-inspection.ts
|
|
14
15
|
var radar_inspection_exports = {};
|
|
@@ -60,10 +61,12 @@ function utf8Tail(value, maxBytes) {
|
|
|
60
61
|
// extension/radar-launch.ts
|
|
61
62
|
var radar_launch_exports = {};
|
|
62
63
|
__export(radar_launch_exports, {
|
|
64
|
+
launchFailure: () => launchFailure,
|
|
63
65
|
launchRadarWorker: () => launchRadarWorker,
|
|
64
66
|
observeRadarChild: () => observeRadarChild,
|
|
65
67
|
readProcessGone: () => readProcessGone,
|
|
66
|
-
relaunchRefusalForChild: () => relaunchRefusalForChild
|
|
68
|
+
relaunchRefusalForChild: () => relaunchRefusalForChild,
|
|
69
|
+
waitForChild: () => waitForChild
|
|
67
70
|
});
|
|
68
71
|
import { setTimeout as delay } from "node:timers/promises";
|
|
69
72
|
|
|
@@ -104,7 +107,7 @@ import { lstatSync } from "node:fs";
|
|
|
104
107
|
import { createConnection } from "node:net";
|
|
105
108
|
import { dirname, join } from "node:path";
|
|
106
109
|
var RADAR_PROTOCOL_VERSION = 2;
|
|
107
|
-
var RADAR_MINIMUM_REVISION =
|
|
110
|
+
var RADAR_MINIMUM_REVISION = 8;
|
|
108
111
|
var RADAR_REGISTRY_CONTRACT = "registry.v1";
|
|
109
112
|
var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
|
|
110
113
|
var RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
@@ -418,6 +421,9 @@ function spawnEdge(value) {
|
|
|
418
421
|
const agentId = stringAt(bound, "registration_id");
|
|
419
422
|
const subjectId = stringAt(bound, "subject_id");
|
|
420
423
|
const exit = exitAt(recordAt(value, "exit_observation"));
|
|
424
|
+
const withdrawnRecord = recordAt(value, "withdrawn");
|
|
425
|
+
const withdrawnAt = stringAt(withdrawnRecord, "at");
|
|
426
|
+
const withdrawnReason = stringAt(withdrawnRecord, "reason");
|
|
421
427
|
return {
|
|
422
428
|
ok: true,
|
|
423
429
|
value: {
|
|
@@ -434,7 +440,9 @@ function spawnEdge(value) {
|
|
|
434
440
|
...isUuid(subjectId) ? { subject_id: subjectId } : {}
|
|
435
441
|
}
|
|
436
442
|
} : {},
|
|
437
|
-
...exit !== void 0 ? { exit_observation: exit } : {}
|
|
443
|
+
...exit !== void 0 ? { exit_observation: exit } : {},
|
|
444
|
+
...stringAt(value, "granted_at") !== void 0 ? { granted_at: stringAt(value, "granted_at") } : {},
|
|
445
|
+
...withdrawnAt !== void 0 && withdrawnReason !== void 0 ? { withdrawn: { at: withdrawnAt, reason: withdrawnReason } } : {}
|
|
438
446
|
}
|
|
439
447
|
};
|
|
440
448
|
}
|
|
@@ -1024,7 +1032,11 @@ function createRadarClient(options = {}) {
|
|
|
1024
1032
|
request.request_id
|
|
1025
1033
|
);
|
|
1026
1034
|
if (answer.ok === false) return answer;
|
|
1027
|
-
|
|
1035
|
+
const settled = focusOutcome(recordAt(answer.value, "request"), request.request_id);
|
|
1036
|
+
if (settled.ok === false) return settled;
|
|
1037
|
+
const launch = recordAt(recordAt(answer.value, "request"), "launch_request_id");
|
|
1038
|
+
if (!isUuid(launch)) return protocol("lifecycle.restart reply names no launch");
|
|
1039
|
+
return { ok: true, value: { ...settled.value, launch_request_id: launch } };
|
|
1028
1040
|
});
|
|
1029
1041
|
},
|
|
1030
1042
|
async spawnList(request) {
|
|
@@ -1394,123 +1406,6 @@ function boundedRadarSnapshot(snapshot) {
|
|
|
1394
1406
|
};
|
|
1395
1407
|
}
|
|
1396
1408
|
|
|
1397
|
-
// extension/radar-location.ts
|
|
1398
|
-
var radar_location_exports = {};
|
|
1399
|
-
__export(radar_location_exports, {
|
|
1400
|
-
radarLocalPane: () => radarLocalPane,
|
|
1401
|
-
radarPaneHandle: () => radarPaneHandle,
|
|
1402
|
-
resolveRadarProcessLocation: () => resolveRadarProcessLocation,
|
|
1403
|
-
tmuxServerName: () => tmuxServerName
|
|
1404
|
-
});
|
|
1405
|
-
import { basename } from "node:path";
|
|
1406
|
-
function radarLocalPane(env) {
|
|
1407
|
-
const pane = env.TMUX_PANE?.trim();
|
|
1408
|
-
return pane && /^%\d+$/.test(pane) ? pane : void 0;
|
|
1409
|
-
}
|
|
1410
|
-
function radarPaneHandle(qualified) {
|
|
1411
|
-
return qualified.slice(qualified.lastIndexOf(":") + 1);
|
|
1412
|
-
}
|
|
1413
|
-
function tmuxServerName(env) {
|
|
1414
|
-
const socket = env.TMUX?.trim().split(",")[0];
|
|
1415
|
-
return socket ? basename(socket) || void 0 : void 0;
|
|
1416
|
-
}
|
|
1417
|
-
function qualifiedOwnPane(env, backend, instance) {
|
|
1418
|
-
if (backend !== "tmux")
|
|
1419
|
-
return {
|
|
1420
|
-
absent: `the daemon serves the ${backend} backend, and Herdsman drives only tmux`
|
|
1421
|
-
};
|
|
1422
|
-
if (instance === void 0 || instance === "")
|
|
1423
|
-
return {
|
|
1424
|
-
pending: "the daemon did not name the tmux server it addresses"
|
|
1425
|
-
};
|
|
1426
|
-
const server = tmuxServerName(env);
|
|
1427
|
-
if (server === void 0)
|
|
1428
|
-
return {
|
|
1429
|
-
absent: "this process's own tmux client names no server, so it is not in the pane it would publish"
|
|
1430
|
-
};
|
|
1431
|
-
if (server !== instance)
|
|
1432
|
-
return {
|
|
1433
|
-
absent: `this process is attached to tmux server ${server}, not to ${instance}`
|
|
1434
|
-
};
|
|
1435
|
-
const pane = radarLocalPane(env);
|
|
1436
|
-
if (pane === void 0)
|
|
1437
|
-
return {
|
|
1438
|
-
absent: "this process's own environment names no tmux pane of that server"
|
|
1439
|
-
};
|
|
1440
|
-
return { pane: `tmux:${instance}:${pane}` };
|
|
1441
|
-
}
|
|
1442
|
-
function observedContainment(inventory, paneId) {
|
|
1443
|
-
const rows = inventory.panes.filter((pane) => pane.pane_id === paneId);
|
|
1444
|
-
if (rows.length === 0) return void 0;
|
|
1445
|
-
if (rows.length > 1) return { ambiguous: `topology reports pane ${paneId} twice` };
|
|
1446
|
-
const row = rows[0];
|
|
1447
|
-
const tab = inventory.tabs.find((candidate) => candidate.tab_id === row.tab_id);
|
|
1448
|
-
if (tab !== void 0 && tab.workspace_id !== row.workspace_id)
|
|
1449
|
-
return {
|
|
1450
|
-
ambiguous: `pane ${paneId} is reported in ${row.workspace_id}, but its tab belongs to ${tab.workspace_id}`
|
|
1451
|
-
};
|
|
1452
|
-
return { pane: row };
|
|
1453
|
-
}
|
|
1454
|
-
async function resolveRadarProcessLocation(client, env = process.env) {
|
|
1455
|
-
let ready;
|
|
1456
|
-
try {
|
|
1457
|
-
ready = await client.ping();
|
|
1458
|
-
} catch {
|
|
1459
|
-
return { state: "pending", reason: "the daemon did not answer this process" };
|
|
1460
|
-
}
|
|
1461
|
-
if (ready.ok === false)
|
|
1462
|
-
return {
|
|
1463
|
-
state: "pending",
|
|
1464
|
-
reason: `the daemon did not answer this process (${ready.code})`
|
|
1465
|
-
};
|
|
1466
|
-
const backend = ready.value.backend;
|
|
1467
|
-
if (backend === void 0 || backend === "none")
|
|
1468
|
-
return { state: "absent", reason: "the daemon serves no physical backend" };
|
|
1469
|
-
const instance = ready.value.instance ?? void 0;
|
|
1470
|
-
const own = qualifiedOwnPane(env, backend, instance);
|
|
1471
|
-
if ("absent" in own) return { state: "absent", reason: own.absent };
|
|
1472
|
-
if ("pending" in own) return { state: "pending", reason: own.pending };
|
|
1473
|
-
const paneId = own.pane;
|
|
1474
|
-
if (paneId === env.PI_HERDSMAN_PARENT_PANE_ID?.trim())
|
|
1475
|
-
return {
|
|
1476
|
-
state: "absent",
|
|
1477
|
-
reason: "this process is in the pane its launch was created beside, not in a pane of its own"
|
|
1478
|
-
};
|
|
1479
|
-
let inventory;
|
|
1480
|
-
try {
|
|
1481
|
-
inventory = await client.observe();
|
|
1482
|
-
} catch {
|
|
1483
|
-
return {
|
|
1484
|
-
state: "pending",
|
|
1485
|
-
reason: "the daemon's topology could not be read for this process's pane"
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
if (inventory.ok === false)
|
|
1489
|
-
return {
|
|
1490
|
-
state: "pending",
|
|
1491
|
-
reason: `the daemon's topology could not be read (${inventory.code})`
|
|
1492
|
-
};
|
|
1493
|
-
const containment = observedContainment(inventory.value, paneId);
|
|
1494
|
-
if (containment === void 0)
|
|
1495
|
-
return {
|
|
1496
|
-
state: "pending",
|
|
1497
|
-
reason: `the daemon's topology does not report pane ${paneId} yet`
|
|
1498
|
-
};
|
|
1499
|
-
if ("ambiguous" in containment)
|
|
1500
|
-
return { state: "pending", reason: containment.ambiguous };
|
|
1501
|
-
const row = containment.pane;
|
|
1502
|
-
return {
|
|
1503
|
-
state: "resolved",
|
|
1504
|
-
location: {
|
|
1505
|
-
backend,
|
|
1506
|
-
...instance !== void 0 ? { instance } : {},
|
|
1507
|
-
workspace: row.workspace_id,
|
|
1508
|
-
tab: row.tab_id,
|
|
1509
|
-
pane: paneId
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
1409
|
// extension/radar-launch.ts
|
|
1515
1410
|
function unknownChild(reason) {
|
|
1516
1411
|
return { kind: "unknown", diagnostic: trajectory(reason) };
|
|
@@ -1602,7 +1497,7 @@ async function relaunchRefusalForChild(client, agentId) {
|
|
|
1602
1497
|
return `the child's registered process is ${verification === "verified" ? "still running" : "not proved gone"}`;
|
|
1603
1498
|
}
|
|
1604
1499
|
function refusedLaunch(message) {
|
|
1605
|
-
return { kind: "
|
|
1500
|
+
return { kind: "failed", message };
|
|
1606
1501
|
}
|
|
1607
1502
|
function unresolvedLaunch(requestId, message) {
|
|
1608
1503
|
return {
|
|
@@ -1611,14 +1506,6 @@ function unresolvedLaunch(requestId, message) {
|
|
|
1611
1506
|
message: `spawn ${requestId} is unresolved (${message})`
|
|
1612
1507
|
};
|
|
1613
1508
|
}
|
|
1614
|
-
function edgeStanding(edge) {
|
|
1615
|
-
if (edge.created === "refused" || edge.launched === "refused")
|
|
1616
|
-
return "refused";
|
|
1617
|
-
return edge.created === "completed" && edge.launched === "completed" ? "launched" : "unknown";
|
|
1618
|
-
}
|
|
1619
|
-
function edgeMessage(edge) {
|
|
1620
|
-
return `the daemon's spawn record is created ${edge.created} and launched ${edge.launched}`;
|
|
1621
|
-
}
|
|
1622
1509
|
var PRE_EFFECT_CODES = [
|
|
1623
1510
|
"absent",
|
|
1624
1511
|
"unsupported",
|
|
@@ -1631,14 +1518,12 @@ var PRE_EFFECT_CODES = [
|
|
|
1631
1518
|
"busy",
|
|
1632
1519
|
"backend_unavailable"
|
|
1633
1520
|
];
|
|
1634
|
-
function
|
|
1521
|
+
function launchFailure(result, requestId) {
|
|
1635
1522
|
return PRE_EFFECT_CODES.includes(result.code) ? refusedLaunch(result.message) : unresolvedLaunch(requestId, result.message);
|
|
1636
1523
|
}
|
|
1637
1524
|
async function launchRadarWorker(options) {
|
|
1638
1525
|
const { client, launch, signal } = options;
|
|
1639
1526
|
const pollMs = options.pollMs ?? 250;
|
|
1640
|
-
const deadline = Date.now() + options.timeoutMs;
|
|
1641
|
-
const remaining = () => Math.max(0, deadline - Date.now());
|
|
1642
1527
|
const parent = await options.publication.subject();
|
|
1643
1528
|
if (parent === void 0 || !isUuid(parent.registration_id))
|
|
1644
1529
|
return refusedLaunch(
|
|
@@ -1669,55 +1554,22 @@ async function launchRadarWorker(options) {
|
|
|
1669
1554
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
1670
1555
|
});
|
|
1671
1556
|
if (spawned.ok === false) {
|
|
1672
|
-
const refusal =
|
|
1673
|
-
if (refusal.kind === "
|
|
1674
|
-
}
|
|
1675
|
-
if (spawned.ok === true) {
|
|
1676
|
-
const standing = edgeStanding(spawned.value);
|
|
1677
|
-
if (standing === "refused")
|
|
1678
|
-
return refusedLaunch(edgeMessage(spawned.value));
|
|
1679
|
-
if (standing === "unknown")
|
|
1680
|
-
return unresolvedLaunch(launch.request_id, edgeMessage(spawned.value));
|
|
1557
|
+
const refusal = launchFailure(spawned, launch.request_id);
|
|
1558
|
+
if (refusal.kind === "failed") return refusal;
|
|
1681
1559
|
}
|
|
1682
1560
|
const bound = await waitForChild(client, launch.request_id, {
|
|
1683
|
-
remaining,
|
|
1684
1561
|
pollMs,
|
|
1685
1562
|
...signal !== void 0 ? { signal } : {}
|
|
1686
1563
|
});
|
|
1687
1564
|
if (bound.kind !== "bound") return bound;
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
launch.
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
workspaceId: launch.identity.workspaceId,
|
|
1697
|
-
agentLabel: launch.identity.agentLabel,
|
|
1698
|
-
// The child writes the raw TMUX_PANE of the pane it runs in. Requiring
|
|
1699
|
-
// the pane the daemon created is what refuses a child that inherited
|
|
1700
|
-
// another process's coordinates.
|
|
1701
|
-
paneId: radarPaneHandle(bound.pane_id)
|
|
1702
|
-
}),
|
|
1703
|
-
{ timeoutMs: remaining(), ...signal !== void 0 ? { signal } : {} }
|
|
1704
|
-
);
|
|
1705
|
-
return {
|
|
1706
|
-
kind: "ready",
|
|
1707
|
-
value: {
|
|
1708
|
-
request_id: launch.request_id,
|
|
1709
|
-
subject_id: bound.subject_id,
|
|
1710
|
-
pane_id: bound.pane_id,
|
|
1711
|
-
state
|
|
1712
|
-
}
|
|
1713
|
-
};
|
|
1714
|
-
} catch (error) {
|
|
1715
|
-
const claimed = options.readers.readState(launch.mailbox)?.paneId;
|
|
1716
|
-
return unresolvedLaunch(
|
|
1717
|
-
launch.request_id,
|
|
1718
|
-
claimed !== void 0 && claimed !== radarPaneHandle(bound.pane_id) ? `the bound child ${agentId} claims the pane ${claimed}, not the pane ${bound.pane_id} this launch created for it` : `the bound child ${agentId} has not acknowledged this launch's startup (${String(error)})`
|
|
1719
|
-
);
|
|
1720
|
-
}
|
|
1565
|
+
return {
|
|
1566
|
+
kind: "ready",
|
|
1567
|
+
value: {
|
|
1568
|
+
request_id: launch.request_id,
|
|
1569
|
+
subject_id: bound.subject_id,
|
|
1570
|
+
pane_id: bound.pane_id
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1721
1573
|
}
|
|
1722
1574
|
async function waitForChild(client, requestId, options) {
|
|
1723
1575
|
let last = "the daemon has not answered";
|
|
@@ -1726,37 +1578,21 @@ async function waitForChild(client, requestId, options) {
|
|
|
1726
1578
|
return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
|
|
1727
1579
|
const answer = await client.spawnGet(requestId);
|
|
1728
1580
|
if (answer.ok === true) {
|
|
1729
|
-
const { edge
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1581
|
+
const { edge } = answer.value;
|
|
1582
|
+
if (edge.withdrawn !== void 0)
|
|
1583
|
+
return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
|
|
1584
|
+
if (edge.granted_at === void 0) {
|
|
1585
|
+
await delay(options.pollMs);
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1735
1588
|
const pane = edge.location?.pane;
|
|
1736
1589
|
if (pane === void 0 || pane === "")
|
|
1737
1590
|
return unresolvedLaunch(
|
|
1738
1591
|
requestId,
|
|
1739
1592
|
"the daemon's launch recorded no pane to verify the child against"
|
|
1740
1593
|
);
|
|
1741
|
-
if (freshness !== "fresh")
|
|
1742
|
-
return unresolvedLaunch(
|
|
1743
|
-
requestId,
|
|
1744
|
-
state === "unresolved" ? `the daemon's inventory does not report the pane ${pane} its own record names for this child` : `the daemon cannot confirm the pane ${pane} it recorded for this child`
|
|
1745
|
-
);
|
|
1746
|
-
if (edge.exit_observation?.state === "exited")
|
|
1747
|
-
return unresolvedLaunch(
|
|
1748
|
-
requestId,
|
|
1749
|
-
"the daemon observed the launched child's process exit"
|
|
1750
|
-
);
|
|
1751
1594
|
const agentId = edge.bound?.registration_id;
|
|
1752
1595
|
if (agentId !== void 0 && isUuid(agentId)) {
|
|
1753
|
-
const source = edge.bound?.source;
|
|
1754
|
-
const incarnation = edge.bound?.incarnation;
|
|
1755
|
-
if (source === void 0 || source === "" || incarnation === void 0 || incarnation === "")
|
|
1756
|
-
return unresolvedLaunch(
|
|
1757
|
-
requestId,
|
|
1758
|
-
`the daemon bound the child ${agentId} without recording the process it registered as`
|
|
1759
|
-
);
|
|
1760
1596
|
const subjectId = edge.bound?.subject_id;
|
|
1761
1597
|
if (subjectId === void 0 || !isUuid(subjectId))
|
|
1762
1598
|
return unresolvedLaunch(
|
|
@@ -1766,21 +1602,14 @@ async function waitForChild(client, requestId, options) {
|
|
|
1766
1602
|
return {
|
|
1767
1603
|
kind: "bound",
|
|
1768
1604
|
subject_id: subjectId,
|
|
1769
|
-
|
|
1770
|
-
pane_id: pane,
|
|
1771
|
-
source,
|
|
1772
|
-
incarnation
|
|
1605
|
+
pane_id: pane
|
|
1773
1606
|
};
|
|
1774
1607
|
}
|
|
1775
1608
|
last = "no child has registered with this launch's token yet";
|
|
1776
1609
|
} else {
|
|
1777
|
-
|
|
1610
|
+
return unresolvedLaunch(requestId, answer.message);
|
|
1778
1611
|
}
|
|
1779
|
-
|
|
1780
|
-
return unresolvedLaunch(requestId, last);
|
|
1781
|
-
await delay(options.pollMs, void 0, {
|
|
1782
|
-
...options.signal !== void 0 ? { signal: options.signal } : {}
|
|
1783
|
-
});
|
|
1612
|
+
await delay(options.pollMs);
|
|
1784
1613
|
}
|
|
1785
1614
|
}
|
|
1786
1615
|
|
|
@@ -1797,7 +1626,7 @@ function createRadarChildren(client, publication) {
|
|
|
1797
1626
|
if (read.ok === false) return read;
|
|
1798
1627
|
for (const edge of read.value.spawns) {
|
|
1799
1628
|
const bound = edge.bound;
|
|
1800
|
-
if (!bound?.subject_id || !bound.registration_id || edge.
|
|
1629
|
+
if (!bound?.subject_id || !bound.registration_id || edge.granted_at == null || edge.withdrawn != null) continue;
|
|
1801
1630
|
const at = Date.parse(bound.bound_at);
|
|
1802
1631
|
if (Number.isNaN(at)) continue;
|
|
1803
1632
|
const current = newest.get(bound.subject_id);
|
|
@@ -1843,6 +1672,34 @@ function createRadarChildren(client, publication) {
|
|
|
1843
1672
|
follow(all.value);
|
|
1844
1673
|
return { ok: true, value: [...all.value.values()].map((entry) => entry.child) };
|
|
1845
1674
|
},
|
|
1675
|
+
async restart(subject, program, options) {
|
|
1676
|
+
const parent = await publication.subject();
|
|
1677
|
+
if (parent?.subject_id === void 0)
|
|
1678
|
+
return { kind: "failed", message: "this process has no Radar registration to restart a child under" };
|
|
1679
|
+
const request_id = randomUUID2();
|
|
1680
|
+
const requested = await client.restart({
|
|
1681
|
+
request_id,
|
|
1682
|
+
parent: parent.registration_id,
|
|
1683
|
+
subject_id: subject,
|
|
1684
|
+
...program
|
|
1685
|
+
});
|
|
1686
|
+
if (requested.ok === false)
|
|
1687
|
+
return launchFailure(requested, request_id);
|
|
1688
|
+
const bound = await waitForChild(client, requested.value.launch_request_id, {
|
|
1689
|
+
pollMs: options?.pollMs ?? 250,
|
|
1690
|
+
...options?.signal !== void 0 ? { signal: options.signal } : {}
|
|
1691
|
+
});
|
|
1692
|
+
if (bound.kind !== "bound") return bound;
|
|
1693
|
+
await resolveAndFollow(subject);
|
|
1694
|
+
return {
|
|
1695
|
+
kind: "ready",
|
|
1696
|
+
value: {
|
|
1697
|
+
request_id: requested.value.launch_request_id,
|
|
1698
|
+
subject_id: bound.subject_id,
|
|
1699
|
+
pane_id: bound.pane_id
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
},
|
|
1846
1703
|
assignment(subject, owner) {
|
|
1847
1704
|
const existing = held.get(subject);
|
|
1848
1705
|
if (existing !== void 0) return existing.writer;
|
|
@@ -1964,7 +1821,7 @@ __export(radar_publication_exports, {
|
|
|
1964
1821
|
radarProcessSlot: () => radarProcessSlot,
|
|
1965
1822
|
readProcessBirth: () => readProcessBirth
|
|
1966
1823
|
});
|
|
1967
|
-
import { createHash, randomUUID as
|
|
1824
|
+
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
1968
1825
|
import {
|
|
1969
1826
|
closeSync,
|
|
1970
1827
|
constants,
|
|
@@ -2094,7 +1951,7 @@ function createPublicationStore(root) {
|
|
|
2094
1951
|
ensureDirectory(directory);
|
|
2095
1952
|
const temporary = join2(
|
|
2096
1953
|
directory,
|
|
2097
|
-
`.${name.slice(name.lastIndexOf("/") + 1)}.${
|
|
1954
|
+
`.${name.slice(name.lastIndexOf("/") + 1)}.${randomUUID3()}.tmp`
|
|
2098
1955
|
);
|
|
2099
1956
|
const bytes = Buffer.from(JSON.stringify(value), "utf8");
|
|
2100
1957
|
if (bytes.length > MAX_RECORD_BYTES)
|
|
@@ -2150,7 +2007,7 @@ function radarProcessSlot() {
|
|
|
2150
2007
|
return host[PROCESS_SLOT];
|
|
2151
2008
|
}
|
|
2152
2009
|
function radarProcessIncarnation(slot = radarProcessSlot()) {
|
|
2153
|
-
return slot.incarnation ??=
|
|
2010
|
+
return slot.incarnation ??= randomUUID3();
|
|
2154
2011
|
}
|
|
2155
2012
|
function radarBindingKey(run, owner, label) {
|
|
2156
2013
|
return createHash("sha256").update(`${run}\0${owner}\0${label}`).digest("hex").slice(0, 32);
|
|
@@ -2618,7 +2475,7 @@ function createPresenceReporter(input) {
|
|
|
2618
2475
|
releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
|
|
2619
2476
|
}
|
|
2620
2477
|
standing = void 0;
|
|
2621
|
-
state = { version: 2, session, claim_id:
|
|
2478
|
+
state = { version: 2, session, claim_id: randomUUID3(), releases };
|
|
2622
2479
|
save();
|
|
2623
2480
|
stopped = false;
|
|
2624
2481
|
void ensure();
|
|
@@ -2655,7 +2512,7 @@ var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
|
|
|
2655
2512
|
function storeIdentity(store) {
|
|
2656
2513
|
const known = STORE_IDENTITY.get(store);
|
|
2657
2514
|
if (known !== void 0) return known;
|
|
2658
|
-
const created =
|
|
2515
|
+
const created = randomUUID3();
|
|
2659
2516
|
STORE_IDENTITY.set(store, created);
|
|
2660
2517
|
return created;
|
|
2661
2518
|
}
|
|
@@ -3182,6 +3039,123 @@ function createRadarExecutionAdapter(pi, options) {
|
|
|
3182
3039
|
};
|
|
3183
3040
|
}
|
|
3184
3041
|
|
|
3042
|
+
// extension/radar-location.ts
|
|
3043
|
+
var radar_location_exports = {};
|
|
3044
|
+
__export(radar_location_exports, {
|
|
3045
|
+
radarLocalPane: () => radarLocalPane,
|
|
3046
|
+
radarPaneHandle: () => radarPaneHandle,
|
|
3047
|
+
resolveRadarProcessLocation: () => resolveRadarProcessLocation,
|
|
3048
|
+
tmuxServerName: () => tmuxServerName
|
|
3049
|
+
});
|
|
3050
|
+
import { basename } from "node:path";
|
|
3051
|
+
function radarLocalPane(env) {
|
|
3052
|
+
const pane = env.TMUX_PANE?.trim();
|
|
3053
|
+
return pane && /^%\d+$/.test(pane) ? pane : void 0;
|
|
3054
|
+
}
|
|
3055
|
+
function radarPaneHandle(qualified) {
|
|
3056
|
+
return qualified.slice(qualified.lastIndexOf(":") + 1);
|
|
3057
|
+
}
|
|
3058
|
+
function tmuxServerName(env) {
|
|
3059
|
+
const socket = env.TMUX?.trim().split(",")[0];
|
|
3060
|
+
return socket ? basename(socket) || void 0 : void 0;
|
|
3061
|
+
}
|
|
3062
|
+
function qualifiedOwnPane(env, backend, instance) {
|
|
3063
|
+
if (backend !== "tmux")
|
|
3064
|
+
return {
|
|
3065
|
+
absent: `the daemon serves the ${backend} backend, and Herdsman drives only tmux`
|
|
3066
|
+
};
|
|
3067
|
+
if (instance === void 0 || instance === "")
|
|
3068
|
+
return {
|
|
3069
|
+
pending: "the daemon did not name the tmux server it addresses"
|
|
3070
|
+
};
|
|
3071
|
+
const server = tmuxServerName(env);
|
|
3072
|
+
if (server === void 0)
|
|
3073
|
+
return {
|
|
3074
|
+
absent: "this process's own tmux client names no server, so it is not in the pane it would publish"
|
|
3075
|
+
};
|
|
3076
|
+
if (server !== instance)
|
|
3077
|
+
return {
|
|
3078
|
+
absent: `this process is attached to tmux server ${server}, not to ${instance}`
|
|
3079
|
+
};
|
|
3080
|
+
const pane = radarLocalPane(env);
|
|
3081
|
+
if (pane === void 0)
|
|
3082
|
+
return {
|
|
3083
|
+
absent: "this process's own environment names no tmux pane of that server"
|
|
3084
|
+
};
|
|
3085
|
+
return { pane: `tmux:${instance}:${pane}` };
|
|
3086
|
+
}
|
|
3087
|
+
function observedContainment(inventory, paneId) {
|
|
3088
|
+
const rows = inventory.panes.filter((pane) => pane.pane_id === paneId);
|
|
3089
|
+
if (rows.length === 0) return void 0;
|
|
3090
|
+
if (rows.length > 1) return { ambiguous: `topology reports pane ${paneId} twice` };
|
|
3091
|
+
const row = rows[0];
|
|
3092
|
+
const tab = inventory.tabs.find((candidate) => candidate.tab_id === row.tab_id);
|
|
3093
|
+
if (tab !== void 0 && tab.workspace_id !== row.workspace_id)
|
|
3094
|
+
return {
|
|
3095
|
+
ambiguous: `pane ${paneId} is reported in ${row.workspace_id}, but its tab belongs to ${tab.workspace_id}`
|
|
3096
|
+
};
|
|
3097
|
+
return { pane: row };
|
|
3098
|
+
}
|
|
3099
|
+
async function resolveRadarProcessLocation(client, env = process.env) {
|
|
3100
|
+
let ready;
|
|
3101
|
+
try {
|
|
3102
|
+
ready = await client.ping();
|
|
3103
|
+
} catch {
|
|
3104
|
+
return { state: "pending", reason: "the daemon did not answer this process" };
|
|
3105
|
+
}
|
|
3106
|
+
if (ready.ok === false)
|
|
3107
|
+
return {
|
|
3108
|
+
state: "pending",
|
|
3109
|
+
reason: `the daemon did not answer this process (${ready.code})`
|
|
3110
|
+
};
|
|
3111
|
+
const backend = ready.value.backend;
|
|
3112
|
+
if (backend === void 0 || backend === "none")
|
|
3113
|
+
return { state: "absent", reason: "the daemon serves no physical backend" };
|
|
3114
|
+
const instance = ready.value.instance ?? void 0;
|
|
3115
|
+
const own = qualifiedOwnPane(env, backend, instance);
|
|
3116
|
+
if ("absent" in own) return { state: "absent", reason: own.absent };
|
|
3117
|
+
if ("pending" in own) return { state: "pending", reason: own.pending };
|
|
3118
|
+
const paneId = own.pane;
|
|
3119
|
+
if (paneId === env.PI_HERDSMAN_PARENT_PANE_ID?.trim())
|
|
3120
|
+
return {
|
|
3121
|
+
state: "absent",
|
|
3122
|
+
reason: "this process is in the pane its launch was created beside, not in a pane of its own"
|
|
3123
|
+
};
|
|
3124
|
+
let inventory;
|
|
3125
|
+
try {
|
|
3126
|
+
inventory = await client.observe();
|
|
3127
|
+
} catch {
|
|
3128
|
+
return {
|
|
3129
|
+
state: "pending",
|
|
3130
|
+
reason: "the daemon's topology could not be read for this process's pane"
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
if (inventory.ok === false)
|
|
3134
|
+
return {
|
|
3135
|
+
state: "pending",
|
|
3136
|
+
reason: `the daemon's topology could not be read (${inventory.code})`
|
|
3137
|
+
};
|
|
3138
|
+
const containment = observedContainment(inventory.value, paneId);
|
|
3139
|
+
if (containment === void 0)
|
|
3140
|
+
return {
|
|
3141
|
+
state: "pending",
|
|
3142
|
+
reason: `the daemon's topology does not report pane ${paneId} yet`
|
|
3143
|
+
};
|
|
3144
|
+
if ("ambiguous" in containment)
|
|
3145
|
+
return { state: "pending", reason: containment.ambiguous };
|
|
3146
|
+
const row = containment.pane;
|
|
3147
|
+
return {
|
|
3148
|
+
state: "resolved",
|
|
3149
|
+
location: {
|
|
3150
|
+
backend,
|
|
3151
|
+
...instance !== void 0 ? { instance } : {},
|
|
3152
|
+
workspace: row.workspace_id,
|
|
3153
|
+
tab: row.tab_id,
|
|
3154
|
+
pane: paneId
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3185
3159
|
// extension/radar-runtime.ts
|
|
3186
3160
|
var radar_runtime_exports = {};
|
|
3187
3161
|
__export(radar_runtime_exports, {
|
|
@@ -3192,7 +3166,7 @@ __export(radar_runtime_exports, {
|
|
|
3192
3166
|
requestRadarRuntime: () => requestRadarRuntime
|
|
3193
3167
|
});
|
|
3194
3168
|
var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
|
|
3195
|
-
var RADAR_RUNTIME_VERSION =
|
|
3169
|
+
var RADAR_RUNTIME_VERSION = 4;
|
|
3196
3170
|
function lazyRadarRuntime(start) {
|
|
3197
3171
|
let pending;
|
|
3198
3172
|
return {
|
|
@@ -911,7 +911,6 @@ export interface ManagedSubject {
|
|
|
911
911
|
session: string | null;
|
|
912
912
|
state: State;
|
|
913
913
|
subject_id: string;
|
|
914
|
-
successor: string | null;
|
|
915
914
|
}
|
|
916
915
|
/**
|
|
917
916
|
* Exact spawn-edge child identity required by `spawn.close`.
|
|
@@ -1323,7 +1322,6 @@ export interface LifecycleObservation {
|
|
|
1323
1322
|
session: string | null;
|
|
1324
1323
|
state: State;
|
|
1325
1324
|
subject_id: string;
|
|
1326
|
-
successor: string | null;
|
|
1327
1325
|
}
|
|
1328
1326
|
/**
|
|
1329
1327
|
* A runtime pane: location plus optional display text.
|
|
@@ -1452,6 +1450,7 @@ export interface SpawnListPageSerializePublicSpawnTopology {
|
|
|
1452
1450
|
* nothing to verify.
|
|
1453
1451
|
*/
|
|
1454
1452
|
freshness?: SpawnListPageSerializeFreshness | null;
|
|
1453
|
+
granted_at?: string | null;
|
|
1455
1454
|
launched: SpawnListPageSerializeRequestOutcome;
|
|
1456
1455
|
location?: SpawnListPageSerializeRegistryLocation | null;
|
|
1457
1456
|
parent: string;
|
|
@@ -1461,6 +1460,7 @@ export interface SpawnListPageSerializePublicSpawnTopology {
|
|
|
1461
1460
|
request_id: string;
|
|
1462
1461
|
state: SpawnListPageSerializeSpawnState;
|
|
1463
1462
|
version: number;
|
|
1463
|
+
withdrawn?: SpawnListPageSerializeWithdrawn | null;
|
|
1464
1464
|
}
|
|
1465
1465
|
/**
|
|
1466
1466
|
* The child a token bound: the registering incarnation's own registry identity.
|
|
@@ -1497,3 +1497,11 @@ export interface SpawnListPageSerializeRegistryLocation {
|
|
|
1497
1497
|
tab?: string | null;
|
|
1498
1498
|
workspace?: string | null;
|
|
1499
1499
|
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Why and when a launch ended without ever presenting, so a reader never infers
|
|
1502
|
+
* failure from absence.
|
|
1503
|
+
*/
|
|
1504
|
+
export interface SpawnListPageSerializeWithdrawn {
|
|
1505
|
+
at: string;
|
|
1506
|
+
reason: string;
|
|
1507
|
+
}
|