faces-cli 1.7.4 → 1.7.6

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.
Files changed (33) hide show
  1. package/dist/base.js +6 -0
  2. package/dist/commands/face/get.js +2 -0
  3. package/dist/commands/face/list.js +2 -0
  4. package/dist/commands/face/lock.d.ts +14 -0
  5. package/dist/commands/face/lock.js +34 -0
  6. package/dist/commands/face/unlock.d.ts +14 -0
  7. package/dist/commands/face/unlock.js +34 -0
  8. package/dist/commands/voiceprint/build-status.d.ts +14 -0
  9. package/dist/commands/voiceprint/build-status.js +46 -0
  10. package/dist/commands/voiceprint/build.d.ts +23 -0
  11. package/dist/commands/voiceprint/build.js +109 -0
  12. package/dist/commands/voiceprint/get.d.ts +14 -0
  13. package/dist/commands/voiceprint/get.js +47 -0
  14. package/dist/commands/voiceprint/refine-status.d.ts +14 -0
  15. package/dist/commands/voiceprint/refine-status.js +43 -0
  16. package/dist/commands/voiceprint/refine.d.ts +23 -0
  17. package/dist/commands/voiceprint/refine.js +88 -0
  18. package/dist/commands/voiceprint/revert.d.ts +14 -0
  19. package/dist/commands/voiceprint/revert.js +36 -0
  20. package/dist/commands/voiceprint/score-status.d.ts +14 -0
  21. package/dist/commands/voiceprint/score-status.js +46 -0
  22. package/dist/commands/voiceprint/score.d.ts +24 -0
  23. package/dist/commands/voiceprint/score.js +114 -0
  24. package/dist/commands/voiceprint/upload.d.ts +17 -0
  25. package/dist/commands/voiceprint/upload.js +66 -0
  26. package/dist/commands/voiceprint/versions.d.ts +14 -0
  27. package/dist/commands/voiceprint/versions.js +44 -0
  28. package/dist/utils.d.ts +15 -0
  29. package/dist/utils.js +75 -0
  30. package/dist/voiceprint.d.ts +28 -0
  31. package/dist/voiceprint.js +55 -0
  32. package/oclif.manifest.json +1821 -937
  33. package/package.json +1 -1
package/dist/base.js CHANGED
@@ -47,6 +47,12 @@ export class BaseCommand extends Command {
47
47
  }
48
48
  else if (data !== null && typeof data === 'object') {
49
49
  for (const [k, v] of Object.entries(data)) {
50
+ // read_only: only surface when set; render as a bare marker, never "read_only: false"
51
+ if (k === 'read_only') {
52
+ if (v)
53
+ this.log(`${prefix}read only`);
54
+ continue;
55
+ }
50
56
  if (v !== null && typeof v === 'object') {
51
57
  this.log(`${prefix}${k}:`);
52
58
  this.printHuman(v, indent + 1);
@@ -39,6 +39,8 @@ export default class FaceGet extends BaseCommand {
39
39
  this.log(`name: ${f.name}`);
40
40
  this.log(`owned_by: ${f.owned_by}`);
41
41
  this.log(`created: ${new Date(f.created * 1000).toISOString().slice(0, 10)}`);
42
+ if (f.read_only)
43
+ this.log('read only');
42
44
  if (f.formula) {
43
45
  this.log(`formula: ${f.formula}`);
44
46
  this.log(`type: composite`);
@@ -102,6 +102,8 @@ export default class FaceList extends BaseCommand {
102
102
  }
103
103
  if (f.published)
104
104
  suffix += ' [public · requires @model]';
105
+ if (f.read_only)
106
+ suffix += ' [read only]';
105
107
  this.log(`${handle(f).padEnd(handleWidth)} ${f.name.padEnd(nameWidth)}${suffix}`);
106
108
  }
107
109
  }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class FaceLock extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ face_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,34 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class FaceLock extends BaseCommand {
5
+ static description = 'Lock a face (read-only). A locked face still reads and chats normally, but it — and everything it owns (documents, threads, voiceprint) — cannot be changed until unlocked.';
6
+ static examples = ['<%= config.bin %> <%= command.id %> socrates'];
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ };
10
+ static args = {
11
+ face_id: Args.string({ description: 'Face alias', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(FaceLock);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = await client.patch(`/v1/faces/${args.face_id}/lock`, { body: { read_only: true } });
19
+ }
20
+ catch (err) {
21
+ if (err instanceof FacesAPIError)
22
+ this.error(`Error (${err.statusCode}): ${err.message}`);
23
+ throw err;
24
+ }
25
+ if (!this.jsonEnabled()) {
26
+ const res = data;
27
+ this.log(`Locked '${res.alias ?? args.face_id}' — read only.`);
28
+ const affected = res.affected ?? [];
29
+ if (affected.length > 0)
30
+ this.log(`Affected: ${affected.join(', ')}`);
31
+ }
32
+ return data;
33
+ }
34
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class FaceUnlock extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ face_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,34 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class FaceUnlock extends BaseCommand {
5
+ static description = 'Unlock a face (make it writable again). Thaws the face and everything it owns (documents, threads, voiceprint).';
6
+ static examples = ['<%= config.bin %> <%= command.id %> socrates'];
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ };
10
+ static args = {
11
+ face_id: Args.string({ description: 'Face alias', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(FaceUnlock);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = await client.patch(`/v1/faces/${args.face_id}/lock`, { body: { read_only: false } });
19
+ }
20
+ catch (err) {
21
+ if (err instanceof FacesAPIError)
22
+ this.error(`Error (${err.statusCode}): ${err.message}`);
23
+ throw err;
24
+ }
25
+ if (!this.jsonEnabled()) {
26
+ const res = data;
27
+ this.log(`Unlocked '${res.alias ?? args.face_id}' — writable.`);
28
+ const affected = res.affected ?? [];
29
+ if (affected.length > 0)
30
+ this.log(`Affected: ${affected.join(', ')}`);
31
+ }
32
+ return data;
33
+ }
34
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintBuildStatus extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ job_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,46 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { jobFailureMessage } from '../../voiceprint.js';
5
+ import { printBuildReport } from './build.js';
6
+ export default class VoiceprintBuildStatus extends BaseCommand {
7
+ static description = 'Get the status/result of a voiceprint build job (from voiceprint:build without --wait)';
8
+ static examples = ['<%= config.bin %> <%= command.id %> 4e13bf03-fbc1-4ad0-92ae-16984e2f9a1d'];
9
+ static flags = { ...BaseCommand.baseFlags };
10
+ static args = {
11
+ job_id: Args.string({ description: 'Build job id', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(VoiceprintBuildStatus);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = (await client.get(`/v1/voiceprint/builds/${encodeURIComponent(args.job_id)}`));
19
+ }
20
+ catch (err) {
21
+ if (err instanceof FacesAPIError)
22
+ this.error(`Error (${err.statusCode}): ${err.message}`);
23
+ throw err;
24
+ }
25
+ if (this.jsonEnabled())
26
+ return data;
27
+ const status = String(data.status ?? 'unknown');
28
+ if (status === 'failed') {
29
+ this.log(`job: ${args.job_id}`);
30
+ this.log(`status: failed`);
31
+ this.log(`error: ${jobFailureMessage(data.error)}`);
32
+ }
33
+ else if (status === 'done') {
34
+ printBuildReport(this, args.job_id, data);
35
+ }
36
+ else {
37
+ const p = data.progress;
38
+ this.log(`job: ${args.job_id}`);
39
+ this.log(`status: ${status}`);
40
+ if (p?.stage)
41
+ this.log(`stage: ${p.stage}${p.total ? ` ${p.done}/${p.total}` : ''}`);
42
+ this.log(`(still running — poll again with: faces voiceprint:build-status ${args.job_id})`);
43
+ }
44
+ return data;
45
+ }
46
+ }
@@ -0,0 +1,23 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintBuild extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ model: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'holdout-count': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'min-valid': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ wait: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ static args: {
16
+ self_alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ run(): Promise<unknown>;
19
+ }
20
+ /** Shared build-report formatter (used by voiceprint:build --wait and voiceprint:build-status). */
21
+ export declare function printBuildReport(cmd: {
22
+ log: (m: string) => void;
23
+ }, jobId: string, data: Record<string, unknown>): void;
@@ -0,0 +1,109 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { pollVoiceprintJob, jobFailureMessage } from '../../voiceprint.js';
5
+ export default class VoiceprintBuild extends BaseCommand {
6
+ static description = "Build a self face's voice map from its uploaded corpus. Auto-creates the hidden <self>-voiceprint face and installs the map. Async — use --wait to block.";
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> alice --wait',
9
+ '<%= config.bin %> <%= command.id %> alice --holdout-count 120 --oauth-only --wait',
10
+ ];
11
+ static flags = {
12
+ ...BaseCommand.baseFlags,
13
+ model: Flags.string({ description: 'Model that forges the voice map (default: gpt-5.4)' }),
14
+ 'holdout-count': Flags.integer({ description: 'Stratified scoring holdout size (default: 120)' }),
15
+ 'min-valid': Flags.integer({ description: 'Corpus floor below which no holdout is split — train on all (default: 550)' }),
16
+ 'oauth-only': Flags.boolean({ description: 'Only run on a free (OAuth) route; never fall back to billed inference', default: false }),
17
+ wait: Flags.boolean({ description: 'Block and poll until the build finishes (default: print the job_id and return)', default: false }),
18
+ };
19
+ static args = {
20
+ self_alias: Args.string({ description: 'Self face alias', required: true }),
21
+ };
22
+ async run() {
23
+ const { args, flags } = await this.parse(VoiceprintBuild);
24
+ const client = this.makeClient(flags);
25
+ const json = this.jsonEnabled();
26
+ const payload = { self_face: args.self_alias };
27
+ if (flags.model)
28
+ payload.model = flags.model;
29
+ if (flags['holdout-count'] !== undefined)
30
+ payload.holdout_count = flags['holdout-count'];
31
+ if (flags['min-valid'] !== undefined)
32
+ payload.min_valid = flags['min-valid'];
33
+ if (flags['oauth-only'])
34
+ payload.oauth_only = true;
35
+ let started;
36
+ try {
37
+ started = (await client.post('/v1/voiceprint/builds', { body: payload }));
38
+ }
39
+ catch (err) {
40
+ if (err instanceof FacesAPIError)
41
+ this.error(`Error (${err.statusCode}): ${err.message}`);
42
+ throw err;
43
+ }
44
+ const jobId = String(started.job_id ?? '');
45
+ if (!jobId)
46
+ this.error('Server did not return a job_id');
47
+ if (!flags.wait) {
48
+ if (!json) {
49
+ this.log(`Build started: ${jobId}`);
50
+ this.log(`Poll with: faces voiceprint:build-status ${jobId}`);
51
+ }
52
+ return started;
53
+ }
54
+ if (!json)
55
+ process.stderr.write(`Building voice map for ${args.self_alias} (job ${jobId})...\n`);
56
+ let final;
57
+ let lastStage = '';
58
+ try {
59
+ final = await pollVoiceprintJob(client, '/v1/voiceprint/builds', jobId, {
60
+ timeoutMs: 3_600_000, // a large corpus fans lenses over many chunks — can take a while
61
+ onPoll: (data) => {
62
+ if (json)
63
+ return;
64
+ const p = data.progress;
65
+ const key = p ? `${p.stage}:${p.done}/${p.total}` : String(data.status);
66
+ if (key !== lastStage) {
67
+ lastStage = key;
68
+ process.stderr.write(` ${p?.stage ?? data.status}${p?.total ? ` ${p.done}/${p.total}` : ''}\n`);
69
+ }
70
+ },
71
+ });
72
+ }
73
+ catch (err) {
74
+ if (err instanceof FacesAPIError)
75
+ this.error(`Error (${err.statusCode}): ${err.message}`);
76
+ this.error(err instanceof Error ? err.message : String(err));
77
+ }
78
+ if (final.status === 'failed')
79
+ this.error(`Build failed: ${jobFailureMessage(final.error)}`);
80
+ if (json)
81
+ return final;
82
+ printBuildReport(this, jobId, final);
83
+ return final;
84
+ }
85
+ }
86
+ /** Shared build-report formatter (used by voiceprint:build --wait and voiceprint:build-status). */
87
+ export function printBuildReport(cmd, jobId, data) {
88
+ const report = (data.report ?? {});
89
+ cmd.log(`job: ${jobId}`);
90
+ cmd.log(`status: ${data.status}`);
91
+ if (data.voiceprint_face_id)
92
+ cmd.log(`voiceprint_face_id: ${data.voiceprint_face_id}`);
93
+ if (data.version_id)
94
+ cmd.log(`version_id: ${data.version_id}`);
95
+ if (report.voice_map_chars !== undefined)
96
+ cmd.log(`voice_map_chars: ${report.voice_map_chars}`);
97
+ const baseline = report.baseline;
98
+ if (baseline) {
99
+ if (baseline.skipped) {
100
+ cmd.log(`baseline: skipped (${baseline.skipped})`);
101
+ }
102
+ else {
103
+ cmd.log(`baseline VCI: ${baseline.VCI ?? 'n/a'}`);
104
+ cmd.log(`baseline VFI: ${baseline.VFI ?? 'n/a'}`);
105
+ if (baseline.delta_ratio !== undefined)
106
+ cmd.log(`baseline delta: ${baseline.delta_ratio}`);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintGet extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ job_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,47 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class VoiceprintGet extends BaseCommand {
5
+ static description = 'Get the status/result of a voiceprint scoring job started with voiceprint:score --no-wait';
6
+ static examples = ['<%= config.bin %> <%= command.id %> 4e13bf03-fbc1-4ad0-92ae-16984e2f9a1d'];
7
+ static flags = { ...BaseCommand.baseFlags };
8
+ static args = {
9
+ job_id: Args.string({ description: 'Scoring job id (from voiceprint:score --no-wait)', required: true }),
10
+ };
11
+ async run() {
12
+ const { args, flags } = await this.parse(VoiceprintGet);
13
+ const client = this.makeClient(flags);
14
+ let data;
15
+ try {
16
+ data = (await client.get(`/v1/voiceprint/score/${encodeURIComponent(args.job_id)}`));
17
+ }
18
+ catch (err) {
19
+ if (err instanceof FacesAPIError)
20
+ this.error(`Error (${err.statusCode}): ${err.message}`);
21
+ throw err;
22
+ }
23
+ if (this.jsonEnabled())
24
+ return data;
25
+ const status = String(data.status ?? 'unknown');
26
+ this.log(`job: ${args.job_id}`);
27
+ this.log(`status: ${status}`);
28
+ const progress = data.progress;
29
+ if (progress && typeof progress.pairs_done === 'number') {
30
+ this.log(`pairs: ${progress.pairs_done}/${progress.pairs_total ?? '?'}`);
31
+ }
32
+ if (status === 'done') {
33
+ const report = (data.report ?? {});
34
+ this.log(`VCI: ${report.VCI ?? 'n/a'} (voice closeness — 100 = writes indistinguishably from the person)`);
35
+ this.log(`VFI: ${report.VFI ?? 'n/a'} (voice fidelity — how much of their voice is captured vs a generic baseline)`);
36
+ if (report.n_pairs !== undefined)
37
+ this.log(`n_pairs: ${report.n_pairs}`);
38
+ }
39
+ else if (status === 'failed') {
40
+ this.log(`error: ${data.error ?? 'unknown error'}`);
41
+ }
42
+ else {
43
+ this.log(`(still running — poll again with: faces voiceprint:get ${args.job_id})`);
44
+ }
45
+ return data;
46
+ }
47
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintRefineStatus extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ job_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,43 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { jobFailureMessage } from '../../voiceprint.js';
5
+ import { printRefineReport } from './refine.js';
6
+ export default class VoiceprintRefineStatus extends BaseCommand {
7
+ static description = 'Get the status/result of a voiceprint refine job (from voiceprint:refine without --wait)';
8
+ static examples = ['<%= config.bin %> <%= command.id %> 4e13bf03-fbc1-4ad0-92ae-16984e2f9a1d'];
9
+ static flags = { ...BaseCommand.baseFlags };
10
+ static args = {
11
+ job_id: Args.string({ description: 'Refine job id', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(VoiceprintRefineStatus);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = (await client.get(`/v1/voiceprint/refines/${encodeURIComponent(args.job_id)}`));
19
+ }
20
+ catch (err) {
21
+ if (err instanceof FacesAPIError)
22
+ this.error(`Error (${err.statusCode}): ${err.message}`);
23
+ throw err;
24
+ }
25
+ if (this.jsonEnabled())
26
+ return data;
27
+ const status = String(data.status ?? 'unknown');
28
+ if (status === 'failed') {
29
+ this.log(`job: ${args.job_id}`);
30
+ this.log(`status: failed`);
31
+ this.log(`error: ${jobFailureMessage(data.error)}`);
32
+ }
33
+ else if (status === 'done') {
34
+ printRefineReport(this, args.job_id, (data.report ?? {}));
35
+ }
36
+ else {
37
+ this.log(`job: ${args.job_id}`);
38
+ this.log(`status: ${status}`);
39
+ this.log(`(still running — poll again with: faces voiceprint:refine-status ${args.job_id})`);
40
+ }
41
+ return data;
42
+ }
43
+ }
@@ -0,0 +1,23 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintRefine extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ model: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ temperature: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'n-samples': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ wait: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ static args: {
16
+ self_alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ run(): Promise<unknown>;
19
+ }
20
+ /** Shared refine-report formatter (used by voiceprint:refine --wait and voiceprint:refine-status). */
21
+ export declare function printRefineReport(cmd: {
22
+ log: (m: string) => void;
23
+ }, jobId: string, report: Record<string, unknown>): void;
@@ -0,0 +1,88 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { pollVoiceprintJob, jobFailureMessage } from '../../voiceprint.js';
5
+ export default class VoiceprintRefine extends BaseCommand {
6
+ static description = "Tune the active version's post-processing recipe and promote it only if it beats the current one. Requires an existing build with a holdout ≥ 20. Undo with voiceprint:revert.";
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> alice --wait',
9
+ '<%= config.bin %> <%= command.id %> alice --temperature 0.4 --n-samples 6 --wait',
10
+ ];
11
+ static flags = {
12
+ ...BaseCommand.baseFlags,
13
+ model: Flags.string({ description: "Model used to forge candidate drafts (default: the active version's build model)" }),
14
+ temperature: Flags.string({ description: 'Sampling temperature for candidate drafts (default: 0.4)' }),
15
+ 'n-samples': Flags.integer({ description: 'Drafts forged per holdout pair (default: 6)' }),
16
+ 'oauth-only': Flags.boolean({ description: 'Only run on a free (OAuth) route; never fall back to billed inference', default: false }),
17
+ wait: Flags.boolean({ description: 'Block and poll until refine finishes (default: print the job_id and return)', default: false }),
18
+ };
19
+ static args = {
20
+ self_alias: Args.string({ description: 'Self face alias', required: true }),
21
+ };
22
+ async run() {
23
+ const { args, flags } = await this.parse(VoiceprintRefine);
24
+ const client = this.makeClient(flags);
25
+ const json = this.jsonEnabled();
26
+ const payload = { self_face: args.self_alias };
27
+ if (flags.model)
28
+ payload.model = flags.model;
29
+ if (flags.temperature !== undefined)
30
+ payload.temperature = Number.parseFloat(flags.temperature);
31
+ if (flags['n-samples'] !== undefined)
32
+ payload.n_samples = flags['n-samples'];
33
+ if (flags['oauth-only'])
34
+ payload.oauth_only = true;
35
+ let started;
36
+ try {
37
+ started = (await client.post('/v1/voiceprint/refines', { body: payload }));
38
+ }
39
+ catch (err) {
40
+ if (err instanceof FacesAPIError)
41
+ this.error(`Error (${err.statusCode}): ${err.message}`);
42
+ throw err;
43
+ }
44
+ const jobId = String(started.job_id ?? '');
45
+ if (!jobId)
46
+ this.error('Server did not return a job_id');
47
+ if (!flags.wait) {
48
+ if (!json) {
49
+ this.log(`Refine started: ${jobId}`);
50
+ this.log(`Poll with: faces voiceprint:refine-status ${jobId}`);
51
+ }
52
+ return started;
53
+ }
54
+ if (!json)
55
+ process.stderr.write(`Refining ${args.self_alias}'s recipe (job ${jobId})...\n`);
56
+ let final;
57
+ try {
58
+ final = await pollVoiceprintJob(client, '/v1/voiceprint/refines', jobId, { timeoutMs: 3_600_000 });
59
+ }
60
+ catch (err) {
61
+ if (err instanceof FacesAPIError)
62
+ this.error(`Error (${err.statusCode}): ${err.message}`);
63
+ this.error(err instanceof Error ? err.message : String(err));
64
+ }
65
+ if (final.status === 'failed')
66
+ this.error(`Refine failed: ${jobFailureMessage(final.error)}`);
67
+ if (json)
68
+ return final;
69
+ printRefineReport(this, jobId, (final.report ?? {}));
70
+ return final;
71
+ }
72
+ }
73
+ /** Shared refine-report formatter (used by voiceprint:refine --wait and voiceprint:refine-status). */
74
+ export function printRefineReport(cmd, jobId, report) {
75
+ const decision = String(report.decision ?? 'unknown');
76
+ const baseline = (report.baseline ?? {});
77
+ cmd.log(`job: ${jobId}`);
78
+ cmd.log(`decision: ${decision}${decision === 'promote' ? ' (a better recipe was promoted to a new active version)' : decision === 'keep' ? ' (nothing beat the current recipe; unchanged)' : ''}`);
79
+ if (baseline.VCI !== undefined || baseline.VFI !== undefined) {
80
+ cmd.log(`baseline: VCI ${baseline.VCI ?? 'n/a'} / VFI ${baseline.VFI ?? 'n/a'}`);
81
+ }
82
+ if (report.version_id)
83
+ cmd.log(`version: ${report.version_id}`);
84
+ if (Array.isArray(report.promoted_recipe))
85
+ cmd.log(`recipe: ${JSON.stringify(report.promoted_recipe)}`);
86
+ if (decision === 'promote')
87
+ cmd.log(`\nUndo with: faces voiceprint:revert <self_alias>`);
88
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintRevert extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ self_alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,36 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class VoiceprintRevert extends BaseCommand {
5
+ static description = "Revert a self face's voice map to its prior version (one-deep undo; re-running toggles back). Only changes which stored recipe/baseline is active — the map text is frozen.";
6
+ static examples = ['<%= config.bin %> <%= command.id %> alice'];
7
+ static flags = { ...BaseCommand.baseFlags };
8
+ static args = {
9
+ self_alias: Args.string({ description: 'Self face alias', required: true }),
10
+ };
11
+ async run() {
12
+ const { args, flags } = await this.parse(VoiceprintRevert);
13
+ const client = this.makeClient(flags);
14
+ let data;
15
+ try {
16
+ data = (await client.post('/v1/voiceprint/versions/revert', { body: { self_face: args.self_alias } }));
17
+ }
18
+ catch (err) {
19
+ if (err instanceof FacesAPIError) {
20
+ if (err.statusCode === 409)
21
+ this.error(`Nothing to revert to — ${args.self_alias} has only one voice-map version.`);
22
+ this.error(`Error (${err.statusCode}): ${err.message}`);
23
+ }
24
+ throw err;
25
+ }
26
+ if (this.jsonEnabled())
27
+ return data;
28
+ this.log(`self_face: ${data.self_face ?? args.self_alias}`);
29
+ this.log(`from_version: ${data.from_version ?? '?'}`);
30
+ this.log(`to_version: ${data.to_version ?? '?'}`);
31
+ this.log(`VCI/VFI: ${data.baseline_vci ?? 'n/a'} / ${data.baseline_vfi ?? 'n/a'}`);
32
+ if (data.recipe !== undefined)
33
+ this.log(`recipe: ${JSON.stringify(data.recipe)}`);
34
+ return data;
35
+ }
36
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class VoiceprintScoreStatus extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ job_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }