faces-cli 1.7.6 → 1.7.8
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/chat/chat.js +8 -0
- 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/dist/utils.d.ts +0 -15
- package/dist/utils.js +0 -75
- package/oclif.manifest.json +948 -1830
- package/package.json +1 -1
- package/dist/commands/billing/quota.d.ts +0 -10
- package/dist/commands/billing/quota.js +0 -12
- package/dist/commands/billing/subscription.d.ts +0 -10
- package/dist/commands/billing/subscription.js +0 -24
- package/dist/commands/face/upload.d.ts +0 -17
- package/dist/commands/face/upload.js +0 -66
- package/dist/commands/voiceprint/build-status.d.ts +0 -14
- package/dist/commands/voiceprint/build-status.js +0 -46
- package/dist/commands/voiceprint/build.d.ts +0 -23
- package/dist/commands/voiceprint/build.js +0 -109
- package/dist/commands/voiceprint/get.d.ts +0 -14
- package/dist/commands/voiceprint/get.js +0 -47
- package/dist/commands/voiceprint/refine-status.d.ts +0 -14
- package/dist/commands/voiceprint/refine-status.js +0 -43
- package/dist/commands/voiceprint/refine.d.ts +0 -23
- package/dist/commands/voiceprint/refine.js +0 -88
- package/dist/commands/voiceprint/revert.d.ts +0 -14
- package/dist/commands/voiceprint/revert.js +0 -36
- package/dist/commands/voiceprint/score-status.d.ts +0 -14
- package/dist/commands/voiceprint/score-status.js +0 -46
- package/dist/commands/voiceprint/score.d.ts +0 -24
- package/dist/commands/voiceprint/score.js +0 -114
- package/dist/commands/voiceprint/upload.d.ts +0 -17
- package/dist/commands/voiceprint/upload.js +0 -66
- package/dist/commands/voiceprint/versions.d.ts +0 -14
- package/dist/commands/voiceprint/versions.js +0 -44
- package/dist/voiceprint.d.ts +0 -28
- package/dist/voiceprint.js +0 -55
|
@@ -195,6 +195,14 @@ export default class ChatChat extends BaseCommand {
|
|
|
195
195
|
};
|
|
196
196
|
if (flags.system)
|
|
197
197
|
payload.instructions = flags.system;
|
|
198
|
+
// Responses names the output cap `max_output_tokens`, not `max_tokens`.
|
|
199
|
+
// `temperature` is accepted per-model (gpt-5.1/5.2/5.4 yes; gpt-5.5/5.6-*/o3
|
|
200
|
+
// no) with no capability flag to query, so we always send what the user asked
|
|
201
|
+
// for — the backend strips the param and retries when a model 400s on it.
|
|
202
|
+
if (flags['max-tokens'])
|
|
203
|
+
payload.max_output_tokens = flags['max-tokens'];
|
|
204
|
+
if (flags.temperature !== undefined)
|
|
205
|
+
payload.temperature = Number.parseFloat(flags.temperature);
|
|
198
206
|
if (flags['oauth-only'])
|
|
199
207
|
payload.oauth_only = true;
|
|
200
208
|
if (flags.stream) {
|
|
@@ -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/dist/utils.d.ts
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
/** Max length of a face's profile_addendum (per-face system prompt). Mirrors the backend. */
|
|
2
2
|
export declare const PROFILE_ADDENDUM_MAX_CHARS = 100000;
|
|
3
|
-
export interface VoiceprintEmail {
|
|
4
|
-
id: string;
|
|
5
|
-
text: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Load voiceprint writing samples from a JSON/JSONL file or a directory.
|
|
9
|
-
*
|
|
10
|
-
* - `file`: a JSON array of `{id?, text}` objects or plain strings, OR JSONL
|
|
11
|
-
* (one such value per line). The format is auto-detected.
|
|
12
|
-
* - `dir`: one sample per non-hidden file; `id` is the filename (sans extension),
|
|
13
|
-
* `text` is the file contents. Files are sorted by name; empty files are skipped.
|
|
14
|
-
*
|
|
15
|
-
* Throws on a missing path, malformed content, or no usable samples.
|
|
16
|
-
*/
|
|
17
|
-
export declare function loadVoiceprintEmails(file: string | undefined, dir: string | undefined): VoiceprintEmail[];
|
|
18
3
|
/**
|
|
19
4
|
* Resolve a face's profile_addendum from the `--profile-addendum` / `--profile-addendum-file`
|
|
20
5
|
* flags. Returns undefined when neither is given (field left unchanged). Throws on a missing
|
package/dist/utils.js
CHANGED
|
@@ -1,81 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
2
|
/** Max length of a face's profile_addendum (per-face system prompt). Mirrors the backend. */
|
|
4
3
|
export const PROFILE_ADDENDUM_MAX_CHARS = 100_000;
|
|
5
|
-
function itemToEmail(item, i, where) {
|
|
6
|
-
if (typeof item === 'string')
|
|
7
|
-
return { id: `m${i + 1}`, text: item };
|
|
8
|
-
if (item && typeof item === 'object' && typeof item.text === 'string') {
|
|
9
|
-
const obj = item;
|
|
10
|
-
return { id: obj.id !== undefined ? String(obj.id) : `m${i + 1}`, text: obj.text };
|
|
11
|
-
}
|
|
12
|
-
throw new Error(`${where} must be a string or an object with a "text" field`);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Load voiceprint writing samples from a JSON/JSONL file or a directory.
|
|
16
|
-
*
|
|
17
|
-
* - `file`: a JSON array of `{id?, text}` objects or plain strings, OR JSONL
|
|
18
|
-
* (one such value per line). The format is auto-detected.
|
|
19
|
-
* - `dir`: one sample per non-hidden file; `id` is the filename (sans extension),
|
|
20
|
-
* `text` is the file contents. Files are sorted by name; empty files are skipped.
|
|
21
|
-
*
|
|
22
|
-
* Throws on a missing path, malformed content, or no usable samples.
|
|
23
|
-
*/
|
|
24
|
-
export function loadVoiceprintEmails(file, dir) {
|
|
25
|
-
if (dir !== undefined) {
|
|
26
|
-
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
|
|
27
|
-
throw new Error(`Directory not found: ${dir}`);
|
|
28
|
-
const emails = [];
|
|
29
|
-
const names = fs.readdirSync(dir).filter((n) => !n.startsWith('.')).sort();
|
|
30
|
-
for (const name of names) {
|
|
31
|
-
const full = path.join(dir, name);
|
|
32
|
-
if (!fs.statSync(full).isFile())
|
|
33
|
-
continue;
|
|
34
|
-
const text = fs.readFileSync(full, 'utf8').trim();
|
|
35
|
-
if (!text)
|
|
36
|
-
continue;
|
|
37
|
-
emails.push({ id: name.replace(/\.[^.]+$/, ''), text });
|
|
38
|
-
}
|
|
39
|
-
if (emails.length === 0)
|
|
40
|
-
throw new Error(`No usable files found in ${dir}`);
|
|
41
|
-
return emails;
|
|
42
|
-
}
|
|
43
|
-
if (file !== undefined) {
|
|
44
|
-
if (!fs.existsSync(file))
|
|
45
|
-
throw new Error(`File not found: ${file}`);
|
|
46
|
-
const raw = fs.readFileSync(file, 'utf8');
|
|
47
|
-
// Try a single JSON array first; fall back to JSONL (one value per line).
|
|
48
|
-
let items;
|
|
49
|
-
let asArray;
|
|
50
|
-
try {
|
|
51
|
-
asArray = JSON.parse(raw);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
asArray = undefined;
|
|
55
|
-
}
|
|
56
|
-
if (Array.isArray(asArray)) {
|
|
57
|
-
items = asArray;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
const lines = raw.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
61
|
-
if (lines.length === 0)
|
|
62
|
-
throw new Error(`${file} contains no samples`);
|
|
63
|
-
items = lines.map((line, i) => {
|
|
64
|
-
try {
|
|
65
|
-
return JSON.parse(line);
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
throw new Error(`${file} is neither a JSON array nor valid JSONL (line ${i + 1} did not parse)`);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
const emails = items.map((item, i) => itemToEmail(item, i, `${file}[${i}]`));
|
|
73
|
-
if (emails.length === 0)
|
|
74
|
-
throw new Error(`${file} contains no samples`);
|
|
75
|
-
return emails;
|
|
76
|
-
}
|
|
77
|
-
throw new Error('Provide --emails <file> or --emails-dir <dir>');
|
|
78
|
-
}
|
|
79
4
|
/**
|
|
80
5
|
* Resolve a face's profile_addendum from the `--profile-addendum` / `--profile-addendum-file`
|
|
81
6
|
* flags. Returns undefined when neither is given (field left unchanged). Throws on a missing
|