@kadi.build/core 0.9.0 → 0.11.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 +424 -1
- package/agent.json +19 -0
- package/dist/agent-json.d.ts +231 -0
- package/dist/agent-json.d.ts.map +1 -0
- package/dist/agent-json.js +554 -0
- package/dist/agent-json.js.map +1 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +50 -0
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/process-manager.d.ts +235 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +647 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/stdio-framing.d.ts +88 -0
- package/dist/stdio-framing.d.ts.map +1 -0
- package/dist/stdio-framing.js +194 -0
- package/dist/stdio-framing.js.map +1 -0
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +3 -181
- package/dist/transports/stdio.js.map +1 -1
- package/dist/types.d.ts +256 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +107 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +212 -0
- package/dist/utils.js.map +1 -0
- package/package.json +3 -1
- package/scripts/symlink.mjs +131 -0
- package/src/agent-json.ts +655 -0
- package/src/client.ts +56 -0
- package/src/errors.ts +15 -0
- package/src/index.ts +32 -0
- package/src/process-manager.ts +821 -0
- package/src/stdio-framing.ts +227 -0
- package/src/transports/stdio.ts +4 -221
- package/src/types.ts +277 -0
- package/src/utils.ts +246 -0
package/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# kadi-core
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for building KADI agents. Register tools, connect to brokers, load abilities, publish events.
|
|
3
|
+
TypeScript SDK for building KADI agents. Register tools, connect to brokers, load abilities, publish events, manage agent configuration, and run background processes.
|
|
4
4
|
|
|
5
5
|
kadi-core lets you:
|
|
6
6
|
- **Expose functions** as callable tools over the network
|
|
7
7
|
- **Discover and invoke** remote services without hardcoding URLs
|
|
8
8
|
- **Communicate via events** across your service mesh
|
|
9
|
+
- **Manage agent.json** configuration across projects, abilities, and global scope
|
|
10
|
+
- **Run background processes** with headless, piped, and JSON-RPC bridge modes
|
|
11
|
+
- **Install as an ability** — use kadi-core itself as a dependency in any agent
|
|
9
12
|
|
|
10
13
|
## Install
|
|
11
14
|
|
|
@@ -54,6 +57,9 @@ Now another agent can load and use it:
|
|
|
54
57
|
- [Events from Abilities](#events-from-abilities)
|
|
55
58
|
- [Broker Events (Pub/Sub)](#broker-events-pubsub)
|
|
56
59
|
- [Serving as an Ability](#serving-as-an-ability)
|
|
60
|
+
- [Agent.json Management](#agentjson-management)
|
|
61
|
+
- [Process Manager](#process-manager)
|
|
62
|
+
- [Installing as an Ability](#installing-as-an-ability)
|
|
57
63
|
- [Error Handling](#error-handling)
|
|
58
64
|
- [API Reference](#api-reference)
|
|
59
65
|
- [Troubleshooting](#troubleshooting)
|
|
@@ -400,6 +406,264 @@ await client.serve('broker');
|
|
|
400
406
|
|
|
401
407
|
---
|
|
402
408
|
|
|
409
|
+
## Agent.json Management
|
|
410
|
+
|
|
411
|
+
The `AgentJsonManager` provides unified read/write/delete access to `agent.json` files across three scopes:
|
|
412
|
+
|
|
413
|
+
| Scope | Location | Use Case |
|
|
414
|
+
|-------|----------|----------|
|
|
415
|
+
| **Project** | `./agent.json` | Your agent's own configuration |
|
|
416
|
+
| **Ability** | `./abilities/<name>@<version>/agent.json` | Installed ability configuration |
|
|
417
|
+
| **Home** | `~/.kadi/agent.json` | Global CLI / user-level configuration |
|
|
418
|
+
|
|
419
|
+
### Via KadiClient
|
|
420
|
+
|
|
421
|
+
The client exposes a lazy `agentJson` property — no extra instantiation needed:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
const client = new KadiClient({ name: 'my-agent' });
|
|
425
|
+
|
|
426
|
+
// Read the full project config
|
|
427
|
+
const config = await client.agentJson.readProject();
|
|
428
|
+
|
|
429
|
+
// Read a nested field with dot-path notation
|
|
430
|
+
const target = await client.agentJson.readProject('deploy.local.target');
|
|
431
|
+
|
|
432
|
+
// Write a nested field (deep-merges into existing data)
|
|
433
|
+
await client.agentJson.writeProject('deploy.staging', {
|
|
434
|
+
target: 'akash',
|
|
435
|
+
replicas: 2,
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Delete a field
|
|
439
|
+
await client.agentJson.deleteProject('deploy.staging');
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Standalone Usage
|
|
443
|
+
|
|
444
|
+
You can also use `AgentJsonManager` directly without a client:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { AgentJsonManager } from '@kadi.build/core';
|
|
448
|
+
|
|
449
|
+
const ajm = new AgentJsonManager({
|
|
450
|
+
projectRoot: '/path/to/my-agent', // Optional, auto-detects via agent-lock.json
|
|
451
|
+
kadiHome: '~/.kadi', // Optional, defaults to ~/.kadi
|
|
452
|
+
createOnWrite: true, // Create file if missing on write (default: true)
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const config = await ajm.readProject();
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Reading Ability Config
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
// Read full config for an installed ability (resolves highest semver by default)
|
|
462
|
+
const calcConfig = await client.agentJson.readAbility('calculator');
|
|
463
|
+
|
|
464
|
+
// Read a specific field
|
|
465
|
+
const scripts = await client.agentJson.readAbility('calculator', {
|
|
466
|
+
field: 'scripts.start',
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Pin to a specific version (useful when multiple versions are installed)
|
|
470
|
+
const old = await client.agentJson.readAbility('calculator', {
|
|
471
|
+
version: '1.0.0',
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Global Home Config
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
// Read global KADI configuration
|
|
479
|
+
const homeConfig = await client.agentJson.readHome();
|
|
480
|
+
const defaultBroker = await client.agentJson.readHome('brokers.default');
|
|
481
|
+
|
|
482
|
+
// Write to global config
|
|
483
|
+
await client.agentJson.writeHome('brokers.staging', 'wss://staging.example.com/kadi');
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Discovery
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// List all installed abilities (reads agent-lock.json)
|
|
490
|
+
const abilities = await client.agentJson.listAbilities();
|
|
491
|
+
// [{ name: 'calculator', version: '1.0.0', path: '/abs/path/abilities/calculator@1.0.0' }, ...]
|
|
492
|
+
|
|
493
|
+
// Check if an ability is installed
|
|
494
|
+
const has = await client.agentJson.hasAbility('calculator');
|
|
495
|
+
|
|
496
|
+
// Get all installed versions of an ability
|
|
497
|
+
const versions = await client.agentJson.getAbilityVersions('calculator');
|
|
498
|
+
// ['1.0.0', '2.0.0']
|
|
499
|
+
|
|
500
|
+
// Get resolved paths for all three scopes
|
|
501
|
+
const paths = await client.agentJson.getPaths();
|
|
502
|
+
// { project: '/abs/path/agent.json', home: '/home/user/.kadi/agent.json' }
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Process Manager
|
|
508
|
+
|
|
509
|
+
The `ProcessManager` spawns and supervises background child processes in three modes:
|
|
510
|
+
|
|
511
|
+
| Mode | Communication | Use Case |
|
|
512
|
+
|------|--------------|----------|
|
|
513
|
+
| **headless** | None | Fire-and-forget tasks (docker build, git clone) |
|
|
514
|
+
| **piped** | stdout/stderr streaming | Tasks where you need live output (deploys, logs) |
|
|
515
|
+
| **bridge** | JSON-RPC 2.0 over stdio | Interactive workers (inference engines, language servers) |
|
|
516
|
+
|
|
517
|
+
### Via KadiClient
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
const client = new KadiClient({ name: 'my-agent' });
|
|
521
|
+
|
|
522
|
+
// Headless — fire and forget
|
|
523
|
+
const build = await client.processes.spawn('build', {
|
|
524
|
+
command: 'docker',
|
|
525
|
+
args: ['build', '-t', 'myapp', '.'],
|
|
526
|
+
mode: 'headless',
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Check status later
|
|
530
|
+
const status = client.processes.getStatus('build');
|
|
531
|
+
console.log(status); // 'running' | 'exited' | 'killed' | 'errored'
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Piped Mode — Live Output
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
const deploy = await client.processes.spawn('deploy', {
|
|
538
|
+
command: 'kadi',
|
|
539
|
+
args: ['deploy', '--target', 'akash'],
|
|
540
|
+
mode: 'piped',
|
|
541
|
+
cwd: '/path/to/project',
|
|
542
|
+
env: { KADI_ENV: 'production' },
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Stream stdout/stderr in real time
|
|
546
|
+
deploy.on('stdout', (chunk) => process.stdout.write(chunk));
|
|
547
|
+
deploy.on('stderr', (chunk) => process.stderr.write(chunk));
|
|
548
|
+
deploy.on('exit', ({ exitCode }) => console.log('Deploy exited:', exitCode));
|
|
549
|
+
|
|
550
|
+
// Get buffered output later
|
|
551
|
+
const output = deploy.getOutput();
|
|
552
|
+
console.log(output.stdout); // Full stdout string
|
|
553
|
+
console.log(output.stderr); // Full stderr string
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Bridge Mode — JSON-RPC Workers
|
|
557
|
+
|
|
558
|
+
Bridge mode wraps the child process in the same Content-Length framed JSON-RPC protocol used by stdio transports. This lets you send requests to the child and receive responses:
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
const worker = await client.processes.spawn('inference', {
|
|
562
|
+
command: 'python3',
|
|
563
|
+
args: ['worker.py'],
|
|
564
|
+
mode: 'bridge',
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Send a JSON-RPC request and await the response
|
|
568
|
+
const result = await worker.request('run-inference', {
|
|
569
|
+
model: 'llama3',
|
|
570
|
+
prompt: 'Explain kadi in one sentence',
|
|
571
|
+
});
|
|
572
|
+
console.log(result);
|
|
573
|
+
|
|
574
|
+
// Fire-and-forget notification (no response expected)
|
|
575
|
+
worker.notify('update-config', { temperature: 0.7 });
|
|
576
|
+
|
|
577
|
+
// Listen for notifications from the worker
|
|
578
|
+
worker.on('notification', ({ method, params }) => {
|
|
579
|
+
console.log(`Worker says: ${method}`, params);
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Lifecycle Management
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
// List all managed processes
|
|
587
|
+
const all = client.processes.list();
|
|
588
|
+
const running = client.processes.list({ mode: 'piped' }); // Filter by mode
|
|
589
|
+
|
|
590
|
+
// Get a handle to an existing process
|
|
591
|
+
const proc = client.processes.get('build');
|
|
592
|
+
|
|
593
|
+
// Get detailed info
|
|
594
|
+
const info = proc.getInfo();
|
|
595
|
+
// { id: 'build', pid: 12345, mode: 'piped', state: 'running', startedAt: ... }
|
|
596
|
+
|
|
597
|
+
// Kill a single process (SIGTERM → grace period → SIGKILL)
|
|
598
|
+
await proc.kill();
|
|
599
|
+
|
|
600
|
+
// Kill all managed processes
|
|
601
|
+
await client.processes.killAll();
|
|
602
|
+
|
|
603
|
+
// Wait for a process to exit
|
|
604
|
+
const exitInfo = await proc.waitForExit();
|
|
605
|
+
console.log(exitInfo.exitCode, exitInfo.signal);
|
|
606
|
+
|
|
607
|
+
// Clean up exited/errored entries
|
|
608
|
+
client.processes.prune();
|
|
609
|
+
|
|
610
|
+
// Graceful shutdown — kills all and cleans up
|
|
611
|
+
await client.processes.shutdown();
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Timeout Auto-Kill
|
|
615
|
+
|
|
616
|
+
Processes can have an automatic timeout. When the timeout fires, the process is killed:
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
const task = await client.processes.spawn('migration', {
|
|
620
|
+
command: 'node',
|
|
621
|
+
args: ['migrate.js'],
|
|
622
|
+
mode: 'piped',
|
|
623
|
+
timeout: 60000, // Kill after 60 seconds if still running
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Standalone Usage
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import { ProcessManager } from '@kadi.build/core';
|
|
631
|
+
|
|
632
|
+
const pm = new ProcessManager();
|
|
633
|
+
|
|
634
|
+
const worker = await pm.spawn('worker', {
|
|
635
|
+
command: 'python3',
|
|
636
|
+
args: ['worker.py'],
|
|
637
|
+
mode: 'bridge',
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
const result = await worker.request('echo', { message: 'hello' });
|
|
641
|
+
await pm.shutdown();
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## Installing as an Ability
|
|
647
|
+
|
|
648
|
+
kadi-core v0.10.0 ships its own `agent.json` with `type: "ability"`, so it can be installed into any agent project using the standard `kadi install` workflow:
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
kadi install kadi-core
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
After installation, a `postinstall` script creates a symlink at `node_modules/@kadi.build/core` pointing to the installed ability directory. This means your code can use the standard import path regardless of where the ability lives on disk:
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
import { KadiClient, z } from '@kadi.build/core';
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
This is particularly useful for:
|
|
661
|
+
- **Python agents** (via `kadi-core-py`) that need a reference to kadi-core's protocol definitions
|
|
662
|
+
- **Polyglot projects** that install abilities via `kadi install` rather than `npm install`
|
|
663
|
+
- **Deployed agents** where abilities are resolved from `agent-lock.json`
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
403
667
|
## Error Handling
|
|
404
668
|
|
|
405
669
|
All errors are `KadiError` with structured codes and context:
|
|
@@ -430,6 +694,17 @@ try {
|
|
|
430
694
|
| `BROKER_TIMEOUT` | Request to broker timed out |
|
|
431
695
|
| `INVALID_CONFIG` | Configuration error (missing required fields, etc.) |
|
|
432
696
|
| `LOCKFILE_NOT_FOUND` | `loadNative/loadStdio` when agent-lock.json doesn't exist |
|
|
697
|
+
| `AGENT_JSON_NOT_FOUND` | `readProject/readAbility/readHome` when agent.json doesn't exist |
|
|
698
|
+
| `AGENT_JSON_PARSE_ERROR` | agent.json exists but contains invalid JSON |
|
|
699
|
+
| `AGENT_JSON_WRITE_ERROR` | Failed to write agent.json (permissions, disk, etc.) |
|
|
700
|
+
| `AGENT_JSON_FIELD_NOT_FOUND` | Dot-path field doesn't exist in agent.json |
|
|
701
|
+
| `AGENT_JSON_VERSION_AMBIGUOUS` | Multiple versions installed and no version specified |
|
|
702
|
+
| `PROCESS_ALREADY_EXISTS` | `spawn()` with an ID that's already in use |
|
|
703
|
+
| `PROCESS_NOT_FOUND` | `get()` / `getStatus()` with an unknown process ID |
|
|
704
|
+
| `PROCESS_NOT_RUNNING` | `request()` / `notify()` on an exited process |
|
|
705
|
+
| `PROCESS_BRIDGE_ERROR` | JSON-RPC communication error in bridge mode |
|
|
706
|
+
| `PROCESS_SPAWN_FAILED` | Child process failed to start (bad command, permissions) |
|
|
707
|
+
| `PROCESS_TIMEOUT` | Process exceeded its configured timeout |
|
|
433
708
|
|
|
434
709
|
---
|
|
435
710
|
|
|
@@ -472,6 +747,13 @@ new KadiClient(config: ClientConfig)
|
|
|
472
747
|
| `serve(mode)` | Serve as ability (`'stdio'` or `'broker'`) |
|
|
473
748
|
| `getConnectedBrokers()` | List connected broker names |
|
|
474
749
|
|
|
750
|
+
**Properties:**
|
|
751
|
+
|
|
752
|
+
| Property | Type | Description |
|
|
753
|
+
|----------|------|-------------|
|
|
754
|
+
| `agentJson` | `AgentJsonManager` | Lazy-initialized agent.json manager. See [Agent.json Management](#agentjson-management) |
|
|
755
|
+
| `processes` | `ProcessManager` | Lazy-initialized process manager. See [Process Manager](#process-manager) |
|
|
756
|
+
|
|
475
757
|
### LoadedAbility
|
|
476
758
|
|
|
477
759
|
Returned by `loadNative()`, `loadStdio()`, `loadBroker()`:
|
|
@@ -500,6 +782,97 @@ interface BrokerEvent {
|
|
|
500
782
|
}
|
|
501
783
|
```
|
|
502
784
|
|
|
785
|
+
### AgentJsonManager
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
new AgentJsonManager(options?: AgentJsonManagerOptions)
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Options:**
|
|
792
|
+
|
|
793
|
+
| Option | Type | Default | Description |
|
|
794
|
+
|--------|------|---------|-------------|
|
|
795
|
+
| `projectRoot` | `string` | auto-detect | Path to agent project root |
|
|
796
|
+
| `kadiHome` | `string` | `~/.kadi` | Path to KADI home directory |
|
|
797
|
+
| `createOnWrite` | `boolean` | `true` | Create agent.json if missing on write |
|
|
798
|
+
|
|
799
|
+
**Methods:**
|
|
800
|
+
|
|
801
|
+
| Method | Returns | Description |
|
|
802
|
+
|--------|---------|-------------|
|
|
803
|
+
| `readProject(field?)` | `Promise<unknown>` | Read project agent.json (optional dot-path field) |
|
|
804
|
+
| `readAbility(name, options?)` | `Promise<unknown>` | Read an installed ability's agent.json |
|
|
805
|
+
| `readHome(field?)` | `Promise<unknown>` | Read global ~/.kadi/agent.json |
|
|
806
|
+
| `writeProject(path, value)` | `Promise<void>` | Write/deep-merge a field in project agent.json |
|
|
807
|
+
| `writeAbility(name, path, value, version?)` | `Promise<void>` | Write a field in an ability's agent.json |
|
|
808
|
+
| `writeHome(path, value)` | `Promise<void>` | Write a field in global agent.json |
|
|
809
|
+
| `deleteProject(path)` | `Promise<boolean>` | Delete a field from project agent.json |
|
|
810
|
+
| `deleteAbility(name, path, version?)` | `Promise<boolean>` | Delete a field from an ability's agent.json |
|
|
811
|
+
| `deleteHome(path)` | `Promise<boolean>` | Delete a field from global agent.json |
|
|
812
|
+
| `listAbilities()` | `Promise<AbilityInfo[]>` | List all installed abilities |
|
|
813
|
+
| `hasAbility(name)` | `Promise<boolean>` | Check if an ability is installed |
|
|
814
|
+
| `getAbilityVersions(name)` | `Promise<string[]>` | Get installed versions of an ability |
|
|
815
|
+
| `getPaths()` | `Promise<AgentJsonPaths>` | Get resolved file paths for all scopes |
|
|
816
|
+
|
|
817
|
+
### ProcessManager
|
|
818
|
+
|
|
819
|
+
```typescript
|
|
820
|
+
new ProcessManager()
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
**Methods:**
|
|
824
|
+
|
|
825
|
+
| Method | Returns | Description |
|
|
826
|
+
|--------|---------|-------------|
|
|
827
|
+
| `spawn(id, options)` | `Promise<ManagedProcess>` | Spawn a new background process |
|
|
828
|
+
| `get(id)` | `ManagedProcess` | Get handle to an existing process (throws if not found) |
|
|
829
|
+
| `getStatus(id)` | `ProcessState` | Get process state: `'running'` \| `'exited'` \| `'killed'` \| `'errored'` |
|
|
830
|
+
| `list(options?)` | `ManagedProcess[]` | List all managed processes (optional mode filter) |
|
|
831
|
+
| `kill(id)` | `Promise<void>` | Kill a process by ID |
|
|
832
|
+
| `killAll()` | `Promise<void>` | Kill all managed processes |
|
|
833
|
+
| `shutdown()` | `Promise<void>` | Kill all and clean up |
|
|
834
|
+
| `prune()` | `number` | Remove exited/errored entries, returns count pruned |
|
|
835
|
+
|
|
836
|
+
**SpawnOptions:**
|
|
837
|
+
|
|
838
|
+
| Option | Type | Default | Description |
|
|
839
|
+
|--------|------|---------|-------------|
|
|
840
|
+
| `command` | `string` | required | Command to run |
|
|
841
|
+
| `args` | `string[]` | `[]` | Command arguments |
|
|
842
|
+
| `mode` | `ProcessMode` | required | `'headless'` \| `'piped'` \| `'bridge'` |
|
|
843
|
+
| `cwd` | `string` | `process.cwd()` | Working directory |
|
|
844
|
+
| `env` | `Record<string, string>` | inherited | Environment variables |
|
|
845
|
+
| `timeout` | `number` | none | Auto-kill after N milliseconds |
|
|
846
|
+
| `killGracePeriod` | `number` | `5000` | Time between SIGTERM and SIGKILL |
|
|
847
|
+
|
|
848
|
+
### ManagedProcess
|
|
849
|
+
|
|
850
|
+
Returned by `ProcessManager.spawn()`. Extends `EventEmitter`.
|
|
851
|
+
|
|
852
|
+
| Property/Method | Description |
|
|
853
|
+
|-----------------|-------------|
|
|
854
|
+
| `id` | Unique process identifier |
|
|
855
|
+
| `pid` | OS process ID |
|
|
856
|
+
| `mode` | `'headless'` \| `'piped'` \| `'bridge'` |
|
|
857
|
+
| `state` | Current state: `'running'` \| `'exited'` \| `'killed'` \| `'errored'` |
|
|
858
|
+
| `request(method, params?)` | **Bridge only.** Send JSON-RPC request, returns `Promise<unknown>` |
|
|
859
|
+
| `notify(method, params?)` | **Bridge only.** Send JSON-RPC notification (no response) |
|
|
860
|
+
| `write(data)` | Write raw data to stdin |
|
|
861
|
+
| `kill()` | Kill the process (SIGTERM → grace → SIGKILL) |
|
|
862
|
+
| `waitForExit()` | `Promise<ProcessExitInfo>` — resolves when process exits |
|
|
863
|
+
| `getOutput()` | **Piped only.** Get buffered `{ stdout, stderr }` |
|
|
864
|
+
| `getInfo()` | Get `ProcessInfo` snapshot |
|
|
865
|
+
|
|
866
|
+
**Events:**
|
|
867
|
+
|
|
868
|
+
| Event | Payload | Modes |
|
|
869
|
+
|-------|---------|-------|
|
|
870
|
+
| `stdout` | `string` | piped |
|
|
871
|
+
| `stderr` | `string` | piped |
|
|
872
|
+
| `exit` | `ProcessExitInfo` | all |
|
|
873
|
+
| `error` | `Error` | all |
|
|
874
|
+
| `notification` | `{ method, params }` | bridge |
|
|
875
|
+
|
|
503
876
|
---
|
|
504
877
|
|
|
505
878
|
## Troubleshooting
|
|
@@ -530,6 +903,19 @@ Increase the timeout:
|
|
|
530
903
|
await client.invokeRemote('slow-tool', params, { timeout: 60000 });
|
|
531
904
|
```
|
|
532
905
|
|
|
906
|
+
### AGENT_JSON_NOT_FOUND errors
|
|
907
|
+
|
|
908
|
+
The manager can't find `agent.json` at the expected path. Either:
|
|
909
|
+
- Ensure you're running from inside a project with `agent.json` and `agent-lock.json`
|
|
910
|
+
- Pass an explicit `projectRoot` when constructing `AgentJsonManager`
|
|
911
|
+
|
|
912
|
+
### PROCESS_BRIDGE_ERROR in bridge mode
|
|
913
|
+
|
|
914
|
+
The child process isn't speaking the expected Content-Length framed JSON-RPC protocol. Ensure:
|
|
915
|
+
1. The child writes `Content-Length: <n>\r\n\r\n<json>` to stdout
|
|
916
|
+
2. The child reads the same framing from stdin
|
|
917
|
+
3. No other output is mixed into stdout (use stderr for logging)
|
|
918
|
+
|
|
533
919
|
---
|
|
534
920
|
|
|
535
921
|
## Advanced: Building CLI Tools
|
|
@@ -575,6 +961,43 @@ const calcPath = resolveAbilityPath('calculator', root);
|
|
|
575
961
|
// '/path/to/project/abilities/calculator@1.0.0'
|
|
576
962
|
```
|
|
577
963
|
|
|
964
|
+
### Dot-Path Utilities
|
|
965
|
+
|
|
966
|
+
Low-level helpers for nested object access (used internally by `AgentJsonManager`):
|
|
967
|
+
|
|
968
|
+
```typescript
|
|
969
|
+
import { getByPath, setByPath, deleteByPath, deepMerge } from '@kadi.build/core';
|
|
970
|
+
|
|
971
|
+
const obj = { deploy: { local: { target: 'docker' } } };
|
|
972
|
+
|
|
973
|
+
getByPath(obj, 'deploy.local.target'); // 'docker'
|
|
974
|
+
setByPath(obj, 'deploy.staging.target', 'akash');
|
|
975
|
+
deleteByPath(obj, 'deploy.local');
|
|
976
|
+
|
|
977
|
+
// Deep merge (objects merged recursively, arrays replaced)
|
|
978
|
+
const merged = deepMerge(
|
|
979
|
+
{ deploy: { replicas: 1, target: 'docker' } },
|
|
980
|
+
{ deploy: { replicas: 3 } },
|
|
981
|
+
);
|
|
982
|
+
// { deploy: { replicas: 3, target: 'docker' } }
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
### Stdio Framing
|
|
986
|
+
|
|
987
|
+
For building custom Content-Length framed JSON-RPC protocols (used internally by stdio transport and bridge mode):
|
|
988
|
+
|
|
989
|
+
```typescript
|
|
990
|
+
import { StdioMessageReader, StdioMessageWriter } from '@kadi.build/core';
|
|
991
|
+
|
|
992
|
+
// Wrap a readable stream (e.g., child.stdout)
|
|
993
|
+
const reader = new StdioMessageReader(childProcess.stdout);
|
|
994
|
+
reader.onMessage((msg) => console.log('Received:', msg));
|
|
995
|
+
|
|
996
|
+
// Wrap a writable stream (e.g., child.stdin)
|
|
997
|
+
const writer = new StdioMessageWriter(childProcess.stdin);
|
|
998
|
+
writer.write({ jsonrpc: '2.0', method: 'ping', params: {} });
|
|
999
|
+
```
|
|
1000
|
+
|
|
578
1001
|
---
|
|
579
1002
|
|
|
580
1003
|
## Related
|
package/agent.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kadi-core",
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"type": "ability",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Core SDK for building KADI agents — provides client, transports, agent.json management, and process management",
|
|
7
|
+
"entrypoint": "dist/index.js",
|
|
8
|
+
"repo": "https://gitlab.com/humin-game-lab/agent-abilities/kadi-core.git",
|
|
9
|
+
"lib": "https://gitlab.com/humin-game-lab/agent-abilities/kadi-core/-/archive/v0.11.0/kadi-core-v0.11.0.zip",
|
|
10
|
+
"abilities": {},
|
|
11
|
+
|
|
12
|
+
"scripts": {
|
|
13
|
+
"preflight": "echo 'kadi-core v0.11.0 checking environment...'",
|
|
14
|
+
"setup": "npm install && npm run build",
|
|
15
|
+
"postinstall": "node scripts/symlink.mjs",
|
|
16
|
+
"start": "echo 'kadi-core is a library — import it in your code'",
|
|
17
|
+
"stop": "echo 'kadi-core stopped'"
|
|
18
|
+
}
|
|
19
|
+
}
|