@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.
- 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 +41 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +102 -43
- 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 +274 -21
- 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 +120 -46
- 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 +291 -23
- 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":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,0EAA0E;AAC1E,eAAe;AACf,0EAA0E;AAE1E;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,0EAA0E;AAC1E,MAAM;AACN,0EAA0E;AAE1E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CAAC,GAA4B,EAAE,IAAY;IAClE,uDAAuD;IACvD,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,OAAO,GAAY,GAAG,CAAC;IAE3B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0EAA0E;AAC1E,MAAM;AACN,0EAA0E;AAE1E;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CACvB,MAA+B,EAC/B,MAA+B;IAE/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAE9B,IACE,SAAS,KAAK,IAAI;YAClB,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YACzB,SAAS,KAAK,IAAI;YAClB,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EACzB,CAAC;YACD,mCAAmC;YACnC,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CACrB,SAAoC,EACpC,SAAoC,CACrC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,4CAA4C;YAC5C,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,SAAS,CACvB,GAA4B,EAC5B,IAAY,EACZ,KAAc;IAEd,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAEjC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAElC,+DAA+D;IAC/D,IAAI,OAAO,GAA4B,GAAG,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAE9B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3F,6BAA6B;YAC7B,MAAM,MAAM,GAA4B,EAAE,CAAC;YAC3C,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;YAC1B,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,IAA+B,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAEtC,wDAAwD;IACxD,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,QAAQ,KAAK,IAAI;QACjB,OAAO,QAAQ,KAAK,QAAQ;QAC5B,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EACxB,CAAC;QACD,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS,CAC9B,QAAmC,EACnC,KAAgC,CACjC,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,WAAW,CAAC,GAAG,KAAK,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,SAAS;AACT,0EAA0E;AAE1E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,YAAY,CAAC,GAA4B,EAAE,IAAY;IACrE,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAEjC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAExC,uCAAuC;IACvC,IAAI,OAAO,GAAY,GAAG,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;QAC7B,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC7E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IACnD,MAAM,MAAM,GAAG,OAAkC,CAAC;IAElD,IAAI,CAAC,CAAC,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadi.build/core",
|
|
3
|
-
"version": "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();
|