capman 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/bin/capman.js +221 -5
  2. package/dist/cjs/cache.d.ts +42 -0
  3. package/dist/cjs/cache.d.ts.map +1 -0
  4. package/dist/cjs/cache.js +179 -0
  5. package/dist/cjs/cache.js.map +1 -0
  6. package/dist/cjs/engine.d.ts +82 -0
  7. package/dist/cjs/engine.d.ts.map +1 -0
  8. package/dist/cjs/engine.js +233 -0
  9. package/dist/cjs/engine.js.map +1 -0
  10. package/dist/cjs/generator.js +2 -1
  11. package/dist/cjs/generator.js.map +1 -1
  12. package/dist/cjs/index.d.ts +7 -1
  13. package/dist/cjs/index.d.ts.map +1 -1
  14. package/dist/cjs/index.js +13 -1
  15. package/dist/cjs/index.js.map +1 -1
  16. package/dist/cjs/learning.d.ts +56 -0
  17. package/dist/cjs/learning.d.ts.map +1 -0
  18. package/dist/cjs/learning.js +155 -0
  19. package/dist/cjs/learning.js.map +1 -0
  20. package/dist/cjs/matcher.d.ts.map +1 -1
  21. package/dist/cjs/matcher.js +38 -5
  22. package/dist/cjs/matcher.js.map +1 -1
  23. package/dist/cjs/resolver.d.ts +4 -1
  24. package/dist/cjs/resolver.d.ts.map +1 -1
  25. package/dist/cjs/resolver.js +51 -21
  26. package/dist/cjs/resolver.js.map +1 -1
  27. package/dist/cjs/schema.d.ts +10 -10
  28. package/dist/cjs/types.d.ts +38 -5
  29. package/dist/cjs/types.d.ts.map +1 -1
  30. package/dist/cjs/version.d.ts +2 -0
  31. package/dist/cjs/version.d.ts.map +1 -0
  32. package/dist/cjs/version.js +6 -0
  33. package/dist/cjs/version.js.map +1 -0
  34. package/dist/esm/cache.js +139 -0
  35. package/dist/esm/engine.js +228 -0
  36. package/dist/esm/generator.js +2 -1
  37. package/dist/esm/index.js +6 -0
  38. package/dist/esm/learning.js +116 -0
  39. package/dist/esm/matcher.js +38 -5
  40. package/dist/esm/resolver.js +51 -21
  41. package/dist/esm/version.js +2 -0
  42. package/package.json +18 -12
@@ -87,48 +87,78 @@ export async function resolve(matchResult, params = {}, options = {}) {
87
87
  }
88
88
  }
89
89
  async function resolveApi(resolver, params, options) {
90
+ const startTime = Date.now();
91
+ const retries = options.retries ?? 0;
92
+ const timeoutMs = options.timeoutMs ?? 5000;
90
93
  const apiCalls = resolver.endpoints.map(endpoint => ({
91
94
  method: endpoint.method,
92
95
  url: buildUrl(options.baseUrl ?? '', endpoint.path, params),
93
96
  params,
94
97
  }));
95
98
  if (options.dryRun) {
96
- return { success: true, resolverType: 'api', apiCalls };
99
+ return { success: true, resolverType: 'api', apiCalls, durationMs: Date.now() - startTime };
97
100
  }
98
101
  const fetchFn = options.fetch ?? globalThis.fetch;
99
102
  if (!fetchFn) {
100
103
  return {
101
- success: true,
102
- resolverType: 'api',
103
- apiCalls,
104
+ success: true, resolverType: 'api', apiCalls,
105
+ durationMs: Date.now() - startTime,
104
106
  error: 'No fetch available — returning call plan only',
105
107
  };
106
108
  }
109
+ // ── Fetch with retry + timeout ────────────────────────────────────────────
110
+ async function fetchWithRetry(call, attempt) {
111
+ const controller = new AbortController();
112
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
113
+ try {
114
+ const res = await fetchFn(call.url, {
115
+ method: call.method,
116
+ headers: options.headers ?? {},
117
+ signal: controller.signal,
118
+ body: ['POST', 'PUT', 'PATCH'].includes(call.method)
119
+ ? JSON.stringify(call.params)
120
+ : undefined,
121
+ });
122
+ clearTimeout(timer);
123
+ return res;
124
+ }
125
+ catch (err) {
126
+ clearTimeout(timer);
127
+ const isTimeout = err instanceof Error && err.name === 'AbortError';
128
+ if (attempt < retries) {
129
+ logger.warn(`Request failed (attempt ${attempt + 1}/${retries + 1}) — retrying: ${isTimeout ? 'timeout' : err}`);
130
+ return fetchWithRetry(call, attempt + 1);
131
+ }
132
+ throw isTimeout ? new Error(`Request timed out after ${timeoutMs}ms`) : err;
133
+ }
134
+ }
107
135
  try {
108
- const responses = await Promise.all(apiCalls.map(c => fetchFn(c.url, {
109
- method: c.method,
110
- headers: options.headers ?? {},
111
- body: ['POST', 'PUT', 'PATCH'].includes(c.method)
112
- ? JSON.stringify(c.params)
113
- : undefined,
114
- })));
115
- // Check for HTTP errors
116
- const failed = responses.find(r => !r.ok);
117
- if (failed) {
136
+ const responses = await Promise.all(apiCalls.map(c => fetchWithRetry(c, 0)));
137
+ const failedIdx = responses.findIndex(r => !r.ok);
138
+ if (failedIdx !== -1) {
139
+ const failed = responses[failedIdx];
118
140
  return {
119
- success: false,
120
- resolverType: 'api',
121
- apiCalls,
141
+ success: false, resolverType: 'api', apiCalls,
142
+ durationMs: Date.now() - startTime,
122
143
  error: `API request failed: ${failed.status} ${failed.statusText}`,
123
144
  };
124
145
  }
125
- return { success: true, resolverType: 'api', apiCalls };
146
+ const enrichedCalls = await Promise.all(responses.map(async (res, i) => {
147
+ let data = undefined;
148
+ try {
149
+ const text = await res.text();
150
+ data = text ? JSON.parse(text) : undefined;
151
+ }
152
+ catch { /* non-JSON response */ }
153
+ return { ...apiCalls[i], status: res.status, data };
154
+ }));
155
+ logger.debug(`API calls completed in ${Date.now() - startTime}ms`);
156
+ return { success: true, resolverType: 'api', apiCalls: enrichedCalls, durationMs: Date.now() - startTime };
126
157
  }
127
158
  catch (err) {
128
159
  return {
129
- success: false,
130
- resolverType: 'api',
131
- apiCalls,
160
+ success: false, resolverType: 'api', apiCalls,
161
+ durationMs: Date.now() - startTime,
132
162
  error: err instanceof Error ? err.message : String(err),
133
163
  };
134
164
  }
@@ -0,0 +1,2 @@
1
+ // Auto-generated by scripts/version.js — do not edit manually
2
+ export const VERSION = '0.4.0';
package/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "capman",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Capability Manifest Engine — let AI agents interact with your app without navigating the UI",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
7
7
  "types": "./dist/cjs/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./dist/esm/index.js",
11
- "require": "./dist/cjs/index.js",
12
- "types": "./dist/cjs/index.d.ts"
10
+ "import": {
11
+ "types": "./dist/cjs/index.d.ts",
12
+ "default": "./dist/esm/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/cjs/index.d.ts",
16
+ "default": "./dist/cjs/index.js"
17
+ }
13
18
  }
14
19
  },
15
20
  "bin": {
@@ -22,14 +27,15 @@
22
27
  "CONTRIBUTING.md"
23
28
  ],
24
29
  "scripts": {
25
- "build:cjs": "tsc --project tsconfig.json",
26
- "build:esm": "tsc --project tsconfig.esm.json",
27
- "build": "pnpm run build:cjs && pnpm run build:esm",
28
- "dev": "tsc --watch",
29
- "example": "tsx examples/basic.ts",
30
- "validate": "node bin/capman.js validate",
31
- "inspect": "node bin/capman.js inspect",
32
- "test": "vitest run"
30
+ "prebuild": "node scripts/version.js",
31
+ "build:cjs": "tsc --project tsconfig.json",
32
+ "build:esm": "tsc --project tsconfig.esm.json",
33
+ "build": "pnpm run build:cjs && pnpm run build:esm",
34
+ "dev": "tsc --watch",
35
+ "example": "tsx examples/basic.ts",
36
+ "validate": "node bin/capman.js validate",
37
+ "inspect": "node bin/capman.js inspect",
38
+ "test": "vitest run"
33
39
  },
34
40
  "keywords": [
35
41
  "ai",