@ours.network/fleet 1.2.0-nightly.73 → 1.2.0-nightly.75
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.
|
@@ -65,6 +65,14 @@ function identity(path, followLink = false) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
function within(path, root) { return path === root || path.startsWith(root + sep); }
|
|
68
|
+
/** Lock directories and the claim/reclaim entries derived from their names
|
|
69
|
+
* (`<lock>.claim.<pid>.<uuid>`, `<lock>.reclaim`) are never resources. */
|
|
70
|
+
const lockClaim = /\.claim\.\d+\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$|\.reclaim$/;
|
|
71
|
+
function lockName(name) { return name === '.locks' || name.endsWith('.lock'); }
|
|
72
|
+
function lockArtifact(name) { return lockName(name) || (lockClaim.test(name) && lockName(name.replace(lockClaim, ''))); }
|
|
73
|
+
/** Full discovery is synchronous and lstats every covered directory. Wakes are
|
|
74
|
+
* still delivered promptly, but discovery runs at most once per window. */
|
|
75
|
+
const DISCOVERY_WINDOW_MS = 100;
|
|
68
76
|
/** Native notifications cover supported local mounts. No idle content scan or
|
|
69
77
|
* silent claim of coverage after watch failure. Parents survive atomic rename.
|
|
70
78
|
* Discovery reads directory metadata only, never opens a controller or transcript. */
|
|
@@ -87,9 +95,11 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
87
95
|
trustedPaths.add(parent);
|
|
88
96
|
}
|
|
89
97
|
const resourceIdentity = (path) => identity(path, trustedPaths.has(path));
|
|
98
|
+
const locks = resolve(stateRoot(), 'locks');
|
|
99
|
+
const ignored = (path) => within(path, locks) || path.split(sep).some(name => lockArtifact(name) || name.endsWith('.tmp'));
|
|
90
100
|
let state = { protocolVersion: 1, producerGeneration: randomUUID(), position: 0, health: 'healthy' };
|
|
91
|
-
let disposed = signal.aborted, queued = false;
|
|
92
|
-
let recovery;
|
|
101
|
+
let disposed = signal.aborted, queued = false, discoveredAt = -Infinity;
|
|
102
|
+
let recovery, discovery;
|
|
93
103
|
let installing = false, installFailure;
|
|
94
104
|
const cursors = new Set();
|
|
95
105
|
const watchers = new Map();
|
|
@@ -110,6 +120,7 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
110
120
|
const install = () => {
|
|
111
121
|
installing = true;
|
|
112
122
|
installFailure = undefined;
|
|
123
|
+
let added = 0;
|
|
113
124
|
try {
|
|
114
125
|
const wanted = new Set();
|
|
115
126
|
const cover = (dir) => {
|
|
@@ -131,7 +142,7 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
131
142
|
return;
|
|
132
143
|
// Ignore sibling noise from ancestor coverage, lock churn and delivery acknowledgements.
|
|
133
144
|
const changed = filename === null ? dir : resolve(dir, String(filename));
|
|
134
|
-
if (changed
|
|
145
|
+
if (ignored(changed))
|
|
135
146
|
return;
|
|
136
147
|
if (paths.some(path => within(changed, path) || within(path, changed)) || (configEnabled && changed === config) || filename === null)
|
|
137
148
|
wake(filename === null ? undefined : changed);
|
|
@@ -145,6 +156,7 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
145
156
|
throw error;
|
|
146
157
|
}
|
|
147
158
|
watchers.set(dir, { identity: id, handle });
|
|
159
|
+
added++;
|
|
148
160
|
handle.on('error', (error) => { if (watchers.get(dir)?.handle === handle)
|
|
149
161
|
fail(error); });
|
|
150
162
|
handle.on('close', () => { if (watchers.get(dir)?.handle === handle)
|
|
@@ -153,6 +165,8 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
153
165
|
throw installFailure;
|
|
154
166
|
};
|
|
155
167
|
const visit = (dir, depth) => {
|
|
168
|
+
if (within(dir, locks))
|
|
169
|
+
return;
|
|
156
170
|
const id = resourceIdentity(dir);
|
|
157
171
|
if (!id)
|
|
158
172
|
return;
|
|
@@ -171,7 +185,7 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
171
185
|
throw error;
|
|
172
186
|
}
|
|
173
187
|
for (const entry of entries) {
|
|
174
|
-
if (!entry.isDirectory() || entry.isSymbolicLink() || entry.name
|
|
188
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || lockArtifact(entry.name))
|
|
175
189
|
continue;
|
|
176
190
|
visit(join(dir, entry.name), depth - 1);
|
|
177
191
|
}
|
|
@@ -195,6 +209,7 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
195
209
|
}
|
|
196
210
|
if (installFailure)
|
|
197
211
|
throw installFailure;
|
|
212
|
+
return added;
|
|
198
213
|
}
|
|
199
214
|
finally {
|
|
200
215
|
installing = false;
|
|
@@ -236,21 +251,48 @@ export function observeFleetResources(scope, onCommitted, signal) {
|
|
|
236
251
|
queued = false;
|
|
237
252
|
if (disposed || state.health !== 'healthy')
|
|
238
253
|
return;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
254
|
+
if (!discovery) {
|
|
255
|
+
const wait = discoveredAt + DISCOVERY_WINDOW_MS - performance.now();
|
|
256
|
+
if (wait <= 0) {
|
|
257
|
+
discover(false);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
discovery = setTimeout(() => { discovery = undefined; discover(true); }, wait);
|
|
261
|
+
discovery.unref();
|
|
245
262
|
}
|
|
263
|
+
// Covered directories were watched before this wake; directories created
|
|
264
|
+
// since the last pass are covered by the pending one.
|
|
246
265
|
onCommitted({ ...state });
|
|
247
266
|
});
|
|
248
267
|
};
|
|
268
|
+
const discover = (deferred) => {
|
|
269
|
+
if (disposed || state.health !== 'healthy')
|
|
270
|
+
return;
|
|
271
|
+
discoveredAt = performance.now();
|
|
272
|
+
let added;
|
|
273
|
+
try {
|
|
274
|
+
added = install();
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
fail(error);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Every wake before a deferred pass was already delivered. Writes into
|
|
281
|
+
// directories it newly covers have no event of their own: treat as unknown.
|
|
282
|
+
if (deferred) {
|
|
283
|
+
if (!added)
|
|
284
|
+
return;
|
|
285
|
+
state = { ...state, position: state.position + 1 };
|
|
286
|
+
for (const cursor of cursors)
|
|
287
|
+
cursor.position = state.position;
|
|
288
|
+
}
|
|
289
|
+
onCommitted({ ...state });
|
|
290
|
+
};
|
|
249
291
|
const direct = (path) => { if (paths.some(root => within(path, root)) || (configEnabled && path === config))
|
|
250
292
|
wake(path); };
|
|
251
293
|
let detach;
|
|
252
294
|
const dispose = () => { if (disposed)
|
|
253
|
-
return; disposed = true; clearTimeout(recovery); recovery = undefined; detach?.(); for (const cursor of [...cursors])
|
|
295
|
+
return; disposed = true; clearTimeout(recovery); recovery = undefined; clearTimeout(discovery); discovery = undefined; detach?.(); for (const cursor of [...cursors])
|
|
254
296
|
cursor.dispose(); commits.delete(direct); signal.removeEventListener('abort', dispose); release(); };
|
|
255
297
|
if (!disposed) {
|
|
256
298
|
commits.add(direct);
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.0-nightly.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "1.2.0-nightly.75",
|
|
3
|
+
"buildId": "adbb39fb78cd",
|
|
4
|
+
"commit": "3a4e46ab47afdcfe370c827809357d8c67f69783",
|
|
5
5
|
"dirty": true,
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-18T20:22:00.847Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"service.refresh-v1",
|
|
9
9
|
"managed-cli.codex-reasoning-effort-v1",
|
package/dist/web/server.js
CHANGED
|
@@ -56,8 +56,9 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
56
56
|
if (auth.appOrigin && request.headers.origin === auth.appOrigin && request.url.startsWith('/api/')) {
|
|
57
57
|
reply.header('Access-Control-Allow-Origin', auth.appOrigin).header('Vary', 'Origin');
|
|
58
58
|
reply.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
|
|
59
|
-
|
|
60
|
-
reply.header('Access-Control-
|
|
59
|
+
// The App binds resource reads by these headers; it must be able to send and read them cross-origin.
|
|
60
|
+
reply.header('Access-Control-Expose-Headers', 'X-Ours-Workspace-Auth, X-Ours-Request-Id, X-Resource-Producer, X-Resource-Revision, X-Resource-Source-Generation, X-Resource-Source-Position, X-Resource-Connection, X-Resource-View, X-Resource-Epoch, X-Resource-Baseline');
|
|
61
|
+
reply.header('Access-Control-Allow-Headers', 'Authorization,Content-Type,X-CSRF-Token,Idempotency-Key,X-Ours-Messenger-CSRF,X-Resource-Connection,X-Resource-View,X-Resource-Epoch,X-Resource-Baseline');
|
|
61
62
|
if (request.method === 'OPTIONS') {
|
|
62
63
|
auth.validateBoundary(request, false);
|
|
63
64
|
return reply.code(204).send();
|
|
@@ -428,6 +429,10 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
428
429
|
app.get('/api/v1/resource-events', async (request, reply) => {
|
|
429
430
|
const browser = auth.authenticate(request);
|
|
430
431
|
reply.hijack();
|
|
432
|
+
// A hijacked reply bypasses Fastify headers: keep the exact-origin CORS and security headers.
|
|
433
|
+
for (const [name, value] of Object.entries(reply.getHeaders()))
|
|
434
|
+
if (value !== undefined)
|
|
435
|
+
reply.raw.setHeader(name, Array.isArray(value) ? value : String(value));
|
|
431
436
|
reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache, no-transform', Connection: 'keep-alive', 'X-Accel-Buffering': 'no' });
|
|
432
437
|
reply.raw.flushHeaders();
|
|
433
438
|
const opened = resources.open(reply.raw, browser.id, () => auth.sessionValid(browser));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "1.2.0-nightly.
|
|
3
|
+
"version": "1.2.0-nightly.75",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, managed native/ACP sessions, supervision, and ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|