@kadi.build/core 0.8.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.
Files changed (45) hide show
  1. package/README.md +424 -1
  2. package/agent.json +19 -0
  3. package/dist/agent-json.d.ts +231 -0
  4. package/dist/agent-json.d.ts.map +1 -0
  5. package/dist/agent-json.js +554 -0
  6. package/dist/agent-json.js.map +1 -0
  7. package/dist/client.d.ts +41 -8
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +102 -43
  10. package/dist/client.js.map +1 -1
  11. package/dist/errors.d.ts +1 -1
  12. package/dist/errors.d.ts.map +1 -1
  13. package/dist/errors.js.map +1 -1
  14. package/dist/index.d.ts +5 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +8 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/process-manager.d.ts +235 -0
  19. package/dist/process-manager.d.ts.map +1 -0
  20. package/dist/process-manager.js +647 -0
  21. package/dist/process-manager.js.map +1 -0
  22. package/dist/stdio-framing.d.ts +88 -0
  23. package/dist/stdio-framing.d.ts.map +1 -0
  24. package/dist/stdio-framing.js +194 -0
  25. package/dist/stdio-framing.js.map +1 -0
  26. package/dist/transports/stdio.d.ts.map +1 -1
  27. package/dist/transports/stdio.js +3 -181
  28. package/dist/transports/stdio.js.map +1 -1
  29. package/dist/types.d.ts +274 -21
  30. package/dist/types.d.ts.map +1 -1
  31. package/dist/utils.d.ts +107 -0
  32. package/dist/utils.d.ts.map +1 -0
  33. package/dist/utils.js +212 -0
  34. package/dist/utils.js.map +1 -0
  35. package/package.json +3 -1
  36. package/scripts/symlink.mjs +131 -0
  37. package/src/agent-json.ts +655 -0
  38. package/src/client.ts +120 -46
  39. package/src/errors.ts +15 -0
  40. package/src/index.ts +32 -0
  41. package/src/process-manager.ts +821 -0
  42. package/src/stdio-framing.ts +227 -0
  43. package/src/transports/stdio.ts +4 -221
  44. package/src/types.ts +291 -23
  45. package/src/utils.ts +246 -0
package/src/client.ts CHANGED
@@ -59,6 +59,8 @@ import { resolveAbilityEntry, resolveAbilityScript } from './lockfile.js';
59
59
  import { loadNativeTransport } from './transports/native.js';
60
60
  import { loadStdioTransport } from './transports/stdio.js';
61
61
  import { loadBrokerTransport } from './transports/broker.js';
62
+ import { AgentJsonManager } from './agent-json.js';
63
+ import { ProcessManager } from './process-manager.js';
62
64
 
63
65
  // ═══════════════════════════════════════════════════════════════
64
66
  // CONSTANTS
@@ -204,6 +206,12 @@ export class KadiClient {
204
206
  /** Registered disconnect hooks */
205
207
  private readonly disconnectHooks: Array<() => Promise<void>> = [];
206
208
 
209
+ /** Lazy-initialized AgentJsonManager */
210
+ private _agentJson: AgentJsonManager | null = null;
211
+
212
+ /** Lazy-initialized ProcessManager */
213
+ private _processes: ProcessManager | null = null;
214
+
207
215
  // ─────────────────────────────────────────────────────────────
208
216
  // IDENTITY (single keypair shared across all broker connections)
209
217
  // ─────────────────────────────────────────────────────────────
@@ -1823,8 +1831,12 @@ export class KadiClient {
1823
1831
  * output: z.object({ result: z.number() }),
1824
1832
  * }, async ({ a, b }) => ({ result: a + b }));
1825
1833
  *
1826
- * // Register only on specific broker
1827
- * client.registerTool(def, handler, { brokers: ['internal'] });
1834
+ * // Register on specific brokers with per-broker networks
1835
+ * client.registerTool(def, handler, {
1836
+ * brokers: {
1837
+ * internal: { networks: ['private'] },
1838
+ * },
1839
+ * });
1828
1840
  * ```
1829
1841
  */
1830
1842
  registerTool<TInput, TOutput>(
@@ -1850,21 +1862,36 @@ export class KadiClient {
1850
1862
  : undefined,
1851
1863
  };
1852
1864
 
1853
- // Validate per-tool networks against broker networks (skip when no brokers)
1854
- const targetNetworks = options.networks ?? [];
1855
- if (targetNetworks.length > 0 && Object.keys(this.config.brokers).length > 0) {
1856
- const allNetworks = this.getAllConfiguredNetworks();
1857
- const invalid = targetNetworks.filter((n) => !allNetworks.includes(n));
1858
- if (invalid.length > 0) {
1859
- throw new KadiError(
1860
- `Tool "${definition.name}" has networks ${JSON.stringify(invalid)} not present in client networks ${JSON.stringify(allNetworks)}. Per-tool networks must be a subset of the client's networks — a tool can only be visible on networks the agent has joined.`,
1861
- 'INVALID_CONFIG',
1862
- {
1863
- toolName: definition.name,
1864
- invalidNetworks: invalid,
1865
- clientNetworks: allNetworks,
1866
- }
1867
- );
1865
+ // Validate per-broker network scoping (skip when no brokers configured)
1866
+ const brokerScopes = options.brokers ?? {};
1867
+ if (Object.keys(brokerScopes).length > 0 && Object.keys(this.config.brokers).length > 0) {
1868
+ for (const [brokerName, scope] of Object.entries(brokerScopes)) {
1869
+ const brokerConfig = this.config.brokers[brokerName];
1870
+ if (!brokerConfig) {
1871
+ throw new KadiError(
1872
+ `Tool "${definition.name}" references broker "${brokerName}" which is not configured. Available brokers: ${JSON.stringify(Object.keys(this.config.brokers))}`,
1873
+ 'INVALID_CONFIG',
1874
+ {
1875
+ toolName: definition.name,
1876
+ broker: brokerName,
1877
+ availableBrokers: Object.keys(this.config.brokers),
1878
+ }
1879
+ );
1880
+ }
1881
+ const brokerNetworks = brokerConfig.networks;
1882
+ const invalid = scope.networks.filter((n) => !brokerNetworks.includes(n));
1883
+ if (invalid.length > 0) {
1884
+ throw new KadiError(
1885
+ `Tool "${definition.name}" has networks ${JSON.stringify(invalid)} not present on broker "${brokerName}" networks ${JSON.stringify(brokerNetworks)}. Per-tool networks must be a subset of the broker's networks — a tool can only be visible on networks the agent has joined.`,
1886
+ 'INVALID_CONFIG',
1887
+ {
1888
+ toolName: definition.name,
1889
+ broker: brokerName,
1890
+ invalidNetworks: invalid,
1891
+ brokerNetworks,
1892
+ }
1893
+ );
1894
+ }
1868
1895
  }
1869
1896
  }
1870
1897
 
@@ -1872,46 +1899,45 @@ export class KadiClient {
1872
1899
  const registered: RegisteredTool = {
1873
1900
  definition: jsonDefinition,
1874
1901
  handler: handler as ToolHandler,
1875
- targetBrokers: options.brokers ?? [],
1876
- targetNetworks,
1902
+ brokerNetworks: brokerScopes,
1877
1903
  };
1878
1904
  this.tools.set(definition.name, registered);
1879
1905
  }
1880
1906
 
1881
- /**
1882
- * Get the union of all networks configured across all brokers.
1883
- * Used by registerTool() to validate per-tool network targeting.
1884
- */
1885
- private getAllConfiguredNetworks(): string[] {
1886
- const all = new Set<string>();
1887
- for (const broker of Object.values(this.config.brokers)) {
1888
- for (const n of broker.networks) all.add(n);
1889
- }
1890
- return [...all];
1891
- }
1892
-
1893
1907
  /**
1894
1908
  * Get tool definitions, optionally filtered for a specific broker.
1895
1909
  *
1896
1910
  * @param forBroker - If provided, only return tools targeted for this broker.
1897
- * Tools with empty targetBrokers are included for all brokers.
1911
+ * Tools with empty brokerNetworks are included for all brokers.
1898
1912
  */
1899
1913
  private getToolDefinitions(forBroker?: string): BrokerToolDefinition[] {
1900
- return Array.from(this.tools.values())
1901
- .filter((t) => {
1902
- // If no broker specified, return all tools (e.g., for readAgentJson)
1903
- if (!forBroker) return true;
1904
-
1905
- // Empty targetBrokers means "register with all brokers"
1906
- if (t.targetBrokers.length === 0) return true;
1907
-
1908
- // Otherwise, only include if this broker is in the target list
1909
- return t.targetBrokers.includes(forBroker);
1910
- })
1911
- .map((t) => ({
1914
+ const results: BrokerToolDefinition[] = [];
1915
+
1916
+ for (const t of this.tools.values()) {
1917
+ const hasBrokerNetworks = Object.keys(t.brokerNetworks).length > 0;
1918
+
1919
+ if (!forBroker) {
1920
+ // No broker filter — return all tools (e.g., for readAgentJson)
1921
+ results.push({ ...t.definition });
1922
+ continue;
1923
+ }
1924
+
1925
+ if (!hasBrokerNetworks) {
1926
+ // Empty brokerNetworks = register with all brokers, no network field
1927
+ results.push({ ...t.definition });
1928
+ continue;
1929
+ }
1930
+
1931
+ const scope = t.brokerNetworks[forBroker];
1932
+ if (!scope) continue; // Tool not registered on this broker
1933
+
1934
+ results.push({
1912
1935
  ...t.definition,
1913
- ...(t.targetNetworks.length > 0 && { networks: t.targetNetworks }),
1914
- }));
1936
+ ...(scope.networks.length > 0 && { networks: scope.networks }),
1937
+ });
1938
+ }
1939
+
1940
+ return results;
1915
1941
  }
1916
1942
 
1917
1943
  /**
@@ -2391,6 +2417,54 @@ export class KadiClient {
2391
2417
  await new Promise(() => {});
2392
2418
  }
2393
2419
 
2420
+ // ─────────────────────────────────────────────────────────────
2421
+ // AGENT JSON MANAGER
2422
+ // ─────────────────────────────────────────────────────────────
2423
+
2424
+ /**
2425
+ * Access the AgentJsonManager for reading/writing agent.json files.
2426
+ *
2427
+ * Lazily created on first access. Provides unified API for project root,
2428
+ * installed ability, and KADI home agent.json files.
2429
+ *
2430
+ * @example
2431
+ * ```typescript
2432
+ * const config = await client.agentJson.readProject();
2433
+ * const deploy = await client.agentJson.readProject('deploy.local');
2434
+ * await client.agentJson.writeProject('deploy.staging', { target: 'akash' });
2435
+ * ```
2436
+ */
2437
+ get agentJson(): AgentJsonManager {
2438
+ if (!this._agentJson) {
2439
+ this._agentJson = new AgentJsonManager();
2440
+ }
2441
+ return this._agentJson;
2442
+ }
2443
+
2444
+ // ─────────────────────────────────────────────────────────────
2445
+ // PROCESS MANAGER
2446
+ // ─────────────────────────────────────────────────────────────
2447
+
2448
+ /**
2449
+ * Access the ProcessManager for spawning and managing background processes.
2450
+ *
2451
+ * Lazily created on first access. Supports headless, piped, and bridge modes.
2452
+ *
2453
+ * @example
2454
+ * ```typescript
2455
+ * const build = await client.processes.spawn('build', {
2456
+ * command: 'docker', args: ['build', '.'], mode: 'piped',
2457
+ * });
2458
+ * build.on('exit', ({ exitCode }) => console.log('done:', exitCode));
2459
+ * ```
2460
+ */
2461
+ get processes(): ProcessManager {
2462
+ if (!this._processes) {
2463
+ this._processes = new ProcessManager();
2464
+ }
2465
+ return this._processes;
2466
+ }
2467
+
2394
2468
  // ─────────────────────────────────────────────────────────────
2395
2469
  // UTILITY
2396
2470
  // ─────────────────────────────────────────────────────────────
package/src/errors.ts CHANGED
@@ -65,6 +65,21 @@ export type ErrorCode =
65
65
  | 'INVALID_INPUT'
66
66
  | 'PROJECT_ROOT_NOT_FOUND'
67
67
 
68
+ // Agent.json errors
69
+ | 'AGENT_JSON_NOT_FOUND'
70
+ | 'AGENT_JSON_PARSE_ERROR'
71
+ | 'AGENT_JSON_WRITE_ERROR'
72
+ | 'AGENT_JSON_FIELD_NOT_FOUND'
73
+ | 'AGENT_JSON_VERSION_AMBIGUOUS'
74
+
75
+ // Process manager errors
76
+ | 'PROCESS_ALREADY_EXISTS'
77
+ | 'PROCESS_NOT_FOUND'
78
+ | 'PROCESS_NOT_RUNNING'
79
+ | 'PROCESS_BRIDGE_ERROR'
80
+ | 'PROCESS_SPAWN_FAILED'
81
+ | 'PROCESS_TIMEOUT'
82
+
68
83
  // Transport errors
69
84
  | 'STDIO_ERROR'
70
85
  | 'STDIO_PROCESS_FAILED'
package/src/index.ts CHANGED
@@ -91,8 +91,40 @@ export type {
91
91
 
92
92
  // Script resolution
93
93
  ResolvedScript,
94
+
95
+ // Agent.json types
96
+ AgentJson,
97
+ AgentJsonScripts,
98
+ AgentJsonBuildProfile,
99
+ AgentJsonDeployProfile,
100
+ AgentJsonManagerOptions,
101
+ ReadAbilityOptions,
102
+ AbilityInfo,
103
+ AgentJsonPaths,
104
+
105
+ // Process manager types
106
+ ProcessMode,
107
+ ProcessState,
108
+ SpawnOptions,
109
+ ProcessInfo,
110
+ ProcessExitInfo,
111
+ ProcessOutput,
112
+ ProcessListOptions,
113
+ ProcessPruneOptions,
94
114
  } from './types.js';
95
115
 
116
+ // Agent.json manager
117
+ export { AgentJsonManager } from './agent-json.js';
118
+
119
+ // Process manager
120
+ export { ProcessManager, ManagedProcess } from './process-manager.js';
121
+
122
+ // Dot-path utilities
123
+ export { getByPath, setByPath, deleteByPath, deepMerge } from './utils.js';
124
+
125
+ // Stdio framing (for advanced use cases — building custom bridge protocols)
126
+ export { StdioMessageReader, StdioMessageWriter } from './stdio-framing.js';
127
+
96
128
  // Zod utilities
97
129
  export { zodToJsonSchema, isZodSchema } from './zod.js';
98
130