@agentxjs/node-platform 2.0.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.
Files changed (46) hide show
  1. package/README.md +134 -0
  2. package/dist/WebSocketConnection-BUL85bFC.d.ts +66 -0
  3. package/dist/WebSocketFactory-SDWPRZVB.js +8 -0
  4. package/dist/WebSocketFactory-SDWPRZVB.js.map +1 -0
  5. package/dist/chunk-BBZV6B5R.js +264 -0
  6. package/dist/chunk-BBZV6B5R.js.map +1 -0
  7. package/dist/chunk-DGUM43GV.js +11 -0
  8. package/dist/chunk-DGUM43GV.js.map +1 -0
  9. package/dist/chunk-PK2K7CCJ.js +213 -0
  10. package/dist/chunk-PK2K7CCJ.js.map +1 -0
  11. package/dist/chunk-TXESAX3X.js +361 -0
  12. package/dist/chunk-TXESAX3X.js.map +1 -0
  13. package/dist/chunk-V664KD3R.js +14 -0
  14. package/dist/chunk-V664KD3R.js.map +1 -0
  15. package/dist/index.d.ts +140 -0
  16. package/dist/index.js +223 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/mq/index.d.ts +63 -0
  19. package/dist/mq/index.js +10 -0
  20. package/dist/mq/index.js.map +1 -0
  21. package/dist/network/index.d.ts +17 -0
  22. package/dist/network/index.js +14 -0
  23. package/dist/network/index.js.map +1 -0
  24. package/dist/persistence/index.d.ts +175 -0
  25. package/dist/persistence/index.js +18 -0
  26. package/dist/persistence/index.js.map +1 -0
  27. package/package.json +50 -0
  28. package/src/bash/NodeBashProvider.ts +54 -0
  29. package/src/index.ts +151 -0
  30. package/src/logger/FileLoggerFactory.ts +175 -0
  31. package/src/logger/index.ts +5 -0
  32. package/src/mq/OffsetGenerator.ts +48 -0
  33. package/src/mq/SqliteMessageQueue.ts +240 -0
  34. package/src/mq/index.ts +30 -0
  35. package/src/network/WebSocketConnection.ts +206 -0
  36. package/src/network/WebSocketFactory.ts +17 -0
  37. package/src/network/WebSocketServer.ts +156 -0
  38. package/src/network/index.ts +32 -0
  39. package/src/persistence/Persistence.ts +53 -0
  40. package/src/persistence/StorageContainerRepository.ts +58 -0
  41. package/src/persistence/StorageImageRepository.ts +153 -0
  42. package/src/persistence/StorageSessionRepository.ts +171 -0
  43. package/src/persistence/index.ts +38 -0
  44. package/src/persistence/memory.ts +27 -0
  45. package/src/persistence/sqlite.ts +111 -0
  46. package/src/persistence/types.ts +32 -0
@@ -0,0 +1,18 @@
1
+ import {
2
+ StorageContainerRepository,
3
+ StorageImageRepository,
4
+ StorageSessionRepository,
5
+ createPersistence,
6
+ memoryDriver,
7
+ sqliteDriver
8
+ } from "../chunk-TXESAX3X.js";
9
+ import "../chunk-DGUM43GV.js";
10
+ export {
11
+ StorageContainerRepository,
12
+ StorageImageRepository,
13
+ StorageSessionRepository,
14
+ createPersistence,
15
+ memoryDriver,
16
+ sqliteDriver
17
+ };
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@agentxjs/node-platform",
3
+ "version": "2.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./persistence": {
14
+ "types": "./dist/persistence/index.d.ts",
15
+ "import": "./dist/persistence/index.js",
16
+ "default": "./dist/persistence/index.js"
17
+ },
18
+ "./mq": {
19
+ "types": "./dist/mq/index.d.ts",
20
+ "import": "./dist/mq/index.js",
21
+ "default": "./dist/mq/index.js"
22
+ },
23
+ "./network": {
24
+ "types": "./dist/network/index.d.ts",
25
+ "import": "./dist/network/index.js",
26
+ "default": "./dist/network/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "src"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "echo 'No tests yet'"
37
+ },
38
+ "dependencies": {
39
+ "@agentxjs/core": "^2.0.0",
40
+ "commonxjs": "^0.1.1",
41
+ "execa": "9.6.1",
42
+ "rxjs": "^7.8.2",
43
+ "unstorage": "^1.10.2",
44
+ "ws": "^8.18.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/ws": "^8.5.10",
48
+ "typescript": "^5.3.3"
49
+ }
50
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * NodeBashProvider - Node.js implementation of BashProvider
3
+ *
4
+ * Uses execa for subprocess execution with proper timeout,
5
+ * error handling, and cross-platform shell support.
6
+ */
7
+
8
+ import { execa } from "execa";
9
+ import type { BashProvider, BashResult, BashOptions } from "@agentxjs/core/bash";
10
+ import { createLogger } from "commonxjs/logger";
11
+
12
+ const logger = createLogger("node-platform/NodeBashProvider");
13
+
14
+ /**
15
+ * Default timeout: 30 seconds
16
+ */
17
+ const DEFAULT_TIMEOUT = 30_000;
18
+
19
+ /**
20
+ * NodeBashProvider - Executes shell commands via execa
21
+ */
22
+ export class NodeBashProvider implements BashProvider {
23
+ readonly type = "child-process";
24
+
25
+ async execute(command: string, options?: BashOptions): Promise<BashResult> {
26
+ const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
27
+
28
+ logger.debug("Executing command", {
29
+ command: command.substring(0, 100),
30
+ cwd: options?.cwd,
31
+ timeout,
32
+ });
33
+
34
+ const result = await execa({
35
+ shell: true,
36
+ cwd: options?.cwd,
37
+ timeout,
38
+ env: options?.env ? { ...process.env, ...options.env } : undefined,
39
+ reject: false,
40
+ })`${command}`;
41
+
42
+ logger.debug("Command completed", {
43
+ exitCode: result.exitCode,
44
+ stdoutLength: result.stdout.length,
45
+ stderrLength: result.stderr.length,
46
+ });
47
+
48
+ return {
49
+ stdout: result.stdout,
50
+ stderr: result.stderr,
51
+ exitCode: result.exitCode ?? 1,
52
+ };
53
+ }
54
+ }
package/src/index.ts ADDED
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @agentxjs/node-platform
3
+ *
4
+ * Node.js platform for AgentX.
5
+ * Provides implementations for persistence, bash, and network.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { createNodePlatform } from "@agentxjs/node-platform";
10
+ *
11
+ * const platform = await createNodePlatform({ dataPath: "./data" });
12
+ * ```
13
+ */
14
+
15
+ import type { AgentXPlatform } from "@agentxjs/core/runtime";
16
+ import type { LogLevel } from "commonxjs/logger";
17
+ import { setLoggerFactory, ConsoleLogger } from "commonxjs/logger";
18
+ import { EventBusImpl } from "@agentxjs/core/event";
19
+ import { createPersistence, sqliteDriver } from "./persistence";
20
+ import { NodeBashProvider } from "./bash/NodeBashProvider";
21
+ import { FileLoggerFactory } from "./logger";
22
+ import { join } from "node:path";
23
+
24
+ /**
25
+ * Options for creating a Node platform
26
+ */
27
+ export interface NodePlatformOptions {
28
+ /**
29
+ * Base path for data storage
30
+ * @default "./data"
31
+ */
32
+ dataPath?: string;
33
+
34
+ /**
35
+ * Directory for log files
36
+ * If provided, enables file logging instead of console
37
+ * @example ".agentx/logs"
38
+ */
39
+ logDir?: string;
40
+
41
+ /**
42
+ * Log level
43
+ * @default "debug" for file logging, "info" for console
44
+ */
45
+ logLevel?: LogLevel;
46
+ }
47
+
48
+ /**
49
+ * Deferred platform config - resolved lazily
50
+ */
51
+ export interface DeferredPlatformConfig {
52
+ readonly __deferred: true;
53
+ readonly options: NodePlatformOptions;
54
+ resolve(): Promise<AgentXPlatform>;
55
+ }
56
+
57
+ /**
58
+ * Create a Node.js platform configuration (deferred initialization)
59
+ *
60
+ * Use this for function-style API. The platform is initialized lazily.
61
+ *
62
+ * @param options - Platform options
63
+ * @returns Deferred platform config
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const server = await createServer({
68
+ * platform: nodePlatform({ dataPath: "./data" }),
69
+ * });
70
+ * ```
71
+ */
72
+ export function nodePlatform(options: NodePlatformOptions = {}): DeferredPlatformConfig {
73
+ return {
74
+ __deferred: true,
75
+ options,
76
+ resolve: () => createNodePlatform(options),
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Create a Node.js platform for AgentX (immediate initialization)
82
+ *
83
+ * @param options - Platform options
84
+ * @returns AgentXPlatform instance
85
+ */
86
+ export async function createNodePlatform(
87
+ options: NodePlatformOptions = {}
88
+ ): Promise<AgentXPlatform> {
89
+ const dataPath = options.dataPath ?? "./data";
90
+
91
+ // Configure logging
92
+ if (options.logDir) {
93
+ const loggerFactory = new FileLoggerFactory({
94
+ logDir: options.logDir,
95
+ level: options.logLevel ?? "debug",
96
+ });
97
+ setLoggerFactory(loggerFactory);
98
+ } else if (options.logLevel) {
99
+ setLoggerFactory({
100
+ getLogger: (name: string) => new ConsoleLogger(name, { level: options.logLevel }),
101
+ });
102
+ }
103
+
104
+ // Create persistence with SQLite
105
+ const persistence = await createPersistence(sqliteDriver({ path: join(dataPath, "agentx.db") }));
106
+
107
+ // Create bash provider
108
+ const bashProvider = new NodeBashProvider();
109
+
110
+ // Create event bus
111
+ const eventBus = new EventBusImpl();
112
+
113
+ // Create WebSocket factory (uses ws library for Node.js)
114
+ const { createNodeWebSocket } = await import("./network/WebSocketFactory");
115
+
116
+ return {
117
+ containerRepository: persistence.containers,
118
+ imageRepository: persistence.images,
119
+ sessionRepository: persistence.sessions,
120
+ eventBus,
121
+ bashProvider,
122
+ webSocketFactory: createNodeWebSocket,
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Check if value is a deferred platform config
128
+ */
129
+ export function isDeferredPlatform(value: unknown): value is DeferredPlatformConfig {
130
+ return (
131
+ typeof value === "object" &&
132
+ value !== null &&
133
+ "__deferred" in value &&
134
+ (value as DeferredPlatformConfig).__deferred === true
135
+ );
136
+ }
137
+
138
+ // Re-export persistence
139
+ export * from "./persistence";
140
+
141
+ // Re-export bash
142
+ export { NodeBashProvider } from "./bash/NodeBashProvider";
143
+
144
+ // Re-export mq
145
+ export { SqliteMessageQueue, OffsetGenerator } from "./mq";
146
+
147
+ // Re-export network
148
+ export { WebSocketServer, WebSocketConnection } from "./network";
149
+
150
+ // Re-export logger
151
+ export { FileLoggerFactory, type FileLoggerFactoryOptions } from "./logger";
@@ -0,0 +1,175 @@
1
+ /**
2
+ * FileLoggerFactory - File-based logger for Node.js
3
+ *
4
+ * Writes logs to a file instead of console.
5
+ * Useful for TUI applications where console output interferes with the UI.
6
+ *
7
+ * Usage:
8
+ * tail -f .agentx/logs/app.log
9
+ */
10
+
11
+ import { appendFileSync, mkdirSync, existsSync } from "node:fs";
12
+ import { dirname, join } from "node:path";
13
+ import type { Logger, LoggerFactory, LogContext, LogLevel } from "commonxjs/logger";
14
+
15
+ export interface FileLoggerOptions {
16
+ level?: LogLevel;
17
+ timestamps?: boolean;
18
+ }
19
+
20
+ class FileLogger implements Logger {
21
+ readonly name: string;
22
+ readonly level: LogLevel;
23
+ private readonly timestamps: boolean;
24
+ private readonly filePath: string;
25
+ private initialized = false;
26
+
27
+ constructor(name: string, filePath: string, options: FileLoggerOptions = {}) {
28
+ this.name = name;
29
+ this.filePath = filePath;
30
+ this.level = options.level ?? "debug";
31
+ this.timestamps = options.timestamps ?? true;
32
+ }
33
+
34
+ private ensureDir(): void {
35
+ if (this.initialized) return;
36
+ const dir = dirname(this.filePath);
37
+ if (!existsSync(dir)) {
38
+ mkdirSync(dir, { recursive: true });
39
+ }
40
+ this.initialized = true;
41
+ }
42
+
43
+ debug(message: string, context?: LogContext): void {
44
+ if (this.isDebugEnabled()) {
45
+ this.log("DEBUG", message, context);
46
+ }
47
+ }
48
+
49
+ info(message: string, context?: LogContext): void {
50
+ if (this.isInfoEnabled()) {
51
+ this.log("INFO", message, context);
52
+ }
53
+ }
54
+
55
+ warn(message: string, context?: LogContext): void {
56
+ if (this.isWarnEnabled()) {
57
+ this.log("WARN", message, context);
58
+ }
59
+ }
60
+
61
+ error(message: string | Error, context?: LogContext): void {
62
+ if (this.isErrorEnabled()) {
63
+ if (message instanceof Error) {
64
+ this.log("ERROR", message.message, { ...context, stack: message.stack });
65
+ } else {
66
+ this.log("ERROR", message, context);
67
+ }
68
+ }
69
+ }
70
+
71
+ isDebugEnabled(): boolean {
72
+ return this.getLevelValue(this.level) <= this.getLevelValue("debug");
73
+ }
74
+
75
+ isInfoEnabled(): boolean {
76
+ return this.getLevelValue(this.level) <= this.getLevelValue("info");
77
+ }
78
+
79
+ isWarnEnabled(): boolean {
80
+ return this.getLevelValue(this.level) <= this.getLevelValue("warn");
81
+ }
82
+
83
+ isErrorEnabled(): boolean {
84
+ return this.getLevelValue(this.level) <= this.getLevelValue("error");
85
+ }
86
+
87
+ private getLevelValue(level: LogLevel): number {
88
+ const levels: Record<LogLevel, number> = {
89
+ debug: 0,
90
+ info: 1,
91
+ warn: 2,
92
+ error: 3,
93
+ silent: 4,
94
+ };
95
+ return levels[level];
96
+ }
97
+
98
+ private log(level: string, message: string, context?: LogContext): void {
99
+ this.ensureDir();
100
+
101
+ const parts: string[] = [];
102
+
103
+ if (this.timestamps) {
104
+ parts.push(new Date().toISOString());
105
+ }
106
+
107
+ parts.push(level.padEnd(5));
108
+ parts.push(`[${this.name}]`);
109
+ parts.push(message);
110
+
111
+ let logLine = parts.join(" ");
112
+
113
+ if (context && Object.keys(context).length > 0) {
114
+ logLine += " " + JSON.stringify(context);
115
+ }
116
+
117
+ logLine += "\n";
118
+
119
+ try {
120
+ appendFileSync(this.filePath, logLine);
121
+ } catch {
122
+ // Fallback to stderr if file write fails
123
+ process.stderr.write(`[FileLogger] Failed to write: ${logLine}`);
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * FileLoggerFactory options
130
+ */
131
+ export interface FileLoggerFactoryOptions {
132
+ /**
133
+ * Directory for log files
134
+ */
135
+ logDir: string;
136
+
137
+ /**
138
+ * Log level
139
+ * @default "debug"
140
+ */
141
+ level?: LogLevel;
142
+
143
+ /**
144
+ * Log file name
145
+ * @default "app.log"
146
+ */
147
+ filename?: string;
148
+ }
149
+
150
+ /**
151
+ * FileLoggerFactory - Creates FileLogger instances
152
+ */
153
+ export class FileLoggerFactory implements LoggerFactory {
154
+ private readonly filePath: string;
155
+ private readonly level: LogLevel;
156
+ private readonly loggers: Map<string, FileLogger> = new Map();
157
+
158
+ constructor(options: FileLoggerFactoryOptions) {
159
+ this.filePath = join(options.logDir, options.filename ?? "app.log");
160
+ this.level = options.level ?? "debug";
161
+ }
162
+
163
+ getLogger(name: string): Logger {
164
+ if (this.loggers.has(name)) {
165
+ return this.loggers.get(name)!;
166
+ }
167
+
168
+ const logger = new FileLogger(name, this.filePath, {
169
+ level: this.level,
170
+ });
171
+
172
+ this.loggers.set(name, logger);
173
+ return logger;
174
+ }
175
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Logger module for Node.js
3
+ */
4
+
5
+ export { FileLoggerFactory, type FileLoggerFactoryOptions } from "./FileLoggerFactory";
@@ -0,0 +1,48 @@
1
+ /**
2
+ * OffsetGenerator - Generates monotonically increasing offsets
3
+ *
4
+ * Format: "{timestamp_base36}-{sequence_padded}"
5
+ * Example: "lq5x4g2-0001"
6
+ *
7
+ * This format ensures:
8
+ * - Lexicographic ordering matches temporal ordering
9
+ * - Multiple events in same millisecond get unique offsets
10
+ * - Human-readable and compact
11
+ */
12
+ export class OffsetGenerator {
13
+ private lastTimestamp = 0;
14
+ private sequence = 0;
15
+
16
+ /**
17
+ * Generate a new offset
18
+ */
19
+ generate(): string {
20
+ const now = Date.now();
21
+
22
+ if (now === this.lastTimestamp) {
23
+ this.sequence++;
24
+ } else {
25
+ this.lastTimestamp = now;
26
+ this.sequence = 0;
27
+ }
28
+
29
+ const timestampPart = now.toString(36);
30
+ const sequencePart = this.sequence.toString().padStart(4, "0");
31
+
32
+ return `${timestampPart}-${sequencePart}`;
33
+ }
34
+
35
+ /**
36
+ * Compare two offsets
37
+ * @returns negative if a < b, 0 if a == b, positive if a > b
38
+ */
39
+ static compare(a: string, b: string): number {
40
+ const [aTime, aSeq] = a.split("-");
41
+ const [bTime, bSeq] = b.split("-");
42
+
43
+ const timeDiff = parseInt(aTime, 36) - parseInt(bTime, 36);
44
+ if (timeDiff !== 0) return timeDiff;
45
+
46
+ return parseInt(aSeq) - parseInt(bSeq);
47
+ }
48
+ }