facult 1.0.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/LICENSE +21 -0
- package/README.md +383 -0
- package/bin/facult.cjs +302 -0
- package/package.json +78 -0
- package/src/adapters/claude-cli.ts +18 -0
- package/src/adapters/claude-desktop.ts +15 -0
- package/src/adapters/clawdbot.ts +18 -0
- package/src/adapters/codex.ts +19 -0
- package/src/adapters/cursor.ts +18 -0
- package/src/adapters/index.ts +69 -0
- package/src/adapters/mcp.ts +270 -0
- package/src/adapters/reference.ts +9 -0
- package/src/adapters/skills.ts +47 -0
- package/src/adapters/types.ts +42 -0
- package/src/adapters/version.ts +18 -0
- package/src/audit/agent.ts +1071 -0
- package/src/audit/index.ts +74 -0
- package/src/audit/static.ts +1130 -0
- package/src/audit/tui.ts +704 -0
- package/src/audit/types.ts +68 -0
- package/src/audit/update-index.ts +115 -0
- package/src/conflicts.ts +135 -0
- package/src/consolidate-conflict-action.ts +57 -0
- package/src/consolidate.ts +1637 -0
- package/src/enable-disable.ts +349 -0
- package/src/index-builder.ts +562 -0
- package/src/index.ts +589 -0
- package/src/manage.ts +894 -0
- package/src/migrate.ts +272 -0
- package/src/paths.ts +238 -0
- package/src/quarantine.ts +217 -0
- package/src/query.ts +186 -0
- package/src/remote-manifest-integrity.ts +367 -0
- package/src/remote-providers.ts +905 -0
- package/src/remote-source-policy.ts +237 -0
- package/src/remote-sources.ts +162 -0
- package/src/remote-types.ts +136 -0
- package/src/remote.ts +1970 -0
- package/src/scan.ts +2427 -0
- package/src/schema.ts +39 -0
- package/src/self-update.ts +408 -0
- package/src/snippets-cli.ts +293 -0
- package/src/snippets.ts +706 -0
- package/src/source-trust.ts +203 -0
- package/src/trust-list.ts +232 -0
- package/src/trust.ts +170 -0
- package/src/tui.ts +118 -0
- package/src/util/codex-toml.ts +126 -0
- package/src/util/json.ts +32 -0
- package/src/util/skills.ts +55 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import {
|
|
3
|
+
clearSourceTrustPolicy,
|
|
4
|
+
defaultSourceTrustLevel,
|
|
5
|
+
loadSourceTrustState,
|
|
6
|
+
type SourceTrustLevel,
|
|
7
|
+
type SourceTrustState,
|
|
8
|
+
setSourceTrustPolicy,
|
|
9
|
+
sourceTrustLevelFor,
|
|
10
|
+
} from "./source-trust";
|
|
11
|
+
|
|
12
|
+
const SOURCE_POLICY_ACTIONS = new Set(["trust", "review", "block", "clear"]);
|
|
13
|
+
|
|
14
|
+
export interface SourceIndexRef {
|
|
15
|
+
name: string;
|
|
16
|
+
url: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SourcePolicyCommandContext {
|
|
20
|
+
homeDir?: string;
|
|
21
|
+
cwd?: string;
|
|
22
|
+
now?: () => Date;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SourcePolicyRow {
|
|
26
|
+
source: string;
|
|
27
|
+
level: SourceTrustLevel;
|
|
28
|
+
explicit: boolean;
|
|
29
|
+
defaultLevel: SourceTrustLevel;
|
|
30
|
+
note?: string;
|
|
31
|
+
updatedAt?: string;
|
|
32
|
+
url?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseLongFlag(argv: string[], flag: string): string | null {
|
|
36
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
37
|
+
const arg = argv[i];
|
|
38
|
+
if (!arg) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (arg === flag) {
|
|
42
|
+
return argv[i + 1] ?? null;
|
|
43
|
+
}
|
|
44
|
+
if (arg.startsWith(`${flag}=`)) {
|
|
45
|
+
return arg.slice(flag.length + 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function printSourcesHelp(args: { builtinIndexName: string }) {
|
|
52
|
+
console.log(`facult sources — manage source trust policy for remote indices
|
|
53
|
+
|
|
54
|
+
Usage:
|
|
55
|
+
facult sources list [--json]
|
|
56
|
+
facult sources trust <source> [--note <text>]
|
|
57
|
+
facult sources review <source> [--note <text>]
|
|
58
|
+
facult sources block <source> [--note <text>]
|
|
59
|
+
facult sources clear <source>
|
|
60
|
+
|
|
61
|
+
Notes:
|
|
62
|
+
- Default policy is "${args.builtinIndexName}=trusted", all other sources=review.
|
|
63
|
+
- Blocked sources are always denied for install/update.
|
|
64
|
+
- Review sources are allowed unless --strict-source-trust is enabled.
|
|
65
|
+
`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function evaluateSourceTrust(args: {
|
|
69
|
+
sourceName: string;
|
|
70
|
+
trustState: SourceTrustState;
|
|
71
|
+
}): {
|
|
72
|
+
level: SourceTrustLevel;
|
|
73
|
+
explicit: boolean;
|
|
74
|
+
note?: string;
|
|
75
|
+
updatedAt?: string;
|
|
76
|
+
} {
|
|
77
|
+
const trust = sourceTrustLevelFor({
|
|
78
|
+
sourceName: args.sourceName,
|
|
79
|
+
state: args.trustState,
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
level: trust.level,
|
|
83
|
+
explicit: trust.explicit,
|
|
84
|
+
note: trust.policy?.note,
|
|
85
|
+
updatedAt: trust.policy?.updatedAt,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function assertSourceAllowed(args: {
|
|
90
|
+
sourceName: string;
|
|
91
|
+
trustState: SourceTrustState;
|
|
92
|
+
strictSourceTrust: boolean;
|
|
93
|
+
}): SourceTrustLevel {
|
|
94
|
+
const trust = evaluateSourceTrust(args);
|
|
95
|
+
if (trust.level === "blocked") {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Source "${args.sourceName}" is blocked by policy. Use "facult sources clear ${args.sourceName}" to remove the block.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (args.strictSourceTrust && trust.level === "review") {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Source "${args.sourceName}" requires review (strict mode). Use "facult sources trust ${args.sourceName}" after review.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return trust.level;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function sourcesCommand(args: {
|
|
109
|
+
argv: string[];
|
|
110
|
+
ctx?: SourcePolicyCommandContext;
|
|
111
|
+
readIndexSources: (home: string, cwd: string) => Promise<SourceIndexRef[]>;
|
|
112
|
+
builtinIndexName: string;
|
|
113
|
+
}) {
|
|
114
|
+
const [sub = "list", ...rest] = args.argv;
|
|
115
|
+
if (
|
|
116
|
+
sub === "--help" ||
|
|
117
|
+
sub === "-h" ||
|
|
118
|
+
sub === "help" ||
|
|
119
|
+
(sub !== "list" && !SOURCE_POLICY_ACTIONS.has(sub))
|
|
120
|
+
) {
|
|
121
|
+
printSourcesHelp({ builtinIndexName: args.builtinIndexName });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const home = args.ctx?.homeDir ?? homedir();
|
|
126
|
+
const cwd = args.ctx?.cwd ?? process.cwd();
|
|
127
|
+
const json = rest.includes("--json");
|
|
128
|
+
|
|
129
|
+
if (sub === "list") {
|
|
130
|
+
try {
|
|
131
|
+
const [sources, trustState] = await Promise.all([
|
|
132
|
+
args.readIndexSources(home, cwd),
|
|
133
|
+
loadSourceTrustState({ homeDir: home }),
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
const urlsByName = new Map<string, string>();
|
|
137
|
+
for (const source of sources) {
|
|
138
|
+
urlsByName.set(source.name, source.url);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const names = new Set<string>([
|
|
142
|
+
...sources.map((source) => source.name),
|
|
143
|
+
...Object.keys(trustState.sources),
|
|
144
|
+
]);
|
|
145
|
+
const rows: SourcePolicyRow[] = Array.from(names)
|
|
146
|
+
.sort((a, b) => a.localeCompare(b))
|
|
147
|
+
.map((name) => {
|
|
148
|
+
const assessed = evaluateSourceTrust({
|
|
149
|
+
sourceName: name,
|
|
150
|
+
trustState,
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
source: name,
|
|
154
|
+
level: assessed.level,
|
|
155
|
+
explicit: assessed.explicit,
|
|
156
|
+
defaultLevel: defaultSourceTrustLevel({ sourceName: name }),
|
|
157
|
+
note: assessed.note,
|
|
158
|
+
updatedAt: assessed.updatedAt,
|
|
159
|
+
url: urlsByName.get(name),
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
if (json) {
|
|
164
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!rows.length) {
|
|
169
|
+
console.log("(no sources)");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
for (const row of rows) {
|
|
173
|
+
const origin = row.explicit ? "explicit" : "default";
|
|
174
|
+
const url = row.url ?? "-";
|
|
175
|
+
const note = row.note ? `\t${row.note}` : "";
|
|
176
|
+
console.log(`${row.source}\t${row.level}\t${origin}\t${url}${note}`);
|
|
177
|
+
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const positional: string[] = [];
|
|
186
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
187
|
+
const arg = rest[i];
|
|
188
|
+
if (!arg) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (arg === "--note") {
|
|
192
|
+
i += 1;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (arg.startsWith("--note=") || arg.startsWith("-")) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
positional.push(arg);
|
|
199
|
+
}
|
|
200
|
+
const sourceName = positional[0];
|
|
201
|
+
if (!sourceName) {
|
|
202
|
+
console.error(`${sub} requires a source name`);
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const note = parseLongFlag(rest, "--note") ?? undefined;
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
if (sub === "clear") {
|
|
210
|
+
await clearSourceTrustPolicy({
|
|
211
|
+
sourceName,
|
|
212
|
+
homeDir: home,
|
|
213
|
+
now: args.ctx?.now,
|
|
214
|
+
});
|
|
215
|
+
console.log(`Cleared source trust policy: ${sourceName}`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const level =
|
|
220
|
+
sub === "trust"
|
|
221
|
+
? ("trusted" as const)
|
|
222
|
+
: sub === "review"
|
|
223
|
+
? ("review" as const)
|
|
224
|
+
: ("blocked" as const);
|
|
225
|
+
await setSourceTrustPolicy({
|
|
226
|
+
sourceName,
|
|
227
|
+
level,
|
|
228
|
+
note,
|
|
229
|
+
homeDir: home,
|
|
230
|
+
now: args.ctx?.now,
|
|
231
|
+
});
|
|
232
|
+
console.log(`Set source policy: ${sourceName} -> ${level}`);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
235
|
+
process.exitCode = 1;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
import { facultStateDir } from "./paths";
|
|
4
|
+
import {
|
|
5
|
+
type ManifestSignature,
|
|
6
|
+
type ManifestSignatureKey,
|
|
7
|
+
parseManifestIntegrity,
|
|
8
|
+
parseManifestSignature,
|
|
9
|
+
parseManifestSignatureKeys,
|
|
10
|
+
} from "./remote-manifest-integrity";
|
|
11
|
+
import {
|
|
12
|
+
BUILTIN_INDEX_NAME,
|
|
13
|
+
BUILTIN_INDEX_URL,
|
|
14
|
+
type IndexSource,
|
|
15
|
+
KNOWN_PROVIDER_SOURCES,
|
|
16
|
+
} from "./remote-types";
|
|
17
|
+
import { parseJsonLenient } from "./util/json";
|
|
18
|
+
|
|
19
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
20
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
24
|
+
try {
|
|
25
|
+
await Bun.file(path).stat();
|
|
26
|
+
return true;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseEntrySignature(
|
|
33
|
+
entry: Record<string, unknown>
|
|
34
|
+
): ManifestSignature | undefined {
|
|
35
|
+
if (isPlainObject(entry.signature) || typeof entry.signature === "string") {
|
|
36
|
+
return parseManifestSignature(entry.signature);
|
|
37
|
+
}
|
|
38
|
+
const synthetic = {
|
|
39
|
+
algorithm: entry.signatureAlgorithm,
|
|
40
|
+
value: entry.sig ?? entry.signature,
|
|
41
|
+
keyId: entry.keyId ?? entry.signatureKeyId ?? entry.kid,
|
|
42
|
+
publicKey: entry.publicKey,
|
|
43
|
+
publicKeyPath: entry.publicKeyPath ?? entry.keyPath,
|
|
44
|
+
};
|
|
45
|
+
return parseManifestSignature(synthetic);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function mergeSignatureKeys(
|
|
49
|
+
globalKeys: ManifestSignatureKey[],
|
|
50
|
+
sourceKeys: ManifestSignatureKey[]
|
|
51
|
+
): ManifestSignatureKey[] {
|
|
52
|
+
const merged = new Map<string, ManifestSignatureKey>();
|
|
53
|
+
for (const key of globalKeys) {
|
|
54
|
+
merged.set(key.id, key);
|
|
55
|
+
}
|
|
56
|
+
for (const key of sourceKeys) {
|
|
57
|
+
merged.set(key.id, key);
|
|
58
|
+
}
|
|
59
|
+
return Array.from(merged.values()).sort((a, b) => a.id.localeCompare(b.id));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function readIndexSources(
|
|
63
|
+
home: string,
|
|
64
|
+
cwd: string
|
|
65
|
+
): Promise<IndexSource[]> {
|
|
66
|
+
const out: IndexSource[] = [
|
|
67
|
+
{ name: BUILTIN_INDEX_NAME, url: BUILTIN_INDEX_URL, kind: "builtin" },
|
|
68
|
+
];
|
|
69
|
+
const configPath = resolve(facultStateDir(home), "indices.json");
|
|
70
|
+
if (!(await fileExists(configPath))) {
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const parsed = parseJsonLenient(await readFile(configPath, "utf8"));
|
|
76
|
+
if (!isPlainObject(parsed)) {
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
const obj = parsed as Record<string, unknown>;
|
|
80
|
+
const globalKeys = parseManifestSignatureKeys(
|
|
81
|
+
obj.signatureKeys ?? obj.trustedKeys ?? obj.keys
|
|
82
|
+
);
|
|
83
|
+
const candidateLists: unknown[] = [obj.indices, obj.sources];
|
|
84
|
+
for (const list of candidateLists) {
|
|
85
|
+
if (!Array.isArray(list)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
for (const entry of list) {
|
|
89
|
+
if (!isPlainObject(entry)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const name = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
93
|
+
if (!name) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const provider =
|
|
97
|
+
typeof entry.provider === "string" ? entry.provider.trim() : "";
|
|
98
|
+
const providerDefault = provider
|
|
99
|
+
? KNOWN_PROVIDER_SOURCES[provider]
|
|
100
|
+
: undefined;
|
|
101
|
+
const rawUrl = typeof entry.url === "string" ? entry.url.trim() : "";
|
|
102
|
+
const integrity = parseManifestIntegrity(
|
|
103
|
+
entry.integrity ?? entry.checksum
|
|
104
|
+
);
|
|
105
|
+
const signature = parseEntrySignature(entry);
|
|
106
|
+
const sourceKeys = parseManifestSignatureKeys(
|
|
107
|
+
entry.signatureKeys ?? entry.trustedKeys ?? entry.keys
|
|
108
|
+
);
|
|
109
|
+
const signatureKeys = mergeSignatureKeys(globalKeys, sourceKeys);
|
|
110
|
+
|
|
111
|
+
if (providerDefault) {
|
|
112
|
+
out.push({
|
|
113
|
+
name,
|
|
114
|
+
kind: providerDefault.kind,
|
|
115
|
+
url: rawUrl || providerDefault.url,
|
|
116
|
+
integrity,
|
|
117
|
+
signature,
|
|
118
|
+
signatureKeys: signatureKeys.length ? signatureKeys : undefined,
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!rawUrl) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const resolvedUrl =
|
|
127
|
+
rawUrl.startsWith("http://") ||
|
|
128
|
+
rawUrl.startsWith("https://") ||
|
|
129
|
+
rawUrl.startsWith("file://") ||
|
|
130
|
+
isAbsolute(rawUrl)
|
|
131
|
+
? rawUrl
|
|
132
|
+
: resolve(cwd, rawUrl);
|
|
133
|
+
out.push({
|
|
134
|
+
name,
|
|
135
|
+
url: resolvedUrl,
|
|
136
|
+
kind: "manifest",
|
|
137
|
+
integrity,
|
|
138
|
+
signature,
|
|
139
|
+
signatureKeys: signatureKeys.length ? signatureKeys : undefined,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
// Ignore malformed index config and keep builtin defaults.
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const dedup = new Map<string, IndexSource>();
|
|
148
|
+
for (const source of out) {
|
|
149
|
+
dedup.set(source.name, source);
|
|
150
|
+
}
|
|
151
|
+
return Array.from(dedup.values()).sort((a, b) =>
|
|
152
|
+
a.name.localeCompare(b.name)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function resolveKnownIndexSource(name: string): IndexSource | null {
|
|
157
|
+
const source = KNOWN_PROVIDER_SOURCES[name];
|
|
158
|
+
if (!source) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return { ...source };
|
|
162
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ManifestIntegrity,
|
|
3
|
+
ManifestSignature,
|
|
4
|
+
ManifestSignatureKey,
|
|
5
|
+
} from "./remote-manifest-integrity";
|
|
6
|
+
|
|
7
|
+
export type RemoteItemType = "skill" | "mcp" | "agent" | "snippet";
|
|
8
|
+
|
|
9
|
+
export interface RemoteSkillPayload {
|
|
10
|
+
name: string;
|
|
11
|
+
files: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RemoteMcpPayload {
|
|
15
|
+
name: string;
|
|
16
|
+
definition: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RemoteAgentPayload {
|
|
20
|
+
fileName: string;
|
|
21
|
+
content: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RemoteSnippetPayload {
|
|
25
|
+
marker: string;
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RemoteIndexItemBase {
|
|
30
|
+
id: string;
|
|
31
|
+
type: RemoteItemType;
|
|
32
|
+
title?: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
version?: string;
|
|
35
|
+
tags?: string[];
|
|
36
|
+
sourceUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface RemoteSkillItem extends RemoteIndexItemBase {
|
|
40
|
+
type: "skill";
|
|
41
|
+
skill: RemoteSkillPayload;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RemoteMcpItem extends RemoteIndexItemBase {
|
|
45
|
+
type: "mcp";
|
|
46
|
+
mcp: RemoteMcpPayload;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RemoteAgentItem extends RemoteIndexItemBase {
|
|
50
|
+
type: "agent";
|
|
51
|
+
agent: RemoteAgentPayload;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface RemoteSnippetItem extends RemoteIndexItemBase {
|
|
55
|
+
type: "snippet";
|
|
56
|
+
snippet: RemoteSnippetPayload;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type RemoteIndexItem =
|
|
60
|
+
| RemoteSkillItem
|
|
61
|
+
| RemoteMcpItem
|
|
62
|
+
| RemoteAgentItem
|
|
63
|
+
| RemoteSnippetItem;
|
|
64
|
+
|
|
65
|
+
export interface RemoteIndexManifest {
|
|
66
|
+
name: string;
|
|
67
|
+
url: string;
|
|
68
|
+
updatedAt?: string;
|
|
69
|
+
items: RemoteIndexItem[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type IndexSourceKind =
|
|
73
|
+
| "builtin"
|
|
74
|
+
| "manifest"
|
|
75
|
+
| "smithery"
|
|
76
|
+
| "glama"
|
|
77
|
+
| "skills-sh"
|
|
78
|
+
| "clawhub";
|
|
79
|
+
|
|
80
|
+
export interface IndexSource {
|
|
81
|
+
name: string;
|
|
82
|
+
url: string;
|
|
83
|
+
kind: IndexSourceKind;
|
|
84
|
+
integrity?: ManifestIntegrity;
|
|
85
|
+
signature?: ManifestSignature;
|
|
86
|
+
signatureKeys?: ManifestSignatureKey[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface LoadManifestHints {
|
|
90
|
+
query?: string;
|
|
91
|
+
itemId?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const BUILTIN_INDEX_NAME = "facult";
|
|
95
|
+
export const BUILTIN_INDEX_URL = "builtin://facult";
|
|
96
|
+
export const SMITHERY_INDEX_NAME = "smithery";
|
|
97
|
+
export const GLAMA_INDEX_NAME = "glama";
|
|
98
|
+
export const SKILLS_SH_INDEX_NAME = "skills.sh";
|
|
99
|
+
export const CLAWHUB_INDEX_NAME = "clawhub";
|
|
100
|
+
export const SMITHERY_API_BASE = "https://api.smithery.ai";
|
|
101
|
+
export const GLAMA_API_BASE = "https://glama.ai/api/mcp/v1";
|
|
102
|
+
export const SKILLS_SH_WEB_BASE = "https://skills.sh";
|
|
103
|
+
export const CLAWHUB_API_BASE = "https://wry-manatee-359.convex.site/api/v1";
|
|
104
|
+
|
|
105
|
+
export const KNOWN_PROVIDER_SOURCES: Record<string, IndexSource> = {
|
|
106
|
+
[SMITHERY_INDEX_NAME]: {
|
|
107
|
+
name: SMITHERY_INDEX_NAME,
|
|
108
|
+
url: SMITHERY_API_BASE,
|
|
109
|
+
kind: "smithery",
|
|
110
|
+
},
|
|
111
|
+
[GLAMA_INDEX_NAME]: {
|
|
112
|
+
name: GLAMA_INDEX_NAME,
|
|
113
|
+
url: GLAMA_API_BASE,
|
|
114
|
+
kind: "glama",
|
|
115
|
+
},
|
|
116
|
+
[SKILLS_SH_INDEX_NAME]: {
|
|
117
|
+
name: SKILLS_SH_INDEX_NAME,
|
|
118
|
+
url: SKILLS_SH_WEB_BASE,
|
|
119
|
+
kind: "skills-sh",
|
|
120
|
+
},
|
|
121
|
+
[CLAWHUB_INDEX_NAME]: {
|
|
122
|
+
name: CLAWHUB_INDEX_NAME,
|
|
123
|
+
url: CLAWHUB_API_BASE,
|
|
124
|
+
kind: "clawhub",
|
|
125
|
+
},
|
|
126
|
+
"skills-sh": {
|
|
127
|
+
name: SKILLS_SH_INDEX_NAME,
|
|
128
|
+
url: SKILLS_SH_WEB_BASE,
|
|
129
|
+
kind: "skills-sh",
|
|
130
|
+
},
|
|
131
|
+
"clawhub.ai": {
|
|
132
|
+
name: CLAWHUB_INDEX_NAME,
|
|
133
|
+
url: CLAWHUB_API_BASE,
|
|
134
|
+
kind: "clawhub",
|
|
135
|
+
},
|
|
136
|
+
};
|