acn-client 0.12.0 → 0.14.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 +34 -35
- package/dist/index.d.mts +380 -15
- package/dist/index.d.ts +380 -15
- package/dist/index.js +272 -12
- package/dist/index.mjs +271 -12
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
ACNClient: () => ACNClient,
|
|
34
34
|
ACNError: () => ACNError,
|
|
35
35
|
ACNRealtime: () => ACNRealtime,
|
|
36
|
+
KNOWN_INBOX_MESSAGE_STATUSES: () => KNOWN_INBOX_MESSAGE_STATUSES,
|
|
36
37
|
KNOWN_PAYMENT_TASK_STATUSES: () => KNOWN_PAYMENT_TASK_STATUSES,
|
|
37
38
|
subscribeToACN: () => subscribeToACN
|
|
38
39
|
});
|
|
@@ -123,6 +124,9 @@ var ACNClient = class {
|
|
|
123
124
|
post(path, body) {
|
|
124
125
|
return this.request("POST", path, { body });
|
|
125
126
|
}
|
|
127
|
+
patch(path, body) {
|
|
128
|
+
return this.request("PATCH", path, { body });
|
|
129
|
+
}
|
|
126
130
|
delete(path) {
|
|
127
131
|
return this.request("DELETE", path);
|
|
128
132
|
}
|
|
@@ -239,16 +243,39 @@ var ACNClient = class {
|
|
|
239
243
|
return this.get("/api/v1/subnets");
|
|
240
244
|
}
|
|
241
245
|
/** Get subnet by ID */
|
|
242
|
-
async getSubnet(
|
|
243
|
-
return this.get(`/api/v1/subnets/${
|
|
246
|
+
async getSubnet(slug) {
|
|
247
|
+
return this.get(`/api/v1/subnets/${slug}`);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* List immediate children of a subnet (ADR-0003).
|
|
251
|
+
*
|
|
252
|
+
* Wraps `GET /api/v1/subnets/{parentSlug}/children`. Returns
|
|
253
|
+
* `SUBNET_NOT_FOUND` when the parent does not exist. Visibility
|
|
254
|
+
* matches `listSubnets` — private children you cannot see are
|
|
255
|
+
* omitted from the result set.
|
|
256
|
+
*/
|
|
257
|
+
async listChildren(parentSlug) {
|
|
258
|
+
const data = await this.get(
|
|
259
|
+
`/api/v1/subnets/${parentSlug}/children`
|
|
260
|
+
);
|
|
261
|
+
return data.subnets;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Promote a `task_scoped` subnet to `persistent` (ADR-0003).
|
|
265
|
+
*
|
|
266
|
+
* Owner-only. Idempotent — promoting an already-persistent subnet
|
|
267
|
+
* returns its current state unchanged.
|
|
268
|
+
*/
|
|
269
|
+
async promoteSubnet(slug) {
|
|
270
|
+
return this.post(`/api/v1/subnets/${slug}/promote`);
|
|
244
271
|
}
|
|
245
272
|
/** Delete a subnet you own (requires Agent API Key — only the owning agent can delete) */
|
|
246
|
-
async deleteSubnet(
|
|
247
|
-
return this.request("DELETE", `/api/v1/subnets/${
|
|
273
|
+
async deleteSubnet(slug) {
|
|
274
|
+
return this.request("DELETE", `/api/v1/subnets/${slug}`);
|
|
248
275
|
}
|
|
249
276
|
/** Get agents in a subnet */
|
|
250
|
-
async getSubnetAgents(
|
|
251
|
-
return this.get(`/api/v1/subnets/${
|
|
277
|
+
async getSubnetAgents(slug) {
|
|
278
|
+
return this.get(`/api/v1/subnets/${slug}/agents`);
|
|
252
279
|
}
|
|
253
280
|
// ──────────────────────────────────────────────────────────────────────
|
|
254
281
|
// Subnet membership (agent-side)
|
|
@@ -263,18 +290,219 @@ var ACNClient = class {
|
|
|
263
290
|
// scheduled for removal. Requires ACN backend ≥ post-PR-#42.
|
|
264
291
|
// ──────────────────────────────────────────────────────────────────────
|
|
265
292
|
/** Join agent to subnet */
|
|
266
|
-
async joinSubnet(agentId,
|
|
267
|
-
return this.post(`/api/v1/agents/${agentId}/subnets/${
|
|
293
|
+
async joinSubnet(agentId, slug) {
|
|
294
|
+
return this.post(`/api/v1/agents/${agentId}/subnets/${slug}`);
|
|
268
295
|
}
|
|
269
296
|
/** Remove agent from subnet */
|
|
270
|
-
async leaveSubnet(agentId,
|
|
271
|
-
return this.delete(`/api/v1/agents/${agentId}/subnets/${
|
|
297
|
+
async leaveSubnet(agentId, slug) {
|
|
298
|
+
return this.delete(`/api/v1/agents/${agentId}/subnets/${slug}`);
|
|
272
299
|
}
|
|
273
300
|
/** Get agent's subnets */
|
|
274
301
|
async getAgentSubnets(agentId) {
|
|
275
302
|
return this.get(`/api/v1/agents/${agentId}/subnets`);
|
|
276
303
|
}
|
|
277
304
|
// ============================================
|
|
305
|
+
// ADR-0004 Subnet Admission
|
|
306
|
+
// ============================================
|
|
307
|
+
//
|
|
308
|
+
// 13 verbs gated by `subnet.join_policy === 'approval'`:
|
|
309
|
+
// - Allowlist (3): owner pre-authorisation.
|
|
310
|
+
// - Join requests (4): applicant-initiated path.
|
|
311
|
+
// - Invitations (5): owner-initiated path.
|
|
312
|
+
// - Agent-side (1): invitee's cross-subnet pending view.
|
|
313
|
+
//
|
|
314
|
+
// The plain `joinSubnet` verb dispatches the six-branch decision
|
|
315
|
+
// tree on the server side — these methods are the admin-side
|
|
316
|
+
// controls used by subnet owners and the per-row decisions used
|
|
317
|
+
// by applicants and invitees.
|
|
318
|
+
//
|
|
319
|
+
// Method names use the `subnet*` prefix to avoid colliding with
|
|
320
|
+
// the existing inbox `addToAllowlist` surface (which lives at
|
|
321
|
+
// `/api/v1/agents/{a}/allowlist/{target}` and is unrelated).
|
|
322
|
+
// ----- Allowlist (owner-only, 3 verbs) ---------------------------------
|
|
323
|
+
/**
|
|
324
|
+
* Pre-authorise `agentId` on `slug`'s allowlist (owner only).
|
|
325
|
+
*
|
|
326
|
+
* Allowlisted agents skip the approval queue: their next
|
|
327
|
+
* `joinSubnet` lands in branch 4 (allowlist hit) and becomes an
|
|
328
|
+
* immediate member with an `allowlist_auto` audit row.
|
|
329
|
+
*
|
|
330
|
+
* Server returns 201 with the persisted entry; duplicate adds
|
|
331
|
+
* return 409 ALREADY_ON_ALLOWLIST (raised as an error, never
|
|
332
|
+
* silently no-op'd).
|
|
333
|
+
*/
|
|
334
|
+
async subnetAllowlistAdd(slug, agentId) {
|
|
335
|
+
return this.post(`/api/v1/subnets/${slug}/allowlist`, {
|
|
336
|
+
agent_id: agentId
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Remove `agentId` from `slug`'s allowlist (owner only).
|
|
341
|
+
*
|
|
342
|
+
* Idempotent — removing an entry that doesn't exist still
|
|
343
|
+
* returns 204. Per ADR-0004 §"Allowlist mutation does not
|
|
344
|
+
* affect agents who already joined", this does NOT revoke
|
|
345
|
+
* membership for agents already admitted via the allowlist.
|
|
346
|
+
*/
|
|
347
|
+
async subnetAllowlistRemove(slug, agentId) {
|
|
348
|
+
await this.delete(`/api/v1/subnets/${slug}/allowlist/${agentId}`);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* List `slug`'s allowlist entries (owner only).
|
|
352
|
+
*
|
|
353
|
+
* Owner-only by design — the allowlist is a privacy-sensitive
|
|
354
|
+
* trust signal and exposing it publicly would leak relationship
|
|
355
|
+
* metadata.
|
|
356
|
+
*/
|
|
357
|
+
async subnetAllowlistList(slug, options) {
|
|
358
|
+
const params = {
|
|
359
|
+
limit: options?.limit ?? 100,
|
|
360
|
+
offset: options?.offset ?? 0
|
|
361
|
+
};
|
|
362
|
+
return this.get(`/api/v1/subnets/${slug}/allowlist`, params);
|
|
363
|
+
}
|
|
364
|
+
// ----- Join requests (4 verbs: 3 owner-side + 1 applicant-side) --------
|
|
365
|
+
/**
|
|
366
|
+
* Owner approves a pending join_request (CAS pending → approved).
|
|
367
|
+
*
|
|
368
|
+
* Side effects: applicant added to `subnet.member_agent_ids` and
|
|
369
|
+
* the `subnet.join_approved` webhook fires. The applicant is
|
|
370
|
+
* still expected to call `joinSubnet` to register the
|
|
371
|
+
* `agent.subnet_ids` back-reference (per ADR-0004 §"State
|
|
372
|
+
* machine edges").
|
|
373
|
+
*
|
|
374
|
+
* Optional `note` (≤500 chars) is recorded on the audit row.
|
|
375
|
+
*/
|
|
376
|
+
async subnetJoinRequestApprove(slug, requestId, options) {
|
|
377
|
+
return this.post(
|
|
378
|
+
`/api/v1/subnets/${slug}/join-requests/${requestId}/approve`,
|
|
379
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Owner rejects a pending join_request (CAS pending → rejected).
|
|
384
|
+
*
|
|
385
|
+
* No membership change. `subnet.join_rejected` webhook fires.
|
|
386
|
+
*/
|
|
387
|
+
async subnetJoinRequestReject(slug, requestId, options) {
|
|
388
|
+
return this.post(
|
|
389
|
+
`/api/v1/subnets/${slug}/join-requests/${requestId}/reject`,
|
|
390
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Applicant withdraws their own pending join_request.
|
|
395
|
+
*
|
|
396
|
+
* Self-only — caller must be the agent who originally created
|
|
397
|
+
* the request. `subnet.join_withdrawn` webhook fires.
|
|
398
|
+
*/
|
|
399
|
+
async subnetJoinRequestWithdraw(slug, requestId, options) {
|
|
400
|
+
return this.request(
|
|
401
|
+
"DELETE",
|
|
402
|
+
`/api/v1/subnets/${slug}/join-requests/${requestId}`,
|
|
403
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Owner lists join_request / allowlist_auto rows for `slug`.
|
|
408
|
+
*
|
|
409
|
+
* `kind` defaults to `'join_request'`; pass `'allowlist_auto'`
|
|
410
|
+
* to inspect synthesised allowlist-hit audit rows. Server
|
|
411
|
+
* rejects `kind='invitation'` with 400 INVALID_KIND_FILTER —
|
|
412
|
+
* use `subnetInvitationList` instead.
|
|
413
|
+
*/
|
|
414
|
+
async subnetJoinRequestList(slug, options) {
|
|
415
|
+
const params = {
|
|
416
|
+
kind: options?.kind ?? "join_request",
|
|
417
|
+
limit: options?.limit ?? 100,
|
|
418
|
+
offset: options?.offset ?? 0
|
|
419
|
+
};
|
|
420
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
421
|
+
return this.get(`/api/v1/subnets/${slug}/join-requests`, params);
|
|
422
|
+
}
|
|
423
|
+
// ----- Invitations (5 + 1 verbs) ---------------------------------------
|
|
424
|
+
/**
|
|
425
|
+
* Owner sends an invitation to `agentId` (or merges into a
|
|
426
|
+
* pending join_request from the same target).
|
|
427
|
+
*
|
|
428
|
+
* Two response shapes per ADR-0004 §"Invitation merge path":
|
|
429
|
+
*
|
|
430
|
+
* - **Normal path** (server returns 202): `{ invitation_id, status: 'pending' }`.
|
|
431
|
+
* - **Merge path** (server returns 200, request auto-approved):
|
|
432
|
+
* `{ auto_resolved: true, resolved_kind: 'join_request', request_id }`.
|
|
433
|
+
*
|
|
434
|
+
* Discriminate on `auto_resolved` to dispatch.
|
|
435
|
+
*/
|
|
436
|
+
async subnetInvitationSend(slug, agentId, options) {
|
|
437
|
+
const body = { agent_id: agentId };
|
|
438
|
+
if (options?.note !== void 0) body.note = options.note;
|
|
439
|
+
return this.post(`/api/v1/subnets/${slug}/invitations`, body);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Invitee accepts a pending invitation (CAS pending → approved).
|
|
443
|
+
*
|
|
444
|
+
* Self-only against the row's `agent_id`. Side effects: invitee
|
|
445
|
+
* added to `subnet.member_agent_ids`, the agent's `subnet_ids`
|
|
446
|
+
* gains the back-reference, and `subnet.invitation_accepted`
|
|
447
|
+
* webhook fires.
|
|
448
|
+
*/
|
|
449
|
+
async subnetInvitationAccept(slug, requestId, options) {
|
|
450
|
+
return this.post(
|
|
451
|
+
`/api/v1/subnets/${slug}/invitations/${requestId}/accept`,
|
|
452
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Invitee rejects a pending invitation (CAS pending → rejected).
|
|
457
|
+
*
|
|
458
|
+
* No membership change. `subnet.invitation_rejected` webhook
|
|
459
|
+
* fires.
|
|
460
|
+
*/
|
|
461
|
+
async subnetInvitationReject(slug, requestId, options) {
|
|
462
|
+
return this.post(
|
|
463
|
+
`/api/v1/subnets/${slug}/invitations/${requestId}/reject`,
|
|
464
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Owner cancels a pending invitation (CAS pending → withdrawn).
|
|
469
|
+
*
|
|
470
|
+
* Owner-only counterpart to applicant withdraw. The row goes to
|
|
471
|
+
* `withdrawn` (not `rejected`) — distinct audit token so
|
|
472
|
+
* consumers can tell "owner gave up" from "invitee said no".
|
|
473
|
+
*/
|
|
474
|
+
async subnetInvitationCancel(slug, requestId, options) {
|
|
475
|
+
return this.request(
|
|
476
|
+
"DELETE",
|
|
477
|
+
`/api/v1/subnets/${slug}/invitations/${requestId}`,
|
|
478
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Owner lists invitation rows for `slug`.
|
|
483
|
+
*
|
|
484
|
+
* Owner-only — invitees use `agentSubnetInvitations` for their
|
|
485
|
+
* own cross-subnet view.
|
|
486
|
+
*/
|
|
487
|
+
async subnetInvitationList(slug, options) {
|
|
488
|
+
const params = {
|
|
489
|
+
limit: options?.limit ?? 100,
|
|
490
|
+
offset: options?.offset ?? 0
|
|
491
|
+
};
|
|
492
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
493
|
+
return this.get(`/api/v1/subnets/${slug}/invitations`, params);
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Invitee's cross-subnet pending-invitation list (self only).
|
|
497
|
+
*
|
|
498
|
+
* Returns only `status='pending'` rows. Historical decisions
|
|
499
|
+
* are queryable per-subnet through the owner-only
|
|
500
|
+
* `subnetInvitationList`.
|
|
501
|
+
*/
|
|
502
|
+
async agentSubnetInvitations(agentId) {
|
|
503
|
+
return this.get(`/api/v1/agents/${agentId}/subnet-invitations`);
|
|
504
|
+
}
|
|
505
|
+
// ============================================
|
|
278
506
|
// Communication
|
|
279
507
|
// ============================================
|
|
280
508
|
/** Send message to an agent */
|
|
@@ -327,6 +555,31 @@ var ACNClient = class {
|
|
|
327
555
|
if (options?.consume) params.ack = true;
|
|
328
556
|
return this.get(`/api/v1/communication/history/${agentId}`, params);
|
|
329
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Precisely acknowledge (remove) specific messages from the inbox.
|
|
560
|
+
*
|
|
561
|
+
* Unlike `getMessageHistory({ consume: true })` which clears the entire inbox,
|
|
562
|
+
* this method removes only the messages whose `route_id` values are listed.
|
|
563
|
+
*
|
|
564
|
+
* @param agentId Must match the authenticated agent's ID.
|
|
565
|
+
* @param routeIds List of `route_id` values to remove (up to 500).
|
|
566
|
+
* @returns Number of messages actually removed.
|
|
567
|
+
*/
|
|
568
|
+
async ackInbox(agentId, routeIds) {
|
|
569
|
+
return this.post(`/api/v1/communication/history/${agentId}/ack`, { route_ids: routeIds });
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Update the lifecycle status of a specific inbox message.
|
|
573
|
+
*
|
|
574
|
+
* @param agentId Must match the authenticated agent's ID.
|
|
575
|
+
* @param routeId `route_id` of the target message (from inbox listing).
|
|
576
|
+
* @param status New status: `"unread"` | `"read"` | `"processed"`.
|
|
577
|
+
* @returns Object with `agent_id`, `route_id`, and `status`.
|
|
578
|
+
* @throws 404 (`inbox_message_not_found`) if route_id is absent from inbox.
|
|
579
|
+
*/
|
|
580
|
+
async updateInboxMessageStatus(agentId, routeId, status) {
|
|
581
|
+
return this.patch(`/api/v1/communication/history/${agentId}/${routeId}`, { status });
|
|
582
|
+
}
|
|
330
583
|
// ============================================
|
|
331
584
|
// Manifest Queue (Phase 2/3)
|
|
332
585
|
// ============================================
|
|
@@ -903,8 +1156,8 @@ var ACNClient = class {
|
|
|
903
1156
|
* Register (or clear) an org-harness webhook URL for a subnet.
|
|
904
1157
|
* Pass `harnessUrl: null` to deregister.
|
|
905
1158
|
*/
|
|
906
|
-
async registerSubnetHarness(
|
|
907
|
-
await this.request("PATCH", `/api/v1/subnets/${
|
|
1159
|
+
async registerSubnetHarness(slug, harnessUrl, harnessSecret) {
|
|
1160
|
+
await this.request("PATCH", `/api/v1/subnets/${slug}/harness`, {
|
|
908
1161
|
body: {
|
|
909
1162
|
harness_url: harnessUrl,
|
|
910
1163
|
harness_secret: harnessSecret ?? null
|
|
@@ -920,6 +1173,7 @@ var ACNError = class extends Error {
|
|
|
920
1173
|
this.errorCode = options?.errorCode;
|
|
921
1174
|
this.requestId = options?.requestId;
|
|
922
1175
|
}
|
|
1176
|
+
status;
|
|
923
1177
|
/** ACN internal error code (present on sanitised 5xx responses) */
|
|
924
1178
|
errorCode;
|
|
925
1179
|
/** Request ID minted by ACN for 5xx responses (useful for support) */
|
|
@@ -1138,11 +1392,17 @@ var KNOWN_PAYMENT_TASK_STATUSES = [
|
|
|
1138
1392
|
"payment_failed",
|
|
1139
1393
|
"refunded"
|
|
1140
1394
|
];
|
|
1395
|
+
var KNOWN_INBOX_MESSAGE_STATUSES = [
|
|
1396
|
+
"unread",
|
|
1397
|
+
"read",
|
|
1398
|
+
"processed"
|
|
1399
|
+
];
|
|
1141
1400
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1142
1401
|
0 && (module.exports = {
|
|
1143
1402
|
ACNClient,
|
|
1144
1403
|
ACNError,
|
|
1145
1404
|
ACNRealtime,
|
|
1405
|
+
KNOWN_INBOX_MESSAGE_STATUSES,
|
|
1146
1406
|
KNOWN_PAYMENT_TASK_STATUSES,
|
|
1147
1407
|
subscribeToACN
|
|
1148
1408
|
});
|