faces-cli 1.7.7 → 1.7.9
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/commands/compile/doc/index.d.ts +8 -1
- package/dist/commands/compile/doc/index.js +173 -64
- package/dist/commands/compile/doc/pause.d.ts +3 -0
- package/dist/commands/compile/doc/pause.js +52 -5
- package/dist/commands/compile/thread/pause.d.ts +3 -0
- package/dist/commands/compile/thread/pause.js +53 -5
- package/dist/poll.d.ts +21 -0
- package/dist/poll.js +34 -0
- package/oclif.manifest.json +176 -141
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base.js';
|
|
2
2
|
export default class CompileDoc extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static examples: string[];
|
|
4
5
|
static flags: {
|
|
5
6
|
label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
7
|
content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
file: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
perspective: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -16,4 +17,10 @@ export default class CompileDoc extends BaseCommand {
|
|
|
16
17
|
face_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
17
18
|
};
|
|
18
19
|
run(): Promise<unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* `documents` is always present and always in input order. For a single
|
|
22
|
+
* source the compiled/started body is also spread at the top level, so
|
|
23
|
+
* callers that already read `.document_id` keep working.
|
|
24
|
+
*/
|
|
25
|
+
private shape;
|
|
19
26
|
}
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
3
4
|
import { BaseCommand } from '../../../base.js';
|
|
4
5
|
import { FacesAPIError } from '../../../client.js';
|
|
5
6
|
import { pollCompileProgress } from '../../../poll.js';
|
|
6
7
|
export default class CompileDoc extends BaseCommand {
|
|
7
|
-
static description = 'Compile
|
|
8
|
+
static description = 'Compile one or more documents into a face (create → make in one step). ' +
|
|
9
|
+
'Repeat --file to submit several documents in a single command; each file becomes its own ' +
|
|
10
|
+
'document rather than being concatenated.';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> <%= command.id %> alice --file notes.txt',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> alice --file a.txt --file b.txt --file c.txt --no-wait --json',
|
|
14
|
+
];
|
|
8
15
|
static flags = {
|
|
9
16
|
...BaseCommand.baseFlags,
|
|
10
|
-
label: Flags.string({ description: 'Document label/title' }),
|
|
11
|
-
content: Flags.string({ description: 'Inline text content' }),
|
|
12
|
-
file: Flags.string({ description: 'Read content from file' }),
|
|
17
|
+
label: Flags.string({ description: 'Document label/title (single document only; with several files each is labelled by its filename)' }),
|
|
18
|
+
content: Flags.string({ description: 'Inline text content', exclusive: ['file'] }),
|
|
19
|
+
file: Flags.string({ description: 'Read content from file (repeatable — each file becomes its own document)', multiple: true }),
|
|
13
20
|
perspective: Flags.string({
|
|
14
21
|
description: 'Perspective',
|
|
15
22
|
options: ['first-person', 'third-person'],
|
|
16
23
|
default: 'first-person',
|
|
17
24
|
}),
|
|
18
|
-
timeout: Flags.integer({ description: 'Compile timeout in seconds (default: 600)', default: 600 }),
|
|
25
|
+
timeout: Flags.integer({ description: 'Compile timeout in seconds, per document (default: 600)', default: 600 }),
|
|
19
26
|
'no-wait': Flags.boolean({
|
|
20
27
|
description: 'Create and trigger compilation, then return immediately without polling.',
|
|
21
28
|
default: false,
|
|
@@ -28,79 +35,181 @@ export default class CompileDoc extends BaseCommand {
|
|
|
28
35
|
const { args, flags } = await this.parse(CompileDoc);
|
|
29
36
|
const client = this.makeClient(flags);
|
|
30
37
|
const json = this.jsonEnabled();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (flags.file) {
|
|
34
|
-
if (!fs.existsSync(flags.file))
|
|
35
|
-
this.error(`File not found: ${flags.file}`);
|
|
36
|
-
content = fs.readFileSync(flags.file, 'utf8');
|
|
37
|
-
}
|
|
38
|
-
if (!content)
|
|
38
|
+
const files = flags.file ?? [];
|
|
39
|
+
if (files.length === 0 && !flags.content)
|
|
39
40
|
this.error('Provide --content or --file.');
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
process.stderr.write('Creating document... ');
|
|
43
|
-
let doc;
|
|
44
|
-
try {
|
|
45
|
-
const payload = { alias: args.face_id, content };
|
|
46
|
-
if (flags.label)
|
|
47
|
-
payload.label = flags.label;
|
|
48
|
-
if (flags.perspective)
|
|
49
|
-
payload.perspective = flags.perspective;
|
|
50
|
-
doc = await client.post('/v1/compile/documents', { body: payload });
|
|
51
|
-
}
|
|
52
|
-
catch (err) {
|
|
53
|
-
if (err instanceof FacesAPIError)
|
|
54
|
-
this.error(`Error (${err.statusCode}): ${err.message}`);
|
|
55
|
-
throw err;
|
|
41
|
+
if (flags.label && files.length > 1) {
|
|
42
|
+
this.error('--label applies to a single document. With several --file flags each document is labelled by its filename.');
|
|
56
43
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
44
|
+
// Read every source up front, keeping failures in place rather than
|
|
45
|
+
// collecting them separately — results must come back in input order so a
|
|
46
|
+
// caller can line them up against what it passed. A file that cannot be
|
|
47
|
+
// used is a per-file failure, not a reason to abandon the files that are
|
|
48
|
+
// fine: a whole-call failure leaves the caller unable to tell which file
|
|
49
|
+
// was at fault.
|
|
50
|
+
const sources = files.map((file) => {
|
|
51
|
+
try {
|
|
52
|
+
return { name: file, content: readTextFile(file) };
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
return { name: file, readError: err instanceof Error ? err.message : String(err) };
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
if (flags.content)
|
|
59
|
+
sources.push({ name: '(--content)', content: flags.content });
|
|
60
|
+
const results = [];
|
|
61
|
+
const multi = files.length > 1;
|
|
62
|
+
const started = [];
|
|
63
|
+
for (const source of sources) {
|
|
64
|
+
if (source.readError !== undefined) {
|
|
65
|
+
results.push({ file: source.name, error: source.readError });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const payload = { alias: args.face_id, content: source.content };
|
|
70
|
+
if (flags.label)
|
|
71
|
+
payload.label = flags.label;
|
|
72
|
+
else if (multi)
|
|
73
|
+
payload.label = path.basename(source.name);
|
|
74
|
+
if (flags.perspective)
|
|
75
|
+
payload.perspective = flags.perspective;
|
|
76
|
+
if (!json)
|
|
77
|
+
process.stderr.write(`Creating document${multi ? ` (${source.name})` : ''}... `);
|
|
78
|
+
const doc = (await client.post('/v1/compile/documents', { body: payload }));
|
|
79
|
+
const docId = String(doc.document_id ?? doc.id ?? '');
|
|
80
|
+
if (!docId)
|
|
81
|
+
throw new Error('Server did not return a document_id');
|
|
82
|
+
if (!json)
|
|
83
|
+
process.stderr.write(`done (${docId})\n`);
|
|
84
|
+
const makeResponse = (await client.post(`/v1/compile/documents/${docId}/make`));
|
|
85
|
+
const chunksTotal = makeResponse.chunks_total;
|
|
86
|
+
started.push({ source, docId, chunksTotal });
|
|
87
|
+
results.push({ file: source.name, document_id: docId, chunks_total: chunksTotal });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
if (!json)
|
|
91
|
+
process.stderr.write('failed\n');
|
|
92
|
+
const message = err instanceof FacesAPIError ? `Error (${err.statusCode}): ${err.message}` : err instanceof Error ? err.message : String(err);
|
|
93
|
+
results.push({ file: source.name, error: message });
|
|
94
|
+
}
|
|
64
95
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
96
|
+
if (started.length === 0) {
|
|
97
|
+
// Nothing was accepted — report every reason, then fail.
|
|
98
|
+
for (const r of results)
|
|
99
|
+
if (r.error)
|
|
100
|
+
process.stderr.write(`${r.file}: ${r.error}\n`);
|
|
101
|
+
this.error(results.length === 1 ? 'Document was not accepted.' : 'No documents were accepted.');
|
|
69
102
|
}
|
|
70
|
-
|
|
103
|
+
if (!json)
|
|
104
|
+
for (const r of results)
|
|
105
|
+
if (r.error)
|
|
106
|
+
process.stderr.write(`${r.file}: ${r.error}\n`);
|
|
71
107
|
if (flags['no-wait']) {
|
|
72
108
|
if (!json) {
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
for (const s of started) {
|
|
110
|
+
process.stderr.write(`Compilation started in background${s.chunksTotal ? ` (${s.chunksTotal} chunks)` : ''}: ${s.docId}\n`);
|
|
111
|
+
}
|
|
112
|
+
process.stderr.write(`Poll with: faces compile:doc:get <id> --json\n`);
|
|
75
113
|
}
|
|
76
|
-
return
|
|
114
|
+
return this.shape(results);
|
|
77
115
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
116
|
+
for (const s of started) {
|
|
117
|
+
if (!json)
|
|
118
|
+
process.stderr.write(`Compiling ${multi ? `${s.source.name} ` : ''}${s.chunksTotal ? `(${s.chunksTotal} chunks)` : ''}:\n`);
|
|
119
|
+
try {
|
|
120
|
+
const result = (await pollCompileProgress(client, s.docId, {
|
|
121
|
+
timeoutMs: flags.timeout * 1000,
|
|
122
|
+
onProgress: (p) => {
|
|
123
|
+
if (json)
|
|
124
|
+
return;
|
|
87
125
|
const c = p.current_counts;
|
|
88
126
|
const counts = c ? `ε=${c.epsilon} β=${c.beta} δ=${c.delta} α=${c.alpha}` : '';
|
|
89
127
|
const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
|
|
90
128
|
process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
129
|
+
},
|
|
130
|
+
}));
|
|
131
|
+
const hit = results.find((r) => r.document_id === s.docId);
|
|
132
|
+
if (hit)
|
|
133
|
+
hit.compiled = result;
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const hit = results.find((r) => r.document_id === s.docId);
|
|
137
|
+
if (hit)
|
|
138
|
+
hit.error = err instanceof Error ? err.message : String(err);
|
|
139
|
+
if (!json)
|
|
140
|
+
process.stderr.write(`${s.source.name}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
141
|
+
}
|
|
99
142
|
}
|
|
100
143
|
if (!json)
|
|
101
144
|
process.stderr.write('Done.\n');
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
145
|
+
return this.shape(results);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `documents` is always present and always in input order. For a single
|
|
149
|
+
* source the compiled/started body is also spread at the top level, so
|
|
150
|
+
* callers that already read `.document_id` keep working.
|
|
151
|
+
*/
|
|
152
|
+
shape(results) {
|
|
153
|
+
const documents = results.map((r) => {
|
|
154
|
+
const out = { file: r.file };
|
|
155
|
+
if (r.document_id)
|
|
156
|
+
out.document_id = r.document_id;
|
|
157
|
+
if (r.chunks_total !== undefined)
|
|
158
|
+
out.chunks_total = r.chunks_total;
|
|
159
|
+
if (r.error)
|
|
160
|
+
out.error = r.error;
|
|
161
|
+
return out;
|
|
162
|
+
});
|
|
163
|
+
const body = { documents };
|
|
164
|
+
if (results.length === 1) {
|
|
165
|
+
const only = results[0];
|
|
166
|
+
Object.assign(body, only.compiled ?? {}, only.document_id ? { document_id: only.document_id } : {});
|
|
167
|
+
if (only.chunks_total !== undefined && body.chunks_total === undefined)
|
|
168
|
+
body.chunks_total = only.chunks_total;
|
|
169
|
+
body.documents = documents;
|
|
170
|
+
}
|
|
171
|
+
return body;
|
|
105
172
|
}
|
|
106
173
|
}
|
|
174
|
+
/** Extensions the compile-documents endpoint takes as inline text. */
|
|
175
|
+
const BINARY_HINTS = {
|
|
176
|
+
'.pdf': 'PDF is supported, but not by this command — use: faces compile:upload <alias> --file <path> --kind document',
|
|
177
|
+
'.docx': 'Word documents are not supported. Convert it to text first (e.g. `textutil -convert txt file.docx` on macOS).',
|
|
178
|
+
'.doc': 'Word documents are not supported. Convert it to text first.',
|
|
179
|
+
'.pages': 'Pages documents are not supported. Export it as text or PDF first.',
|
|
180
|
+
'.rtf': 'RTF is not supported. Convert it to plain text first (e.g. `textutil -convert txt file.rtf`).',
|
|
181
|
+
'.key': 'Keynote files are not supported. Export the text first.',
|
|
182
|
+
'.pptx': 'PowerPoint files are not supported. Export the text first.',
|
|
183
|
+
'.xlsx': 'Excel files are not supported. Export as .csv first.',
|
|
184
|
+
'.zip': 'Archives are not supported. Extract it and pass the text files.',
|
|
185
|
+
'.epub': 'EPUB is not supported. Convert it to text first.',
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Read a file as text, refusing binary before it reaches the API.
|
|
189
|
+
*
|
|
190
|
+
* `compile:doc` posts its content as a text field, so a binary file used to
|
|
191
|
+
* travel all the way to the server and come back as a 500 ("A string literal
|
|
192
|
+
* cannot contain NUL (0x00) characters") that named neither the file nor the
|
|
193
|
+
* cause. The NUL scan is the general check — the extension table only makes the
|
|
194
|
+
* message more useful for formats people actually try.
|
|
195
|
+
*/
|
|
196
|
+
function readTextFile(file) {
|
|
197
|
+
if (!fs.existsSync(file))
|
|
198
|
+
throw new Error(`File not found: ${file}`);
|
|
199
|
+
const stat = fs.statSync(file);
|
|
200
|
+
if (stat.isDirectory())
|
|
201
|
+
throw new Error(`Not a file: ${file}`);
|
|
202
|
+
// Extension first, and independent of the byte scan: a small PDF can contain
|
|
203
|
+
// no NUL at all and would otherwise be posted as mojibake. A known format
|
|
204
|
+
// always gets the answer for that format.
|
|
205
|
+
const hint = BINARY_HINTS[path.extname(file).toLowerCase()];
|
|
206
|
+
if (hint)
|
|
207
|
+
throw new Error(hint);
|
|
208
|
+
const buf = fs.readFileSync(file);
|
|
209
|
+
if (buf.includes(0))
|
|
210
|
+
throw new Error('Looks like a binary file, not text. Convert it to text first.');
|
|
211
|
+
const content = buf.toString('utf8');
|
|
212
|
+
if (content.trim() === '')
|
|
213
|
+
throw new Error('File is empty.');
|
|
214
|
+
return content;
|
|
215
|
+
}
|
|
@@ -2,6 +2,8 @@ import { BaseCommand } from '../../../base.js';
|
|
|
2
2
|
export default class CompileDocPause extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
|
+
'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
|
+
timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
7
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
8
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
9
|
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -10,4 +12,5 @@ export default class CompileDocPause extends BaseCommand {
|
|
|
10
12
|
doc_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
11
13
|
};
|
|
12
14
|
run(): Promise<unknown>;
|
|
15
|
+
private report;
|
|
13
16
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { Args } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../../base.js';
|
|
3
3
|
import { FacesAPIError } from '../../../client.js';
|
|
4
|
+
import { pollUntilPaused } from '../../../poll.js';
|
|
4
5
|
export default class CompileDocPause extends BaseCommand {
|
|
5
|
-
static description = '
|
|
6
|
+
static description = 'Ask a running document compilation to stop (preserves progress, resume with make). ' +
|
|
7
|
+
'The compile stops at the next chunk boundary; this waits for it unless --no-wait.';
|
|
6
8
|
static flags = {
|
|
7
9
|
...BaseCommand.baseFlags,
|
|
10
|
+
'no-wait': Flags.boolean({
|
|
11
|
+
description: 'Return as soon as the pause is accepted, without waiting for the compile to stop.',
|
|
12
|
+
default: false,
|
|
13
|
+
}),
|
|
14
|
+
timeout: Flags.integer({ description: 'Seconds to wait for the compile to stop (default: 60)', default: 60 }),
|
|
8
15
|
};
|
|
9
16
|
static args = {
|
|
10
17
|
doc_id: Args.string({ description: 'Document ID', required: true }),
|
|
@@ -12,6 +19,7 @@ export default class CompileDocPause extends BaseCommand {
|
|
|
12
19
|
async run() {
|
|
13
20
|
const { args, flags } = await this.parse(CompileDocPause);
|
|
14
21
|
const client = this.makeClient(flags);
|
|
22
|
+
const json = this.jsonEnabled();
|
|
15
23
|
let data;
|
|
16
24
|
try {
|
|
17
25
|
data = await client.post(`/v1/compile/documents/${args.doc_id}/pause`);
|
|
@@ -21,9 +29,48 @@ export default class CompileDocPause extends BaseCommand {
|
|
|
21
29
|
this.error(`Error (${err.statusCode}): ${err.message}`);
|
|
22
30
|
throw err;
|
|
23
31
|
}
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
// POST /pause only acknowledges the request — it answers "pausing", not
|
|
33
|
+
// "paused". Reporting it as stopped here would be a guess, and before the
|
|
34
|
+
// backend fix it was usually a wrong one. prepare_status is the authority.
|
|
35
|
+
if (flags['no-wait']) {
|
|
36
|
+
if (!json) {
|
|
37
|
+
process.stderr.write(`Pausing. Poll with: faces compile:doc:get ${args.doc_id} --json\n`);
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
if (!json)
|
|
42
|
+
process.stderr.write('Pausing');
|
|
43
|
+
const status = await pollUntilPaused(client, args.doc_id, {
|
|
44
|
+
timeoutMs: flags.timeout * 1000,
|
|
45
|
+
onTick: () => {
|
|
46
|
+
if (!json && process.stderr.isTTY)
|
|
47
|
+
process.stderr.write('.');
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
if (!json)
|
|
51
|
+
process.stderr.write('\n');
|
|
52
|
+
if (!json)
|
|
53
|
+
this.log(this.report(status, args.doc_id, flags.timeout));
|
|
54
|
+
return { ...data, prepare_status: status ?? null };
|
|
55
|
+
}
|
|
56
|
+
report(status, id, timeout) {
|
|
57
|
+
switch (status) {
|
|
58
|
+
case 'paused': {
|
|
59
|
+
return `Paused. Resume with: faces compile:doc:make ${id}`;
|
|
60
|
+
}
|
|
61
|
+
case 'synced':
|
|
62
|
+
case 'ready': {
|
|
63
|
+
return 'Compile finished before the pause took effect — nothing to resume.';
|
|
64
|
+
}
|
|
65
|
+
case 'failed': {
|
|
66
|
+
return 'Compile failed before the pause took effect.';
|
|
67
|
+
}
|
|
68
|
+
case null: {
|
|
69
|
+
return 'Stopped — the compile is no longer running.';
|
|
70
|
+
}
|
|
71
|
+
default: {
|
|
72
|
+
return `Still stopping after ${timeout}s. Poll with: faces compile:doc:get ${id} --json`;
|
|
73
|
+
}
|
|
26
74
|
}
|
|
27
|
-
return data;
|
|
28
75
|
}
|
|
29
76
|
}
|
|
@@ -2,6 +2,8 @@ import { BaseCommand } from '../../../base.js';
|
|
|
2
2
|
export default class CompileThreadPause extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
|
+
'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
|
+
timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
7
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
8
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
9
|
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -10,4 +12,5 @@ export default class CompileThreadPause extends BaseCommand {
|
|
|
10
12
|
thread_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
11
13
|
};
|
|
12
14
|
run(): Promise<unknown>;
|
|
15
|
+
private report;
|
|
13
16
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { Args } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../../base.js';
|
|
3
3
|
import { FacesAPIError } from '../../../client.js';
|
|
4
|
+
import { pollUntilPaused } from '../../../poll.js';
|
|
4
5
|
export default class CompileThreadPause extends BaseCommand {
|
|
5
|
-
static description = '
|
|
6
|
+
static description = 'Ask a running thread compilation to stop (preserves progress, resume with make). ' +
|
|
7
|
+
'The compile stops at the next chunk boundary; this waits for it unless --no-wait.';
|
|
6
8
|
static flags = {
|
|
7
9
|
...BaseCommand.baseFlags,
|
|
10
|
+
'no-wait': Flags.boolean({
|
|
11
|
+
description: 'Return as soon as the pause is accepted, without waiting for the compile to stop.',
|
|
12
|
+
default: false,
|
|
13
|
+
}),
|
|
14
|
+
timeout: Flags.integer({ description: 'Seconds to wait for the compile to stop (default: 60)', default: 60 }),
|
|
8
15
|
};
|
|
9
16
|
static args = {
|
|
10
17
|
thread_id: Args.string({ description: 'Thread ID', required: true }),
|
|
@@ -12,6 +19,7 @@ export default class CompileThreadPause extends BaseCommand {
|
|
|
12
19
|
async run() {
|
|
13
20
|
const { args, flags } = await this.parse(CompileThreadPause);
|
|
14
21
|
const client = this.makeClient(flags);
|
|
22
|
+
const json = this.jsonEnabled();
|
|
15
23
|
let data;
|
|
16
24
|
try {
|
|
17
25
|
data = await client.post(`/v1/compile/threads/${args.thread_id}/pause`);
|
|
@@ -21,9 +29,49 @@ export default class CompileThreadPause extends BaseCommand {
|
|
|
21
29
|
this.error(`Error (${err.statusCode}): ${err.message}`);
|
|
22
30
|
throw err;
|
|
23
31
|
}
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
// POST /pause only acknowledges the request — it answers "pausing", not
|
|
33
|
+
// "paused". Reporting it as stopped here would be a guess, and before the
|
|
34
|
+
// backend fix it was usually a wrong one. prepare_status is the authority.
|
|
35
|
+
if (flags['no-wait']) {
|
|
36
|
+
if (!json) {
|
|
37
|
+
process.stderr.write(`Pausing. Poll with: faces compile:thread:get ${args.thread_id} --json\n`);
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
if (!json)
|
|
42
|
+
process.stderr.write('Pausing');
|
|
43
|
+
const status = await pollUntilPaused(client, args.thread_id, {
|
|
44
|
+
endpoint: `/v1/compile/threads/${args.thread_id}`,
|
|
45
|
+
timeoutMs: flags.timeout * 1000,
|
|
46
|
+
onTick: () => {
|
|
47
|
+
if (!json && process.stderr.isTTY)
|
|
48
|
+
process.stderr.write('.');
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
if (!json)
|
|
52
|
+
process.stderr.write('\n');
|
|
53
|
+
if (!json)
|
|
54
|
+
this.log(this.report(status, args.thread_id, flags.timeout));
|
|
55
|
+
return { ...data, prepare_status: status ?? null };
|
|
56
|
+
}
|
|
57
|
+
report(status, id, timeout) {
|
|
58
|
+
switch (status) {
|
|
59
|
+
case 'paused': {
|
|
60
|
+
return `Paused. Resume with: faces compile:thread:make ${id}`;
|
|
61
|
+
}
|
|
62
|
+
case 'synced':
|
|
63
|
+
case 'ready': {
|
|
64
|
+
return 'Compile finished before the pause took effect — nothing to resume.';
|
|
65
|
+
}
|
|
66
|
+
case 'failed': {
|
|
67
|
+
return 'Compile failed before the pause took effect.';
|
|
68
|
+
}
|
|
69
|
+
case null: {
|
|
70
|
+
return 'Stopped — the compile is no longer running.';
|
|
71
|
+
}
|
|
72
|
+
default: {
|
|
73
|
+
return `Still stopping after ${timeout}s. Poll with: faces compile:thread:get ${id} --json`;
|
|
74
|
+
}
|
|
26
75
|
}
|
|
27
|
-
return data;
|
|
28
76
|
}
|
|
29
77
|
}
|
package/dist/poll.d.ts
CHANGED
|
@@ -30,3 +30,24 @@ export interface PollOptions {
|
|
|
30
30
|
* Throws on timeout or failure.
|
|
31
31
|
*/
|
|
32
32
|
export declare function pollCompileProgress(client: FacesClient, id: string, opts?: PollOptions): Promise<unknown>;
|
|
33
|
+
export interface PausePollOptions {
|
|
34
|
+
intervalMs?: number;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
onTick?: () => void;
|
|
37
|
+
/** Override the GET endpoint path. Defaults to /v1/compile/documents/{id}. */
|
|
38
|
+
endpoint?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Wait for a pause request to actually land.
|
|
42
|
+
*
|
|
43
|
+
* POST /pause only acknowledges the request — it returns `status: "pausing"` and
|
|
44
|
+
* the compile stops at the next chunk boundary, usually within a few seconds.
|
|
45
|
+
* `prepare_status` on the document is the authority on whether it has stopped.
|
|
46
|
+
*
|
|
47
|
+
* Returns that final status: `paused` when the pause landed, `synced`/`ready`
|
|
48
|
+
* when the compile finished before it could (a normal race, not an error),
|
|
49
|
+
* `failed` if it died, or null when nothing was running. Returns undefined if
|
|
50
|
+
* the timeout expires while it is still stopping — the pause was accepted
|
|
51
|
+
* either way, so callers should report rather than fail.
|
|
52
|
+
*/
|
|
53
|
+
export declare function pollUntilPaused(client: FacesClient, id: string, opts?: PausePollOptions): Promise<string | null | undefined>;
|
package/dist/poll.js
CHANGED
|
@@ -39,3 +39,37 @@ export async function pollCompileProgress(client, id, opts = {}) {
|
|
|
39
39
|
function sleep(ms) {
|
|
40
40
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Statuses that mean the compile is no longer running. `paused` is the outcome a
|
|
44
|
+
* pause is waiting for; the rest mean it stopped for some other reason first.
|
|
45
|
+
*/
|
|
46
|
+
const PAUSE_TERMINAL = new Set(['paused', 'synced', 'ready', 'failed']);
|
|
47
|
+
/**
|
|
48
|
+
* Wait for a pause request to actually land.
|
|
49
|
+
*
|
|
50
|
+
* POST /pause only acknowledges the request — it returns `status: "pausing"` and
|
|
51
|
+
* the compile stops at the next chunk boundary, usually within a few seconds.
|
|
52
|
+
* `prepare_status` on the document is the authority on whether it has stopped.
|
|
53
|
+
*
|
|
54
|
+
* Returns that final status: `paused` when the pause landed, `synced`/`ready`
|
|
55
|
+
* when the compile finished before it could (a normal race, not an error),
|
|
56
|
+
* `failed` if it died, or null when nothing was running. Returns undefined if
|
|
57
|
+
* the timeout expires while it is still stopping — the pause was accepted
|
|
58
|
+
* either way, so callers should report rather than fail.
|
|
59
|
+
*/
|
|
60
|
+
export async function pollUntilPaused(client, id, opts = {}) {
|
|
61
|
+
const interval = opts.intervalMs ?? 1000;
|
|
62
|
+
const timeout = opts.timeoutMs ?? 60_000;
|
|
63
|
+
const endpoint = opts.endpoint ?? `/v1/compile/documents/${id}`;
|
|
64
|
+
const start = Date.now();
|
|
65
|
+
while (Date.now() - start < timeout) {
|
|
66
|
+
await sleep(interval);
|
|
67
|
+
opts.onTick?.();
|
|
68
|
+
// status_only trims the body to the progress fields — this is a poll loop.
|
|
69
|
+
const data = await client.get(`${endpoint}?status_only=true`);
|
|
70
|
+
const status = data.prepare_status ?? null;
|
|
71
|
+
if (status === null || PAUSE_TERMINAL.has(status))
|
|
72
|
+
return status;
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -632,10 +632,10 @@
|
|
|
632
632
|
"whoami.js"
|
|
633
633
|
]
|
|
634
634
|
},
|
|
635
|
-
"
|
|
635
|
+
"billing:balance": {
|
|
636
636
|
"aliases": [],
|
|
637
637
|
"args": {},
|
|
638
|
-
"description": "
|
|
638
|
+
"description": "Show credit balance and payment method status",
|
|
639
639
|
"flags": {
|
|
640
640
|
"json": {
|
|
641
641
|
"description": "Format output as json.",
|
|
@@ -671,7 +671,7 @@
|
|
|
671
671
|
},
|
|
672
672
|
"hasDynamicHelp": false,
|
|
673
673
|
"hiddenAliases": [],
|
|
674
|
-
"id": "
|
|
674
|
+
"id": "billing:balance",
|
|
675
675
|
"pluginAlias": "faces-cli",
|
|
676
676
|
"pluginName": "faces-cli",
|
|
677
677
|
"pluginType": "core",
|
|
@@ -681,14 +681,14 @@
|
|
|
681
681
|
"relativePath": [
|
|
682
682
|
"dist",
|
|
683
683
|
"commands",
|
|
684
|
-
"
|
|
685
|
-
"
|
|
684
|
+
"billing",
|
|
685
|
+
"balance.js"
|
|
686
686
|
]
|
|
687
687
|
},
|
|
688
|
-
"
|
|
688
|
+
"billing:card-setup": {
|
|
689
689
|
"aliases": [],
|
|
690
690
|
"args": {},
|
|
691
|
-
"description": "
|
|
691
|
+
"description": "Get a Stripe card setup URL to save a payment method",
|
|
692
692
|
"flags": {
|
|
693
693
|
"json": {
|
|
694
694
|
"description": "Format output as json.",
|
|
@@ -720,23 +720,11 @@
|
|
|
720
720
|
"hasDynamicHelp": false,
|
|
721
721
|
"multiple": false,
|
|
722
722
|
"type": "option"
|
|
723
|
-
},
|
|
724
|
-
"fix": {
|
|
725
|
-
"description": "Rebuild missing or stale catalog entries from API",
|
|
726
|
-
"name": "fix",
|
|
727
|
-
"allowNo": false,
|
|
728
|
-
"type": "boolean"
|
|
729
|
-
},
|
|
730
|
-
"generate": {
|
|
731
|
-
"description": "Fix + generate missing descriptions via LLM",
|
|
732
|
-
"name": "generate",
|
|
733
|
-
"allowNo": false,
|
|
734
|
-
"type": "boolean"
|
|
735
723
|
}
|
|
736
724
|
},
|
|
737
725
|
"hasDynamicHelp": false,
|
|
738
726
|
"hiddenAliases": [],
|
|
739
|
-
"id": "
|
|
727
|
+
"id": "billing:card-setup",
|
|
740
728
|
"pluginAlias": "faces-cli",
|
|
741
729
|
"pluginName": "faces-cli",
|
|
742
730
|
"pluginType": "core",
|
|
@@ -746,14 +734,14 @@
|
|
|
746
734
|
"relativePath": [
|
|
747
735
|
"dist",
|
|
748
736
|
"commands",
|
|
749
|
-
"
|
|
750
|
-
"
|
|
737
|
+
"billing",
|
|
738
|
+
"card-setup.js"
|
|
751
739
|
]
|
|
752
740
|
},
|
|
753
|
-
"
|
|
741
|
+
"billing:llm-costs": {
|
|
754
742
|
"aliases": [],
|
|
755
743
|
"args": {},
|
|
756
|
-
"description": "List
|
|
744
|
+
"description": "List available LLMs and their per-token costs",
|
|
757
745
|
"flags": {
|
|
758
746
|
"json": {
|
|
759
747
|
"description": "Format output as json.",
|
|
@@ -785,11 +773,18 @@
|
|
|
785
773
|
"hasDynamicHelp": false,
|
|
786
774
|
"multiple": false,
|
|
787
775
|
"type": "option"
|
|
776
|
+
},
|
|
777
|
+
"provider": {
|
|
778
|
+
"description": "Filter by provider (e.g. openai, anthropic)",
|
|
779
|
+
"name": "provider",
|
|
780
|
+
"hasDynamicHelp": false,
|
|
781
|
+
"multiple": false,
|
|
782
|
+
"type": "option"
|
|
788
783
|
}
|
|
789
784
|
},
|
|
790
785
|
"hasDynamicHelp": false,
|
|
791
786
|
"hiddenAliases": [],
|
|
792
|
-
"id": "
|
|
787
|
+
"id": "billing:llm-costs",
|
|
793
788
|
"pluginAlias": "faces-cli",
|
|
794
789
|
"pluginName": "faces-cli",
|
|
795
790
|
"pluginType": "core",
|
|
@@ -799,14 +794,14 @@
|
|
|
799
794
|
"relativePath": [
|
|
800
795
|
"dist",
|
|
801
796
|
"commands",
|
|
802
|
-
"
|
|
803
|
-
"
|
|
797
|
+
"billing",
|
|
798
|
+
"llm-costs.js"
|
|
804
799
|
]
|
|
805
800
|
},
|
|
806
|
-
"
|
|
801
|
+
"billing:topup": {
|
|
807
802
|
"aliases": [],
|
|
808
803
|
"args": {},
|
|
809
|
-
"description": "
|
|
804
|
+
"description": "Top up credit balance using saved payment method",
|
|
810
805
|
"flags": {
|
|
811
806
|
"json": {
|
|
812
807
|
"description": "Format output as json.",
|
|
@@ -839,37 +834,25 @@
|
|
|
839
834
|
"multiple": false,
|
|
840
835
|
"type": "option"
|
|
841
836
|
},
|
|
842
|
-
"
|
|
843
|
-
"description": "
|
|
844
|
-
"name": "
|
|
845
|
-
"
|
|
846
|
-
"multiple": false,
|
|
847
|
-
"type": "option"
|
|
848
|
-
},
|
|
849
|
-
"install": {
|
|
850
|
-
"description": "Install a skill by name",
|
|
851
|
-
"name": "install",
|
|
837
|
+
"amount": {
|
|
838
|
+
"description": "Top-up amount in USD (min $1)",
|
|
839
|
+
"name": "amount",
|
|
840
|
+
"required": true,
|
|
852
841
|
"hasDynamicHelp": false,
|
|
853
842
|
"multiple": false,
|
|
854
843
|
"type": "option"
|
|
855
844
|
},
|
|
856
|
-
"
|
|
857
|
-
"description": "
|
|
858
|
-
"name": "
|
|
845
|
+
"payment-ref": {
|
|
846
|
+
"description": "Payment reference (admin/test path)",
|
|
847
|
+
"name": "payment-ref",
|
|
859
848
|
"hasDynamicHelp": false,
|
|
860
849
|
"multiple": false,
|
|
861
850
|
"type": "option"
|
|
862
|
-
},
|
|
863
|
-
"refresh": {
|
|
864
|
-
"description": "Force refresh the skill index (ignore cache)",
|
|
865
|
-
"name": "refresh",
|
|
866
|
-
"allowNo": false,
|
|
867
|
-
"type": "boolean"
|
|
868
851
|
}
|
|
869
852
|
},
|
|
870
853
|
"hasDynamicHelp": false,
|
|
871
854
|
"hiddenAliases": [],
|
|
872
|
-
"id": "
|
|
855
|
+
"id": "billing:topup",
|
|
873
856
|
"pluginAlias": "faces-cli",
|
|
874
857
|
"pluginName": "faces-cli",
|
|
875
858
|
"pluginType": "core",
|
|
@@ -879,19 +862,14 @@
|
|
|
879
862
|
"relativePath": [
|
|
880
863
|
"dist",
|
|
881
864
|
"commands",
|
|
882
|
-
"
|
|
883
|
-
"
|
|
865
|
+
"billing",
|
|
866
|
+
"topup.js"
|
|
884
867
|
]
|
|
885
868
|
},
|
|
886
|
-
"
|
|
869
|
+
"billing:usage": {
|
|
887
870
|
"aliases": [],
|
|
888
|
-
"args": {
|
|
889
|
-
|
|
890
|
-
"description": "Backup file path (default: most recent in ~/.faces/backups/)",
|
|
891
|
-
"name": "file"
|
|
892
|
-
}
|
|
893
|
-
},
|
|
894
|
-
"description": "Restore faces, teams, and source material from a backup snapshot",
|
|
871
|
+
"args": {},
|
|
872
|
+
"description": "Aggregated usage analytics",
|
|
895
873
|
"flags": {
|
|
896
874
|
"json": {
|
|
897
875
|
"description": "Format output as json.",
|
|
@@ -924,16 +902,37 @@
|
|
|
924
902
|
"multiple": false,
|
|
925
903
|
"type": "option"
|
|
926
904
|
},
|
|
927
|
-
"
|
|
928
|
-
"description": "
|
|
929
|
-
"name": "
|
|
930
|
-
"
|
|
931
|
-
"
|
|
905
|
+
"group-by": {
|
|
906
|
+
"description": "Group results",
|
|
907
|
+
"name": "group-by",
|
|
908
|
+
"hasDynamicHelp": false,
|
|
909
|
+
"multiple": false,
|
|
910
|
+
"options": [
|
|
911
|
+
"api_key",
|
|
912
|
+
"model",
|
|
913
|
+
"llm",
|
|
914
|
+
"date"
|
|
915
|
+
],
|
|
916
|
+
"type": "option"
|
|
917
|
+
},
|
|
918
|
+
"from": {
|
|
919
|
+
"description": "Start date (YYYY-MM-DD)",
|
|
920
|
+
"name": "from",
|
|
921
|
+
"hasDynamicHelp": false,
|
|
922
|
+
"multiple": false,
|
|
923
|
+
"type": "option"
|
|
924
|
+
},
|
|
925
|
+
"to": {
|
|
926
|
+
"description": "End date (YYYY-MM-DD)",
|
|
927
|
+
"name": "to",
|
|
928
|
+
"hasDynamicHelp": false,
|
|
929
|
+
"multiple": false,
|
|
930
|
+
"type": "option"
|
|
932
931
|
}
|
|
933
932
|
},
|
|
934
933
|
"hasDynamicHelp": false,
|
|
935
934
|
"hiddenAliases": [],
|
|
936
|
-
"id": "
|
|
935
|
+
"id": "billing:usage",
|
|
937
936
|
"pluginAlias": "faces-cli",
|
|
938
937
|
"pluginName": "faces-cli",
|
|
939
938
|
"pluginType": "core",
|
|
@@ -943,14 +942,14 @@
|
|
|
943
942
|
"relativePath": [
|
|
944
943
|
"dist",
|
|
945
944
|
"commands",
|
|
946
|
-
"
|
|
947
|
-
"
|
|
945
|
+
"billing",
|
|
946
|
+
"usage.js"
|
|
948
947
|
]
|
|
949
948
|
},
|
|
950
|
-
"
|
|
949
|
+
"catalog:backup": {
|
|
951
950
|
"aliases": [],
|
|
952
951
|
"args": {},
|
|
953
|
-
"description": "
|
|
952
|
+
"description": "Snapshot all faces, teams, and source material for migration",
|
|
954
953
|
"flags": {
|
|
955
954
|
"json": {
|
|
956
955
|
"description": "Format output as json.",
|
|
@@ -986,7 +985,7 @@
|
|
|
986
985
|
},
|
|
987
986
|
"hasDynamicHelp": false,
|
|
988
987
|
"hiddenAliases": [],
|
|
989
|
-
"id": "
|
|
988
|
+
"id": "catalog:backup",
|
|
990
989
|
"pluginAlias": "faces-cli",
|
|
991
990
|
"pluginName": "faces-cli",
|
|
992
991
|
"pluginType": "core",
|
|
@@ -996,14 +995,14 @@
|
|
|
996
995
|
"relativePath": [
|
|
997
996
|
"dist",
|
|
998
997
|
"commands",
|
|
999
|
-
"
|
|
1000
|
-
"
|
|
998
|
+
"catalog",
|
|
999
|
+
"backup.js"
|
|
1001
1000
|
]
|
|
1002
1001
|
},
|
|
1003
|
-
"
|
|
1002
|
+
"catalog:doctor": {
|
|
1004
1003
|
"aliases": [],
|
|
1005
1004
|
"args": {},
|
|
1006
|
-
"description": "
|
|
1005
|
+
"description": "Diagnose and repair the local face and team catalog",
|
|
1007
1006
|
"flags": {
|
|
1008
1007
|
"json": {
|
|
1009
1008
|
"description": "Format output as json.",
|
|
@@ -1035,11 +1034,23 @@
|
|
|
1035
1034
|
"hasDynamicHelp": false,
|
|
1036
1035
|
"multiple": false,
|
|
1037
1036
|
"type": "option"
|
|
1037
|
+
},
|
|
1038
|
+
"fix": {
|
|
1039
|
+
"description": "Rebuild missing or stale catalog entries from API",
|
|
1040
|
+
"name": "fix",
|
|
1041
|
+
"allowNo": false,
|
|
1042
|
+
"type": "boolean"
|
|
1043
|
+
},
|
|
1044
|
+
"generate": {
|
|
1045
|
+
"description": "Fix + generate missing descriptions via LLM",
|
|
1046
|
+
"name": "generate",
|
|
1047
|
+
"allowNo": false,
|
|
1048
|
+
"type": "boolean"
|
|
1038
1049
|
}
|
|
1039
1050
|
},
|
|
1040
1051
|
"hasDynamicHelp": false,
|
|
1041
1052
|
"hiddenAliases": [],
|
|
1042
|
-
"id": "
|
|
1053
|
+
"id": "catalog:doctor",
|
|
1043
1054
|
"pluginAlias": "faces-cli",
|
|
1044
1055
|
"pluginName": "faces-cli",
|
|
1045
1056
|
"pluginType": "core",
|
|
@@ -1049,14 +1060,14 @@
|
|
|
1049
1060
|
"relativePath": [
|
|
1050
1061
|
"dist",
|
|
1051
1062
|
"commands",
|
|
1052
|
-
"
|
|
1053
|
-
"
|
|
1063
|
+
"catalog",
|
|
1064
|
+
"doctor.js"
|
|
1054
1065
|
]
|
|
1055
1066
|
},
|
|
1056
|
-
"
|
|
1067
|
+
"catalog:list": {
|
|
1057
1068
|
"aliases": [],
|
|
1058
1069
|
"args": {},
|
|
1059
|
-
"description": "List
|
|
1070
|
+
"description": "List all faces in the local catalog",
|
|
1060
1071
|
"flags": {
|
|
1061
1072
|
"json": {
|
|
1062
1073
|
"description": "Format output as json.",
|
|
@@ -1088,18 +1099,11 @@
|
|
|
1088
1099
|
"hasDynamicHelp": false,
|
|
1089
1100
|
"multiple": false,
|
|
1090
1101
|
"type": "option"
|
|
1091
|
-
},
|
|
1092
|
-
"provider": {
|
|
1093
|
-
"description": "Filter by provider (e.g. openai, anthropic)",
|
|
1094
|
-
"name": "provider",
|
|
1095
|
-
"hasDynamicHelp": false,
|
|
1096
|
-
"multiple": false,
|
|
1097
|
-
"type": "option"
|
|
1098
1102
|
}
|
|
1099
1103
|
},
|
|
1100
1104
|
"hasDynamicHelp": false,
|
|
1101
1105
|
"hiddenAliases": [],
|
|
1102
|
-
"id": "
|
|
1106
|
+
"id": "catalog:list",
|
|
1103
1107
|
"pluginAlias": "faces-cli",
|
|
1104
1108
|
"pluginName": "faces-cli",
|
|
1105
1109
|
"pluginType": "core",
|
|
@@ -1109,14 +1113,14 @@
|
|
|
1109
1113
|
"relativePath": [
|
|
1110
1114
|
"dist",
|
|
1111
1115
|
"commands",
|
|
1112
|
-
"
|
|
1113
|
-
"
|
|
1116
|
+
"catalog",
|
|
1117
|
+
"list.js"
|
|
1114
1118
|
]
|
|
1115
1119
|
},
|
|
1116
|
-
"
|
|
1120
|
+
"catalog:manyfaced": {
|
|
1117
1121
|
"aliases": [],
|
|
1118
1122
|
"args": {},
|
|
1119
|
-
"description": "
|
|
1123
|
+
"description": "Browse and install community manyfaced skills from github.com/facessh/manyfaced",
|
|
1120
1124
|
"flags": {
|
|
1121
1125
|
"json": {
|
|
1122
1126
|
"description": "Format output as json.",
|
|
@@ -1149,25 +1153,37 @@
|
|
|
1149
1153
|
"multiple": false,
|
|
1150
1154
|
"type": "option"
|
|
1151
1155
|
},
|
|
1152
|
-
"
|
|
1153
|
-
"description": "
|
|
1154
|
-
"name": "
|
|
1155
|
-
"required": true,
|
|
1156
|
+
"skill": {
|
|
1157
|
+
"description": "Show details for a specific skill",
|
|
1158
|
+
"name": "skill",
|
|
1156
1159
|
"hasDynamicHelp": false,
|
|
1157
1160
|
"multiple": false,
|
|
1158
1161
|
"type": "option"
|
|
1159
1162
|
},
|
|
1160
|
-
"
|
|
1161
|
-
"description": "
|
|
1162
|
-
"name": "
|
|
1163
|
+
"install": {
|
|
1164
|
+
"description": "Install a skill by name",
|
|
1165
|
+
"name": "install",
|
|
1163
1166
|
"hasDynamicHelp": false,
|
|
1164
1167
|
"multiple": false,
|
|
1165
1168
|
"type": "option"
|
|
1169
|
+
},
|
|
1170
|
+
"skills-dir": {
|
|
1171
|
+
"description": "Directory to install skills into (required for --install)",
|
|
1172
|
+
"name": "skills-dir",
|
|
1173
|
+
"hasDynamicHelp": false,
|
|
1174
|
+
"multiple": false,
|
|
1175
|
+
"type": "option"
|
|
1176
|
+
},
|
|
1177
|
+
"refresh": {
|
|
1178
|
+
"description": "Force refresh the skill index (ignore cache)",
|
|
1179
|
+
"name": "refresh",
|
|
1180
|
+
"allowNo": false,
|
|
1181
|
+
"type": "boolean"
|
|
1166
1182
|
}
|
|
1167
1183
|
},
|
|
1168
1184
|
"hasDynamicHelp": false,
|
|
1169
1185
|
"hiddenAliases": [],
|
|
1170
|
-
"id": "
|
|
1186
|
+
"id": "catalog:manyfaced",
|
|
1171
1187
|
"pluginAlias": "faces-cli",
|
|
1172
1188
|
"pluginName": "faces-cli",
|
|
1173
1189
|
"pluginType": "core",
|
|
@@ -1177,14 +1193,19 @@
|
|
|
1177
1193
|
"relativePath": [
|
|
1178
1194
|
"dist",
|
|
1179
1195
|
"commands",
|
|
1180
|
-
"
|
|
1181
|
-
"
|
|
1196
|
+
"catalog",
|
|
1197
|
+
"manyfaced.js"
|
|
1182
1198
|
]
|
|
1183
1199
|
},
|
|
1184
|
-
"
|
|
1200
|
+
"catalog:restore": {
|
|
1185
1201
|
"aliases": [],
|
|
1186
|
-
"args": {
|
|
1187
|
-
|
|
1202
|
+
"args": {
|
|
1203
|
+
"file": {
|
|
1204
|
+
"description": "Backup file path (default: most recent in ~/.faces/backups/)",
|
|
1205
|
+
"name": "file"
|
|
1206
|
+
}
|
|
1207
|
+
},
|
|
1208
|
+
"description": "Restore faces, teams, and source material from a backup snapshot",
|
|
1188
1209
|
"flags": {
|
|
1189
1210
|
"json": {
|
|
1190
1211
|
"description": "Format output as json.",
|
|
@@ -1217,37 +1238,16 @@
|
|
|
1217
1238
|
"multiple": false,
|
|
1218
1239
|
"type": "option"
|
|
1219
1240
|
},
|
|
1220
|
-
"
|
|
1221
|
-
"description": "
|
|
1222
|
-
"name": "
|
|
1223
|
-
"
|
|
1224
|
-
"
|
|
1225
|
-
"options": [
|
|
1226
|
-
"api_key",
|
|
1227
|
-
"model",
|
|
1228
|
-
"llm",
|
|
1229
|
-
"date"
|
|
1230
|
-
],
|
|
1231
|
-
"type": "option"
|
|
1232
|
-
},
|
|
1233
|
-
"from": {
|
|
1234
|
-
"description": "Start date (YYYY-MM-DD)",
|
|
1235
|
-
"name": "from",
|
|
1236
|
-
"hasDynamicHelp": false,
|
|
1237
|
-
"multiple": false,
|
|
1238
|
-
"type": "option"
|
|
1239
|
-
},
|
|
1240
|
-
"to": {
|
|
1241
|
-
"description": "End date (YYYY-MM-DD)",
|
|
1242
|
-
"name": "to",
|
|
1243
|
-
"hasDynamicHelp": false,
|
|
1244
|
-
"multiple": false,
|
|
1245
|
-
"type": "option"
|
|
1241
|
+
"compile": {
|
|
1242
|
+
"description": "Run compile:all after restoring",
|
|
1243
|
+
"name": "compile",
|
|
1244
|
+
"allowNo": false,
|
|
1245
|
+
"type": "boolean"
|
|
1246
1246
|
}
|
|
1247
1247
|
},
|
|
1248
1248
|
"hasDynamicHelp": false,
|
|
1249
1249
|
"hiddenAliases": [],
|
|
1250
|
-
"id": "
|
|
1250
|
+
"id": "catalog:restore",
|
|
1251
1251
|
"pluginAlias": "faces-cli",
|
|
1252
1252
|
"pluginName": "faces-cli",
|
|
1253
1253
|
"pluginType": "core",
|
|
@@ -1257,8 +1257,8 @@
|
|
|
1257
1257
|
"relativePath": [
|
|
1258
1258
|
"dist",
|
|
1259
1259
|
"commands",
|
|
1260
|
-
"
|
|
1261
|
-
"
|
|
1260
|
+
"catalog",
|
|
1261
|
+
"restore.js"
|
|
1262
1262
|
]
|
|
1263
1263
|
},
|
|
1264
1264
|
"chat:chat": {
|
|
@@ -4499,7 +4499,11 @@
|
|
|
4499
4499
|
"required": true
|
|
4500
4500
|
}
|
|
4501
4501
|
},
|
|
4502
|
-
"description": "Compile
|
|
4502
|
+
"description": "Compile one or more documents into a face (create → make in one step). Repeat --file to submit several documents in a single command; each file becomes its own document rather than being concatenated.",
|
|
4503
|
+
"examples": [
|
|
4504
|
+
"<%= config.bin %> <%= command.id %> alice --file notes.txt",
|
|
4505
|
+
"<%= config.bin %> <%= command.id %> alice --file a.txt --file b.txt --file c.txt --no-wait --json"
|
|
4506
|
+
],
|
|
4503
4507
|
"flags": {
|
|
4504
4508
|
"json": {
|
|
4505
4509
|
"description": "Format output as json.",
|
|
@@ -4533,7 +4537,7 @@
|
|
|
4533
4537
|
"type": "option"
|
|
4534
4538
|
},
|
|
4535
4539
|
"label": {
|
|
4536
|
-
"description": "Document label/title",
|
|
4540
|
+
"description": "Document label/title (single document only; with several files each is labelled by its filename)",
|
|
4537
4541
|
"name": "label",
|
|
4538
4542
|
"hasDynamicHelp": false,
|
|
4539
4543
|
"multiple": false,
|
|
@@ -4541,16 +4545,19 @@
|
|
|
4541
4545
|
},
|
|
4542
4546
|
"content": {
|
|
4543
4547
|
"description": "Inline text content",
|
|
4548
|
+
"exclusive": [
|
|
4549
|
+
"file"
|
|
4550
|
+
],
|
|
4544
4551
|
"name": "content",
|
|
4545
4552
|
"hasDynamicHelp": false,
|
|
4546
4553
|
"multiple": false,
|
|
4547
4554
|
"type": "option"
|
|
4548
4555
|
},
|
|
4549
4556
|
"file": {
|
|
4550
|
-
"description": "Read content from file",
|
|
4557
|
+
"description": "Read content from file (repeatable — each file becomes its own document)",
|
|
4551
4558
|
"name": "file",
|
|
4552
4559
|
"hasDynamicHelp": false,
|
|
4553
|
-
"multiple":
|
|
4560
|
+
"multiple": true,
|
|
4554
4561
|
"type": "option"
|
|
4555
4562
|
},
|
|
4556
4563
|
"perspective": {
|
|
@@ -4566,7 +4573,7 @@
|
|
|
4566
4573
|
"type": "option"
|
|
4567
4574
|
},
|
|
4568
4575
|
"timeout": {
|
|
4569
|
-
"description": "Compile timeout in seconds (default: 600)",
|
|
4576
|
+
"description": "Compile timeout in seconds, per document (default: 600)",
|
|
4570
4577
|
"name": "timeout",
|
|
4571
4578
|
"default": 600,
|
|
4572
4579
|
"hasDynamicHelp": false,
|
|
@@ -4740,7 +4747,7 @@
|
|
|
4740
4747
|
"required": true
|
|
4741
4748
|
}
|
|
4742
4749
|
},
|
|
4743
|
-
"description": "
|
|
4750
|
+
"description": "Ask a running document compilation to stop (preserves progress, resume with make). The compile stops at the next chunk boundary; this waits for it unless --no-wait.",
|
|
4744
4751
|
"flags": {
|
|
4745
4752
|
"json": {
|
|
4746
4753
|
"description": "Format output as json.",
|
|
@@ -4772,6 +4779,20 @@
|
|
|
4772
4779
|
"hasDynamicHelp": false,
|
|
4773
4780
|
"multiple": false,
|
|
4774
4781
|
"type": "option"
|
|
4782
|
+
},
|
|
4783
|
+
"no-wait": {
|
|
4784
|
+
"description": "Return as soon as the pause is accepted, without waiting for the compile to stop.",
|
|
4785
|
+
"name": "no-wait",
|
|
4786
|
+
"allowNo": false,
|
|
4787
|
+
"type": "boolean"
|
|
4788
|
+
},
|
|
4789
|
+
"timeout": {
|
|
4790
|
+
"description": "Seconds to wait for the compile to stop (default: 60)",
|
|
4791
|
+
"name": "timeout",
|
|
4792
|
+
"default": 60,
|
|
4793
|
+
"hasDynamicHelp": false,
|
|
4794
|
+
"multiple": false,
|
|
4795
|
+
"type": "option"
|
|
4775
4796
|
}
|
|
4776
4797
|
},
|
|
4777
4798
|
"hasDynamicHelp": false,
|
|
@@ -5348,7 +5369,7 @@
|
|
|
5348
5369
|
"required": true
|
|
5349
5370
|
}
|
|
5350
5371
|
},
|
|
5351
|
-
"description": "
|
|
5372
|
+
"description": "Ask a running thread compilation to stop (preserves progress, resume with make). The compile stops at the next chunk boundary; this waits for it unless --no-wait.",
|
|
5352
5373
|
"flags": {
|
|
5353
5374
|
"json": {
|
|
5354
5375
|
"description": "Format output as json.",
|
|
@@ -5380,6 +5401,20 @@
|
|
|
5380
5401
|
"hasDynamicHelp": false,
|
|
5381
5402
|
"multiple": false,
|
|
5382
5403
|
"type": "option"
|
|
5404
|
+
},
|
|
5405
|
+
"no-wait": {
|
|
5406
|
+
"description": "Return as soon as the pause is accepted, without waiting for the compile to stop.",
|
|
5407
|
+
"name": "no-wait",
|
|
5408
|
+
"allowNo": false,
|
|
5409
|
+
"type": "boolean"
|
|
5410
|
+
},
|
|
5411
|
+
"timeout": {
|
|
5412
|
+
"description": "Seconds to wait for the compile to stop (default: 60)",
|
|
5413
|
+
"name": "timeout",
|
|
5414
|
+
"default": 60,
|
|
5415
|
+
"hasDynamicHelp": false,
|
|
5416
|
+
"multiple": false,
|
|
5417
|
+
"type": "option"
|
|
5383
5418
|
}
|
|
5384
5419
|
},
|
|
5385
5420
|
"hasDynamicHelp": false,
|
|
@@ -6102,5 +6137,5 @@
|
|
|
6102
6137
|
]
|
|
6103
6138
|
}
|
|
6104
6139
|
},
|
|
6105
|
-
"version": "1.7.
|
|
6140
|
+
"version": "1.7.9"
|
|
6106
6141
|
}
|