capman 0.3.0 → 0.4.1
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 +235 -98
- package/bin/capman.js +70 -0
- package/dist/cjs/cache.d.ts +16 -8
- package/dist/cjs/cache.d.ts.map +1 -1
- package/dist/cjs/cache.js +59 -47
- package/dist/cjs/cache.js.map +1 -1
- package/dist/cjs/engine.d.ts +5 -5
- package/dist/cjs/engine.d.ts.map +1 -1
- package/dist/cjs/engine.js +92 -11
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/generator.js +1 -1
- package/dist/cjs/generator.js.map +1 -1
- package/dist/cjs/index.d.ts +11 -4
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +19 -38
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/learning.d.ts.map +1 -1
- package/dist/cjs/learning.js +70 -89
- package/dist/cjs/learning.js.map +1 -1
- package/dist/cjs/matcher.d.ts.map +1 -1
- package/dist/cjs/matcher.js +29 -9
- package/dist/cjs/matcher.js.map +1 -1
- package/dist/cjs/resolver.d.ts +4 -1
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +43 -39
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/types.d.ts +26 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/cache.js +58 -48
- package/dist/esm/engine.js +93 -12
- package/dist/esm/generator.js +1 -1
- package/dist/esm/index.js +17 -36
- package/dist/esm/learning.js +70 -89
- package/dist/esm/matcher.js +29 -9
- package/dist/esm/resolver.js +43 -39
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/dist/cjs/resolver.js
CHANGED
|
@@ -48,7 +48,7 @@ async function resolve(matchResult, params = {}, options = {}) {
|
|
|
48
48
|
const enrichedParams = { ...params };
|
|
49
49
|
if (options.auth?.userId) {
|
|
50
50
|
for (const param of capability.params) {
|
|
51
|
-
if (param.source === 'session'
|
|
51
|
+
if (param.source === 'session') {
|
|
52
52
|
enrichedParams[param.name] = options.auth.userId;
|
|
53
53
|
logger_1.logger.debug(`Injected session param "${param.name}" = "${options.auth.userId}"`);
|
|
54
54
|
}
|
|
@@ -91,78 +91,82 @@ async function resolve(matchResult, params = {}, options = {}) {
|
|
|
91
91
|
}
|
|
92
92
|
async function resolveApi(resolver, params, options) {
|
|
93
93
|
const startTime = Date.now();
|
|
94
|
+
const retries = options.retries ?? 0;
|
|
95
|
+
const timeoutMs = options.timeoutMs ?? 5000;
|
|
94
96
|
const apiCalls = resolver.endpoints.map(endpoint => ({
|
|
95
97
|
method: endpoint.method,
|
|
96
98
|
url: buildUrl(options.baseUrl ?? '', endpoint.path, params),
|
|
97
99
|
params,
|
|
98
100
|
}));
|
|
99
101
|
if (options.dryRun) {
|
|
100
|
-
return {
|
|
101
|
-
success: true,
|
|
102
|
-
resolverType: 'api',
|
|
103
|
-
apiCalls,
|
|
104
|
-
durationMs: Date.now() - startTime,
|
|
105
|
-
};
|
|
102
|
+
return { success: true, resolverType: 'api', apiCalls, durationMs: Date.now() - startTime };
|
|
106
103
|
}
|
|
107
104
|
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
108
105
|
if (!fetchFn) {
|
|
109
106
|
return {
|
|
110
|
-
success: true,
|
|
111
|
-
resolverType: 'api',
|
|
112
|
-
apiCalls,
|
|
107
|
+
success: true, resolverType: 'api', apiCalls,
|
|
113
108
|
durationMs: Date.now() - startTime,
|
|
114
109
|
error: 'No fetch available — returning call plan only',
|
|
115
110
|
};
|
|
116
111
|
}
|
|
112
|
+
// ── Fetch with retry + timeout (iterative — no recursion) ────────────────
|
|
113
|
+
async function fetchWithRetry(call) {
|
|
114
|
+
let lastErr;
|
|
115
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
116
|
+
const controller = new AbortController();
|
|
117
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
118
|
+
try {
|
|
119
|
+
const res = await fetchFn(call.url, {
|
|
120
|
+
method: call.method,
|
|
121
|
+
headers: options.headers ?? {},
|
|
122
|
+
signal: controller.signal,
|
|
123
|
+
body: ['POST', 'PUT', 'PATCH'].includes(call.method)
|
|
124
|
+
? JSON.stringify(call.params)
|
|
125
|
+
: undefined,
|
|
126
|
+
});
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
return res;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
lastErr = err;
|
|
133
|
+
const isTimeout = err instanceof Error && err.name === 'AbortError';
|
|
134
|
+
if (attempt < retries) {
|
|
135
|
+
logger_1.logger.warn(`Request failed (attempt ${attempt + 1}/${retries + 1}) — retrying: ${isTimeout ? 'timeout' : err}`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw isTimeout ? new Error(`Request timed out after ${timeoutMs}ms`) : err;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw lastErr;
|
|
143
|
+
}
|
|
117
144
|
try {
|
|
118
|
-
const responses = await Promise.all(apiCalls.map(c =>
|
|
119
|
-
method: c.method,
|
|
120
|
-
headers: options.headers ?? {},
|
|
121
|
-
body: ['POST', 'PUT', 'PATCH'].includes(c.method)
|
|
122
|
-
? JSON.stringify(c.params)
|
|
123
|
-
: undefined,
|
|
124
|
-
})));
|
|
125
|
-
// Check for HTTP errors
|
|
145
|
+
const responses = await Promise.all(apiCalls.map(c => fetchWithRetry(c)));
|
|
126
146
|
const failedIdx = responses.findIndex(r => !r.ok);
|
|
127
147
|
if (failedIdx !== -1) {
|
|
128
148
|
const failed = responses[failedIdx];
|
|
129
149
|
return {
|
|
130
|
-
success: false,
|
|
131
|
-
resolverType: 'api',
|
|
132
|
-
apiCalls,
|
|
150
|
+
success: false, resolverType: 'api', apiCalls,
|
|
133
151
|
durationMs: Date.now() - startTime,
|
|
134
152
|
error: `API request failed: ${failed.status} ${failed.statusText}`,
|
|
135
153
|
};
|
|
136
154
|
}
|
|
137
|
-
// Parse response bodies
|
|
138
155
|
const enrichedCalls = await Promise.all(responses.map(async (res, i) => {
|
|
139
156
|
let data = undefined;
|
|
140
157
|
try {
|
|
141
158
|
const text = await res.text();
|
|
142
159
|
data = text ? JSON.parse(text) : undefined;
|
|
143
160
|
}
|
|
144
|
-
catch {
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
...apiCalls[i],
|
|
149
|
-
status: res.status,
|
|
150
|
-
data,
|
|
151
|
-
};
|
|
161
|
+
catch { /* non-JSON response */ }
|
|
162
|
+
return { ...apiCalls[i], status: res.status, data };
|
|
152
163
|
}));
|
|
153
164
|
logger_1.logger.debug(`API calls completed in ${Date.now() - startTime}ms`);
|
|
154
|
-
return {
|
|
155
|
-
success: true,
|
|
156
|
-
resolverType: 'api',
|
|
157
|
-
apiCalls: enrichedCalls,
|
|
158
|
-
durationMs: Date.now() - startTime,
|
|
159
|
-
};
|
|
165
|
+
return { success: true, resolverType: 'api', apiCalls: enrichedCalls, durationMs: Date.now() - startTime };
|
|
160
166
|
}
|
|
161
167
|
catch (err) {
|
|
162
168
|
return {
|
|
163
|
-
success: false,
|
|
164
|
-
resolverType: 'api',
|
|
165
|
-
apiCalls,
|
|
169
|
+
success: false, resolverType: 'api', apiCalls,
|
|
166
170
|
durationMs: Date.now() - startTime,
|
|
167
171
|
error: err instanceof Error ? err.message : String(err),
|
|
168
172
|
};
|
package/dist/cjs/resolver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/resolver.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/resolver.ts"],"names":[],"mappings":";;AAuDA,0BA2EC;AAlID,qCAAiC;AA0BjC,SAAS,YAAY,CACnB,UAAwC,EACxC,IAAkB;IAElB,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAA;IAEtC,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAEnC,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC;YAC3B,OAAO,eAAe,UAAU,CAAC,EAAE,iDAAiD,CAAA;QACtF,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC;YAC3B,OAAO,eAAe,UAAU,CAAC,EAAE,4CAA4C,CAAA;QACjF,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,OAAO,eAAe,UAAU,CAAC,EAAE,wCAAwC,IAAI,CAAC,IAAI,IAAI,MAAM,GAAG,CAAA;QACnG,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAGM,KAAK,UAAU,OAAO,CAC3B,WAAwB,EACxB,SAAkC,EAAE,EACpC,UAA0B,EAAE;IAE5B,MAAM,EAAE,UAAU,EAAE,GAAG,WAAW,CAAA;IAElC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,eAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAA;QAC1D,OAAO;YACL,OAAO,EAAE,KAAK;YACd,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,wCAAwC;SAChD,CAAA;IACH,CAAC;IAED,4EAA4E;IAC5E,MAAM,YAAY,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,IAAI,YAAY,EAAE,CAAC;QACjB,eAAM,CAAC,IAAI,CAAC,yBAAyB,YAAY,EAAE,CAAC,CAAA;QACpD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,YAAY;SACpB,CAAA;IACH,CAAC;IAED,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,cAAc,GAAG,EAAE,GAAG,MAAM,EAAE,CAAA;IACpC,IAAI,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC/B,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAA;gBAChD,eAAM,CAAC,KAAK,CAAC,2BAA2B,KAAK,CAAC,IAAI,QAAQ,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAA;IACpC,eAAM,CAAC,IAAI,CAAC,yBAAyB,UAAU,CAAC,EAAE,SAAS,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAA;IACpF,eAAM,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACjD,eAAM,CAAC,KAAK,CAAC,oBAAoB,OAAO,CAAC,OAAO,WAAW,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAE5E,IAAI,CAAC;QACH,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;YACtB,KAAK,KAAK;gBACR,OAAO,MAAM,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC,CAAA;YAE5D,KAAK,KAAK;gBACR,OAAO,UAAU,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;YAE7C,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,eAAM,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAA;gBACjE,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;oBAC/C,UAAU,CAAC,QAAQ,CAAC,GAAkB,EAAE,cAAc,EAAE,OAAO,CAAC;oBAChE,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAkB,EAAE,cAAc,CAAC,CAAC;iBACzE,CAAC,CAAA;gBACF,OAAO;oBACL,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,OAAO;oBAC/C,YAAY,EAAE,QAAQ;oBACtB,QAAQ,EAAE,SAAS,CAAC,QAAQ;oBAC5B,SAAS,EAAE,SAAS,CAAC,SAAS;oBAC9B,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK;iBAC1C,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,KAAK,CAAC,0BAA0B,UAAU,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAA;QAChE,OAAO;YACL,OAAO,EAAE,KAAK;YACd,YAAY,EAAE,QAAQ,CAAC,IAAI;YAC3B,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAA;IACH,CAAC;AACH,CAAC;AAGD,KAAK,UAAU,UAAU,CACvB,QAAiD,EACjD,MAA+B,EAC/B,OAAuB;IAEvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAK,OAAO,CAAC,OAAO,IAAK,CAAC,CAAA;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAA;IAE3C,MAAM,QAAQ,GAAoB,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACpE,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAC3D,MAAM;KACP,CAAC,CAAC,CAAA;IAEH,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAA;IAC7F,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAA;IACjD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ;YAC5C,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YAClC,KAAK,EAAE,+CAA+C;SACvD,CAAA;IACH,CAAC;IAED,4EAA4E;IAC5E,KAAK,UAAU,cAAc,CAAC,IAAmB;QAC/C,IAAI,OAAgB,CAAA;QACpB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;YACpD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;YACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAA;YAC7D,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;oBAClC,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;oBAC9B,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;wBAClD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;wBAC7B,CAAC,CAAC,SAAS;iBACd,CAAC,CAAA;gBACF,YAAY,CAAC,KAAK,CAAC,CAAA;gBACnB,OAAO,GAAG,CAAA;YACZ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,YAAY,CAAC,KAAK,CAAC,CAAA;gBACnB,OAAO,GAAG,GAAG,CAAA;gBACb,MAAM,SAAS,GAAG,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAA;gBACnE,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;oBACtB,eAAM,CAAC,IAAI,CAAC,2BAA2B,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,iBAAiB,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAA;gBAClH,CAAC;qBAAM,CAAC;oBACN,MAAM,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,2BAA2B,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;gBAC7E,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,OAAO,CAAA;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAEzE,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACjD,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;YACnC,OAAO;gBACL,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ;gBAC7C,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBAClC,KAAK,EAAE,uBAAuB,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,EAAE;aACnE,CAAA;QACH,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CACrC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;YAC7B,IAAI,IAAI,GAAY,SAAS,CAAA;YAC7B,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;gBAC7B,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;YAC5C,CAAC;YAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC;YACnC,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CAAA;QACrD,CAAC,CAAC,CACH,CAAA;QAED,eAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,CAAC,CAAA;QAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAA;IAE5G,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ;YAC7C,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YAClC,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAA;IACH,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,QAAiD,EACjD,MAA+B;IAE/B,IAAI,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAA;IACtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC9D,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAA;AACvE,CAAC;AAED,SAAS,QAAQ,CACf,OAAe,EACf,OAAe,EACf,MAA+B;IAE/B,IAAI,QAAQ,GAAG,OAAO,CAAA;IACtB,MAAM,MAAM,GAA4B,EAAE,CAAA;IAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YAClC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC5E,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAA;IACvD,MAAM,EAAE,GAAK,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,CAAC;SAChD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5E,IAAI,CAAC,GAAG,CAAC,CAAA;IAEZ,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACpC,CAAC"}
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -57,6 +57,8 @@ export interface MatchResult {
|
|
|
57
57
|
intent: 'navigation' | 'retrieval' | 'hybrid' | 'out_of_scope';
|
|
58
58
|
extractedParams: Record<string, string | null>;
|
|
59
59
|
reasoning: string;
|
|
60
|
+
/** All scored candidates — always present after match() */
|
|
61
|
+
candidates: MatchCandidate[];
|
|
60
62
|
}
|
|
61
63
|
export interface ApiCallResult {
|
|
62
64
|
method: string;
|
|
@@ -81,4 +83,28 @@ export interface ValidationResult {
|
|
|
81
83
|
errors: string[];
|
|
82
84
|
warnings: string[];
|
|
83
85
|
}
|
|
86
|
+
export interface MatchCandidate {
|
|
87
|
+
capabilityId: string;
|
|
88
|
+
score: number;
|
|
89
|
+
matched: boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface TraceStep {
|
|
92
|
+
type: 'cache_check' | 'keyword_match' | 'llm_match' | 'privacy_check' | 'resolve';
|
|
93
|
+
status: 'hit' | 'miss' | 'pass' | 'fail' | 'skip';
|
|
94
|
+
durationMs: number;
|
|
95
|
+
detail?: string;
|
|
96
|
+
}
|
|
97
|
+
export interface ExecutionTrace {
|
|
98
|
+
query: string;
|
|
99
|
+
/** All capabilities scored — not just the winner */
|
|
100
|
+
candidates: MatchCandidate[];
|
|
101
|
+
/** Why the winning capability was selected */
|
|
102
|
+
reasoning: string[];
|
|
103
|
+
/** Step-by-step execution breakdown */
|
|
104
|
+
steps: TraceStep[];
|
|
105
|
+
/** Which matcher was used */
|
|
106
|
+
resolvedVia: 'cache' | 'keyword' | 'llm';
|
|
107
|
+
/** Total duration */
|
|
108
|
+
totalMs: number;
|
|
109
|
+
}
|
|
84
110
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/cjs/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AACnD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAIpE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;IACvD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CACpC;AAID,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,CAAA;IACX,SAAS,EAAE,KAAK,CAAC;QACf,MAAM,EAAE,UAAU,CAAA;QAClB,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAClB,CAAC,CAAA;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IAC9B,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAA;AAIjE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAA;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAID,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,YAAY,CAAA;CACtB;AAID,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,UAAU,EAAE,CAAA;CAC3B;AAID,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,UAAU,EAAE,CAAA;CAC3B;AAID,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,cAAc,CAAA;IAC9D,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;IAC9C,SAAS,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.4.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cjs/version.js
CHANGED
package/dist/esm/cache.js
CHANGED
|
@@ -2,40 +2,57 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { logger } from './logger';
|
|
4
4
|
// ─── Normalize query for cache key ────────────────────────────────────────────
|
|
5
|
-
function normalizeQuery(query) {
|
|
5
|
+
export function normalizeQuery(query) {
|
|
6
6
|
return query.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Build a smarter cache key based on matched capability + extracted params.
|
|
10
|
+
* Two different queries that resolve to the same capability with the same params
|
|
11
|
+
* will share a cache entry — dramatically improving hit rate.
|
|
12
|
+
* Falls back to normalized query if no capability matched.
|
|
13
|
+
*/
|
|
14
|
+
export function buildCacheKey(query, capabilityId, extractedParams) {
|
|
15
|
+
if (!capabilityId)
|
|
16
|
+
return `query:${normalizeQuery(query)}`;
|
|
17
|
+
const paramStr = Object.entries(extractedParams)
|
|
18
|
+
.filter(([, v]) => v !== null)
|
|
19
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
20
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
21
|
+
.join('&');
|
|
22
|
+
return `cap:${capabilityId}${paramStr ? `:${paramStr}` : ''}`;
|
|
23
|
+
}
|
|
8
24
|
// ─── Memory Cache ─────────────────────────────────────────────────────────────
|
|
25
|
+
const MEMORY_CACHE_MAX = 512;
|
|
9
26
|
export class MemoryCache {
|
|
10
27
|
constructor() {
|
|
11
28
|
this.store = new Map();
|
|
12
29
|
}
|
|
13
|
-
async get(
|
|
14
|
-
const key = normalizeQuery(query);
|
|
30
|
+
async get(key) {
|
|
15
31
|
const entry = this.store.get(key);
|
|
16
32
|
if (entry) {
|
|
17
33
|
entry.hits++;
|
|
18
|
-
logger.debug(`Cache hit (memory): "${
|
|
34
|
+
logger.debug(`Cache hit (memory): "${key}"`);
|
|
19
35
|
return entry;
|
|
20
36
|
}
|
|
21
37
|
return null;
|
|
22
38
|
}
|
|
23
|
-
async set(
|
|
24
|
-
|
|
39
|
+
async set(key, result) {
|
|
40
|
+
if (this.store.size >= MEMORY_CACHE_MAX) {
|
|
41
|
+
const oldest = this.store.keys().next().value;
|
|
42
|
+
if (oldest !== undefined)
|
|
43
|
+
this.store.delete(oldest);
|
|
44
|
+
logger.debug(`Cache evicted oldest entry (max size ${MEMORY_CACHE_MAX} reached)`);
|
|
45
|
+
}
|
|
25
46
|
this.store.set(key, {
|
|
26
|
-
query,
|
|
47
|
+
query: key,
|
|
27
48
|
result,
|
|
28
49
|
cachedAt: new Date().toISOString(),
|
|
29
50
|
hits: 0,
|
|
30
51
|
});
|
|
31
|
-
logger.debug(`Cache set (memory): "${
|
|
32
|
-
}
|
|
33
|
-
async clear() {
|
|
34
|
-
this.store.clear();
|
|
35
|
-
}
|
|
36
|
-
async size() {
|
|
37
|
-
return this.store.size;
|
|
52
|
+
logger.debug(`Cache set (memory): "${key}"`);
|
|
38
53
|
}
|
|
54
|
+
async clear() { this.store.clear(); }
|
|
55
|
+
async size() { return this.store.size; }
|
|
39
56
|
}
|
|
40
57
|
// ─── File Cache ───────────────────────────────────────────────────────────────
|
|
41
58
|
export class FileCache {
|
|
@@ -43,63 +60,58 @@ export class FileCache {
|
|
|
43
60
|
this.store = new Map();
|
|
44
61
|
this.loaded = false;
|
|
45
62
|
this.filePath = path.resolve(process.cwd(), filePath);
|
|
63
|
+
logger.info(`FileCache initialized — writing to: ${this.filePath}`);
|
|
46
64
|
}
|
|
47
|
-
load() {
|
|
65
|
+
async load() {
|
|
48
66
|
if (this.loaded)
|
|
49
67
|
return;
|
|
50
68
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
logger.debug(`File cache loaded: ${this.store.size} entries`);
|
|
55
|
-
}
|
|
69
|
+
const raw = await fs.promises.readFile(this.filePath, 'utf-8');
|
|
70
|
+
this.store = new Map(Object.entries(JSON.parse(raw)));
|
|
71
|
+
logger.debug(`File cache loaded: ${this.store.size} entries`);
|
|
56
72
|
}
|
|
57
73
|
catch {
|
|
58
|
-
|
|
74
|
+
// File doesn't exist yet — start fresh
|
|
59
75
|
}
|
|
60
76
|
this.loaded = true;
|
|
61
77
|
}
|
|
62
|
-
save() {
|
|
78
|
+
async save() {
|
|
63
79
|
try {
|
|
64
80
|
const dir = path.dirname(this.filePath);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const obj = Object.fromEntries(this.store);
|
|
68
|
-
fs.writeFileSync(this.filePath, JSON.stringify(obj, null, 2));
|
|
81
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
82
|
+
await fs.promises.writeFile(this.filePath, JSON.stringify(Object.fromEntries(this.store), null, 2));
|
|
69
83
|
}
|
|
70
84
|
catch {
|
|
71
85
|
logger.warn(`Failed to save file cache to ${this.filePath}`);
|
|
72
86
|
}
|
|
73
87
|
}
|
|
74
|
-
async get(
|
|
75
|
-
this.load();
|
|
76
|
-
const key = normalizeQuery(query);
|
|
88
|
+
async get(key) {
|
|
89
|
+
await this.load();
|
|
77
90
|
const entry = this.store.get(key);
|
|
78
91
|
if (entry) {
|
|
79
92
|
entry.hits++;
|
|
80
|
-
logger.debug(`Cache hit (file): "${
|
|
93
|
+
logger.debug(`Cache hit (file): "${key}"`);
|
|
81
94
|
return entry;
|
|
82
95
|
}
|
|
83
96
|
return null;
|
|
84
97
|
}
|
|
85
|
-
async set(
|
|
86
|
-
this.load();
|
|
87
|
-
const key = normalizeQuery(query);
|
|
98
|
+
async set(key, result) {
|
|
99
|
+
await this.load();
|
|
88
100
|
this.store.set(key, {
|
|
89
|
-
query,
|
|
101
|
+
query: key,
|
|
90
102
|
result,
|
|
91
103
|
cachedAt: new Date().toISOString(),
|
|
92
104
|
hits: 0,
|
|
93
105
|
});
|
|
94
|
-
this.save();
|
|
95
|
-
logger.debug(`Cache set (file): "${
|
|
106
|
+
await this.save();
|
|
107
|
+
logger.debug(`Cache set (file): "${key}"`);
|
|
96
108
|
}
|
|
97
109
|
async clear() {
|
|
98
110
|
this.store.clear();
|
|
99
|
-
this.save();
|
|
111
|
+
await this.save();
|
|
100
112
|
}
|
|
101
113
|
async size() {
|
|
102
|
-
this.load();
|
|
114
|
+
await this.load();
|
|
103
115
|
return this.store.size;
|
|
104
116
|
}
|
|
105
117
|
}
|
|
@@ -109,24 +121,22 @@ export class ComboCache {
|
|
|
109
121
|
this.memory = new MemoryCache();
|
|
110
122
|
this.file = new FileCache(filePath);
|
|
111
123
|
}
|
|
112
|
-
async get(
|
|
113
|
-
|
|
114
|
-
const memHit = await this.memory.get(query);
|
|
124
|
+
async get(key) {
|
|
125
|
+
const memHit = await this.memory.get(key);
|
|
115
126
|
if (memHit)
|
|
116
127
|
return memHit;
|
|
117
|
-
|
|
118
|
-
const fileHit = await this.file.get(query);
|
|
128
|
+
const fileHit = await this.file.get(key);
|
|
119
129
|
if (fileHit) {
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
await this.memory.set(key, fileHit.result);
|
|
131
|
+
logger.debug(`Cache promoted to memory: "${key}"`);
|
|
122
132
|
return fileHit;
|
|
123
133
|
}
|
|
124
134
|
return null;
|
|
125
135
|
}
|
|
126
|
-
async set(
|
|
136
|
+
async set(key, result) {
|
|
127
137
|
await Promise.all([
|
|
128
|
-
this.memory.set(
|
|
129
|
-
this.file.set(
|
|
138
|
+
this.memory.set(key, result),
|
|
139
|
+
this.file.set(key, result),
|
|
130
140
|
]);
|
|
131
141
|
}
|
|
132
142
|
async clear() {
|
package/dist/esm/engine.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { match as _match, matchWithLLM as _matchWithLLM } from './matcher';
|
|
2
2
|
import { resolve as _resolve } from './resolver';
|
|
3
|
-
import {
|
|
4
|
-
import { FileLearningStore } from './learning';
|
|
3
|
+
import { MemoryLearningStore } from './learning';
|
|
5
4
|
import { logger } from './logger';
|
|
5
|
+
import { MemoryCache, normalizeQuery } from './cache';
|
|
6
6
|
// ─── CapmanEngine ─────────────────────────────────────────────────────────────
|
|
7
7
|
export class CapmanEngine {
|
|
8
8
|
constructor(options) {
|
|
@@ -13,14 +13,16 @@ export class CapmanEngine {
|
|
|
13
13
|
this.auth = options.auth;
|
|
14
14
|
this.headers = options.headers;
|
|
15
15
|
this.threshold = options.threshold ?? 50;
|
|
16
|
-
// Cache — default
|
|
16
|
+
// Cache — default MemoryCache (no filesystem writes), or disabled with false
|
|
17
|
+
// Use FileCache or ComboCache explicitly for persistence across restarts
|
|
17
18
|
this.cache = options.cache === false
|
|
18
19
|
? null
|
|
19
|
-
: (options.cache ?? new
|
|
20
|
-
// Learning — default
|
|
20
|
+
: (options.cache ?? new MemoryCache());
|
|
21
|
+
// Learning — default MemoryLearningStore (no filesystem writes), or disabled with false
|
|
22
|
+
// Use FileLearningStore explicitly for persistence across restarts
|
|
21
23
|
this.learning = options.learning === false
|
|
22
24
|
? null
|
|
23
|
-
: (options.learning ?? new
|
|
25
|
+
: (options.learning ?? new MemoryLearningStore());
|
|
24
26
|
logger.info(`CapmanEngine initialized — mode: ${this.mode}, cache: ${this.cache ? 'enabled' : 'disabled'}, learning: ${this.learning ? 'enabled' : 'disabled'}`);
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
@@ -36,68 +38,147 @@ export class CapmanEngine {
|
|
|
36
38
|
*/
|
|
37
39
|
async ask(query, overrides = {}) {
|
|
38
40
|
const start = Date.now();
|
|
41
|
+
const steps = [];
|
|
42
|
+
let resolvedVia = 'keyword';
|
|
39
43
|
// ── Step 1: Check cache ──────────────────────────────────────────────────
|
|
44
|
+
const cacheStart = Date.now();
|
|
40
45
|
if (this.cache) {
|
|
41
|
-
const
|
|
46
|
+
const queryKey = normalizeQuery(query);
|
|
47
|
+
const cached = await this.cache.get(queryKey);
|
|
42
48
|
if (cached) {
|
|
49
|
+
steps.push({ type: 'cache_check', status: 'hit', durationMs: Date.now() - cacheStart, detail: 'Served from cache' });
|
|
43
50
|
logger.info(`Cache hit for: "${query}"`);
|
|
44
51
|
const resolution = await _resolve(cached.result, cached.result.extractedParams, this.resolveOptions(overrides));
|
|
52
|
+
const trace = {
|
|
53
|
+
query,
|
|
54
|
+
candidates: cached.result.candidates ?? [],
|
|
55
|
+
reasoning: [`Served from cache (original: ${cached.result.reasoning})`],
|
|
56
|
+
steps,
|
|
57
|
+
resolvedVia: 'cache',
|
|
58
|
+
totalMs: Date.now() - start,
|
|
59
|
+
};
|
|
45
60
|
const result = {
|
|
46
61
|
match: cached.result,
|
|
47
62
|
resolution,
|
|
48
63
|
resolvedVia: 'cache',
|
|
49
64
|
durationMs: Date.now() - start,
|
|
65
|
+
trace,
|
|
50
66
|
};
|
|
51
67
|
await this.recordLearning(query, cached.result, 'cache');
|
|
52
68
|
return result;
|
|
53
69
|
}
|
|
70
|
+
steps.push({ type: 'cache_check', status: 'miss', durationMs: Date.now() - cacheStart });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
steps.push({ type: 'cache_check', status: 'skip', durationMs: 0, detail: 'Cache disabled' });
|
|
54
74
|
}
|
|
55
75
|
// ── Step 2: Match ────────────────────────────────────────────────────────
|
|
56
76
|
let matchResult;
|
|
57
|
-
let resolvedVia = 'keyword';
|
|
58
77
|
switch (this.mode) {
|
|
59
78
|
case 'cheap': {
|
|
79
|
+
const t = Date.now();
|
|
60
80
|
matchResult = _match(query, this.manifest);
|
|
81
|
+
steps.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t, detail: `confidence: ${matchResult.confidence}%` });
|
|
61
82
|
break;
|
|
62
83
|
}
|
|
63
84
|
case 'accurate': {
|
|
64
85
|
if (this.llm) {
|
|
86
|
+
const t = Date.now();
|
|
65
87
|
matchResult = await _matchWithLLM(query, this.manifest, { llm: this.llm });
|
|
66
88
|
resolvedVia = 'llm';
|
|
89
|
+
steps.push({ type: 'llm_match', status: 'pass', durationMs: Date.now() - t, detail: `confidence: ${matchResult.confidence}%` });
|
|
67
90
|
}
|
|
68
91
|
else {
|
|
69
92
|
logger.warn('accurate mode requires llm — falling back to keyword');
|
|
93
|
+
const t = Date.now();
|
|
70
94
|
matchResult = _match(query, this.manifest);
|
|
95
|
+
steps.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t, detail: 'llm not provided, used keyword' });
|
|
71
96
|
}
|
|
72
97
|
break;
|
|
73
98
|
}
|
|
74
99
|
case 'balanced':
|
|
75
100
|
default: {
|
|
101
|
+
const t1 = Date.now();
|
|
76
102
|
const keywordResult = _match(query, this.manifest);
|
|
103
|
+
steps.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t1, detail: `confidence: ${keywordResult.confidence}%` });
|
|
77
104
|
if (keywordResult.confidence >= this.threshold || !this.llm) {
|
|
78
105
|
matchResult = keywordResult;
|
|
79
106
|
}
|
|
80
107
|
else {
|
|
81
108
|
logger.info(`Low confidence (${keywordResult.confidence}%) — escalating to LLM`);
|
|
109
|
+
const t2 = Date.now();
|
|
82
110
|
matchResult = await _matchWithLLM(query, this.manifest, { llm: this.llm });
|
|
83
111
|
resolvedVia = 'llm';
|
|
112
|
+
steps.push({ type: 'llm_match', status: 'pass', durationMs: Date.now() - t2, detail: `confidence: ${matchResult.confidence}%` });
|
|
84
113
|
}
|
|
85
114
|
break;
|
|
86
115
|
}
|
|
87
116
|
}
|
|
88
|
-
// ── Step 3:
|
|
117
|
+
// ── Step 3: Privacy check ────────────────────────────────────────────────
|
|
118
|
+
if (matchResult.capability) {
|
|
119
|
+
const privacyLevel = matchResult.capability.privacy.level;
|
|
120
|
+
steps.push({
|
|
121
|
+
type: 'privacy_check',
|
|
122
|
+
status: 'pass',
|
|
123
|
+
durationMs: 0,
|
|
124
|
+
detail: `level: ${privacyLevel}`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// ── Step 4: Cache the match result ───────────────────────────────────────
|
|
89
128
|
if (this.cache && matchResult.capability) {
|
|
90
|
-
|
|
129
|
+
const queryKey = normalizeQuery(query);
|
|
130
|
+
await this.cache.set(queryKey, matchResult);
|
|
91
131
|
}
|
|
92
|
-
// ── Step
|
|
132
|
+
// ── Step 5: Resolve ──────────────────────────────────────────────────────
|
|
133
|
+
const resolveStart = Date.now();
|
|
93
134
|
const resolution = await _resolve(matchResult, matchResult.extractedParams, this.resolveOptions(overrides));
|
|
94
|
-
|
|
135
|
+
steps.push({
|
|
136
|
+
type: 'resolve',
|
|
137
|
+
status: resolution.success ? 'pass' : 'fail',
|
|
138
|
+
durationMs: Date.now() - resolveStart,
|
|
139
|
+
detail: resolution.error ?? `via ${resolution.resolverType}`,
|
|
140
|
+
});
|
|
141
|
+
// ── Step 6: Build reasoning array ────────────────────────────────────────
|
|
142
|
+
const reasoning = [];
|
|
143
|
+
if (matchResult.candidates?.length) {
|
|
144
|
+
const winner = matchResult.candidates.find(c => c.matched);
|
|
145
|
+
const rejected = matchResult.candidates
|
|
146
|
+
.filter(c => !c.matched && c.score > 0)
|
|
147
|
+
.sort((a, b) => b.score - a.score)
|
|
148
|
+
.slice(0, 3);
|
|
149
|
+
if (winner) {
|
|
150
|
+
reasoning.push(`Matched "${winner.capabilityId}" with ${winner.score}% confidence`);
|
|
151
|
+
}
|
|
152
|
+
if (rejected.length) {
|
|
153
|
+
reasoning.push(`Rejected: ${rejected.map(r => `${r.capabilityId} (${r.score}%)`).join(', ')}`);
|
|
154
|
+
}
|
|
155
|
+
reasoning.push(`Resolved via: ${resolvedVia}`);
|
|
156
|
+
if (matchResult.extractedParams && Object.keys(matchResult.extractedParams).length) {
|
|
157
|
+
const params = Object.entries(matchResult.extractedParams)
|
|
158
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
159
|
+
.join(', ');
|
|
160
|
+
reasoning.push(`Extracted params: ${params}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
reasoning.push(matchResult.reasoning);
|
|
165
|
+
}
|
|
166
|
+
// ── Step 7: Record learning ──────────────────────────────────────────────
|
|
95
167
|
await this.recordLearning(query, matchResult, resolvedVia);
|
|
168
|
+
const trace = {
|
|
169
|
+
query,
|
|
170
|
+
candidates: matchResult.candidates ?? [],
|
|
171
|
+
reasoning,
|
|
172
|
+
steps,
|
|
173
|
+
resolvedVia,
|
|
174
|
+
totalMs: Date.now() - start,
|
|
175
|
+
};
|
|
96
176
|
return {
|
|
97
177
|
match: matchResult,
|
|
98
178
|
resolution,
|
|
99
179
|
resolvedVia,
|
|
100
180
|
durationMs: Date.now() - start,
|
|
181
|
+
trace,
|
|
101
182
|
};
|
|
102
183
|
}
|
|
103
184
|
/**
|
package/dist/esm/generator.js
CHANGED
|
@@ -8,7 +8,7 @@ export function generate(config) {
|
|
|
8
8
|
version: VERSION,
|
|
9
9
|
app: config.app,
|
|
10
10
|
generatedAt: new Date().toISOString(),
|
|
11
|
-
capabilities: config.capabilities,
|
|
11
|
+
capabilities: config.capabilities.map(cap => ({ ...cap, params: [...cap.params] })),
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
export function loadConfig(configPath) {
|