@buildinternet/uploads 0.36.0 → 0.37.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/dist/cli-catalog.js +5 -3
- package/dist/client.d.ts +32 -3
- package/dist/client.js +15 -3
- package/dist/commands.js +101 -25
- package/dist/index.d.ts +1 -1
- package/dist/mcp/tools.js +38 -8
- package/package.json +1 -1
package/dist/cli-catalog.js
CHANGED
|
@@ -173,15 +173,17 @@ export const ROOT_COMMANDS = [
|
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
name: "find",
|
|
176
|
-
usage: "find k=v...",
|
|
177
|
-
summary: "
|
|
176
|
+
usage: "find [k=v...] [--name <term>]",
|
|
177
|
+
summary: "Find objects by metadata and/or filename substring",
|
|
178
178
|
},
|
|
179
179
|
{
|
|
180
180
|
name: "meta",
|
|
181
|
-
summary: "Get/set
|
|
181
|
+
summary: "Get/set object metadata; discover workspace keys/values",
|
|
182
182
|
subcommands: [
|
|
183
183
|
{ name: "get", summary: "Show metadata for an object" },
|
|
184
184
|
{ name: "set", summary: "Merge-set and/or delete metadata pairs" },
|
|
185
|
+
{ name: "keys", summary: "List distinct metadata keys in the workspace" },
|
|
186
|
+
{ name: "values", summary: "List distinct values for one metadata key" },
|
|
185
187
|
],
|
|
186
188
|
},
|
|
187
189
|
{
|
package/dist/client.d.ts
CHANGED
|
@@ -47,6 +47,11 @@ export interface ListOptions {
|
|
|
47
47
|
export interface FindFilesOptions {
|
|
48
48
|
prefix?: string;
|
|
49
49
|
limit?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Case-insensitive substring match on object keys (`?name=`).
|
|
52
|
+
* At least one of non-empty `filters` or `name` is required.
|
|
53
|
+
*/
|
|
54
|
+
name?: string;
|
|
50
55
|
}
|
|
51
56
|
export interface FindFilesItem {
|
|
52
57
|
key: string;
|
|
@@ -56,6 +61,24 @@ export interface FindFilesItem {
|
|
|
56
61
|
export interface FindFilesResult {
|
|
57
62
|
items: FindFilesItem[];
|
|
58
63
|
cursor: string | null;
|
|
64
|
+
/** Present when a `name` term was used — true if the underlying query hit its cap. */
|
|
65
|
+
truncated?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export interface MetadataKeysResult {
|
|
68
|
+
keys: Array<{
|
|
69
|
+
key: string;
|
|
70
|
+
count: number;
|
|
71
|
+
distinctValues: number;
|
|
72
|
+
}>;
|
|
73
|
+
truncated: boolean;
|
|
74
|
+
}
|
|
75
|
+
export interface MetadataValuesResult {
|
|
76
|
+
key: string;
|
|
77
|
+
values: Array<{
|
|
78
|
+
value: string;
|
|
79
|
+
count: number;
|
|
80
|
+
}>;
|
|
81
|
+
truncated: boolean;
|
|
59
82
|
}
|
|
60
83
|
export interface GetMetadataResult {
|
|
61
84
|
metadata: Record<string, string>;
|
|
@@ -523,10 +546,16 @@ export declare function createUploadsClient(config: UploadsClientConfig): {
|
|
|
523
546
|
/** `PATCH /v1/:workspace/files/:key` — merge `set`/`delete`; returns the merged map. */
|
|
524
547
|
patchMetadata(key: string, opts: PatchMetadataOptions): Promise<GetMetadataResult>;
|
|
525
548
|
/**
|
|
526
|
-
* `GET /v1/:workspace/files?meta.<k>=<v
|
|
527
|
-
* queryable metadata
|
|
549
|
+
* `GET /v1/:workspace/files?meta.<k>=<v>&…&name=…` — ANDed equality filter
|
|
550
|
+
* over queryable metadata and/or a case-insensitive filename substring.
|
|
551
|
+
* At least one of non-empty `filters` or `opts.name` is required.
|
|
552
|
+
* `filters` must be pre-validated when present (see `metadata.ts`).
|
|
528
553
|
*/
|
|
529
|
-
findFiles(filters
|
|
554
|
+
findFiles(filters?: Record<string, string>, opts?: FindFilesOptions): Promise<FindFilesResult>;
|
|
555
|
+
/** `GET /v1/:workspace/files/facets` — workspace metadata key vocabulary. */
|
|
556
|
+
listMetadataKeys(): Promise<MetadataKeysResult>;
|
|
557
|
+
/** `GET /v1/:workspace/files/facets?key=` — distinct values for one key. */
|
|
558
|
+
listMetadataValues(key: string): Promise<MetadataValuesResult>;
|
|
530
559
|
head(key: string): Promise<HeadResult>;
|
|
531
560
|
createGallery(opts: CreateGalleryOptions): Promise<Gallery>;
|
|
532
561
|
getGallery(id: string): Promise<Gallery>;
|
package/dist/client.js
CHANGED
|
@@ -405,19 +405,31 @@ export function createUploadsClient(config) {
|
|
|
405
405
|
});
|
|
406
406
|
},
|
|
407
407
|
/**
|
|
408
|
-
* `GET /v1/:workspace/files?meta.<k>=<v
|
|
409
|
-
* queryable metadata
|
|
408
|
+
* `GET /v1/:workspace/files?meta.<k>=<v>&…&name=…` — ANDed equality filter
|
|
409
|
+
* over queryable metadata and/or a case-insensitive filename substring.
|
|
410
|
+
* At least one of non-empty `filters` or `opts.name` is required.
|
|
411
|
+
* `filters` must be pre-validated when present (see `metadata.ts`).
|
|
410
412
|
*/
|
|
411
|
-
async findFiles(filters, opts = {}) {
|
|
413
|
+
async findFiles(filters = {}, opts = {}) {
|
|
412
414
|
const params = new URLSearchParams();
|
|
413
415
|
for (const [k, v] of Object.entries(filters))
|
|
414
416
|
params.append(`meta.${k}`, v);
|
|
417
|
+
if (opts.name)
|
|
418
|
+
params.set("name", opts.name);
|
|
415
419
|
if (opts.prefix)
|
|
416
420
|
params.set("prefix", opts.prefix);
|
|
417
421
|
if (opts.limit != null)
|
|
418
422
|
params.set("limit", String(opts.limit));
|
|
419
423
|
return request("GET", `${filesBase(config)}?${params.toString()}`);
|
|
420
424
|
},
|
|
425
|
+
/** `GET /v1/:workspace/files/facets` — workspace metadata key vocabulary. */
|
|
426
|
+
async listMetadataKeys() {
|
|
427
|
+
return request("GET", `${filesBase(config)}/facets`);
|
|
428
|
+
},
|
|
429
|
+
/** `GET /v1/:workspace/files/facets?key=` — distinct values for one key. */
|
|
430
|
+
async listMetadataValues(key) {
|
|
431
|
+
return request("GET", `${filesBase(config)}/facets?${new URLSearchParams({ key })}`);
|
|
432
|
+
},
|
|
421
433
|
async head(key) {
|
|
422
434
|
const result = await request("GET", `${filesBase(config)}/${encodeKeyPath(key)}`);
|
|
423
435
|
return { ...result, embedUrl: resolveEmbedUrl(result.url, result.embedUrl) };
|
package/dist/commands.js
CHANGED
|
@@ -2134,31 +2134,45 @@ export async function runGallery(ctx, args, help = false) {
|
|
|
2134
2134
|
}
|
|
2135
2135
|
}
|
|
2136
2136
|
// --- list ---
|
|
2137
|
-
const LIST_HELP = `uploads list [--prefix <p>] [--pr <num> | --issue <num>] [--repo <owner/name>] [--limit <n>] [--cursor <c>] [--all] [--meta <k=v>]... [--workspace <name>]
|
|
2137
|
+
const LIST_HELP = `uploads list [--prefix <p>] [--pr <num> | --issue <num>] [--repo <owner/name>] [--limit <n>] [--cursor <c>] [--all] [--meta <k=v>]... [--name <term>] [--workspace <name>]
|
|
2138
2138
|
|
|
2139
2139
|
Default prefix: UPLOADS_DEFAULT_PREFIX (screenshots if unset).
|
|
2140
2140
|
|
|
2141
|
-
--meta <k=v> (repeatable, ANDed)
|
|
2142
|
-
returned items include their matched metadata. Combines with
|
|
2143
|
-
with --pr/--issue/--all.
|
|
2141
|
+
--meta <k=v> (repeatable, ANDed) and/or --name <term> switch to the search
|
|
2142
|
+
endpoint — returned items include their matched metadata. Combines with
|
|
2143
|
+
--prefix, not with --pr/--issue/--all. --name is a case-insensitive
|
|
2144
|
+
substring match on object keys. See also: uploads find.
|
|
2144
2145
|
|
|
2145
2146
|
Examples:
|
|
2146
2147
|
uploads list --prefix screenshots/
|
|
2147
2148
|
uploads list --pr 123
|
|
2148
2149
|
uploads list --all --json
|
|
2149
2150
|
uploads list --meta gh.repo=buildinternet/uploads --meta gh.number=123
|
|
2151
|
+
uploads list --name hero --meta app=web
|
|
2150
2152
|
`;
|
|
2151
|
-
/**
|
|
2152
|
-
|
|
2153
|
+
/** Human-mode stderr note when a paged API response was capped server-side. */
|
|
2154
|
+
function writeTruncatedNotice(truncated, quiet, detail) {
|
|
2155
|
+
if (truncated && !quiet) {
|
|
2156
|
+
process.stderr.write(`truncated: true (${detail})\n`);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
/** `--meta` / `--name` search path, shared by `runList` and `runFind`. */
|
|
2160
|
+
async function runFindFiles(ctx, filters, flags, name) {
|
|
2153
2161
|
if (flagString(flags, "--cursor") !== undefined) {
|
|
2154
|
-
throw new UsageError("--cursor is not supported with metadata filters");
|
|
2162
|
+
throw new UsageError("--cursor is not supported with metadata or name filters");
|
|
2155
2163
|
}
|
|
2156
2164
|
const prefix = flagString(flags, "--prefix");
|
|
2157
2165
|
const limit = flagInt(flags, "--limit", "--limit");
|
|
2158
|
-
const
|
|
2166
|
+
const nameTerm = name ?? flagString(flags, "--name");
|
|
2167
|
+
if (Object.keys(filters).length === 0 && !nameTerm) {
|
|
2168
|
+
throw new UsageError("find requires at least one k=v pair, --meta k=v, or --name <term>", {
|
|
2169
|
+
example: "uploads find path=/settings state=after",
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
const result = await ctx.client.findFiles(filters, { prefix, limit, name: nameTerm });
|
|
2159
2173
|
if (ctx.json)
|
|
2160
2174
|
await writeJson(result);
|
|
2161
|
-
else
|
|
2175
|
+
else {
|
|
2162
2176
|
for (const item of result.items) {
|
|
2163
2177
|
// LIST_HELP promises matched metadata in the output; render it inline
|
|
2164
2178
|
// (sorted for stable output) so human mode honors that, not just --json.
|
|
@@ -2168,6 +2182,8 @@ async function runFindFiles(ctx, filters, flags) {
|
|
|
2168
2182
|
.join(" ");
|
|
2169
2183
|
await writeStdout(`${item.key}${item.url ? ` ${item.url}` : ""}${meta ? ` ${meta}` : ""}\n`);
|
|
2170
2184
|
}
|
|
2185
|
+
writeTruncatedNotice(result.truncated, ctx.quiet, "more matches may exist beyond this page");
|
|
2186
|
+
}
|
|
2171
2187
|
return 0;
|
|
2172
2188
|
}
|
|
2173
2189
|
export async function runList(ctx, args, help = false, run = execRunner) {
|
|
@@ -2177,14 +2193,15 @@ export async function runList(ctx, args, help = false, run = execRunner) {
|
|
|
2177
2193
|
return 0;
|
|
2178
2194
|
}
|
|
2179
2195
|
const metaPairs = flagValues(parsed.flags, "--meta");
|
|
2180
|
-
|
|
2196
|
+
const nameFlag = flagString(parsed.flags, "--name");
|
|
2197
|
+
if (metaPairs.length > 0 || nameFlag !== undefined) {
|
|
2181
2198
|
if (ghTargetFromFlags(parsed.flags, run)) {
|
|
2182
|
-
throw new UsageError("--meta cannot be combined with --pr/--issue");
|
|
2199
|
+
throw new UsageError("--meta/--name cannot be combined with --pr/--issue");
|
|
2183
2200
|
}
|
|
2184
2201
|
if (flagBool(parsed.flags, "--all")) {
|
|
2185
|
-
throw new UsageError("--meta cannot be combined with --all");
|
|
2202
|
+
throw new UsageError("--meta/--name cannot be combined with --all");
|
|
2186
2203
|
}
|
|
2187
|
-
return runFindFiles(ctx, parseMetaFlags(metaPairs), parsed.flags);
|
|
2204
|
+
return runFindFiles(ctx, metaPairs.length > 0 ? parseMetaFlags(metaPairs) : {}, parsed.flags, nameFlag);
|
|
2188
2205
|
}
|
|
2189
2206
|
const defaults = resolvePutDefaults({ envFile: ctx.envFile });
|
|
2190
2207
|
const prefixFlag = flagString(parsed.flags, "--prefix");
|
|
@@ -2219,15 +2236,21 @@ export async function runList(ctx, args, help = false, run = execRunner) {
|
|
|
2219
2236
|
return 0;
|
|
2220
2237
|
}
|
|
2221
2238
|
// --- find ---
|
|
2222
|
-
const FIND_HELP = `uploads find k=v [k=v...] [--prefix <p>] [--limit <n>] [--workspace <name>]
|
|
2239
|
+
const FIND_HELP = `uploads find [k=v...] [--meta k=v]... [--name <term>] [--prefix <p>] [--limit <n>] [--workspace <name>]
|
|
2223
2240
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2241
|
+
Find objects by queryable metadata (ANDed equality) and/or a case-insensitive
|
|
2242
|
+
filename substring. Same output as \`uploads list --meta\` / \`--name\`.
|
|
2243
|
+
|
|
2244
|
+
Pairs are positional k=v, or spelled --meta k=v. A bare positional without
|
|
2245
|
+
\`=\` is treated as --name (e.g. \`uploads find hero\`). At least one of a
|
|
2246
|
+
meta pair or a name term is required.
|
|
2226
2247
|
|
|
2227
2248
|
Examples:
|
|
2228
2249
|
uploads find gh.repo=buildinternet/uploads gh.number=123
|
|
2229
2250
|
uploads find path=/settings state=after --prefix screenshots/
|
|
2230
2251
|
uploads find --meta path=/settings
|
|
2252
|
+
uploads find hero
|
|
2253
|
+
uploads find --name hero --meta app=web
|
|
2231
2254
|
`;
|
|
2232
2255
|
export async function runFind(ctx, args, help = false) {
|
|
2233
2256
|
const parsed = parseCommandArgs(args);
|
|
@@ -2237,24 +2260,43 @@ export async function runFind(ctx, args, help = false) {
|
|
|
2237
2260
|
}
|
|
2238
2261
|
// Same flag/positional symmetry as `meta set` (issue #545): `find` is the
|
|
2239
2262
|
// alias for `list --meta`, so `find --meta k=v` must not dead-end.
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2263
|
+
// Bare positionals without `=` are the filename term (issue #528) so
|
|
2264
|
+
// `uploads find hero` works without a flag. Empty input is rejected inside
|
|
2265
|
+
// `runFindFiles` (shared with `list --name` / `--meta`).
|
|
2266
|
+
const nameFlag = flagString(parsed.flags, "--name");
|
|
2267
|
+
const kvPositionals = [];
|
|
2268
|
+
const bareNames = [];
|
|
2269
|
+
for (const pos of parsed.positionals) {
|
|
2270
|
+
if (pos.includes("="))
|
|
2271
|
+
kvPositionals.push(pos);
|
|
2272
|
+
else
|
|
2273
|
+
bareNames.push(pos);
|
|
2274
|
+
}
|
|
2275
|
+
if (bareNames.length > 1) {
|
|
2276
|
+
throw new UsageError("find accepts at most one bare name term (or use --name)", {
|
|
2277
|
+
example: "uploads find hero --meta app=web",
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
if (bareNames.length === 1 && nameFlag !== undefined) {
|
|
2281
|
+
throw new UsageError("pass the name term either as a bare positional or --name, not both", {
|
|
2282
|
+
example: "uploads find hero",
|
|
2244
2283
|
});
|
|
2245
2284
|
}
|
|
2246
|
-
const
|
|
2247
|
-
return runFindFiles(ctx,
|
|
2285
|
+
const pairs = [...kvPositionals, ...flagValues(parsed.flags, "--meta")];
|
|
2286
|
+
return runFindFiles(ctx, pairs.length > 0 ? parseMetaFlags(pairs) : {}, parsed.flags, nameFlag ?? bareNames[0]);
|
|
2248
2287
|
}
|
|
2249
2288
|
// --- meta ---
|
|
2250
2289
|
const META_HELP = `uploads meta <command> [args]
|
|
2251
2290
|
|
|
2252
2291
|
Read/write an object's queryable custom metadata (D1-backed key-value pairs;
|
|
2253
|
-
distinct from the R2 provenance headers put on upload).
|
|
2292
|
+
distinct from the R2 provenance headers put on upload). Discover which keys
|
|
2293
|
+
and values exist in the workspace before filtering with find/list.
|
|
2254
2294
|
|
|
2255
2295
|
Commands:
|
|
2256
2296
|
get <key> Show metadata for an object
|
|
2257
2297
|
set <key> k=v [k=v...] [--delete k]... Merge-set and/or delete pairs
|
|
2298
|
+
keys List distinct metadata keys (with counts)
|
|
2299
|
+
values <meta-key> List distinct values for one key
|
|
2258
2300
|
|
|
2259
2301
|
Pairs take either form: positional k=v, or --meta k=v (same spelling as
|
|
2260
2302
|
put/screenshot/list). Both can appear in one call.
|
|
@@ -2264,6 +2306,8 @@ Examples:
|
|
|
2264
2306
|
uploads meta set screenshots/myapp/42/shot.png path=/settings state=after
|
|
2265
2307
|
uploads meta set screenshots/myapp/42/shot.png --meta path=/settings
|
|
2266
2308
|
uploads meta set screenshots/myapp/42/shot.png --delete path --delete state
|
|
2309
|
+
uploads meta keys
|
|
2310
|
+
uploads meta values app
|
|
2267
2311
|
`;
|
|
2268
2312
|
export async function runMeta(ctx, args, help = false) {
|
|
2269
2313
|
const parsed = parseCommandArgs(args);
|
|
@@ -2273,7 +2317,7 @@ export async function runMeta(ctx, args, help = false) {
|
|
|
2273
2317
|
return 0;
|
|
2274
2318
|
}
|
|
2275
2319
|
if (!action) {
|
|
2276
|
-
throw new UsageError("meta requires a subcommand: get or
|
|
2320
|
+
throw new UsageError("meta requires a subcommand: get, set, keys, or values", {
|
|
2277
2321
|
example: "uploads meta set screenshots/myapp/42/shot.png --meta path=/settings",
|
|
2278
2322
|
});
|
|
2279
2323
|
}
|
|
@@ -2330,8 +2374,40 @@ export async function runMeta(ctx, args, help = false) {
|
|
|
2330
2374
|
await resyncCommentAfterMetaSet(ctx, key, [...Object.keys(set ?? {}), ...del]);
|
|
2331
2375
|
return 0;
|
|
2332
2376
|
}
|
|
2377
|
+
case "keys": {
|
|
2378
|
+
// Workspace vocabulary discovery (issue #528) — which meta keys exist
|
|
2379
|
+
// and how common they are, before agents guess at find filters.
|
|
2380
|
+
const result = await ctx.client.listMetadataKeys();
|
|
2381
|
+
if (ctx.json)
|
|
2382
|
+
await writeJson(result);
|
|
2383
|
+
else {
|
|
2384
|
+
for (const row of result.keys) {
|
|
2385
|
+
await writeStdout(`${row.key} count=${row.count} distinct=${row.distinctValues}\n`);
|
|
2386
|
+
}
|
|
2387
|
+
writeTruncatedNotice(result.truncated, ctx.quiet, "more keys may exist beyond this page");
|
|
2388
|
+
}
|
|
2389
|
+
return 0;
|
|
2390
|
+
}
|
|
2391
|
+
case "values": {
|
|
2392
|
+
const key = parsed.positionals[1];
|
|
2393
|
+
if (!key) {
|
|
2394
|
+
throw new UsageError("meta values requires a metadata key", {
|
|
2395
|
+
example: "uploads meta values app",
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
const result = await ctx.client.listMetadataValues(key);
|
|
2399
|
+
if (ctx.json)
|
|
2400
|
+
await writeJson(result);
|
|
2401
|
+
else {
|
|
2402
|
+
for (const row of result.values) {
|
|
2403
|
+
await writeStdout(`${row.value} count=${row.count}\n`);
|
|
2404
|
+
}
|
|
2405
|
+
writeTruncatedNotice(result.truncated, ctx.quiet, "more values may exist beyond this page");
|
|
2406
|
+
}
|
|
2407
|
+
return 0;
|
|
2408
|
+
}
|
|
2333
2409
|
default:
|
|
2334
|
-
throw new UsageError(`unknown meta command: ${action} (expected get or
|
|
2410
|
+
throw new UsageError(`unknown meta command: ${action} (expected get, set, keys, or values)`, {
|
|
2335
2411
|
example: "uploads meta get screenshots/myapp/42/shot.png",
|
|
2336
2412
|
});
|
|
2337
2413
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { sanitizeKeySegment, sha256Short, deriveRepoFromGit, buildScreenshotKey
|
|
|
4
4
|
export { BUILTIN_DESTINATIONS, isBuiltinDestination, keyMatchesDestination, resolveDestinationRoot, resolvePutPrefix, type BuiltinDestinationId, } from "./destinations.js";
|
|
5
5
|
export { DEFAULT_API_URL, DEFAULT_WORKSPACE, UPLOADS_CONFIG_KEYS, defaultConfigPath, resolveConfigPath, loadConfigFile, loadEnvFile, resolveApiUrl, resolveConfig, describeConfigSources, redactToken, writeConfigKeys, configValuesFromClient, putDefaultsToConfigValues, resolvePutDefaults, mergePutDefaults, workspaceFromToken, workspaceMismatch, type UploadsClientConfig, type ResolvedConfig, type WorkspaceSource, type ConfigValueSource, type ConfigSources, type UploadsConfigKey, type UploadsConfigValues, type PutDefaults, } from "./config.js";
|
|
6
6
|
export { UploadsError, type UploadsErrorCode } from "./errors.js";
|
|
7
|
-
export { createUploadsClient, type UploadsClient, type PutOptions, type ProvenanceInput, type ListOptions, type PutResult, type ListItem, type ListResult, type HeadResult, type DeleteResult, type GalleryItem, type Gallery, type GallerySummary, type GalleryListOptions, type GalleryListResult, type CreateGalleryOptions, type AddGalleryItemOptions, type DeleteGalleryOptions, type HealthResult, type UsageResult, type ReconcileResult, type PurgeExpiredResult, type PurgeExpiredResponse, type FindFilesOptions, type FindFilesItem, type FindFilesResult, type GetMetadataResult, type PatchMetadataOptions, } from "./client.js";
|
|
7
|
+
export { createUploadsClient, type UploadsClient, type PutOptions, type ProvenanceInput, type ListOptions, type PutResult, type ListItem, type ListResult, type HeadResult, type DeleteResult, type GalleryItem, type Gallery, type GallerySummary, type GalleryListOptions, type GalleryListResult, type CreateGalleryOptions, type AddGalleryItemOptions, type DeleteGalleryOptions, type HealthResult, type UsageResult, type ReconcileResult, type PurgeExpiredResult, type PurgeExpiredResponse, type FindFilesOptions, type FindFilesItem, type FindFilesResult, type MetadataKeysResult, type MetadataValuesResult, type GetMetadataResult, type PatchMetadataOptions, } from "./client.js";
|
|
8
8
|
export { buildCliProvenance } from "./provenance.js";
|
|
9
9
|
export { META_KEY_RE, META_VALUE_MAX, META_MAX_KEYS, META_MAX_TOTAL_BYTES, validateMetaEntry, parseMetaPair, parseMetaFlags, } from "./metadata.js";
|
|
10
10
|
export { ATTACHMENTS_MARKER, attachmentsCommentBody, GH_FALLBACK_AUTHOR_NOTE, ghAttachmentKey, ghKeyPrefix, ghMetadataFromTarget, ghBranchAttachmentKey, ghBranchKeyPrefix, ghMetadataForBranch, isValidRepo, parseRepoFromRemoteUrl, type AttachmentItem, type GhTarget, type GhTargetKind, } from "./github.js";
|
package/dist/mcp/tools.js
CHANGED
|
@@ -1075,34 +1075,64 @@ export function createUploadsMcpTools(opts) {
|
|
|
1075
1075
|
},
|
|
1076
1076
|
{
|
|
1077
1077
|
name: "find_files",
|
|
1078
|
-
description: "Find objects
|
|
1078
|
+
description: "Find objects whose queryable custom metadata matches ALL of `filters` (ANDed equality) and/or whose key contains `name` (case-insensitive substring). At least one of `filters` or `name` is required. Returns each match's key, public URL, full metadata map, and optional `truncated`. Same as `uploads find k=v...` / `uploads find --name <term>`.",
|
|
1079
1079
|
inputSchema: {
|
|
1080
1080
|
type: "object",
|
|
1081
1081
|
properties: {
|
|
1082
1082
|
filters: {
|
|
1083
1083
|
...metadataProp,
|
|
1084
|
-
description: "Metadata equality filters (
|
|
1084
|
+
description: "Metadata equality filters (optional when `name` is set). " + METADATA_DESCRIPTION,
|
|
1085
|
+
},
|
|
1086
|
+
name: {
|
|
1087
|
+
type: "string",
|
|
1088
|
+
description: "Case-insensitive substring match on object keys (1–128 chars). Optional when `filters` is non-empty.",
|
|
1089
|
+
},
|
|
1090
|
+
prefix: {
|
|
1091
|
+
type: "string",
|
|
1092
|
+
description: "Key prefix filter, combinable with filters/name.",
|
|
1085
1093
|
},
|
|
1086
|
-
prefix: { type: "string", description: "Key prefix filter, combinable with filters." },
|
|
1087
1094
|
limit: { type: "number", description: "Page size (default 50, max 500)." },
|
|
1088
1095
|
workspace: workspaceProp,
|
|
1089
1096
|
},
|
|
1090
|
-
required: ["filters"],
|
|
1091
1097
|
additionalProperties: false,
|
|
1092
1098
|
},
|
|
1093
1099
|
async handler(args) {
|
|
1094
|
-
const filters = optStringRecord(args, "filters");
|
|
1095
|
-
|
|
1096
|
-
|
|
1100
|
+
const filters = optStringRecord(args, "filters") ?? {};
|
|
1101
|
+
const name = optString(args, "name");
|
|
1102
|
+
const hasMeta = Object.keys(filters).length > 0;
|
|
1103
|
+
if (!hasMeta && !name) {
|
|
1104
|
+
usage("find_files requires filters and/or name");
|
|
1097
1105
|
}
|
|
1098
|
-
|
|
1106
|
+
if (hasMeta)
|
|
1107
|
+
validateMetaMap(filters);
|
|
1099
1108
|
const { client } = clientFor(args);
|
|
1100
1109
|
return client.findFiles(filters, {
|
|
1110
|
+
name,
|
|
1101
1111
|
prefix: optString(args, "prefix"),
|
|
1102
1112
|
limit: optPosInt(args, "limit"),
|
|
1103
1113
|
});
|
|
1104
1114
|
},
|
|
1105
1115
|
},
|
|
1116
|
+
{
|
|
1117
|
+
name: "list_metadata_keys",
|
|
1118
|
+
description: "List the distinct queryable metadata keys present in the workspace, with file counts and distinct-value counts. Use this to discover what is filterable before calling find_files — keys are user/agent-defined, not a fixed schema. Same as `uploads meta keys`. Pass optional `key` to list that key's values instead (`uploads meta values <key>`).",
|
|
1119
|
+
inputSchema: {
|
|
1120
|
+
type: "object",
|
|
1121
|
+
properties: {
|
|
1122
|
+
key: {
|
|
1123
|
+
type: "string",
|
|
1124
|
+
description: "When set, return distinct values for this metadata key (with counts) instead of the key list.",
|
|
1125
|
+
},
|
|
1126
|
+
workspace: workspaceProp,
|
|
1127
|
+
},
|
|
1128
|
+
additionalProperties: false,
|
|
1129
|
+
},
|
|
1130
|
+
async handler(args) {
|
|
1131
|
+
const { client } = clientFor(args);
|
|
1132
|
+
const key = optString(args, "key");
|
|
1133
|
+
return key ? client.listMetadataValues(key) : client.listMetadataKeys();
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1106
1136
|
{
|
|
1107
1137
|
name: "usage",
|
|
1108
1138
|
description: "Workspace storage and monthly upload counters (and remaining headroom when budgets are configured). Same as `uploads usage`.",
|