@openagents-org/agent-launcher 0.2.165 → 0.2.167

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.
@@ -40,6 +40,72 @@ function getOrCreateNodeKey() {
40
40
  return key;
41
41
  }
42
42
 
43
+ /**
44
+ * Every workspace this device is paired to, most recent first.
45
+ *
46
+ * All of them are live: the server-side node row is keyed per (workspace,
47
+ * node_key), so one device holds a membership in each workspace it paired with
48
+ * and the daemon heartbeats every one. Records written before `pairings`
49
+ * existed carry a single top-level pairing, which is reconstructed here.
50
+ */
51
+ function listPairings() {
52
+ const record = loadNode();
53
+ if (!record) return [];
54
+ if (record.pairings && record.pairings.length) return record.pairings;
55
+ return record.workspace_id ? [record] : [];
56
+ }
57
+
58
+ /**
59
+ * Add a pairing, or refresh it if this workspace is already paired.
60
+ *
61
+ * Additive on purpose: pairing a second workspace must NOT take the device away
62
+ * from the ones it already belongs to (each issues its own node row). The newest
63
+ * pairing is mirrored at the top level, which is what a daemon predating
64
+ * multi-pairing reads. Mirrors the launcher's writer (`node-pairing.ts`).
65
+ */
66
+ function recordPairing(nodeKey, pairing) {
67
+ const stamped = { ...pairing, paired_at: new Date().toISOString() };
68
+ const pairings = [
69
+ stamped,
70
+ ...listPairings().filter((p) => p.workspace_id !== pairing.workspace_id),
71
+ ];
72
+ saveNode({ node_key: nodeKey, ...stamped, pairings });
73
+ }
74
+
75
+ /**
76
+ * Forget one workspace's pairing — that workspace no longer recognizes this node.
77
+ *
78
+ * Keeps `node_key` (so a re-pair reuses the same device id) and drops just the
79
+ * one entry: the server-side node row for THAT workspace is gone and can only
80
+ * be replaced by a fresh code, while every other pairing is still good. The top
81
+ * level is re-pointed at whichever pairing is now first, since that is what a
82
+ * daemon predating multi-pairing reads. Mirrors the launcher's writer
83
+ * (`node-pairing.ts`) so the two stay schema-compatible.
84
+ *
85
+ * @param workspaceId which pairing to drop; defaults to the top-level one.
86
+ * @returns the pairing that was dropped, or null if there was no such pairing.
87
+ */
88
+ function clearPairing(workspaceId) {
89
+ const record = loadNode();
90
+ if (!record) return null;
91
+ const target = workspaceId || record.workspace_id;
92
+ if (!target) return null;
93
+
94
+ const pairings = listPairings();
95
+ const dropped = pairings.find((p) => p.workspace_id === target);
96
+ if (!dropped) return null;
97
+
98
+ const remaining = pairings.filter((p) => p.workspace_id !== target);
99
+ saveNode({ node_key: record.node_key, ...remaining[0], pairings: remaining });
100
+ return {
101
+ node_id: dropped.node_id,
102
+ workspace_id: dropped.workspace_id,
103
+ workspace_slug: dropped.workspace_slug,
104
+ workspace_name: dropped.workspace_name,
105
+ endpoint: dropped.endpoint,
106
+ };
107
+ }
108
+
43
109
  /** Best-effort device type from the platform (user/UI can override). */
44
110
  function inferDeviceType() {
45
111
  const p = os.platform();
@@ -64,6 +130,9 @@ function gatherDeviceInfo() {
64
130
  module.exports = {
65
131
  loadNode,
66
132
  saveNode,
133
+ listPairings,
134
+ recordPairing,
135
+ clearPairing,
67
136
  getOrCreateNodeKey,
68
137
  inferDeviceType,
69
138
  gatherDeviceInfo,
package/src/registry.js CHANGED
@@ -3,7 +3,10 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
- const DEFAULT_REGISTRY_URL = 'https://endpoint.openagents.org/v1/agent-registry';
6
+ // Served by the workspace backend from the repo's /registry files (one JSON
7
+ // per agent type, models resolved server-side) — the same catalog the
8
+ // workspace UI reads. See workspace/backend/app/services/agent_registry.py.
9
+ const DEFAULT_REGISTRY_URL = 'https://workspace-endpoint.openagents.org/v1/agent-registry';
7
10
  const CACHE_FILE = 'agent_catalog.json';
8
11
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
9
12
 
@@ -969,6 +969,10 @@ class WorkspaceClient {
969
969
  // Preserve structured error details (error_code, hint,
970
970
  // quota occupancy, ...) so tool handlers can surface them.
971
971
  if (parsed.data && typeof parsed.data === 'object') err.data = parsed.data;
972
+ // The HTTP status lets callers tell a definitive rejection
973
+ // (e.g. 404 "not found") from a transient one (5xx/timeout);
974
+ // the node heartbeat uses it to clear a removed pairing.
975
+ err.status = res.statusCode;
972
976
  reject(err);
973
977
  }
974
978
  } else {