@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/dist/utils.js ADDED
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Dot-path utility functions for kadi-core
3
+ *
4
+ * Provides get/set/delete operations on nested objects using dot-notation paths.
5
+ * Used by AgentJsonManager for field-level reads and writes.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const obj = { deploy: { local: { target: 'docker' } } };
10
+ *
11
+ * getByPath(obj, 'deploy.local.target');
12
+ * // → 'docker'
13
+ *
14
+ * setByPath(obj, 'deploy.staging.target', 'akash');
15
+ * // obj is now: { deploy: { local: { target: 'docker' }, staging: { target: 'akash' } } }
16
+ *
17
+ * deleteByPath(obj, 'deploy.staging');
18
+ * // obj is now: { deploy: { local: { target: 'docker' } } }
19
+ * ```
20
+ */
21
+ // ═══════════════════════════════════════════════════════════════════════
22
+ // PATH PARSING
23
+ // ═══════════════════════════════════════════════════════════════════════
24
+ /**
25
+ * Split a dot-notation path into segments.
26
+ *
27
+ * If the full path exists as a literal key in the object, it takes precedence
28
+ * over dot-splitting. This handles the (rare) case where a key literally
29
+ * contains a dot.
30
+ *
31
+ * @param path - Dot-notation path (e.g., 'deploy.local.services')
32
+ * @returns Array of path segments
33
+ */
34
+ export function splitPath(path) {
35
+ if (!path)
36
+ return [];
37
+ return path.split('.');
38
+ }
39
+ // ═══════════════════════════════════════════════════════════════════════
40
+ // GET
41
+ // ═══════════════════════════════════════════════════════════════════════
42
+ /**
43
+ * Get a value from a nested object by dot-notation path.
44
+ *
45
+ * @param obj - The object to read from
46
+ * @param path - Dot-notation path (e.g., 'deploy.local.target')
47
+ * @returns The value at the path, or undefined if not found
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const config = { deploy: { local: { target: 'docker' } } };
52
+ *
53
+ * getByPath(config, 'deploy.local.target'); // 'docker'
54
+ * getByPath(config, 'deploy.local'); // { target: 'docker' }
55
+ * getByPath(config, 'missing'); // undefined
56
+ * getByPath(config, 'deploy.missing.deep'); // undefined
57
+ * ```
58
+ */
59
+ export function getByPath(obj, path) {
60
+ // Check if the full path exists as a literal key first
61
+ if (path in obj) {
62
+ return obj[path];
63
+ }
64
+ const segments = splitPath(path);
65
+ let current = obj;
66
+ for (const segment of segments) {
67
+ if (current === null || current === undefined || typeof current !== 'object') {
68
+ return undefined;
69
+ }
70
+ current = current[segment];
71
+ }
72
+ return current;
73
+ }
74
+ // ═══════════════════════════════════════════════════════════════════════
75
+ // SET
76
+ // ═══════════════════════════════════════════════════════════════════════
77
+ /**
78
+ * Deep merge two objects. Arrays and scalars are replaced, not merged.
79
+ *
80
+ * @param target - The object to merge into
81
+ * @param source - The object to merge from
82
+ * @returns The merged target object
83
+ */
84
+ export function deepMerge(target, source) {
85
+ for (const key of Object.keys(source)) {
86
+ const sourceVal = source[key];
87
+ const targetVal = target[key];
88
+ if (sourceVal !== null &&
89
+ typeof sourceVal === 'object' &&
90
+ !Array.isArray(sourceVal) &&
91
+ targetVal !== null &&
92
+ typeof targetVal === 'object' &&
93
+ !Array.isArray(targetVal)) {
94
+ // Both are plain objects — recurse
95
+ target[key] = deepMerge(targetVal, sourceVal);
96
+ }
97
+ else {
98
+ // Scalar, array, or type mismatch — replace
99
+ target[key] = sourceVal;
100
+ }
101
+ }
102
+ return target;
103
+ }
104
+ /**
105
+ * Set a value in a nested object by dot-notation path.
106
+ *
107
+ * Creates intermediate objects as needed. For object values, performs
108
+ * a deep merge with existing values (preserving sibling keys).
109
+ * For scalars and arrays, replaces the value.
110
+ *
111
+ * @param obj - The object to modify (mutated in place)
112
+ * @param path - Dot-notation path (e.g., 'deploy.staging.target')
113
+ * @param value - The value to set
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const config = { deploy: { local: { target: 'docker', engine: 'docker' } } };
118
+ *
119
+ * // Set a scalar — replaces
120
+ * setByPath(config, 'deploy.local.target', 'akash');
121
+ * // { deploy: { local: { target: 'akash', engine: 'docker' } } }
122
+ *
123
+ * // Set an object — deep merges
124
+ * setByPath(config, 'deploy.local', { network: 'mainnet' });
125
+ * // { deploy: { local: { target: 'akash', engine: 'docker', network: 'mainnet' } } }
126
+ *
127
+ * // Set on non-existent path — creates intermediates
128
+ * setByPath(config, 'build.arm64.from', 'node:22-slim');
129
+ * // { deploy: {...}, build: { arm64: { from: 'node:22-slim' } } }
130
+ * ```
131
+ */
132
+ export function setByPath(obj, path, value) {
133
+ const segments = splitPath(path);
134
+ if (segments.length === 0)
135
+ return;
136
+ // Walk to the parent of the target key, creating intermediates
137
+ let current = obj;
138
+ for (let i = 0; i < segments.length - 1; i++) {
139
+ const segment = segments[i];
140
+ const next = current[segment];
141
+ if (next === null || next === undefined || typeof next !== 'object' || Array.isArray(next)) {
142
+ // Create intermediate object
143
+ const newObj = {};
144
+ current[segment] = newObj;
145
+ current = newObj;
146
+ }
147
+ else {
148
+ current = next;
149
+ }
150
+ }
151
+ const lastSegment = segments[segments.length - 1];
152
+ const existing = current[lastSegment];
153
+ // Deep merge if both existing and new are plain objects
154
+ if (value !== null &&
155
+ typeof value === 'object' &&
156
+ !Array.isArray(value) &&
157
+ existing !== null &&
158
+ typeof existing === 'object' &&
159
+ !Array.isArray(existing)) {
160
+ current[lastSegment] = deepMerge(existing, value);
161
+ }
162
+ else {
163
+ current[lastSegment] = value;
164
+ }
165
+ }
166
+ // ═══════════════════════════════════════════════════════════════════════
167
+ // DELETE
168
+ // ═══════════════════════════════════════════════════════════════════════
169
+ /**
170
+ * Delete a key from a nested object by dot-notation path.
171
+ *
172
+ * @param obj - The object to modify (mutated in place)
173
+ * @param path - Dot-notation path to the key to delete
174
+ * @returns true if the key was found and deleted, false otherwise
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const config = { deploy: { local: { target: 'docker' }, staging: { target: 'akash' } } };
179
+ *
180
+ * deleteByPath(config, 'deploy.staging');
181
+ * // config is now: { deploy: { local: { target: 'docker' } } }
182
+ * // returns true
183
+ *
184
+ * deleteByPath(config, 'missing.key');
185
+ * // returns false
186
+ * ```
187
+ */
188
+ export function deleteByPath(obj, path) {
189
+ const segments = splitPath(path);
190
+ if (segments.length === 0)
191
+ return false;
192
+ // Walk to the parent of the target key
193
+ let current = obj;
194
+ for (let i = 0; i < segments.length - 1; i++) {
195
+ const segment = segments[i];
196
+ if (current === null || current === undefined || typeof current !== 'object') {
197
+ return false;
198
+ }
199
+ current = current[segment];
200
+ }
201
+ if (current === null || current === undefined || typeof current !== 'object') {
202
+ return false;
203
+ }
204
+ const lastSegment = segments[segments.length - 1];
205
+ const parent = current;
206
+ if (!(lastSegment in parent)) {
207
+ return false;
208
+ }
209
+ delete parent[lastSegment];
210
+ return true;
211
+ }
212
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadi.build/core",
3
- "version": "0.8.0",
3
+ "version": "0.11.0",
4
4
  "description": "A lean, readable SDK for building KADI agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,6 +14,8 @@
14
14
  "files": [
15
15
  "dist",
16
16
  "src",
17
+ "scripts",
18
+ "agent.json",
17
19
  "tsconfig.json",
18
20
  "README.md",
19
21
  "LICENSE"
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * kadi-core postinstall symlink script
5
+ *
6
+ * When kadi-core is installed as an ability (via `kadi install`), it ends up in:
7
+ * <project>/abilities/kadi-core@x.y.z/
8
+ *
9
+ * This script creates a symlink so that regular Node.js imports work seamlessly:
10
+ * <project>/node_modules/@kadi.build/core → <project>/abilities/kadi-core@x.y.z/
11
+ *
12
+ * This means users can do:
13
+ * import { KadiClient } from '@kadi.build/core';
14
+ *
15
+ * without needing to know the ability install path or version number.
16
+ *
17
+ * The script is idempotent — safe to run multiple times.
18
+ */
19
+
20
+ import { existsSync, mkdirSync, symlinkSync, lstatSync, readlinkSync, unlinkSync } from 'fs';
21
+ import { join, dirname, resolve, relative } from 'path';
22
+ import { fileURLToPath } from 'url';
23
+
24
+ const __filename = fileURLToPath(import.meta.url);
25
+ const __dirname = dirname(__filename);
26
+
27
+ // This script lives at <ability>/scripts/symlink.mjs
28
+ // The ability root is one level up
29
+ const abilityRoot = resolve(__dirname, '..');
30
+
31
+ /**
32
+ * Walk up from the ability directory to find the project root.
33
+ * The project root is the parent of the "abilities" directory.
34
+ *
35
+ * Expected structure:
36
+ * <project>/abilities/kadi-core@0.10.0/scripts/symlink.mjs
37
+ * <project>/node_modules/@kadi.build/core ← symlink target
38
+ */
39
+ function findProjectRoot() {
40
+ let current = abilityRoot;
41
+
42
+ // Walk up looking for the abilities/ parent
43
+ for (let i = 0; i < 5; i++) {
44
+ const parent = dirname(current);
45
+ const dirName = current.split('/').pop() || current.split('\\').pop();
46
+
47
+ // Check if we're inside an "abilities" directory
48
+ if (parent && dirname(current).endsWith('abilities')) {
49
+ // Parent of abilities/ is the project root
50
+ return dirname(dirname(current));
51
+ }
52
+
53
+ // Also check if the parent directory name starts with "abilities"
54
+ const parentDirName = dirname(current).split('/').pop() || dirname(current).split('\\').pop();
55
+ if (parentDirName === 'abilities') {
56
+ return dirname(dirname(current));
57
+ }
58
+
59
+ current = parent;
60
+ }
61
+
62
+ // Fallback: if abilities/ directory exists as a sibling pattern,
63
+ // the project root is likely 2 levels up from where the ability is installed
64
+ // e.g., <project>/abilities/kadi-core@x.y.z → <project>
65
+ const twoUp = resolve(abilityRoot, '..', '..');
66
+ if (existsSync(join(twoUp, 'agent.json'))) {
67
+ return twoUp;
68
+ }
69
+
70
+ return null;
71
+ }
72
+
73
+ function main() {
74
+ const projectRoot = findProjectRoot();
75
+
76
+ if (!projectRoot) {
77
+ // Not installed as an ability (maybe running from source) — skip silently
78
+ console.log('[kadi-core] Not installed as an ability, skipping symlink creation.');
79
+ return;
80
+ }
81
+
82
+ const symlinkDir = join(projectRoot, 'node_modules', '@kadi.build');
83
+ const symlinkPath = join(symlinkDir, 'core');
84
+
85
+ // Compute relative path from symlink location to ability root
86
+ const relativeTarget = relative(symlinkDir, abilityRoot);
87
+
88
+ // Create the @kadi.build/ directory in node_modules
89
+ if (!existsSync(symlinkDir)) {
90
+ mkdirSync(symlinkDir, { recursive: true });
91
+ console.log(`[kadi-core] Created ${symlinkDir}`);
92
+ }
93
+
94
+ // Check if symlink already exists and points to the right place
95
+ if (existsSync(symlinkPath)) {
96
+ try {
97
+ const stats = lstatSync(symlinkPath);
98
+ if (stats.isSymbolicLink()) {
99
+ const currentTarget = readlinkSync(symlinkPath);
100
+ if (resolve(symlinkDir, currentTarget) === abilityRoot) {
101
+ console.log(`[kadi-core] Symlink already exists and is correct: ${symlinkPath}`);
102
+ return;
103
+ }
104
+ // Wrong target — remove and recreate
105
+ console.log(`[kadi-core] Updating symlink (was pointing to ${currentTarget})`);
106
+ unlinkSync(symlinkPath);
107
+ } else {
108
+ // It's a real directory/file — don't touch it
109
+ console.log(`[kadi-core] ${symlinkPath} exists and is not a symlink — skipping.`);
110
+ console.log('[kadi-core] If you want the symlink, remove the existing directory first.');
111
+ return;
112
+ }
113
+ } catch {
114
+ // Can't read — try to remove
115
+ try { unlinkSync(symlinkPath); } catch { /* ignore */ }
116
+ }
117
+ }
118
+
119
+ // Create the symlink
120
+ try {
121
+ symlinkSync(relativeTarget, symlinkPath, 'dir');
122
+ console.log(`[kadi-core] Created symlink: ${symlinkPath} → ${relativeTarget}`);
123
+ console.log('[kadi-core] You can now: import { KadiClient } from \'@kadi.build/core\'');
124
+ } catch (error) {
125
+ console.error(`[kadi-core] Failed to create symlink: ${error.message}`);
126
+ console.error(`[kadi-core] You can create it manually:`);
127
+ console.error(` ln -s ${relativeTarget} ${symlinkPath}`);
128
+ }
129
+ }
130
+
131
+ main();