capman 0.4.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 +0 -0
- package/dist/cjs/cache.d.ts +16 -8
- package/dist/cjs/cache.d.ts.map +1 -1
- package/dist/cjs/cache.js +45 -31
- package/dist/cjs/cache.js.map +1 -1
- package/dist/cjs/engine.d.ts +2 -2
- package/dist/cjs/engine.d.ts.map +1 -1
- package/dist/cjs/engine.js +5 -3
- 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 +10 -3
- 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 +10 -0
- package/dist/cjs/learning.js.map +1 -1
- package/dist/cjs/matcher.d.ts.map +1 -1
- package/dist/cjs/matcher.js +18 -9
- package/dist/cjs/matcher.js.map +1 -1
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +31 -25
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/types.d.ts +2 -2
- 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 +44 -32
- package/dist/esm/engine.js +5 -3
- package/dist/esm/generator.js +1 -1
- package/dist/esm/index.js +17 -36
- package/dist/esm/learning.js +10 -0
- package/dist/esm/matcher.js +18 -9
- package/dist/esm/resolver.js +31 -25
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,55 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# capman
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Capability Manifest Engine** — let AI agents interact with your app reliably and explainably.
|
|
4
4
|
|
|
5
|
-
Instead of an AI blindly clicking through screens
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Instead of an AI blindly clicking through screens, capman gives it a structured map of what your app can do — and shows you exactly why it made every decision.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install capman
|
|
9
|
+
```
|
|
8
10
|
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## The Problem
|
|
12
14
|
|
|
13
|
-
When an AI agent
|
|
14
|
-
today it navigates your entire app like a tourist with no map:
|
|
15
|
+
When an AI agent answers *"are there seats available Friday?"*, today it navigates your app like a tourist with no map:
|
|
15
16
|
|
|
16
17
|
```
|
|
17
|
-
AI clicks → Home → Explore → Events → Category →
|
|
18
|
+
AI clicks → Home → Explore → Events → Category → Availability
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Slow. Wasteful. Touches screens it shouldn't.
|
|
21
22
|
|
|
22
23
|
## The Solution
|
|
23
24
|
|
|
24
|
-
Your app publishes a **capability manifest** — a machine-readable list of
|
|
25
|
-
everything it can do, what API to call, and what data scope is allowed.
|
|
26
|
-
|
|
27
|
-
The AI reads the manifest and goes directly to the answer.
|
|
25
|
+
Your app publishes a **capability manifest** — a machine-readable list of everything it can do, what API to call, and what data is allowed. The AI reads the manifest and goes directly to the answer.
|
|
28
26
|
|
|
29
27
|
```
|
|
30
|
-
User query → match capability → resolve via API or nav →
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Install
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npm install capman
|
|
28
|
+
User query → match capability → resolve via API or nav → structured result
|
|
39
29
|
```
|
|
40
30
|
|
|
41
31
|
---
|
|
42
32
|
|
|
43
33
|
## Quick Start
|
|
44
34
|
|
|
45
|
-
**1. Create
|
|
35
|
+
**1. Create your manifest config**
|
|
46
36
|
|
|
47
37
|
```bash
|
|
48
38
|
npx capman init
|
|
49
39
|
```
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
your app's capabilities.
|
|
41
|
+
Edit `capman.config.js` to define your app's capabilities.
|
|
53
42
|
|
|
54
43
|
**2. Generate the manifest**
|
|
55
44
|
|
|
@@ -57,122 +46,248 @@ your app's capabilities.
|
|
|
57
46
|
npx capman generate
|
|
58
47
|
```
|
|
59
48
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
**3. Use the SDK in your AI agent**
|
|
49
|
+
**3. Use the engine in your AI agent**
|
|
63
50
|
|
|
64
51
|
```typescript
|
|
65
|
-
import {
|
|
52
|
+
import { CapmanEngine, readManifest } from 'capman'
|
|
66
53
|
|
|
67
54
|
const manifest = readManifest()
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// Resolve it
|
|
73
|
-
const result = await resolve(matchResult, {}, {
|
|
74
|
-
baseUrl: 'https://api.your-app.com'
|
|
56
|
+
const engine = new CapmanEngine({
|
|
57
|
+
manifest,
|
|
58
|
+
baseUrl: 'https://api.your-app.com',
|
|
75
59
|
})
|
|
76
60
|
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
const result = await engine.ask('Check availability for blue jacket')
|
|
62
|
+
|
|
63
|
+
console.log(result.match.capability?.id) // 'check_product_availability'
|
|
64
|
+
console.log(result.resolution.apiCalls) // [{ method: 'GET', url: '...' }]
|
|
65
|
+
console.log(result.resolvedVia) // 'keyword' | 'llm' | 'cache'
|
|
66
|
+
console.log(result.trace.reasoning) // ['Matched "check_product_availability" with 100% confidence', ...]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**4. See it live**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx capman demo
|
|
79
73
|
```
|
|
80
74
|
|
|
81
75
|
---
|
|
82
76
|
|
|
83
|
-
##
|
|
77
|
+
## Execution Trace
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
Every `engine.ask()` call returns a full execution trace — so you always know why the AI did what it did.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const result = await engine.ask('Check availability for blue jacket')
|
|
83
|
+
|
|
84
|
+
console.log(result.trace)
|
|
85
|
+
// {
|
|
86
|
+
// query: 'Check availability for blue jacket',
|
|
87
|
+
// candidates: [
|
|
88
|
+
// { capabilityId: 'check_product_availability', score: 100, matched: true },
|
|
89
|
+
// { capabilityId: 'get_order_status', score: 12, matched: false },
|
|
90
|
+
// { capabilityId: 'navigate_to_screen', score: 0, matched: false },
|
|
91
|
+
// ],
|
|
92
|
+
// reasoning: [
|
|
93
|
+
// 'Matched "check_product_availability" with 100% confidence',
|
|
94
|
+
// 'Rejected: get_order_status (12%)',
|
|
95
|
+
// 'Resolved via: keyword',
|
|
96
|
+
// 'Extracted params: product=blue-jacket',
|
|
97
|
+
// ],
|
|
98
|
+
// steps: [
|
|
99
|
+
// { type: 'cache_check', status: 'miss', durationMs: 0 },
|
|
100
|
+
// { type: 'keyword_match', status: 'pass', durationMs: 1, detail: 'confidence: 100%' },
|
|
101
|
+
// { type: 'privacy_check', status: 'pass', durationMs: 0, detail: 'level: public' },
|
|
102
|
+
// { type: 'resolve', status: 'pass', durationMs: 2, detail: 'via api' },
|
|
103
|
+
// ],
|
|
104
|
+
// resolvedVia: 'keyword',
|
|
105
|
+
// totalMs: 4,
|
|
106
|
+
// }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Debug any query from the CLI:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx capman run "check availability for blue jacket" --debug
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
✓ Matched: check_product_availability
|
|
117
|
+
Intent: retrieval
|
|
118
|
+
Confidence: 100%
|
|
119
|
+
Resolver: api
|
|
120
|
+
Params: product=blue-jacket
|
|
121
|
+
|
|
122
|
+
── All candidates:
|
|
123
|
+
✓ check_product_availability: 100%
|
|
124
|
+
○ get_order_status: 12%
|
|
125
|
+
○ navigate_to_screen: 0%
|
|
126
|
+
```
|
|
91
127
|
|
|
92
128
|
---
|
|
93
129
|
|
|
94
|
-
##
|
|
130
|
+
## Matching Modes
|
|
95
131
|
|
|
96
|
-
|
|
97
|
-
Matches a user query to the best capability using keyword scoring.
|
|
98
|
-
Returns a `MatchResult` with the capability, confidence score, and intent.
|
|
132
|
+
Control the cost/accuracy tradeoff with three matching modes:
|
|
99
133
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
134
|
+
```typescript
|
|
135
|
+
// cheap — keyword only, no LLM, free
|
|
136
|
+
const engine = new CapmanEngine({ manifest, mode: 'cheap' })
|
|
137
|
+
|
|
138
|
+
// balanced — keyword first, LLM fallback if confidence < 50% (default)
|
|
139
|
+
const engine = new CapmanEngine({ manifest, mode: 'balanced', llm: myLLM })
|
|
140
|
+
|
|
141
|
+
// accurate — LLM first, keyword fallback
|
|
142
|
+
const engine = new CapmanEngine({ manifest, mode: 'accurate', llm: myLLM })
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Pass any LLM function — works with Anthropic, OpenAI, or any model:
|
|
103
146
|
|
|
104
147
|
```typescript
|
|
105
|
-
|
|
148
|
+
import Anthropic from '@anthropic-ai/sdk'
|
|
149
|
+
const anthropic = new Anthropic()
|
|
150
|
+
|
|
151
|
+
const engine = new CapmanEngine({
|
|
152
|
+
manifest,
|
|
153
|
+
mode: 'balanced',
|
|
106
154
|
llm: async (prompt) => {
|
|
107
155
|
const res = await anthropic.messages.create({
|
|
108
156
|
model: 'claude-sonnet-4-20250514',
|
|
109
157
|
max_tokens: 500,
|
|
110
|
-
messages: [{ role: 'user', content: prompt }]
|
|
158
|
+
messages: [{ role: 'user', content: prompt }],
|
|
111
159
|
})
|
|
112
160
|
return res.content[0].text
|
|
113
|
-
}
|
|
161
|
+
},
|
|
114
162
|
})
|
|
115
163
|
```
|
|
116
164
|
|
|
117
|
-
|
|
118
|
-
Executes a matched capability via API call, navigation, or both.
|
|
165
|
+
---
|
|
119
166
|
|
|
120
|
-
|
|
121
|
-
Convenience function — match + resolve in one call.
|
|
167
|
+
## Caching + Learning
|
|
122
168
|
|
|
123
169
|
```typescript
|
|
124
|
-
|
|
170
|
+
import { CapmanEngine, FileCache, FileLearningStore } from 'capman'
|
|
171
|
+
|
|
172
|
+
const engine = new CapmanEngine({
|
|
173
|
+
manifest,
|
|
174
|
+
// Default: MemoryCache (fast, resets on restart)
|
|
175
|
+
// For persistence across restarts:
|
|
176
|
+
cache: new FileCache('.capman/cache.json'),
|
|
177
|
+
learning: new FileLearningStore('.capman/learning.json'),
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// After real usage, see what's happening
|
|
181
|
+
const stats = await engine.getStats()
|
|
182
|
+
console.log(stats)
|
|
183
|
+
// {
|
|
184
|
+
// totalQueries: 142,
|
|
185
|
+
// llmQueries: 18,
|
|
186
|
+
// cacheHits: 67,
|
|
187
|
+
// outOfScope: 3,
|
|
188
|
+
// index: { 'availability': { 'check_product_availability': 34 }, ... }
|
|
189
|
+
// }
|
|
190
|
+
|
|
191
|
+
const top = await engine.getTopCapabilities(3)
|
|
192
|
+
// [
|
|
193
|
+
// { id: 'check_product_availability', hits: 58 },
|
|
194
|
+
// { id: 'navigate_to_screen', hits: 41 },
|
|
195
|
+
// { id: 'get_order_status', hits: 28 },
|
|
196
|
+
// ]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Privacy + Auth
|
|
202
|
+
|
|
203
|
+
Privacy scope is enforced **per capability**, before resolution happens:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const engine = new CapmanEngine({
|
|
207
|
+
manifest,
|
|
208
|
+
baseUrl: 'https://api.your-app.com',
|
|
209
|
+
auth: {
|
|
210
|
+
isAuthenticated: true,
|
|
211
|
+
role: 'user',
|
|
212
|
+
userId: 'user-123', // auto-injected into session params
|
|
213
|
+
},
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// user_owned capabilities require auth — blocked without it
|
|
217
|
+
// admin capabilities require role: 'admin' — blocked for regular users
|
|
218
|
+
// session params like {user_id} are auto-replaced from auth.userId
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Resolver Hardening
|
|
224
|
+
|
|
225
|
+
Configure retries and timeouts per call:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const result = await engine.ask('show my orders', {
|
|
229
|
+
retries: 2, // retry failed requests (default: 0)
|
|
230
|
+
timeoutMs: 3000, // abort after 3 seconds (default: 5000)
|
|
231
|
+
})
|
|
125
232
|
```
|
|
126
233
|
|
|
127
234
|
---
|
|
128
235
|
|
|
129
236
|
## Capability Config
|
|
130
237
|
|
|
131
|
-
Each capability in
|
|
238
|
+
Each capability in `capman.config.js`:
|
|
132
239
|
|
|
133
240
|
```javascript
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
examples: [ // improves accuracy
|
|
139
|
-
'Show me resource details',
|
|
140
|
-
'Find resource by ID',
|
|
141
|
-
],
|
|
142
|
-
params: [
|
|
241
|
+
module.exports = {
|
|
242
|
+
app: 'your-app',
|
|
243
|
+
baseUrl: 'https://api.your-app.com',
|
|
244
|
+
capabilities: [
|
|
143
245
|
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
246
|
+
id: 'check_product_availability',
|
|
247
|
+
name: 'Check product availability',
|
|
248
|
+
description: 'Check stock and pricing for a product by name or ID.',
|
|
249
|
+
examples: [
|
|
250
|
+
'Is the blue jacket available?',
|
|
251
|
+
'Check availability for product 42',
|
|
252
|
+
'Do you have size M in stock?',
|
|
253
|
+
],
|
|
254
|
+
params: [
|
|
255
|
+
{
|
|
256
|
+
name: 'product',
|
|
257
|
+
description: 'Product name or ID',
|
|
258
|
+
required: true,
|
|
259
|
+
source: 'user_query', // extracted from the query
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
returns: ['stock', 'price', 'variants'],
|
|
263
|
+
resolver: {
|
|
264
|
+
type: 'api', // 'api' | 'nav' | 'hybrid'
|
|
265
|
+
endpoints: [
|
|
266
|
+
{ method: 'GET', path: '/products/{product}/availability' },
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
privacy: {
|
|
270
|
+
level: 'public', // 'public' | 'user_owned' | 'admin'
|
|
271
|
+
note: 'No auth required',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
149
274
|
],
|
|
150
|
-
returns: ['resource', 'metadata'],
|
|
151
|
-
resolver: {
|
|
152
|
-
type: 'api', // 'api', 'nav', or 'hybrid'
|
|
153
|
-
endpoints: [
|
|
154
|
-
{ method: 'GET', path: '/resources/{resource_id}' }
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
|
-
privacy: {
|
|
158
|
-
level: 'public', // 'public', 'user_owned', or 'admin'
|
|
159
|
-
note: 'No auth required'
|
|
160
|
-
}
|
|
161
275
|
}
|
|
162
276
|
```
|
|
163
277
|
|
|
164
278
|
---
|
|
165
279
|
|
|
166
|
-
##
|
|
280
|
+
## CLI Commands
|
|
167
281
|
|
|
168
|
-
|
|
|
282
|
+
| Command | What it does |
|
|
169
283
|
|---|---|
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
284
|
+
| `capman init` | Create a starter `capman.config.js` |
|
|
285
|
+
| `capman generate` | Generate `manifest.json` from config |
|
|
286
|
+
| `capman validate` | Validate your manifest for errors |
|
|
287
|
+
| `capman inspect` | Print all capabilities in the manifest |
|
|
288
|
+
| `capman run "query"` | Run a query against your manifest |
|
|
289
|
+
| `capman run "query" --debug` | Run with full candidate scoring |
|
|
290
|
+
| `capman demo` | Live demo with a sample app |
|
|
176
291
|
|
|
177
292
|
---
|
|
178
293
|
|
|
@@ -186,23 +301,45 @@ what each capability allows, before resolution happens.
|
|
|
186
301
|
|
|
187
302
|
---
|
|
188
303
|
|
|
304
|
+
## Privacy Scopes
|
|
305
|
+
|
|
306
|
+
| Level | Meaning |
|
|
307
|
+
|---|---|
|
|
308
|
+
| `public` | No auth required |
|
|
309
|
+
| `user_owned` | Requires auth, scoped to current user only |
|
|
310
|
+
| `admin` | Restricted to admin roles |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Param Sources
|
|
315
|
+
|
|
316
|
+
| Source | Meaning |
|
|
317
|
+
|---|---|
|
|
318
|
+
| `user_query` | Extracted from the user's query |
|
|
319
|
+
| `session` | Injected from `auth.userId` automatically |
|
|
320
|
+
| `context` | Provided by the caller |
|
|
321
|
+
| `static` | Fixed value, never changes |
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
189
325
|
## Honest Limits
|
|
190
326
|
|
|
191
327
|
**Works well:**
|
|
192
328
|
- Structured data retrieval via APIs
|
|
193
329
|
- Navigating to known app screens
|
|
194
330
|
- Multi-endpoint aggregation
|
|
195
|
-
- Privacy
|
|
196
|
-
-
|
|
331
|
+
- Privacy enforcement per capability
|
|
332
|
+
- Caching repeated queries
|
|
333
|
+
- Full execution tracing and debugging
|
|
197
334
|
|
|
198
335
|
**Current limits:**
|
|
199
336
|
- Real-time infra status (is the server down?)
|
|
200
337
|
- UI-only state with no API backing
|
|
201
|
-
-
|
|
202
|
-
-
|
|
338
|
+
- Very ambiguous queries — use `mode: 'accurate'` with an LLM
|
|
339
|
+
- Cross-app orchestration (planned)
|
|
203
340
|
|
|
204
341
|
---
|
|
205
342
|
|
|
206
343
|
## License
|
|
207
344
|
|
|
208
|
-
MIT
|
|
345
|
+
MIT — [github.com/Hobbydefiningdoctory/capman](https://github.com/Hobbydefiningdoctory/capman)
|
package/bin/capman.js
CHANGED
|
File without changes
|
package/dist/cjs/cache.d.ts
CHANGED
|
@@ -6,15 +6,23 @@ export interface CacheEntry {
|
|
|
6
6
|
hits: number;
|
|
7
7
|
}
|
|
8
8
|
export interface CacheStore {
|
|
9
|
-
get(
|
|
10
|
-
set(
|
|
9
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
10
|
+
set(key: string, result: MatchResult): Promise<void>;
|
|
11
11
|
clear(): Promise<void>;
|
|
12
12
|
size(): Promise<number>;
|
|
13
13
|
}
|
|
14
|
+
export declare function normalizeQuery(query: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Build a smarter cache key based on matched capability + extracted params.
|
|
17
|
+
* Two different queries that resolve to the same capability with the same params
|
|
18
|
+
* will share a cache entry — dramatically improving hit rate.
|
|
19
|
+
* Falls back to normalized query if no capability matched.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildCacheKey(query: string, capabilityId: string | null, extractedParams: Record<string, string | null>): string;
|
|
14
22
|
export declare class MemoryCache implements CacheStore {
|
|
15
23
|
private store;
|
|
16
|
-
get(
|
|
17
|
-
set(
|
|
24
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
25
|
+
set(key: string, result: MatchResult): Promise<void>;
|
|
18
26
|
clear(): Promise<void>;
|
|
19
27
|
size(): Promise<number>;
|
|
20
28
|
}
|
|
@@ -25,8 +33,8 @@ export declare class FileCache implements CacheStore {
|
|
|
25
33
|
constructor(filePath?: string);
|
|
26
34
|
private load;
|
|
27
35
|
private save;
|
|
28
|
-
get(
|
|
29
|
-
set(
|
|
36
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
37
|
+
set(key: string, result: MatchResult): Promise<void>;
|
|
30
38
|
clear(): Promise<void>;
|
|
31
39
|
size(): Promise<number>;
|
|
32
40
|
}
|
|
@@ -34,8 +42,8 @@ export declare class ComboCache implements CacheStore {
|
|
|
34
42
|
private memory;
|
|
35
43
|
private file;
|
|
36
44
|
constructor(filePath?: string);
|
|
37
|
-
get(
|
|
38
|
-
set(
|
|
45
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
46
|
+
set(key: string, result: MatchResult): Promise<void>;
|
|
39
47
|
clear(): Promise<void>;
|
|
40
48
|
size(): Promise<number>;
|
|
41
49
|
}
|
package/dist/cjs/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAID,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAID,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACxB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AAEH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAC7C,MAAM,CAQR;AAMD,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,KAAK,CAAgC;IAEvC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAU5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAC9B;AAID,qBAAa,SAAU,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,MAAM,CAAQ;gBAEV,QAAQ,SAAuB;YAK7B,IAAI;YAYJ,IAAI;IAaZ,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAW5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAI9B;AAID,qBAAa,UAAW,YAAW,UAAU;IAC3C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,IAAI,CAAW;gBAEX,QAAQ,SAAuB;IAKrC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAY5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG9B"}
|
package/dist/cjs/cache.js
CHANGED
|
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ComboCache = exports.FileCache = exports.MemoryCache = void 0;
|
|
37
|
+
exports.normalizeQuery = normalizeQuery;
|
|
38
|
+
exports.buildCacheKey = buildCacheKey;
|
|
37
39
|
const fs = __importStar(require("fs"));
|
|
38
40
|
const path = __importStar(require("path"));
|
|
39
41
|
const logger_1 = require("./logger");
|
|
@@ -41,37 +43,54 @@ const logger_1 = require("./logger");
|
|
|
41
43
|
function normalizeQuery(query) {
|
|
42
44
|
return query.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
43
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Build a smarter cache key based on matched capability + extracted params.
|
|
48
|
+
* Two different queries that resolve to the same capability with the same params
|
|
49
|
+
* will share a cache entry — dramatically improving hit rate.
|
|
50
|
+
* Falls back to normalized query if no capability matched.
|
|
51
|
+
*/
|
|
52
|
+
function buildCacheKey(query, capabilityId, extractedParams) {
|
|
53
|
+
if (!capabilityId)
|
|
54
|
+
return `query:${normalizeQuery(query)}`;
|
|
55
|
+
const paramStr = Object.entries(extractedParams)
|
|
56
|
+
.filter(([, v]) => v !== null)
|
|
57
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
58
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
59
|
+
.join('&');
|
|
60
|
+
return `cap:${capabilityId}${paramStr ? `:${paramStr}` : ''}`;
|
|
61
|
+
}
|
|
44
62
|
// ─── Memory Cache ─────────────────────────────────────────────────────────────
|
|
63
|
+
const MEMORY_CACHE_MAX = 512;
|
|
45
64
|
class MemoryCache {
|
|
46
65
|
constructor() {
|
|
47
66
|
this.store = new Map();
|
|
48
67
|
}
|
|
49
|
-
async get(
|
|
50
|
-
const key = normalizeQuery(query);
|
|
68
|
+
async get(key) {
|
|
51
69
|
const entry = this.store.get(key);
|
|
52
70
|
if (entry) {
|
|
53
71
|
entry.hits++;
|
|
54
|
-
logger_1.logger.debug(`Cache hit (memory): "${
|
|
72
|
+
logger_1.logger.debug(`Cache hit (memory): "${key}"`);
|
|
55
73
|
return entry;
|
|
56
74
|
}
|
|
57
75
|
return null;
|
|
58
76
|
}
|
|
59
|
-
async set(
|
|
60
|
-
|
|
77
|
+
async set(key, result) {
|
|
78
|
+
if (this.store.size >= MEMORY_CACHE_MAX) {
|
|
79
|
+
const oldest = this.store.keys().next().value;
|
|
80
|
+
if (oldest !== undefined)
|
|
81
|
+
this.store.delete(oldest);
|
|
82
|
+
logger_1.logger.debug(`Cache evicted oldest entry (max size ${MEMORY_CACHE_MAX} reached)`);
|
|
83
|
+
}
|
|
61
84
|
this.store.set(key, {
|
|
62
|
-
query,
|
|
85
|
+
query: key,
|
|
63
86
|
result,
|
|
64
87
|
cachedAt: new Date().toISOString(),
|
|
65
88
|
hits: 0,
|
|
66
89
|
});
|
|
67
|
-
logger_1.logger.debug(`Cache set (memory): "${
|
|
68
|
-
}
|
|
69
|
-
async clear() {
|
|
70
|
-
this.store.clear();
|
|
71
|
-
}
|
|
72
|
-
async size() {
|
|
73
|
-
return this.store.size;
|
|
90
|
+
logger_1.logger.debug(`Cache set (memory): "${key}"`);
|
|
74
91
|
}
|
|
92
|
+
async clear() { this.store.clear(); }
|
|
93
|
+
async size() { return this.store.size; }
|
|
75
94
|
}
|
|
76
95
|
exports.MemoryCache = MemoryCache;
|
|
77
96
|
// ─── File Cache ───────────────────────────────────────────────────────────────
|
|
@@ -105,28 +124,26 @@ class FileCache {
|
|
|
105
124
|
logger_1.logger.warn(`Failed to save file cache to ${this.filePath}`);
|
|
106
125
|
}
|
|
107
126
|
}
|
|
108
|
-
async get(
|
|
127
|
+
async get(key) {
|
|
109
128
|
await this.load();
|
|
110
|
-
const key = normalizeQuery(query);
|
|
111
129
|
const entry = this.store.get(key);
|
|
112
130
|
if (entry) {
|
|
113
131
|
entry.hits++;
|
|
114
|
-
logger_1.logger.debug(`Cache hit (file): "${
|
|
132
|
+
logger_1.logger.debug(`Cache hit (file): "${key}"`);
|
|
115
133
|
return entry;
|
|
116
134
|
}
|
|
117
135
|
return null;
|
|
118
136
|
}
|
|
119
|
-
async set(
|
|
137
|
+
async set(key, result) {
|
|
120
138
|
await this.load();
|
|
121
|
-
const key = normalizeQuery(query);
|
|
122
139
|
this.store.set(key, {
|
|
123
|
-
query,
|
|
140
|
+
query: key,
|
|
124
141
|
result,
|
|
125
142
|
cachedAt: new Date().toISOString(),
|
|
126
143
|
hits: 0,
|
|
127
144
|
});
|
|
128
145
|
await this.save();
|
|
129
|
-
logger_1.logger.debug(`Cache set (file): "${
|
|
146
|
+
logger_1.logger.debug(`Cache set (file): "${key}"`);
|
|
130
147
|
}
|
|
131
148
|
async clear() {
|
|
132
149
|
this.store.clear();
|
|
@@ -144,25 +161,22 @@ class ComboCache {
|
|
|
144
161
|
this.memory = new MemoryCache();
|
|
145
162
|
this.file = new FileCache(filePath);
|
|
146
163
|
}
|
|
147
|
-
async get(
|
|
148
|
-
|
|
149
|
-
const memHit = await this.memory.get(query);
|
|
164
|
+
async get(key) {
|
|
165
|
+
const memHit = await this.memory.get(key);
|
|
150
166
|
if (memHit)
|
|
151
167
|
return memHit;
|
|
152
|
-
|
|
153
|
-
const fileHit = await this.file.get(query);
|
|
168
|
+
const fileHit = await this.file.get(key);
|
|
154
169
|
if (fileHit) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
logger_1.logger.debug(`Cache promoted to memory: "${query}"`);
|
|
170
|
+
await this.memory.set(key, fileHit.result);
|
|
171
|
+
logger_1.logger.debug(`Cache promoted to memory: "${key}"`);
|
|
158
172
|
return fileHit;
|
|
159
173
|
}
|
|
160
174
|
return null;
|
|
161
175
|
}
|
|
162
|
-
async set(
|
|
176
|
+
async set(key, result) {
|
|
163
177
|
await Promise.all([
|
|
164
|
-
this.memory.set(
|
|
165
|
-
this.file.set(
|
|
178
|
+
this.memory.set(key, result),
|
|
179
|
+
this.file.set(key, result),
|
|
166
180
|
]);
|
|
167
181
|
}
|
|
168
182
|
async clear() {
|
package/dist/cjs/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
|