@apitap/core 1.6.3 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -69
- package/dist/cli.js +25 -1
- package/dist/cli.js.map +1 -1
- package/dist/skill/index.d.ts +52 -0
- package/dist/skill/index.js +173 -0
- package/dist/skill/index.js.map +1 -0
- package/dist/skill/merge.js +21 -1
- package/dist/skill/merge.js.map +1 -1
- package/dist/skill/search.d.ts +1 -0
- package/dist/skill/search.js +12 -14
- package/dist/skill/search.js.map +1 -1
- package/dist/skill/store.d.ts +7 -0
- package/dist/skill/store.js +38 -25
- package/dist/skill/store.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +24 -1
- package/src/skill/index.ts +238 -0
- package/src/skill/merge.ts +23 -1
- package/src/skill/search.ts +13 -14
- package/src/skill/store.ts +51 -22
package/src/skill/store.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { join, dirname } from 'node:path';
|
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import type { SkillFile, SkillSummary } from '../types.js';
|
|
6
6
|
import { validateSkillFile } from './validate.js';
|
|
7
|
+
import { updateIndex, ensureIndex } from './index.js';
|
|
7
8
|
|
|
8
9
|
const DEFAULT_SKILLS_DIR = join(homedir(), '.apitap', 'skills');
|
|
9
10
|
|
|
@@ -37,6 +38,9 @@ export async function writeSkillFile(
|
|
|
37
38
|
skill: SkillFile,
|
|
38
39
|
skillsDir: string = DEFAULT_SKILLS_DIR,
|
|
39
40
|
): Promise<string> {
|
|
41
|
+
// Validate before writing — catch bad data at the source, not on read
|
|
42
|
+
validateSkillFile(skill);
|
|
43
|
+
|
|
40
44
|
await mkdir(skillsDir, { recursive: true, mode: 0o700 });
|
|
41
45
|
await ensureGitignore(skillsDir);
|
|
42
46
|
const filePath = skillPath(skill.domain, skillsDir);
|
|
@@ -44,6 +48,26 @@ export async function writeSkillFile(
|
|
|
44
48
|
const content = JSON.stringify(skill, null, 2) + '\n';
|
|
45
49
|
await writeFile(tmpPath, content, { mode: 0o600 });
|
|
46
50
|
await rename(tmpPath, filePath);
|
|
51
|
+
|
|
52
|
+
// Incrementally update the search index
|
|
53
|
+
try {
|
|
54
|
+
await updateIndex(
|
|
55
|
+
skill.domain,
|
|
56
|
+
skill.endpoints.map(ep => ({
|
|
57
|
+
id: ep.id,
|
|
58
|
+
method: ep.method,
|
|
59
|
+
path: ep.path,
|
|
60
|
+
...(ep.replayability?.tier ? { tier: ep.replayability.tier } : {}),
|
|
61
|
+
...(ep.replayability?.verified ? { verified: true } : {}),
|
|
62
|
+
})),
|
|
63
|
+
skill.provenance ?? 'unsigned',
|
|
64
|
+
skillsDir,
|
|
65
|
+
skill.capturedAt,
|
|
66
|
+
);
|
|
67
|
+
} catch {
|
|
68
|
+
// Index update failure should not block writes
|
|
69
|
+
}
|
|
70
|
+
|
|
47
71
|
return filePath;
|
|
48
72
|
}
|
|
49
73
|
|
|
@@ -111,33 +135,38 @@ export async function readSkillFile(
|
|
|
111
135
|
}
|
|
112
136
|
}
|
|
113
137
|
|
|
114
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Fault-tolerant wrapper around readSkillFile — returns null instead of
|
|
140
|
+
* throwing on validation errors, bad signatures, etc. ENOENT (missing
|
|
141
|
+
* file) also returns null. Use this when iterating many files where one
|
|
142
|
+
* bad file should not abort the whole operation.
|
|
143
|
+
*/
|
|
144
|
+
export async function safeReadSkillFile(
|
|
145
|
+
domain: string,
|
|
115
146
|
skillsDir: string = DEFAULT_SKILLS_DIR,
|
|
116
|
-
|
|
117
|
-
|
|
147
|
+
options?: Parameters<typeof readSkillFile>[2],
|
|
148
|
+
): Promise<SkillFile | null> {
|
|
118
149
|
try {
|
|
119
|
-
|
|
120
|
-
} catch
|
|
121
|
-
|
|
122
|
-
throw err;
|
|
150
|
+
return await readSkillFile(domain, skillsDir, options);
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
123
153
|
}
|
|
154
|
+
}
|
|
124
155
|
|
|
156
|
+
export async function listSkillFiles(
|
|
157
|
+
skillsDir: string = DEFAULT_SKILLS_DIR,
|
|
158
|
+
): Promise<SkillSummary[]> {
|
|
159
|
+
const index = await ensureIndex(skillsDir);
|
|
125
160
|
const summaries: SkillSummary[] = [];
|
|
126
|
-
|
|
127
|
-
for (const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
skillFile: join(skillsDir, file),
|
|
136
|
-
endpointCount: skill.endpoints.length,
|
|
137
|
-
capturedAt: skill.capturedAt,
|
|
138
|
-
provenance: skill.provenance ?? 'unsigned',
|
|
139
|
-
});
|
|
140
|
-
}
|
|
161
|
+
|
|
162
|
+
for (const [domain, entry] of Object.entries(index.domains)) {
|
|
163
|
+
summaries.push({
|
|
164
|
+
domain,
|
|
165
|
+
skillFile: join(skillsDir, `${domain}.json`),
|
|
166
|
+
endpointCount: entry.endpointCount,
|
|
167
|
+
capturedAt: entry.capturedAt || index.builtAt,
|
|
168
|
+
provenance: entry.provenance,
|
|
169
|
+
});
|
|
141
170
|
}
|
|
142
171
|
|
|
143
172
|
return summaries;
|