capman 0.4.5 → 0.5.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/CHANGELOG.md +63 -0
- package/CODEBASE.md +94 -156
- package/README.md +23 -0
- package/bin/lib/cmd-generate.js +20 -3
- package/dist/cjs/cache.d.ts +2 -0
- package/dist/cjs/cache.d.ts.map +1 -1
- package/dist/cjs/cache.js +16 -3
- package/dist/cjs/cache.js.map +1 -1
- package/dist/cjs/engine.d.ts +26 -4
- package/dist/cjs/engine.d.ts.map +1 -1
- package/dist/cjs/engine.js +74 -80
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/learning.d.ts +14 -1
- package/dist/cjs/learning.d.ts.map +1 -1
- package/dist/cjs/learning.js +92 -8
- package/dist/cjs/learning.js.map +1 -1
- package/dist/cjs/matcher.d.ts +12 -0
- package/dist/cjs/matcher.d.ts.map +1 -1
- package/dist/cjs/matcher.js +41 -18
- package/dist/cjs/matcher.js.map +1 -1
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +14 -3
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/cache.d.ts +2 -0
- package/dist/esm/cache.js +16 -3
- package/dist/esm/engine.d.ts +26 -4
- package/dist/esm/engine.js +75 -81
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/learning.d.ts +14 -1
- package/dist/esm/learning.js +92 -8
- package/dist/esm/matcher.d.ts +12 -0
- package/dist/esm/matcher.js +37 -16
- package/dist/esm/resolver.js +14 -3
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,69 @@ All notable changes to capman are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.5.0] — 2026-04-15
|
|
8
|
+
### Added
|
|
9
|
+
- Learning index is now incremental — `record()` updates the index in O(w) per entry instead of rebuilding from scratch on every `getStats()` call. Eliminates O(n) CPU spike on every query under load.
|
|
10
|
+
- `getIndex()` method on both `FileLearningStore` and `MemoryLearningStore` — returns live keyword index directly in O(1)
|
|
11
|
+
- `extractParams` exported from public API — enables direct param extraction without going through `match()`
|
|
12
|
+
- `resolverToIntent()` exported from public API — converts a capability's resolver type to its intent string
|
|
13
|
+
- `STOPWORDS` exported from public API — same set used by matcher and learning index
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
**Security:**
|
|
18
|
+
- Auth bypass via cache key — non-public capabilities are no longer cached. Previously User A's cached match for a `user_owned` capability could be served to User B before privacy checks ran
|
|
19
|
+
- Arbitrary file write via CLI — `--out` and `--config-out` flags in `capman generate` now validated against working directory. Path traversal attempts exit with a clear error
|
|
20
|
+
- Prompt injection hardening — system instructions now come before user data in `matchWithLLM` prompt, with explicit `USER_QUERY_START/END` delimiters
|
|
21
|
+
|
|
22
|
+
**Learning system:**
|
|
23
|
+
- Boost feedback loop — learning now records the pre-boost match result, not the post-boost winner
|
|
24
|
+
- Boosted winner no longer gets empty `extractedParams` — params extracted directly via `extractParams()`
|
|
25
|
+
- Cache now stores post-boost result — previously cache/live path could return different capabilities
|
|
26
|
+
- OOS results can no longer be promoted via boost alone — boost skipped when all candidates score 0
|
|
27
|
+
- Tied boost scores now preserve original winner
|
|
28
|
+
- Learning index no longer includes stopwords — words like `"show"`, `"get"`, `"for"` were inflating unrelated scores
|
|
29
|
+
- Boost logic deduplicated — `ask()` and `explain()` share `applyBoostToMatchResult()`
|
|
30
|
+
|
|
31
|
+
**Security — logs:**
|
|
32
|
+
- PII no longer logged at debug level — param values and `auth.userId` redacted as `[REDACTED]`
|
|
33
|
+
|
|
34
|
+
**Cache:**
|
|
35
|
+
- `FileCache` now has a 2048-entry cap with oldest-first eviction — previously grew without bound
|
|
36
|
+
|
|
37
|
+
**Resolver:**
|
|
38
|
+
- Nav params validated against allowlist before substitution — prevents open redirect via encoded path separators
|
|
39
|
+
|
|
40
|
+
**Matcher:**
|
|
41
|
+
- `JSON.parse` failures no longer trigger the circuit breaker — prefixed `LLM_PARSE_ERROR` and treated separately from network failures
|
|
42
|
+
- Required param fallback rejects generic nouns — only accepts identifier-shaped last words
|
|
43
|
+
|
|
44
|
+
**Engine:**
|
|
45
|
+
- Version compatibility warning now uses `console.warn` — was using `logger.warn` suppressed by default `'silent'` log level
|
|
46
|
+
- Version warning wording softened — advisory not mandatory
|
|
47
|
+
- Concurrency limitation documented in `EngineOptions` JSDoc and README
|
|
48
|
+
|
|
49
|
+
### Tests
|
|
50
|
+
- 80 tests passing (up from 73)
|
|
51
|
+
- Boost tests now use `mode: 'balanced'` — previously used `mode: 'cheap'` making them vacuous
|
|
52
|
+
- Nav open redirect test added
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [0.4.5] — 2026-04-08
|
|
57
|
+
### Added
|
|
58
|
+
- Learning index wired into keyword matcher — boost up to +15 points for historically matched capabilities
|
|
59
|
+
- Manifest version compatibility check — warns when manifest `major.minor` differs from engine version
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
- Dead logger warning condition in `matchWithLLM` corrected
|
|
63
|
+
- Empty string `userId` no longer injected into session params
|
|
64
|
+
- `resolverToIntent()` exported and reused in engine
|
|
65
|
+
- Learning boost skipped in `cheap` mode
|
|
66
|
+
- Boost logic applied consistently in both `ask()` and `explain()`
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
7
70
|
## [0.4.4] — 2026-04-05
|
|
8
71
|
### Fixed
|
|
9
72
|
- Rate limit double-counting on LLM failure — `recordLLMFailure()` no longer increments `llmCallsThisMinute` (slot already reserved by `checkLLMAllowed()`)
|
package/CODEBASE.md
CHANGED
|
@@ -47,7 +47,7 @@ Key exports:
|
|
|
47
47
|
- `validateManifest(manifest)` — validates a `Manifest` at read time
|
|
48
48
|
|
|
49
49
|
Notable rules:
|
|
50
|
-
- `id` must match `/^[a-
|
|
50
|
+
- `id` must match `/^[a-z0-9_]+$/` — snake_case only
|
|
51
51
|
- `description` minimum 10 characters
|
|
52
52
|
- Capability IDs must be unique within a manifest
|
|
53
53
|
|
|
@@ -61,35 +61,38 @@ Key exports:
|
|
|
61
61
|
- `loadConfig(path?)` → `CapmanConfig` — loads `capman.config.js` via `require()`
|
|
62
62
|
- `writeManifest(manifest, path?)` — writes `manifest.json`
|
|
63
63
|
- `readManifest(path?)` → `Manifest` — reads and Zod-validates `manifest.json`
|
|
64
|
-
- `validate(manifest)` → `ValidationResult`
|
|
65
|
-
- `generateStarterConfig()` → `string`
|
|
64
|
+
- `validate(manifest)` → `ValidationResult`
|
|
65
|
+
- `generateStarterConfig()` → `string`
|
|
66
66
|
- `VERSION` — current version string, auto-generated by `scripts/version.js`
|
|
67
67
|
|
|
68
|
+
Note: `loadConfig()` uses `require()` internally — CJS config files only. ESM config files (`.mjs` or `"type": "module"`) are not supported. Full ESM config support is planned for v0.6.
|
|
69
|
+
|
|
68
70
|
---
|
|
69
71
|
|
|
70
72
|
### `src/matcher.ts`
|
|
71
73
|
Intent matching — keyword scoring and LLM-based matching.
|
|
72
74
|
|
|
73
75
|
Key exports:
|
|
74
|
-
- `match(query, manifest)` → `MatchResult`
|
|
75
|
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
- `matchWithLLM(query, manifest, { llm })` → `MatchResult`
|
|
80
|
-
- Sends structured prompt to LLM with query as JSON field (prompt injection safe)
|
|
81
|
-
- Returns matched capability, confidence, intent, extracted params
|
|
76
|
+
- `match(query, manifest)` → `MatchResult` — scores all capabilities, returns winner + all candidates
|
|
77
|
+
- `matchWithLLM(query, manifest, { llm })` → `MatchResult` — LLM-based matching
|
|
78
|
+
- Query passed as `JSON.stringify({ user_query })` with system instructions before user data
|
|
79
|
+
- `USER_QUERY_START/END` delimiters separate instructions from user-controlled content
|
|
80
|
+
- Parse failures throw `LLM_PARSE_ERROR:` prefixed errors — not counted as network failures
|
|
82
81
|
- Errors propagate to caller — no internal try/catch
|
|
82
|
+
- `extractParams(query, capability)` → `Record<string, string | null>` — direct param extraction
|
|
83
|
+
- `resolverToIntent(capability)` → intent string — converts resolver type to intent
|
|
84
|
+
- `STOPWORDS` — set of words filtered from scoring and learning index
|
|
83
85
|
|
|
84
86
|
Scoring algorithm (weights):
|
|
85
|
-
- Examples
|
|
87
|
+
- Examples: best single-example overlap score — up to 60 points (not accumulated across examples)
|
|
86
88
|
- Description match: up to 30 points
|
|
87
89
|
- Name match: up to 10 points
|
|
88
90
|
|
|
89
91
|
Param extraction:
|
|
90
92
|
- `isIdParam` — single token (e.g. `order_id=1234`)
|
|
91
93
|
- `isNavParam` — single token after nav keywords (`to`, `open`, `show`)
|
|
92
|
-
-
|
|
94
|
+
- Multi-word — joined with `-` (e.g. `product=blue-jacket`)
|
|
95
|
+
- Required param fallback — only accepts identifier-shaped last word (rejects generic nouns)
|
|
93
96
|
- Optional params stay `null` if no keyword match found
|
|
94
97
|
|
|
95
98
|
---
|
|
@@ -100,26 +103,23 @@ Capability execution — API calls, navigation, hybrid.
|
|
|
100
103
|
Key exports:
|
|
101
104
|
- `resolve(matchResult, params, options)` → `ResolveResult`
|
|
102
105
|
- Enforces privacy before executing
|
|
103
|
-
- Injects `auth.userId` into session params
|
|
106
|
+
- Injects `auth.userId` into session params (skipped if empty string or undefined)
|
|
104
107
|
- Supports `dryRun: true` — returns call plan without executing
|
|
105
108
|
- Retries with `AbortController` timeout on failure
|
|
106
|
-
-
|
|
107
|
-
- `null` and `undefined` params
|
|
108
|
-
- Nav param values
|
|
109
|
+
- Returns `status` and parsed `data` from API response
|
|
110
|
+
- `null` and `undefined` params never written into URLs
|
|
111
|
+
- Nav param values URL-encoded via `encodeURIComponent`
|
|
112
|
+
- Nav params validated against `[a-zA-Z0-9_-]` allowlist — rejects path separators
|
|
109
113
|
|
|
110
114
|
`ResolveOptions`:
|
|
111
|
-
- `baseUrl`
|
|
112
|
-
- `auth` — `{ isAuthenticated, role, userId }`
|
|
113
|
-
- `dryRun` — skip actual fetch
|
|
114
|
-
- `retries` — retry count on failure (default: 0)
|
|
115
|
-
- `timeoutMs` — abort timeout (default: 5000)
|
|
116
|
-
- `headers` — custom request headers
|
|
117
|
-
- `fetch` — injectable fetch function (used in tests)
|
|
115
|
+
- `baseUrl`, `auth`, `dryRun`, `retries`, `timeoutMs`, `headers`, `fetch`
|
|
118
116
|
|
|
119
117
|
Privacy enforcement:
|
|
120
118
|
- `public` — always allowed
|
|
121
119
|
- `user_owned` — requires `auth.isAuthenticated === true`
|
|
122
|
-
- `admin` — requires `auth.role === 'admin'`
|
|
120
|
+
- `admin` — requires `auth.isAuthenticated === true` AND `auth.role === 'admin'`
|
|
121
|
+
|
|
122
|
+
Debug logging: param values and `auth.userId` are redacted as `[REDACTED]` — never logged in plaintext.
|
|
123
123
|
|
|
124
124
|
---
|
|
125
125
|
|
|
@@ -129,22 +129,24 @@ Pluggable cache backends.
|
|
|
129
129
|
Key exports:
|
|
130
130
|
- `CacheStore` interface — `get(key)`, `set(key, result)`, `clear()`, `size()`
|
|
131
131
|
- `MemoryCache` — in-memory Map, 512-entry cap with oldest-first eviction
|
|
132
|
-
- `FileCache` — async `fs.promises` read/write,
|
|
133
|
-
- `ComboCache` — memory-first with file fallback
|
|
132
|
+
- `FileCache` — async `fs.promises` read/write, 2048-entry cap with oldest-first eviction
|
|
133
|
+
- `ComboCache` — memory-first with file fallback
|
|
134
134
|
- `normalizeQuery(query)` — lowercase + trim + collapse whitespace → cache key
|
|
135
|
-
- `buildCacheKey(query, capabilityId, params)` —
|
|
135
|
+
- `buildCacheKey(query, capabilityId, params)` — exported for future post-match cache layer (not currently used by engine)
|
|
136
|
+
|
|
137
|
+
Security: Only `public` capabilities are cached. Non-public (`user_owned`, `admin`) are never cached — prevents auth bypass where one user's cached match is served to another.
|
|
136
138
|
|
|
137
139
|
Notes:
|
|
138
140
|
- `FileCache` and `ComboCache` are single-instance only — concurrent writers will corrupt
|
|
139
|
-
- For multi-instance deployments, use a Redis adapter (planned v0.
|
|
141
|
+
- For multi-instance deployments, use a Redis adapter (planned v0.6)
|
|
140
142
|
|
|
141
143
|
---
|
|
142
144
|
|
|
143
145
|
### `src/learning.ts`
|
|
144
|
-
Usage analytics and keyword index.
|
|
146
|
+
Usage analytics and keyword index — now incremental.
|
|
145
147
|
|
|
146
148
|
Key exports:
|
|
147
|
-
- `LearningStore` interface — `record(entry)`, `getStats()`, `getTopCapabilities(limit)`
|
|
149
|
+
- `LearningStore` interface — `record(entry)`, `getStats()`, `getTopCapabilities(limit)`, `getIndex()`
|
|
148
150
|
- `FileLearningStore` — persists to `.capman/learning.json`, caps at 10,000 entries
|
|
149
151
|
- `MemoryLearningStore` — in-memory only, used in tests
|
|
150
152
|
|
|
@@ -154,9 +156,16 @@ Key exports:
|
|
|
154
156
|
- `timestamp`
|
|
155
157
|
|
|
156
158
|
`KeywordStats` (from `getStats()`):
|
|
157
|
-
- `index` — `{ word → { capabilityId → hitCount } }` —
|
|
159
|
+
- `index` — `{ word → { capabilityId → hitCount } }` — used by engine for learning boost
|
|
158
160
|
- `totalQueries`, `llmQueries`, `cacheHits`, `outOfScope`
|
|
159
161
|
|
|
162
|
+
Performance:
|
|
163
|
+
- Index is maintained incrementally in `record()` — O(w) per entry where w = meaningful words
|
|
164
|
+
- `getStats()` returns cached counters — O(1), no rebuild
|
|
165
|
+
- `getIndex()` returns live index — O(1)
|
|
166
|
+
- Full rebuild only on pruning (when entries exceed 10,000 cap)
|
|
167
|
+
- Stopwords filtered from index — same `STOPWORDS` set as `matcher.ts`
|
|
168
|
+
|
|
160
169
|
---
|
|
161
170
|
|
|
162
171
|
### `src/engine.ts`
|
|
@@ -167,27 +176,31 @@ Key exports:
|
|
|
167
176
|
- `EngineOptions` — all constructor options
|
|
168
177
|
- `EngineResult` — `{ match, resolution, resolvedVia, durationMs, trace }`
|
|
169
178
|
|
|
179
|
+
⚠️ **Concurrency:** `CapmanEngine` is not safe for sharing across concurrent async request handlers. The LLM rate limiter, circuit breaker, and learning index cache are instance-level mutable state. Create one engine per request in server deployments, or use `cheap` mode for shared instances.
|
|
180
|
+
|
|
170
181
|
`CapmanEngine` methods:
|
|
171
|
-
- `ask(query, overrides?)` → `EngineResult` — full pipeline: cache → match → resolve → learn
|
|
172
|
-
- `explain(query)` → `ExplainResult` — match only, no execution,
|
|
182
|
+
- `ask(query, overrides?)` → `EngineResult` — full pipeline: cache → match → boost → resolve → learn
|
|
183
|
+
- `explain(query)` → `ExplainResult` — match + boost only, no execution, no cache/learning write
|
|
173
184
|
- `getStats()` → `KeywordStats | null`
|
|
174
185
|
- `getTopCapabilities(limit?)` → `Array<{ id, hits }>`
|
|
175
186
|
- `clearCache()`
|
|
176
187
|
|
|
177
188
|
Matching pipeline in `ask()`:
|
|
178
|
-
1. Cache check — return immediately on hit
|
|
189
|
+
1. Cache check — return immediately on hit (public capabilities only)
|
|
179
190
|
2. Match — `cheap` / `balanced` / `accurate` mode
|
|
180
191
|
3. Privacy check — recorded in trace
|
|
181
|
-
4.
|
|
182
|
-
5.
|
|
183
|
-
6.
|
|
184
|
-
7.
|
|
192
|
+
4. Learning boost — up to +15 points for historically matched capabilities (skipped in `cheap` mode, skipped if all candidates score 0)
|
|
193
|
+
5. Cache set — stores post-boost result under normalized query key (public only)
|
|
194
|
+
6. Resolve — actual API call or nav
|
|
195
|
+
7. Reasoning build — human-readable array
|
|
196
|
+
8. Learning record — pre-boost result recorded to prevent feedback loop
|
|
185
197
|
|
|
186
198
|
LLM rate limiting (all modes respect these):
|
|
187
199
|
- `maxLLMCallsPerMinute` — sliding window (default: 60)
|
|
188
200
|
- `llmCooldownMs` — minimum gap between calls (default: 0)
|
|
189
201
|
- `llmCircuitBreakerThreshold` — failures before circuit opens (default: 3)
|
|
190
202
|
- `llmCircuitBreakerResetMs` — circuit reset time (default: 60,000ms)
|
|
203
|
+
- Parse failures (`LLM_PARSE_ERROR`) do NOT count toward circuit breaker — only network failures do
|
|
191
204
|
|
|
192
205
|
---
|
|
193
206
|
|
|
@@ -203,9 +216,7 @@ Key exports:
|
|
|
203
216
|
- Extracts path/query/body params
|
|
204
217
|
- Generates examples from operation summaries
|
|
205
218
|
|
|
206
|
-
`ParseResult`:
|
|
207
|
-
- `config` — ready-to-use `CapmanConfig`
|
|
208
|
-
- `stats` — `{ total, skipped, warnings }`
|
|
219
|
+
`ParseResult`: `{ config, stats: { total, skipped, warnings } }`
|
|
209
220
|
|
|
210
221
|
Supported: OpenAPI 3.x, Swagger 2.x (JSON or YAML)
|
|
211
222
|
|
|
@@ -217,7 +228,8 @@ Minimal logger. Silent by default.
|
|
|
217
228
|
Key exports:
|
|
218
229
|
- `logger` — singleton with `debug()`, `info()`, `warn()`, `error()`
|
|
219
230
|
- `setLogLevel(level)` — `'silent' | 'error' | 'warn' | 'info' | 'debug'`
|
|
220
|
-
|
|
231
|
+
|
|
232
|
+
Note: The manifest version compatibility warning uses `console.warn` directly (not `logger.warn`) so it is always visible regardless of log level.
|
|
221
233
|
|
|
222
234
|
---
|
|
223
235
|
|
|
@@ -225,134 +237,58 @@ Key exports:
|
|
|
225
237
|
Public API surface. Re-exports everything the library exposes.
|
|
226
238
|
|
|
227
239
|
Notable:
|
|
228
|
-
- `ask(query, manifest, options?)` — convenience function, delegates to `CapmanEngine`
|
|
240
|
+
- `ask(query, manifest, options?)` — convenience function, delegates to `CapmanEngine`
|
|
229
241
|
- Marked `@deprecated` — use `CapmanEngine` directly for full features
|
|
230
|
-
- `MatchMode`
|
|
231
|
-
-
|
|
242
|
+
- `MatchMode` — `'cheap' | 'balanced' | 'accurate'`
|
|
243
|
+
- `extractParams`, `resolverToIntent`, `STOPWORDS` — exported for advanced use cases
|
|
232
244
|
|
|
233
245
|
---
|
|
234
246
|
|
|
235
247
|
## bin/
|
|
236
248
|
|
|
237
249
|
### `bin/capman.js`
|
|
238
|
-
Entry point only.
|
|
239
|
-
~20 lines. No logic here.
|
|
250
|
+
Entry point only (~20 lines). Routes `command` to the correct module.
|
|
240
251
|
|
|
241
252
|
### `bin/lib/shared.js`
|
|
242
|
-
|
|
253
|
+
Exports: `args`, `command`, `flags`, `getFlag`, `c`, `log`, `header`, `requireSrc`
|
|
243
254
|
|
|
244
|
-
|
|
245
|
-
- `args`, `command`, `flags` — parsed from `process.argv`
|
|
246
|
-
- `getFlag(name)` — returns value of `--name value` flag
|
|
247
|
-
- `c` — ANSI color codes (`reset`, `bold`, `teal`, `yellow`, `red`, `green`, `gray`)
|
|
248
|
-
- `log` — `{ info, success, warn, error, blank }`
|
|
249
|
-
- `header()` — prints capman version header
|
|
250
|
-
- `requireSrc()` — loads `dist/cjs/index.js`, auto-builds if missing
|
|
251
|
-
|
|
252
|
-
### `bin/lib/cmd-init.js`
|
|
253
|
-
Creates `capman.config.js` starter file in current directory.
|
|
255
|
+
`getFlag(name)` — exits with error if flag is present but has no value (e.g. `--from` with no path).
|
|
254
256
|
|
|
255
257
|
### `bin/lib/cmd-generate.js`
|
|
256
|
-
Three generation paths:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
### `bin/lib/cmd-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
### `bin/lib/cmd-inspect.js`
|
|
269
|
-
Prints all capabilities in `manifest.json` — name, ID, resolver type, privacy, description, first example.
|
|
270
|
-
|
|
271
|
-
### `bin/lib/cmd-demo.js`
|
|
272
|
-
Live demo using a hardcoded e-commerce manifest.
|
|
273
|
-
Shows the full **QUERY → MATCH → EXECUTION → RESULT → EXPLANATION** blueprint for 4 sample queries.
|
|
274
|
-
No config or API key required.
|
|
275
|
-
|
|
276
|
-
### `bin/lib/cmd-run.js`
|
|
277
|
-
Runs a single query against the current `manifest.json`.
|
|
278
|
-
`--debug` flag shows all candidate scores.
|
|
279
|
-
|
|
280
|
-
### `bin/lib/cmd-explain.js`
|
|
281
|
-
Runs `engine.explain(query)` and prints the full explanation:
|
|
282
|
-
- What matched and why
|
|
283
|
-
- All candidates with per-candidate explanations
|
|
284
|
-
- What would execute (without executing)
|
|
285
|
-
- Whether privacy would block it
|
|
258
|
+
Three generation paths: `--from` (OpenAPI), `--ai` (LLM-assisted), manual.
|
|
259
|
+
Output paths validated via `safeOutputPath()` — rejects traversal outside working directory.
|
|
260
|
+
Contains `buildAIPrompt()` and `callLLM()`.
|
|
261
|
+
|
|
262
|
+
### `bin/lib/cmd-init.js` — creates `capman.config.js`
|
|
263
|
+
### `bin/lib/cmd-validate.js` — validates `manifest.json`
|
|
264
|
+
### `bin/lib/cmd-inspect.js` — prints all capabilities
|
|
265
|
+
### `bin/lib/cmd-demo.js` — live demo with hardcoded e-commerce manifest
|
|
266
|
+
### `bin/lib/cmd-run.js` — runs a query, `--debug` shows all candidates
|
|
267
|
+
### `bin/lib/cmd-explain.js` — runs `engine.explain()` and prints full breakdown
|
|
268
|
+
### `bin/lib/cmd-help.js` — usage and command list
|
|
286
269
|
|
|
287
270
|
---
|
|
288
271
|
|
|
289
272
|
## tests/
|
|
290
273
|
|
|
291
|
-
### `tests/matcher.test.ts`
|
|
292
|
-
|
|
293
|
-
- Keyword scoring accuracy
|
|
294
|
-
- Out-of-scope detection
|
|
295
|
-
- Param extraction (single token, multi-word, ID params, nav params)
|
|
296
|
-
- `ask()` matching modes (cheap, balanced, accurate, default)
|
|
297
|
-
|
|
298
|
-
### `tests/resolver.test.ts`
|
|
299
|
-
18 tests covering:
|
|
300
|
-
- API resolver — dry run, path substitution, query string params
|
|
301
|
-
- Nav resolver — destination building
|
|
302
|
-
- Hybrid resolver — both API and nav
|
|
303
|
-
- No match — graceful failure
|
|
304
|
-
- Privacy enforcement — public, user_owned, admin, session injection
|
|
305
|
-
- Fetch error handling — network error, non-ok status, ok + data
|
|
306
|
-
- Retry behaviour — succeeds after N failures, exhausts retries
|
|
307
|
-
|
|
308
|
-
### `tests/engine.test.ts`
|
|
309
|
-
26 tests covering:
|
|
310
|
-
- Basic `ask()` — match and resolve
|
|
311
|
-
- Caching — cache hit, cache cleared, ComboCache promotion
|
|
312
|
-
- Learning — records queries, top capabilities, resolvedVia tracking
|
|
313
|
-
- Matching modes — cheap never calls LLM, accurate calls LLM
|
|
314
|
-
- Execution trace — candidates, reasoning, steps, cache hit trace
|
|
315
|
-
- `explain()` — matched, out of scope, candidates with explanations, wouldExecute, blocked, no side effects
|
|
316
|
-
- LLM rate limiting — rate limit, cooldown, circuit breaker, fallback on failure
|
|
317
|
-
|
|
318
|
-
### `tests/parser.test.ts`
|
|
319
|
-
9 tests covering:
|
|
320
|
-
- Capability extraction from spec
|
|
321
|
-
- Correct IDs from `operationId`
|
|
322
|
-
- Privacy inference from tags and security
|
|
323
|
-
- Path and query param extraction
|
|
324
|
-
- Request body field extraction
|
|
325
|
-
- Base URL extraction
|
|
326
|
-
- Skipping operations with insufficient info
|
|
327
|
-
- Error on missing file
|
|
328
|
-
|
|
329
|
-
---
|
|
274
|
+
### `tests/matcher.test.ts` — 16 tests
|
|
275
|
+
Keyword scoring, OOS detection, param extraction, LLM edge cases (hallucinated ID, undefined reasoning)
|
|
330
276
|
|
|
331
|
-
|
|
277
|
+
### `tests/resolver.test.ts` — 22 tests
|
|
278
|
+
API/nav/hybrid resolvers, privacy enforcement, session injection, null params, nav open redirect
|
|
332
279
|
|
|
333
|
-
### `
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
// Auto-generated by scripts/version.js — do not edit manually
|
|
337
|
-
export const VERSION = '0.4.2'
|
|
338
|
-
```
|
|
339
|
-
to `src/version.ts`. Runs automatically before every `pnpm run build`.
|
|
280
|
+
### `tests/engine.test.ts` — 33 tests
|
|
281
|
+
`ask()`, `explain()`, caching, learning, matching modes, trace, rate limiting, manifest version check, learning boost
|
|
340
282
|
|
|
341
|
-
`
|
|
283
|
+
### `tests/parser.test.ts` — 9 tests
|
|
284
|
+
OpenAPI capability extraction, privacy inference, param extraction, base URL, error handling
|
|
342
285
|
|
|
343
286
|
---
|
|
344
287
|
|
|
345
|
-
##
|
|
346
|
-
|
|
347
|
-
### `test/conduit.config.js`
|
|
348
|
-
capman config for the RealWorld Conduit app (`conduit.productionready.io`).
|
|
349
|
-
8 capabilities covering all resolver types.
|
|
350
|
-
|
|
351
|
-
### `test/test-conduit.ts`
|
|
352
|
-
Tests keyword matcher against live Conduit API.
|
|
288
|
+
## scripts/
|
|
353
289
|
|
|
354
|
-
### `
|
|
355
|
-
|
|
290
|
+
### `scripts/version.js`
|
|
291
|
+
Prebuild script. Reads `version` from `package.json` and writes `src/version.ts`. Runs automatically before every build.
|
|
356
292
|
|
|
357
293
|
---
|
|
358
294
|
|
|
@@ -360,13 +296,13 @@ Tests `matchWithLLM` and `CapmanEngine` accurate mode against live API using Ope
|
|
|
360
296
|
|
|
361
297
|
| File | Purpose |
|
|
362
298
|
|---|---|
|
|
363
|
-
| `tsconfig.json` | CJS build → `dist/cjs/` |
|
|
364
|
-
| `tsconfig.esm.json` | ESM build → `dist/esm/` |
|
|
299
|
+
| `tsconfig.json` | CJS build → `dist/cjs/` with `.d.ts` |
|
|
300
|
+
| `tsconfig.esm.json` | ESM build → `dist/esm/` with `.d.ts` |
|
|
365
301
|
| `package.json` | Version, exports map, scripts, dependencies |
|
|
366
|
-
| `.github/workflows/ci.yml` | Build + test + verify dist on every push |
|
|
367
|
-
| `.gitignore` | Ignores `dist/`, `node_modules/`, `src/version.ts`, `.capman/` |
|
|
302
|
+
| `.github/workflows/ci.yml` | Build + test + verify both dist outputs on every push |
|
|
368
303
|
| `CHANGELOG.md` | All notable changes per version |
|
|
369
304
|
| `CODEBASE.md` | This file |
|
|
305
|
+
| `ROADMAP_v0.5.0.md` | Prioritized fix and feature roadmap |
|
|
370
306
|
|
|
371
307
|
---
|
|
372
308
|
|
|
@@ -381,13 +317,15 @@ Developer writes capman.config.js
|
|
|
381
317
|
↓
|
|
382
318
|
CapmanEngine.ask("user query")
|
|
383
319
|
↓
|
|
384
|
-
cache.ts
|
|
320
|
+
cache.ts → cache hit? return immediately (public only)
|
|
321
|
+
↓
|
|
322
|
+
matcher.ts → score all capabilities → pick winner
|
|
385
323
|
↓
|
|
386
|
-
|
|
324
|
+
learning.ts → apply boost (+0 to +15) based on keyword history
|
|
387
325
|
↓
|
|
388
|
-
resolver.ts
|
|
326
|
+
resolver.ts → enforce privacy → call API or navigate
|
|
389
327
|
↓
|
|
390
|
-
learning.ts
|
|
328
|
+
learning.ts → record pre-boost match result
|
|
391
329
|
↓
|
|
392
|
-
EngineResult
|
|
330
|
+
EngineResult → { match, resolution, trace, resolvedVia }
|
|
393
331
|
```
|
package/README.md
CHANGED
|
@@ -66,6 +66,29 @@ console.log(result.resolvedVia) // 'keyword' | 'llm' | 'cache'
|
|
|
66
66
|
console.log(result.trace.reasoning) // ['Matched "check_product_availability" with 100% confidence', ...]
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
|
|
70
|
+
### ⚠️ Concurrency Warning
|
|
71
|
+
|
|
72
|
+
`CapmanEngine` is **not safe** for sharing across concurrent async request handlers. The LLM rate limiter, circuit breaker, and learning index cache are instance-level mutable state.
|
|
73
|
+
|
|
74
|
+
In a Node.js server, create one engine per request:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// ✅ Safe
|
|
78
|
+
app.post('/ask', async (req, res) => {
|
|
79
|
+
const engine = new CapmanEngine({ manifest, llm })
|
|
80
|
+
const result = await engine.ask(req.body.query)
|
|
81
|
+
res.json(result)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// ❌ Unsafe under concurrent load
|
|
85
|
+
const engine = new CapmanEngine({ manifest, llm })
|
|
86
|
+
app.post('/ask', async (req, res) => {
|
|
87
|
+
const result = await engine.ask(req.body.query) // race condition
|
|
88
|
+
res.json(result)
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
69
92
|
**3. See it live**
|
|
70
93
|
|
|
71
94
|
```bash
|
package/bin/lib/cmd-generate.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const path = require('path')
|
|
3
4
|
const fs = require('fs')
|
|
4
5
|
const { header, log, c, flags, getFlag, requireSrc } = require('./shared')
|
|
6
|
+
// ─── Path safety guard ────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
function safeOutputPath(rawPath, cwd) {
|
|
9
|
+
const resolved = path.resolve(cwd, rawPath)
|
|
10
|
+
if (!resolved.startsWith(cwd + path.sep) && resolved !== cwd) {
|
|
11
|
+
log.error(`Output path "${rawPath}" resolves outside the working directory.`)
|
|
12
|
+
console.log(` Resolved: ${resolved}`)
|
|
13
|
+
console.log(` Allowed: ${cwd}`)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
return resolved
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
5
20
|
|
|
6
21
|
// ─── AI prompt builder ────────────────────────────────────────────────────────
|
|
7
22
|
|
|
@@ -120,11 +135,13 @@ module.exports = async function cmdGenerate() {
|
|
|
120
135
|
header()
|
|
121
136
|
const { generate, loadConfig, writeManifest, validate, parseOpenAPI } = requireSrc()
|
|
122
137
|
|
|
123
|
-
|
|
138
|
+
const fromFlag = getFlag('--from')
|
|
124
139
|
const aiFlag = flags.includes('--ai')
|
|
125
|
-
const
|
|
126
|
-
const
|
|
140
|
+
const cwd = process.cwd()
|
|
141
|
+
const outPath = safeOutputPath(getFlag('--out') ?? 'manifest.json', cwd)
|
|
142
|
+
const configOut = safeOutputPath(getFlag('--config-out') ?? 'capman.config.js', cwd)
|
|
127
143
|
|
|
144
|
+
|
|
128
145
|
// ── Path 1: OpenAPI parser ───────────────────────────────────────────────
|
|
129
146
|
if (fromFlag) {
|
|
130
147
|
log.info(`Parsing OpenAPI spec: ${fromFlag}`)
|
package/dist/cjs/cache.d.ts
CHANGED
|
@@ -30,9 +30,11 @@ export declare class FileCache implements CacheStore {
|
|
|
30
30
|
private filePath;
|
|
31
31
|
private store;
|
|
32
32
|
private loaded;
|
|
33
|
+
private saveQueue;
|
|
33
34
|
constructor(filePath?: string);
|
|
34
35
|
private load;
|
|
35
36
|
private save;
|
|
37
|
+
private _doSave;
|
|
36
38
|
get(key: string): Promise<CacheEntry | null>;
|
|
37
39
|
set(key: string, result: MatchResult): Promise<void>;
|
|
38
40
|
clear(): Promise<void>;
|
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,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;
|
|
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;AAOD,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;AAMD,qBAAa,SAAU,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAyC;IACtD,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,SAAS,CAA6C;gBAElD,QAAQ,SAAuB;YAK7B,IAAI;IAiBlB,OAAO,CAAC,IAAI;YAKE,OAAO;IAaf,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;IAmBpD,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
|
@@ -94,10 +94,12 @@ class MemoryCache {
|
|
|
94
94
|
}
|
|
95
95
|
exports.MemoryCache = MemoryCache;
|
|
96
96
|
// ─── File Cache ───────────────────────────────────────────────────────────────
|
|
97
|
+
const FILE_CACHE_MAX = 2048;
|
|
97
98
|
class FileCache {
|
|
98
99
|
constructor(filePath = '.capman/cache.json') {
|
|
99
100
|
this.store = new Map();
|
|
100
101
|
this.loaded = false;
|
|
102
|
+
this.saveQueue = Promise.resolve();
|
|
101
103
|
this.filePath = path.resolve(process.cwd(), filePath);
|
|
102
104
|
logger_1.logger.info(`FileCache initialized — writing to: ${this.filePath}`);
|
|
103
105
|
}
|
|
@@ -120,14 +122,18 @@ class FileCache {
|
|
|
120
122
|
}
|
|
121
123
|
this.loaded = true;
|
|
122
124
|
}
|
|
123
|
-
|
|
125
|
+
save() {
|
|
126
|
+
this.saveQueue = this.saveQueue.then(() => this._doSave());
|
|
127
|
+
return this.saveQueue;
|
|
128
|
+
}
|
|
129
|
+
async _doSave() {
|
|
124
130
|
try {
|
|
125
131
|
const dir = path.dirname(this.filePath);
|
|
126
132
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
127
133
|
await fs.promises.writeFile(this.filePath, JSON.stringify(Object.fromEntries(this.store), null, 2));
|
|
128
134
|
}
|
|
129
|
-
catch {
|
|
130
|
-
logger_1.logger.warn(`Failed to save file cache to ${this.filePath}`);
|
|
135
|
+
catch (err) {
|
|
136
|
+
logger_1.logger.warn(`Failed to save file cache to ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
139
|
async get(key) {
|
|
@@ -142,6 +148,13 @@ class FileCache {
|
|
|
142
148
|
}
|
|
143
149
|
async set(key, result) {
|
|
144
150
|
await this.load();
|
|
151
|
+
if (this.store.size >= FILE_CACHE_MAX) {
|
|
152
|
+
const oldest = this.store.keys().next().value;
|
|
153
|
+
if (oldest !== undefined) {
|
|
154
|
+
this.store.delete(oldest);
|
|
155
|
+
logger_1.logger.debug(`File cache evicted oldest entry (max size ${FILE_CACHE_MAX} reached)`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
145
158
|
this.store.set(key, {
|
|
146
159
|
query: key,
|
|
147
160
|
result,
|
package/dist/cjs/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,wCAEC;AASD,sCAYC;AAhDD,uCAAwB;AACxB,2CAA4B;AAE5B,qCAAiC;AAoBjC,iFAAiF;AAEjF,SAAgB,cAAc,CAAC,KAAa;IAC1C,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AACxD,CAAC;AAED;;;;;GAKG;AAEH,SAAgB,aAAa,CAC3B,KAAa,EACb,YAA2B,EAC3B,eAA8C;IAE9C,IAAI,CAAC,YAAY;QAAE,OAAO,SAAS,cAAc,CAAC,KAAK,CAAC,EAAE,CAAA;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC;SAC7C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;SAC7B,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;SAC5B,IAAI,CAAC,GAAG,CAAC,CAAA;IACZ,OAAO,OAAO,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;AAC/D,CAAC;AAED,iFAAiF;AAEjF,MAAM,gBAAgB,GAAG,GAAG,CAAA;
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
|