@xtrape/capsule-agent-node 0.2.0 → 0.4.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 CHANGED
@@ -6,10 +6,7 @@
6
6
  [![Status: Public Review](https://img.shields.io/badge/status-Public%20Review-orange.svg)](https://xtrape-com.github.io/xtrape-capsule-site/)
7
7
  [![Docs](https://img.shields.io/badge/docs-xtrape--capsule--site-blue.svg)](https://xtrape-com.github.io/xtrape-capsule-site/agents/node-embedded-agent)
8
8
 
9
- `@xtrape/capsule-agent-node` embeds an Opstage Agent inside a Node.js service.
10
- The Agent registers with Opstage CE, persists its Agent token, sends heartbeats,
11
- reports service metadata, exposes configs and health, and polls Commands for
12
- operator-triggered Actions.
9
+ `@xtrape/capsule-agent-node` supports the stable Node.js Embedded Agent SDK path for single Capsule Services. For multi-service OpHub functionality, use OpHub (Go runtime).
13
10
 
14
11
  > **Package status:** Xtrape Capsule is currently in **Public Review** before
15
12
  > the `v0.1.0 Public Preview` release. This package is published under the
@@ -26,6 +23,10 @@ pnpm add @xtrape/capsule-agent-node@public-review
26
23
 
27
24
  The current Public Review version may change before `v0.1.0`.
28
25
 
26
+ During v0.3 development, this repository may use a local workspace, GitHub
27
+ branch dependency, or `0.3.0-rc.x` package for
28
+ `@xtrape/capsule-contracts-node` until the final `0.3.0` package is published.
29
+
29
30
  For this repository itself:
30
31
 
31
32
  ```bash
@@ -457,3 +458,24 @@ safety guidance.
457
458
 
458
459
  Apache-2.0. "Xtrape", "Xtrape Capsule", and "Opstage" are trademarks of their
459
460
  respective owners; the open-source license does not grant trademark rights.
461
+
462
+ ## v0.4 Experimental Capsule Bus Hook
463
+
464
+ The SDK exposes a minimal experimental publish hook into CE's built-in SQLite-backed in-process event-to-command router:
465
+
466
+ ```ts
467
+ await agent.publishBusEvent({
468
+ eventType: "demo.item.created",
469
+ payload: { itemId: "item-1" },
470
+ });
471
+ ```
472
+
473
+ The hook posts to Opstage CE as the registered embedded agent and defaults `sourceServiceCode` to the configured service code.
474
+
475
+ Typed errors for Bus failure modes:
476
+
477
+ - `BusDisabledError` — `CAPSULE_BUS_DISABLED` (404)
478
+ - `BusRateLimitedError` — `BUS_RATE_LIMITED` (429)
479
+ - `BusDepthExceededError` — `BUS_DEPTH_EXCEEDED` (422)
480
+
481
+ Capsule Bus is experimental in v0.4: not a standalone Bus Server, external broker, workflow engine, or service mesh API.
package/dist/index.cjs CHANGED
@@ -23,6 +23,9 @@ __export(index_exports, {
23
23
  AgentApiClient: () => AgentApiClient,
24
24
  AgentApiError: () => AgentApiError,
25
25
  AgentAuthError: () => AgentAuthError,
26
+ BusDepthExceededError: () => BusDepthExceededError,
27
+ BusDisabledError: () => BusDisabledError,
28
+ BusRateLimitedError: () => BusRateLimitedError,
26
29
  CapsuleAgent: () => CapsuleAgent,
27
30
  FileTokenStore: () => FileTokenStore,
28
31
  NetworkError: () => NetworkError,
@@ -64,9 +67,30 @@ var NetworkError = class extends AgentApiError {
64
67
  }
65
68
  }
66
69
  };
70
+ var BusDisabledError = class extends AgentApiError {
71
+ constructor(message, status, body) {
72
+ super(status, message, body, "CAPSULE_BUS_DISABLED");
73
+ this.name = "BusDisabledError";
74
+ }
75
+ };
76
+ var BusRateLimitedError = class extends AgentApiError {
77
+ constructor(message, status, body) {
78
+ super(status, message, body, "BUS_RATE_LIMITED");
79
+ this.name = "BusRateLimitedError";
80
+ }
81
+ };
82
+ var BusDepthExceededError = class extends AgentApiError {
83
+ constructor(message, status, body) {
84
+ super(status, message, body, "BUS_DEPTH_EXCEEDED");
85
+ this.name = "BusDepthExceededError";
86
+ }
87
+ };
67
88
  function classifyAgentApiError(status, message, body, code, path) {
68
89
  if (status === 401 && path.endsWith("/register")) return new RegistrationError(message, status, body, code);
69
90
  if (status === 401 || status === 403) return new AgentAuthError(message, status, body, code);
91
+ if (code === "CAPSULE_BUS_DISABLED") return new BusDisabledError(message, status, body);
92
+ if (code === "BUS_RATE_LIMITED") return new BusRateLimitedError(message, status, body);
93
+ if (code === "BUS_DEPTH_EXCEEDED") return new BusDepthExceededError(message, status, body);
70
94
  return new AgentApiError(status, message, body, code);
71
95
  }
72
96
 
@@ -118,6 +142,10 @@ var AgentApiClient = class {
118
142
  reportResult(agentId, token, commandId, req) {
119
143
  return this.request(`/api/agents/${agentId}/commands/${commandId}/result`, { method: "POST", token, body: JSON.stringify(req) });
120
144
  }
145
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
146
+ publishBusEvent(agentId, token, req) {
147
+ return this.request(`/api/agents/${agentId}/bus/events`, { method: "POST", token, body: JSON.stringify({ ...req, experimental: req.experimental ?? "v0.4-experimental" }) });
148
+ }
121
149
  };
122
150
 
123
151
  // src/token-store/file-token-store.ts
@@ -272,6 +300,14 @@ var CapsuleAgent = class {
272
300
  async runHealth() {
273
301
  return this.healthProvider();
274
302
  }
303
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
304
+ async publishBusEvent(input) {
305
+ if (!this.agentId || !this.token) throw new Error("Agent must be started before publishing Capsule Bus events.");
306
+ const sourceServiceCode = input.sourceServiceCode ?? this.options.service.code;
307
+ const result = await this.retry(() => this.client.publishBusEvent(this.agentId, this.token, { ...input, sourceServiceCode }));
308
+ this.log("info", "experimental bus event published", { eventId: result.eventId, eventType: input.eventType, sourceServiceCode, routedCommands: result.routedCommands.length });
309
+ return result;
310
+ }
275
311
  async reportService() {
276
312
  if (!this.agentId || !this.token) return;
277
313
  const [health, configs] = await Promise.all([this.runHealth(), this.configProvider()]);
@@ -372,6 +408,9 @@ var CapsuleAgent = class {
372
408
  AgentApiClient,
373
409
  AgentApiError,
374
410
  AgentAuthError,
411
+ BusDepthExceededError,
412
+ BusDisabledError,
413
+ BusRateLimitedError,
375
414
  CapsuleAgent,
376
415
  FileTokenStore,
377
416
  NetworkError,
package/dist/index.d.cts CHANGED
@@ -1,12 +1,5 @@
1
- import { HealthReportInput, ConfigItemInput, ActionDefinitionInput, ReportedService, RegisterAgentRequest, AgentHeartbeatRequest, ServiceReportRequest, ReportCommandResultRequest } from '@xtrape/capsule-contracts-node';
1
+ import { ActionPrepareResult, PublishBusEventRequest, RuntimeKind, HealthReportInput, ConfigItemInput, ActionDefinitionInput, PublishBusEventResponse, ReportedService, RegisterAgentRequest, AgentHeartbeatRequest, ServiceReportRequest, ReportCommandResultRequest } from '@xtrape/capsule-contracts-node';
2
2
 
3
- type RuntimeKind = "nodejs" | "java" | "python" | "go" | "other";
4
- type ActionPrepareResult = {
5
- action?: Record<string, unknown>;
6
- inputSchema?: Record<string, unknown>;
7
- initialPayload?: Record<string, unknown>;
8
- currentState?: Record<string, unknown>;
9
- };
10
3
  type AgentMode = "embedded";
11
4
  type ActionHandler = (payload: Record<string, unknown>) => Promise<{
12
5
  success?: boolean;
@@ -94,6 +87,11 @@ interface TokenStore {
94
87
  clear(): Promise<void>;
95
88
  }
96
89
  type ServiceSnapshot = ReportedService;
90
+ /** Experimental v0.4 Capsule Bus publish input. Subject to change before v1.0. */
91
+ type PublishBusEventInput = Omit<PublishBusEventRequest, "sourceServiceCode"> & {
92
+ sourceServiceCode?: string;
93
+ };
94
+ type PublishBusEventResult = PublishBusEventResponse;
97
95
 
98
96
  declare class CapsuleAgent {
99
97
  private readonly options;
@@ -118,6 +116,8 @@ declare class CapsuleAgent {
118
116
  private ensureRegistered;
119
117
  private serviceSnapshot;
120
118
  runHealth(): Promise<HealthReportInput>;
119
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
120
+ publishBusEvent(input: PublishBusEventInput): Promise<PublishBusEventResult>;
121
121
  private reportService;
122
122
  private heartbeat;
123
123
  private pollOnce;
@@ -178,6 +178,15 @@ declare class AgentAuthError extends AgentApiError {
178
178
  declare class NetworkError extends AgentApiError {
179
179
  constructor(message: string, cause?: unknown);
180
180
  }
181
+ declare class BusDisabledError extends AgentApiError {
182
+ constructor(message: string, status: number, body?: unknown);
183
+ }
184
+ declare class BusRateLimitedError extends AgentApiError {
185
+ constructor(message: string, status: number, body?: unknown);
186
+ }
187
+ declare class BusDepthExceededError extends AgentApiError {
188
+ constructor(message: string, status: number, body?: unknown);
189
+ }
181
190
 
182
191
  declare class AgentApiClient {
183
192
  private readonly backendUrl;
@@ -193,7 +202,7 @@ declare class AgentApiClient {
193
202
  heartbeat(agentId: string, token: string, req: AgentHeartbeatRequest): Promise<unknown>;
194
203
  reportServices(agentId: string, token: string, req: ServiceReportRequest): Promise<unknown>;
195
204
  pollCommands(agentId: string, token: string): Promise<{
196
- type: "ACTION_PREPARE" | "ACTION_EXECUTE";
205
+ type: "ACTION_EXECUTE" | "ACTION_PREPARE";
197
206
  id: string;
198
207
  createdAt: string;
199
208
  status: "PENDING" | "RUNNING" | "SUCCEEDED" | "FAILED" | "EXPIRED" | "CANCELLED";
@@ -207,6 +216,21 @@ declare class AgentApiClient {
207
216
  completedAt?: string | null | undefined;
208
217
  }[]>;
209
218
  reportResult(agentId: string, token: string, commandId: string, req: ReportCommandResultRequest): Promise<unknown>;
219
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
220
+ publishBusEvent(agentId: string, token: string, req: PublishBusEventInput): Promise<{
221
+ experimental: "v0.4-experimental";
222
+ eventId: string;
223
+ routedCommands: {
224
+ status: "FAILED" | "DRY_RUN" | "CREATED" | "SKIPPED";
225
+ dryRun: boolean;
226
+ actionName: string;
227
+ routeId: string;
228
+ targetServiceCode: string;
229
+ commandId?: string | undefined;
230
+ errorCode?: string | undefined;
231
+ errorMessage?: string | undefined;
232
+ }[];
233
+ }>;
210
234
  }
211
235
 
212
- export { type ActionHandler, type ActionPrepareHandler, type ActionPrepareResult, AgentApiClient, AgentApiError, AgentAuthError, type AgentLogLevel, type AgentLogRecord, type AgentMode, CapsuleAgent, type CapsuleAgentOptions, type ConfigProvider, type ConsoleLogger, FileTokenStore, type HealthProvider, NetworkError, type RegisteredAction, RegistrationError, type RuntimeKind, type ServiceSnapshot, type StructuredLogger, type TokenStore };
236
+ export { type ActionHandler, type ActionPrepareHandler, AgentApiClient, AgentApiError, AgentAuthError, type AgentLogLevel, type AgentLogRecord, type AgentMode, BusDepthExceededError, BusDisabledError, BusRateLimitedError, CapsuleAgent, type CapsuleAgentOptions, type ConfigProvider, type ConsoleLogger, FileTokenStore, type HealthProvider, NetworkError, type PublishBusEventInput, type PublishBusEventResult, type RegisteredAction, RegistrationError, type ServiceSnapshot, type StructuredLogger, type TokenStore };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,5 @@
1
- import { HealthReportInput, ConfigItemInput, ActionDefinitionInput, ReportedService, RegisterAgentRequest, AgentHeartbeatRequest, ServiceReportRequest, ReportCommandResultRequest } from '@xtrape/capsule-contracts-node';
1
+ import { ActionPrepareResult, PublishBusEventRequest, RuntimeKind, HealthReportInput, ConfigItemInput, ActionDefinitionInput, PublishBusEventResponse, ReportedService, RegisterAgentRequest, AgentHeartbeatRequest, ServiceReportRequest, ReportCommandResultRequest } from '@xtrape/capsule-contracts-node';
2
2
 
3
- type RuntimeKind = "nodejs" | "java" | "python" | "go" | "other";
4
- type ActionPrepareResult = {
5
- action?: Record<string, unknown>;
6
- inputSchema?: Record<string, unknown>;
7
- initialPayload?: Record<string, unknown>;
8
- currentState?: Record<string, unknown>;
9
- };
10
3
  type AgentMode = "embedded";
11
4
  type ActionHandler = (payload: Record<string, unknown>) => Promise<{
12
5
  success?: boolean;
@@ -94,6 +87,11 @@ interface TokenStore {
94
87
  clear(): Promise<void>;
95
88
  }
96
89
  type ServiceSnapshot = ReportedService;
90
+ /** Experimental v0.4 Capsule Bus publish input. Subject to change before v1.0. */
91
+ type PublishBusEventInput = Omit<PublishBusEventRequest, "sourceServiceCode"> & {
92
+ sourceServiceCode?: string;
93
+ };
94
+ type PublishBusEventResult = PublishBusEventResponse;
97
95
 
98
96
  declare class CapsuleAgent {
99
97
  private readonly options;
@@ -118,6 +116,8 @@ declare class CapsuleAgent {
118
116
  private ensureRegistered;
119
117
  private serviceSnapshot;
120
118
  runHealth(): Promise<HealthReportInput>;
119
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
120
+ publishBusEvent(input: PublishBusEventInput): Promise<PublishBusEventResult>;
121
121
  private reportService;
122
122
  private heartbeat;
123
123
  private pollOnce;
@@ -178,6 +178,15 @@ declare class AgentAuthError extends AgentApiError {
178
178
  declare class NetworkError extends AgentApiError {
179
179
  constructor(message: string, cause?: unknown);
180
180
  }
181
+ declare class BusDisabledError extends AgentApiError {
182
+ constructor(message: string, status: number, body?: unknown);
183
+ }
184
+ declare class BusRateLimitedError extends AgentApiError {
185
+ constructor(message: string, status: number, body?: unknown);
186
+ }
187
+ declare class BusDepthExceededError extends AgentApiError {
188
+ constructor(message: string, status: number, body?: unknown);
189
+ }
181
190
 
182
191
  declare class AgentApiClient {
183
192
  private readonly backendUrl;
@@ -193,7 +202,7 @@ declare class AgentApiClient {
193
202
  heartbeat(agentId: string, token: string, req: AgentHeartbeatRequest): Promise<unknown>;
194
203
  reportServices(agentId: string, token: string, req: ServiceReportRequest): Promise<unknown>;
195
204
  pollCommands(agentId: string, token: string): Promise<{
196
- type: "ACTION_PREPARE" | "ACTION_EXECUTE";
205
+ type: "ACTION_EXECUTE" | "ACTION_PREPARE";
197
206
  id: string;
198
207
  createdAt: string;
199
208
  status: "PENDING" | "RUNNING" | "SUCCEEDED" | "FAILED" | "EXPIRED" | "CANCELLED";
@@ -207,6 +216,21 @@ declare class AgentApiClient {
207
216
  completedAt?: string | null | undefined;
208
217
  }[]>;
209
218
  reportResult(agentId: string, token: string, commandId: string, req: ReportCommandResultRequest): Promise<unknown>;
219
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
220
+ publishBusEvent(agentId: string, token: string, req: PublishBusEventInput): Promise<{
221
+ experimental: "v0.4-experimental";
222
+ eventId: string;
223
+ routedCommands: {
224
+ status: "FAILED" | "DRY_RUN" | "CREATED" | "SKIPPED";
225
+ dryRun: boolean;
226
+ actionName: string;
227
+ routeId: string;
228
+ targetServiceCode: string;
229
+ commandId?: string | undefined;
230
+ errorCode?: string | undefined;
231
+ errorMessage?: string | undefined;
232
+ }[];
233
+ }>;
210
234
  }
211
235
 
212
- export { type ActionHandler, type ActionPrepareHandler, type ActionPrepareResult, AgentApiClient, AgentApiError, AgentAuthError, type AgentLogLevel, type AgentLogRecord, type AgentMode, CapsuleAgent, type CapsuleAgentOptions, type ConfigProvider, type ConsoleLogger, FileTokenStore, type HealthProvider, NetworkError, type RegisteredAction, RegistrationError, type RuntimeKind, type ServiceSnapshot, type StructuredLogger, type TokenStore };
236
+ export { type ActionHandler, type ActionPrepareHandler, AgentApiClient, AgentApiError, AgentAuthError, type AgentLogLevel, type AgentLogRecord, type AgentMode, BusDepthExceededError, BusDisabledError, BusRateLimitedError, CapsuleAgent, type CapsuleAgentOptions, type ConfigProvider, type ConsoleLogger, FileTokenStore, type HealthProvider, NetworkError, type PublishBusEventInput, type PublishBusEventResult, type RegisteredAction, RegistrationError, type ServiceSnapshot, type StructuredLogger, type TokenStore };
package/dist/index.js CHANGED
@@ -32,9 +32,30 @@ var NetworkError = class extends AgentApiError {
32
32
  }
33
33
  }
34
34
  };
35
+ var BusDisabledError = class extends AgentApiError {
36
+ constructor(message, status, body) {
37
+ super(status, message, body, "CAPSULE_BUS_DISABLED");
38
+ this.name = "BusDisabledError";
39
+ }
40
+ };
41
+ var BusRateLimitedError = class extends AgentApiError {
42
+ constructor(message, status, body) {
43
+ super(status, message, body, "BUS_RATE_LIMITED");
44
+ this.name = "BusRateLimitedError";
45
+ }
46
+ };
47
+ var BusDepthExceededError = class extends AgentApiError {
48
+ constructor(message, status, body) {
49
+ super(status, message, body, "BUS_DEPTH_EXCEEDED");
50
+ this.name = "BusDepthExceededError";
51
+ }
52
+ };
35
53
  function classifyAgentApiError(status, message, body, code, path) {
36
54
  if (status === 401 && path.endsWith("/register")) return new RegistrationError(message, status, body, code);
37
55
  if (status === 401 || status === 403) return new AgentAuthError(message, status, body, code);
56
+ if (code === "CAPSULE_BUS_DISABLED") return new BusDisabledError(message, status, body);
57
+ if (code === "BUS_RATE_LIMITED") return new BusRateLimitedError(message, status, body);
58
+ if (code === "BUS_DEPTH_EXCEEDED") return new BusDepthExceededError(message, status, body);
38
59
  return new AgentApiError(status, message, body, code);
39
60
  }
40
61
 
@@ -86,6 +107,10 @@ var AgentApiClient = class {
86
107
  reportResult(agentId, token, commandId, req) {
87
108
  return this.request(`/api/agents/${agentId}/commands/${commandId}/result`, { method: "POST", token, body: JSON.stringify(req) });
88
109
  }
110
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
111
+ publishBusEvent(agentId, token, req) {
112
+ return this.request(`/api/agents/${agentId}/bus/events`, { method: "POST", token, body: JSON.stringify({ ...req, experimental: req.experimental ?? "v0.4-experimental" }) });
113
+ }
89
114
  };
90
115
 
91
116
  // src/token-store/file-token-store.ts
@@ -240,6 +265,14 @@ var CapsuleAgent = class {
240
265
  async runHealth() {
241
266
  return this.healthProvider();
242
267
  }
268
+ /** Experimental v0.4 Capsule Bus publish hook. Subject to change before v1.0. */
269
+ async publishBusEvent(input) {
270
+ if (!this.agentId || !this.token) throw new Error("Agent must be started before publishing Capsule Bus events.");
271
+ const sourceServiceCode = input.sourceServiceCode ?? this.options.service.code;
272
+ const result = await this.retry(() => this.client.publishBusEvent(this.agentId, this.token, { ...input, sourceServiceCode }));
273
+ this.log("info", "experimental bus event published", { eventId: result.eventId, eventType: input.eventType, sourceServiceCode, routedCommands: result.routedCommands.length });
274
+ return result;
275
+ }
243
276
  async reportService() {
244
277
  if (!this.agentId || !this.token) return;
245
278
  const [health, configs] = await Promise.all([this.runHealth(), this.configProvider()]);
@@ -339,6 +372,9 @@ export {
339
372
  AgentApiClient,
340
373
  AgentApiError,
341
374
  AgentAuthError,
375
+ BusDepthExceededError,
376
+ BusDisabledError,
377
+ BusRateLimitedError,
342
378
  CapsuleAgent,
343
379
  FileTokenStore,
344
380
  NetworkError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtrape/capsule-agent-node",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Node.js Agent SDK for embedding Capsule Services into the Opstage governance loop.",
5
5
  "keywords": [
6
6
  "xtrape",
@@ -35,8 +35,15 @@
35
35
  "LICENSE",
36
36
  "NOTICE"
37
37
  ],
38
+ "scripts": {
39
+ "build": "tsup src/index.ts --format esm,cjs --dts",
40
+ "prepack": "pnpm build",
41
+ "test": "vitest run",
42
+ "typecheck": "tsc --noEmit",
43
+ "lint": "tsc --noEmit"
44
+ },
38
45
  "dependencies": {
39
- "@xtrape/capsule-contracts-node": "^0.2.0"
46
+ "@xtrape/capsule-contracts-node": "^0.4.0"
40
47
  },
41
48
  "devDependencies": {
42
49
  "tsup": "^8.3.5",
@@ -51,11 +58,5 @@
51
58
  "registry": "https://registry.npmjs.org/",
52
59
  "access": "public"
53
60
  },
54
- "license": "Apache-2.0",
55
- "scripts": {
56
- "build": "tsup src/index.ts --format esm,cjs --dts",
57
- "test": "vitest run",
58
- "typecheck": "tsc --noEmit",
59
- "lint": "tsc --noEmit"
60
- }
61
- }
61
+ "license": "Apache-2.0"
62
+ }