@pitcher/cli-scripts 2.15.0 → 2.16.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.
|
@@ -5,7 +5,7 @@ import { createCliError } from '../core/errors.js';
|
|
|
5
5
|
import { emitEnvelope } from '../core/output.js';
|
|
6
6
|
import { uploadFile } from '../core/upload.js';
|
|
7
7
|
import { listPageParams, listPaginationEcho } from '../core/pagination.js';
|
|
8
|
-
import { parseTags, parseJsonObject, validateAccessType, deepMerge, assertIntFlags } from '../schema/content.js';
|
|
8
|
+
import { parseTags, parseJsonObject, validateAccessType, validateFileType, deepMerge, assertIntFlags } from '../schema/content.js';
|
|
9
9
|
import { withAuthFlags } from './withAuth.js';
|
|
10
10
|
export async function runFilesList(
|
|
11
11
|
// NOTE: no folderId — the Django files list has no folder filter (listing a
|
|
@@ -15,12 +15,25 @@ opts, deps = {}) {
|
|
|
15
15
|
if (!opts.instanceId)
|
|
16
16
|
throw createCliError('VALIDATION', '--instance-id is required for files list');
|
|
17
17
|
assertIntFlags(opts);
|
|
18
|
+
const type = validateFileType(opts.type);
|
|
18
19
|
const client = clientFor(opts, deps);
|
|
19
20
|
const params = { instance_id: opts.instanceId, ...listPageParams(opts) };
|
|
20
21
|
if (opts.idIn)
|
|
21
22
|
params.id__in = opts.idIn;
|
|
22
23
|
if (opts.tags)
|
|
23
24
|
params.tags__in = opts.tags;
|
|
25
|
+
// Installable apps are file records too (type=app). Exclude them by default so a "list files"
|
|
26
|
+
// request returns real content, not the instance's installed apps. `--type <content|asset|app>`
|
|
27
|
+
// isolates one type and always wins; `--type all` restores the unfiltered listing. An explicit
|
|
28
|
+
// --id-in lookup bypasses the default exclusion — fetching a file by id is a targeted lookup, not
|
|
29
|
+
// a browse, so it must return that record whatever its type (an explicit --type still applies).
|
|
30
|
+
// Uses type__in to match the tags__in/id__in CSV convention above (a single value is a valid filter).
|
|
31
|
+
if (type && type !== 'all') {
|
|
32
|
+
params.type__in = type;
|
|
33
|
+
}
|
|
34
|
+
else if (!type && !opts.idIn) {
|
|
35
|
+
params.type__in = 'content,asset';
|
|
36
|
+
}
|
|
24
37
|
const res = await client.get('/api/v1/files/', { params });
|
|
25
38
|
return ok({ results: res.data.results, pagination: listPaginationEcho(opts, res.data) });
|
|
26
39
|
}
|
|
@@ -122,6 +135,7 @@ export function createFilesCommand() {
|
|
|
122
135
|
.option('--instance-id <id>', 'Instance ID (required)')
|
|
123
136
|
.option('--id-in <ids>', 'Comma-separated file IDs')
|
|
124
137
|
.option('--tags <tags>', 'Comma-separated tags (filters via tags__in)')
|
|
138
|
+
.option('--type <type>', 'File type: content, asset, app, or all (default excludes installable apps)')
|
|
125
139
|
.option('--page <n>', 'Page number', (v) => parseInt(v, 10))
|
|
126
140
|
.option('--page-size <n>', 'Page size (max 500)', (v) => parseInt(v, 10))
|
|
127
141
|
.action(async (options, command) => {
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { runFilesList, runFilesGet, runFilesCreate, runFilesUpdate } from '../../commands/files.js';
|
|
3
|
+
import { FILE_TYPES } from '../../schema/content.js';
|
|
3
4
|
export const filesTools = [
|
|
4
5
|
{
|
|
5
6
|
name: 'pitcher_files_list',
|
|
6
7
|
title: 'List files',
|
|
7
|
-
description: 'List content files in an instance.
|
|
8
|
+
description: 'List content files in an instance. Installed apps are stored as file records too, but are ' +
|
|
9
|
+
'EXCLUDED by default (content + assets only) so a "list files" request returns real content. ' +
|
|
10
|
+
'Pass type="app" for installed apps only, or type="all" to include everything. Filter by tags or specific IDs.',
|
|
8
11
|
inputShape: {
|
|
9
12
|
instanceId: z.string().describe('Instance ID (required)'),
|
|
10
13
|
idIn: z.string().optional().describe('Comma-separated file IDs'),
|
|
11
14
|
tags: z.string().optional().describe('Comma-separated tags'),
|
|
15
|
+
type: z
|
|
16
|
+
.enum(FILE_TYPES)
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('File type filter. Omit to list content + assets (installed apps excluded); "app" = apps only; "all" = everything including apps.'),
|
|
12
19
|
page: z.number().int().optional(),
|
|
13
20
|
pageSize: z.number().int().optional(),
|
|
14
21
|
},
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export declare const FILE_TYPES: readonly ["content", "asset", "app", "all"];
|
|
1
2
|
export declare function isPlainObject(v: unknown): v is Record<string, unknown>;
|
|
2
3
|
export declare function parseJsonObject(raw: string, flagName: string): Record<string, unknown>;
|
|
3
4
|
export declare function parseJsonArray(raw: string, flagName: string): unknown[];
|
|
4
5
|
export declare function parseTags(raw: string): string[];
|
|
5
6
|
export declare function validateAccessType(v: string | undefined): string | undefined;
|
|
7
|
+
export declare function validateFileType(v: string | undefined): string | undefined;
|
|
6
8
|
export declare function assertIntFlags(opts: {
|
|
7
9
|
page?: number;
|
|
8
10
|
pageSize?: number;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { createCliError } from '../core/errors.js';
|
|
2
2
|
const ACCESS_TYPES = ['public', 'restricted', 'personal'];
|
|
3
|
+
// File record types on Django's `pitcher_file.type` column, plus the `all` escape hatch.
|
|
4
|
+
// Installable apps are stored as file records (type=app), so `p files list` excludes them by default;
|
|
5
|
+
// `all` opts back into the unfiltered listing. See validateFileType / runFilesList.
|
|
6
|
+
// Exported so the MCP `pitcher_files_list` tool sources its zod enum from the same list (no drift).
|
|
7
|
+
export const FILE_TYPES = ['content', 'asset', 'app', 'all'];
|
|
3
8
|
export function isPlainObject(v) {
|
|
4
9
|
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
5
10
|
}
|
|
@@ -37,6 +42,14 @@ export function validateAccessType(v) {
|
|
|
37
42
|
}
|
|
38
43
|
return v;
|
|
39
44
|
}
|
|
45
|
+
export function validateFileType(v) {
|
|
46
|
+
if (v === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (!FILE_TYPES.includes(v)) {
|
|
49
|
+
throw createCliError('VALIDATION', `--type must be one of: ${FILE_TYPES.join(', ')}`);
|
|
50
|
+
}
|
|
51
|
+
return v;
|
|
52
|
+
}
|
|
40
53
|
// Guards against NaN reaching the request (commander's parseInt yields NaN on
|
|
41
54
|
// non-numeric input, e.g. `--page abc`).
|
|
42
55
|
export function assertIntFlags(opts) {
|