@noosphere/agent-core 0.1.0-alpha.0 → 0.1.0-alpha.10

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 ADDED
@@ -0,0 +1,143 @@
1
+ # @noosphere/agent-core
2
+
3
+ Core modules for building Noosphere compute agents.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @noosphere/agent-core
9
+ ```
10
+
11
+ For alpha version:
12
+ ```bash
13
+ npm install @noosphere/agent-core@alpha
14
+ ```
15
+
16
+ ## Components
17
+
18
+ ### NoosphereAgent
19
+
20
+ Main agent class that orchestrates compute request handling.
21
+
22
+ ```typescript
23
+ import { NoosphereAgent } from '@noosphere/agent-core';
24
+
25
+ const agent = new NoosphereAgent({
26
+ rpcUrl: 'https://sepolia.hpp.io',
27
+ wsUrl: 'wss://sepolia.hpp.io',
28
+ routerAddress: '0x89c76ee71E9cC8D57BEE3d414478B630AE41fF43',
29
+ coordinatorAddress: '0x244D87a7CAe0D557C223C13a90Ae845e56430A50',
30
+ keystore,
31
+ containers: [
32
+ {
33
+ containerId: 'hello-world',
34
+ imageName: 'noosphere/hello-world:latest',
35
+ },
36
+ ],
37
+ });
38
+
39
+ // Event handlers
40
+ agent.on('requestStarted', (event) => {
41
+ console.log('Request started:', event.requestId);
42
+ });
43
+
44
+ agent.on('computeDelivered', (event) => {
45
+ console.log('Compute delivered:', event.requestId);
46
+ });
47
+
48
+ // Start agent
49
+ await agent.start();
50
+ ```
51
+
52
+ ### EventMonitor
53
+
54
+ WebSocket-based blockchain event monitor with automatic reconnection.
55
+
56
+ ```typescript
57
+ import { EventMonitor } from '@noosphere/agent-core';
58
+
59
+ const monitor = new EventMonitor(
60
+ {
61
+ rpcUrl: 'https://sepolia.hpp.io',
62
+ wsUrl: 'wss://sepolia.hpp.io',
63
+ routerAddress: '0x89c76ee71E9cC8D57BEE3d414478B630AE41fF43',
64
+ coordinatorAddress: '0x244D87a7CAe0D557C223C13a90Ae845e56430A50',
65
+ },
66
+ routerAbi,
67
+ coordinatorAbi,
68
+ { enableHeartbeat: true }
69
+ );
70
+
71
+ monitor.on('RequestStarted', (event) => {
72
+ console.log('New request:', event);
73
+ });
74
+
75
+ await monitor.start();
76
+ ```
77
+
78
+ ### ContainerManager
79
+
80
+ Docker container lifecycle management.
81
+
82
+ ```typescript
83
+ import { ContainerManager } from '@noosphere/agent-core';
84
+
85
+ const manager = new ContainerManager();
86
+
87
+ // Run container
88
+ const result = await manager.runContainer({
89
+ imageName: 'noosphere/hello-world:latest',
90
+ input: { message: 'Hello' },
91
+ timeout: 30000,
92
+ });
93
+
94
+ console.log('Output:', result.output);
95
+ ```
96
+
97
+ ### SchedulerService
98
+
99
+ Subscription scheduling and interval management.
100
+
101
+ ```typescript
102
+ import { SchedulerService } from '@noosphere/agent-core';
103
+
104
+ const scheduler = new SchedulerService(
105
+ coordinatorContract,
106
+ walletManager,
107
+ { intervalCheckMs: 60000 }
108
+ );
109
+
110
+ scheduler.on('intervalPrepared', (event) => {
111
+ console.log('Interval prepared:', event);
112
+ });
113
+
114
+ await scheduler.start(subscriptions);
115
+ ```
116
+
117
+ ## Re-exports
118
+
119
+ For convenience, this package re-exports from other Noosphere packages:
120
+
121
+ ```typescript
122
+ // From @noosphere/crypto
123
+ import { KeystoreManager, WalletManager } from '@noosphere/agent-core';
124
+
125
+ // From @noosphere/registry
126
+ import { RegistryManager } from '@noosphere/agent-core';
127
+ ```
128
+
129
+ ## Types
130
+
131
+ ```typescript
132
+ import type {
133
+ NoosphereAgentConfig,
134
+ ContainerConfig,
135
+ CheckpointData,
136
+ ComputeDeliveredEvent,
137
+ RequestStartedCallbackEvent,
138
+ } from '@noosphere/agent-core';
139
+ ```
140
+
141
+ ## License
142
+
143
+ MIT
package/dist/index.cjs CHANGED
@@ -308,7 +308,8 @@ var ContainerManager = class {
308
308
  const startTime = Date.now();
309
309
  try {
310
310
  const port = container.port ? parseInt(container.port) : 8081;
311
- const url = `http://localhost:${port}/computation`;
311
+ const containerHost = process.env.DOCKER_NETWORK ? `noosphere-${container.name}` : "localhost";
312
+ const url = `http://${containerHost}:${port}/computation`;
312
313
  let requestBody;
313
314
  try {
314
315
  const parsedInput = JSON.parse(input);
@@ -494,14 +495,19 @@ var ContainerManager = class {
494
495
  console.log(`
495
496
  \u{1F680} Preparing ${containers.size} containers...`);
496
497
  const pullAndStartPromises = Array.from(containers.entries()).map(async ([id, container]) => {
498
+ const imageTag = `${container.image}:${container.tag || "latest"}`;
497
499
  try {
498
- const imageTag = `${container.image}:${container.tag || "latest"}`;
499
500
  console.log(` Pulling ${imageTag}...`);
500
501
  await this.pullImage(container.image, container.tag || "latest");
501
502
  console.log(` \u2713 ${imageTag} ready`);
503
+ } catch (error) {
504
+ console.error(` \u274C Failed to pull ${imageTag}:`, error.message);
505
+ return;
506
+ }
507
+ try {
502
508
  await this.startPersistentContainer(id, container);
503
509
  } catch (error) {
504
- console.error(` \u274C Failed to prepare ${container.image}:`, error.message);
510
+ console.error(` \u274C Failed to start ${container.name || container.image}:`, error.message);
505
511
  }
506
512
  });
507
513
  await Promise.all(pullAndStartPromises);
@@ -511,26 +517,29 @@ var ContainerManager = class {
511
517
  * Start a persistent container that stays running
512
518
  */
513
519
  async startPersistentContainer(containerId, metadata) {
514
- const containerName = `noosphere-${containerId}`;
520
+ const containerName = `noosphere-${metadata.name}`;
515
521
  const imageTag = `${metadata.image}:${metadata.tag || "latest"}`;
522
+ const existingContainer = this.docker.getContainer(containerName);
516
523
  try {
517
- const existingContainer = this.docker.getContainer(containerName);
518
- try {
519
- const inspect = await existingContainer.inspect();
520
- if (inspect.State.Running) {
521
- console.log(` \u2713 Container ${containerName} already running`);
522
- this.persistentContainers.set(containerId, existingContainer);
523
- return;
524
- } else {
524
+ const inspect = await existingContainer.inspect();
525
+ if (inspect.State.Running) {
526
+ console.log(` \u2713 Container ${containerName} already running`);
527
+ this.persistentContainers.set(containerId, existingContainer);
528
+ return;
529
+ } else {
530
+ try {
525
531
  await existingContainer.start();
526
532
  console.log(` \u2713 Started existing container ${containerName}`);
527
533
  this.persistentContainers.set(containerId, existingContainer);
528
534
  return;
535
+ } catch (startErr) {
536
+ console.log(` Removing stopped container ${containerName} to recreate...`);
537
+ await existingContainer.remove({ force: true });
529
538
  }
530
- } catch (err) {
531
539
  }
532
540
  } catch (err) {
533
541
  }
542
+ const dockerNetwork = process.env.DOCKER_NETWORK;
534
543
  const createOptions = {
535
544
  name: containerName,
536
545
  Image: imageTag,
@@ -541,9 +550,12 @@ var ContainerManager = class {
541
550
  HostConfig: {
542
551
  AutoRemove: false,
543
552
  // Keep container for reuse
544
- PortBindings: metadata.port ? {
553
+ // Only bind ports to host when not using Docker network (local dev)
554
+ PortBindings: metadata.port && !dockerNetwork ? {
545
555
  [`${metadata.port}/tcp`]: [{ HostPort: metadata.port }]
546
- } : void 0
556
+ } : void 0,
557
+ // Join the specified Docker network for DinD communication
558
+ NetworkMode: dockerNetwork || void 0
547
559
  },
548
560
  Env: metadata.env ? Object.entries(metadata.env).map(([k, v]) => `${k}=${v}`) : void 0
549
561
  };
@@ -1615,8 +1627,30 @@ var NoosphereAgent = class _NoosphereAgent {
1615
1627
  verified: registryContainer.verified
1616
1628
  };
1617
1629
  }
1630
+ /**
1631
+ * Get container metadata from available sources
1632
+ * Returns undefined if container is not supported by this agent
1633
+ *
1634
+ * NOTE: Only checks config-defined sources (callback and containers map).
1635
+ * Registry is NOT used here - we only process containers explicitly configured.
1636
+ */
1637
+ getContainerMetadata(containerId) {
1638
+ if (this.getContainer) {
1639
+ const container = this.getContainer(containerId);
1640
+ if (container) return container;
1641
+ }
1642
+ if (this.containers) {
1643
+ const container = this.containers.get(containerId);
1644
+ if (container) return container;
1645
+ }
1646
+ return void 0;
1647
+ }
1618
1648
  async handleRequest(event) {
1619
1649
  const requestIdShort = event.requestId.slice(0, 10);
1650
+ const container = this.getContainerMetadata(event.containerId);
1651
+ if (!container) {
1652
+ return;
1653
+ }
1620
1654
  if (this.processingRequests.has(event.requestId)) {
1621
1655
  console.log(` \u23ED\uFE0F Request ${requestIdShort}... already being processed, skipping duplicate`);
1622
1656
  return;
@@ -1631,6 +1665,7 @@ var NoosphereAgent = class _NoosphereAgent {
1631
1665
  console.log(` SubscriptionId: ${event.subscriptionId}`);
1632
1666
  console.log(` Interval: ${event.interval}`);
1633
1667
  console.log(` ContainerId: ${event.containerId.slice(0, 10)}...`);
1668
+ console.log(` \u{1F4E6} Container: ${container.name} (${container.image}:${container.tag || "latest"})`);
1634
1669
  if (this.options.onRequestStarted) {
1635
1670
  this.options.onRequestStarted({
1636
1671
  requestId: event.requestId,
@@ -1661,6 +1696,7 @@ var NoosphereAgent = class _NoosphereAgent {
1661
1696
  console.warn(` Could not verify interval currency:`, error.message);
1662
1697
  }
1663
1698
  this.scheduler.markIntervalCommitted(BigInt(event.subscriptionId), BigInt(event.interval));
1699
+ let sentTxHash;
1664
1700
  try {
1665
1701
  await this.waitForPriority(event);
1666
1702
  if (this.options.onRequestProcessing) {
@@ -1675,37 +1711,6 @@ var NoosphereAgent = class _NoosphereAgent {
1675
1711
  this.processingRequests.delete(event.requestId);
1676
1712
  return;
1677
1713
  }
1678
- let container;
1679
- if (this.getContainer) {
1680
- container = this.getContainer(event.containerId);
1681
- if (container) {
1682
- console.log(` \u{1F4E6} Container found via callback: ${container.name}`);
1683
- }
1684
- }
1685
- if (!container) {
1686
- const registryContainer = this.registryManager.getContainer(event.containerId);
1687
- if (registryContainer) {
1688
- console.log(` \u{1F4CB} Container found in registry: ${registryContainer.name}`);
1689
- container = this.convertRegistryContainer(registryContainer);
1690
- }
1691
- }
1692
- if (!container && this.containers) {
1693
- container = this.containers.get(event.containerId);
1694
- if (container) {
1695
- console.log(` \u{1F4E6} Container found in config: ${container.name}`);
1696
- }
1697
- }
1698
- if (!container) {
1699
- console.error(` \u274C Container not found: ${event.containerId}`);
1700
- console.error(` \u{1F4A1} Try adding it to the registry or config file`);
1701
- if (this.options.onRequestSkipped) {
1702
- this.options.onRequestSkipped(event.requestId, `Container not found: ${event.containerId}`);
1703
- }
1704
- return;
1705
- }
1706
- console.log(
1707
- ` \u{1F4E6} Using container: ${container.name} (${container.image}:${container.tag || "latest"})`
1708
- );
1709
1714
  const subscription = await this.router.getComputeSubscription(event.subscriptionId);
1710
1715
  const clientAddress = subscription.client;
1711
1716
  if (!clientAddress || clientAddress === "0x0000000000000000000000000000000000000000") {
@@ -1773,6 +1778,7 @@ var NoosphereAgent = class _NoosphereAgent {
1773
1778
  commitmentData,
1774
1779
  nodeWallet
1775
1780
  );
1781
+ sentTxHash = tx.hash;
1776
1782
  console.log(` \u{1F4E4} Transaction sent: ${tx.hash}`);
1777
1783
  const receipt = await tx.wait();
1778
1784
  if (receipt.status === 1) {
@@ -1807,7 +1813,7 @@ var NoosphereAgent = class _NoosphereAgent {
1807
1813
  }
1808
1814
  console.error(` \u274C Error processing request:`, error);
1809
1815
  if (this.options.onRequestFailed) {
1810
- this.options.onRequestFailed(event.requestId, errorMessage);
1816
+ this.options.onRequestFailed(event.requestId, errorMessage, sentTxHash);
1811
1817
  }
1812
1818
  } finally {
1813
1819
  this.processingRequests.delete(event.requestId);