faces-cli 1.7.3 → 1.7.5

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/create.d.ts +2 -0
  3. package/dist/commands/face/create.js +12 -1
  4. package/dist/commands/face/edit.d.ts +3 -0
  5. package/dist/commands/face/edit.js +18 -1
  6. package/dist/commands/face/get.d.ts +1 -0
  7. package/dist/commands/face/get.js +15 -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 +23 -0
  29. package/dist/utils.js +98 -0
  30. package/dist/voiceprint.d.ts +28 -0
  31. package/dist/voiceprint.js +55 -0
  32. package/oclif.manifest.json +1506 -696
  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);
@@ -10,6 +10,8 @@ export default class FaceCreate extends BaseCommand {
10
10
  formula: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  attr: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  tool: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'profile-addendum': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'profile-addendum-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
15
  'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
16
  token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
17
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -2,7 +2,7 @@ import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base.js';
3
3
  import { FacesAPIError } from '../../client.js';
4
4
  import { CatalogService } from '../../catalog.js';
5
- import { renameFaceFields } from '../../utils.js';
5
+ import { renameFaceFields, resolveProfileAddendum } from '../../utils.js';
6
6
  export default class FaceCreate extends BaseCommand {
7
7
  static description = 'Create a new face';
8
8
  static flags = {
@@ -15,6 +15,8 @@ export default class FaceCreate extends BaseCommand {
15
15
  formula: Flags.string({ description: 'Boolean formula over owned concrete face aliases (e.g. "a | b", "(a | b) - c"). Creates a composite face.' }),
16
16
  attr: Flags.string({ description: 'Attribute KEY=VALUE (repeatable)', multiple: true }),
17
17
  tool: Flags.string({ description: 'Tool name to enable (repeatable)', multiple: true }),
18
+ 'profile-addendum': Flags.string({ description: 'Per-face system prompt, injected after the persona profile and before any per-request system prompt (max 100,000 chars).' }),
19
+ 'profile-addendum-file': Flags.string({ description: 'Read the per-face system prompt from a file (alternative to --profile-addendum).', exclusive: ['profile-addendum'] }),
18
20
  };
19
21
  static args = {};
20
22
  async run() {
@@ -26,6 +28,15 @@ export default class FaceCreate extends BaseCommand {
26
28
  const payload = { name: flags.name, alias: flags.alias };
27
29
  if (flags.description)
28
30
  payload.description = flags.description;
31
+ let addendum;
32
+ try {
33
+ addendum = resolveProfileAddendum(flags['profile-addendum'], flags['profile-addendum-file']);
34
+ }
35
+ catch (err) {
36
+ this.error(err instanceof Error ? err.message : String(err));
37
+ }
38
+ if (addendum !== undefined)
39
+ payload.profile_addendum = addendum;
29
40
  if (flags.formula) {
30
41
  payload.formula = flags.formula;
31
42
  if (flags['default-model'])
@@ -9,6 +9,9 @@ export default class FaceEdit extends BaseCommand {
9
9
  formula: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  attr: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  tool: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'profile-addendum': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'profile-addendum-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'clear-profile-addendum': import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
15
  'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
16
  token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
17
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -2,7 +2,7 @@ import { Args, Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base.js';
3
3
  import { FacesAPIError } from '../../client.js';
4
4
  import { CatalogService } from '../../catalog.js';
5
- import { renameFaceFields } from '../../utils.js';
5
+ import { renameFaceFields, resolveProfileAddendum } from '../../utils.js';
6
6
  export default class FaceEdit extends BaseCommand {
7
7
  static description = "Edit a face's metadata";
8
8
  static flags = {
@@ -14,6 +14,9 @@ export default class FaceEdit extends BaseCommand {
14
14
  formula: Flags.string({ description: 'New boolean formula (synthetic faces only)' }),
15
15
  attr: Flags.string({ description: 'Update attribute KEY=VALUE (repeatable)', multiple: true }),
16
16
  tool: Flags.string({ description: 'Tool names to set (replaces list, repeatable)', multiple: true }),
17
+ 'profile-addendum': Flags.string({ description: 'Replace the per-face system prompt (max 100,000 chars).' }),
18
+ 'profile-addendum-file': Flags.string({ description: 'Replace the per-face system prompt from a file.', exclusive: ['profile-addendum'] }),
19
+ 'clear-profile-addendum': Flags.boolean({ description: 'Remove the per-face system prompt.', exclusive: ['profile-addendum', 'profile-addendum-file'] }),
17
20
  };
18
21
  static args = {
19
22
  face_id: Args.string({ description: 'Face alias', required: true }),
@@ -42,6 +45,20 @@ export default class FaceEdit extends BaseCommand {
42
45
  payload.default_model = flags['default-model'];
43
46
  if (flags.description !== undefined)
44
47
  payload.description = flags.description;
48
+ if (flags['clear-profile-addendum']) {
49
+ payload.profile_addendum = '';
50
+ }
51
+ else {
52
+ let addendum;
53
+ try {
54
+ addendum = resolveProfileAddendum(flags['profile-addendum'], flags['profile-addendum-file']);
55
+ }
56
+ catch (err) {
57
+ this.error(err instanceof Error ? err.message : String(err));
58
+ }
59
+ if (addendum !== undefined)
60
+ payload.profile_addendum = addendum;
61
+ }
45
62
  const hasTags = flags.tag && flags.tag.length > 0;
46
63
  if (Object.keys(payload).length === 0 && !hasTags)
47
64
  this.error('Provide at least one field to update.');
@@ -3,6 +3,7 @@ export default class FaceGet extends BaseCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ full: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
7
  'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
8
  token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -7,6 +7,7 @@ export default class FaceGet extends BaseCommand {
7
7
  static flags = {
8
8
  ...BaseCommand.baseFlags,
9
9
  include: Flags.string({ description: 'Include extra fields: tags, teams (comma-separated)' }),
10
+ full: Flags.boolean({ description: 'Print the full profile_addendum instead of a truncated preview' }),
10
11
  };
11
12
  static args = {
12
13
  face_id: Args.string({ description: 'Face alias', required: true }),
@@ -55,6 +56,20 @@ export default class FaceGet extends BaseCommand {
55
56
  if (f.default_model)
56
57
  this.log(`default_model: ${f.default_model}`);
57
58
  }
59
+ if (f.profile_addendum) {
60
+ const text = f.profile_addendum;
61
+ if (flags.full) {
62
+ this.log(`profile_addendum (${text.length} chars):`);
63
+ this.log(text);
64
+ }
65
+ else {
66
+ const oneLine = text.replace(/\s+/g, ' ').trim();
67
+ const preview = oneLine.length > 160 ? `${oneLine.slice(0, 160)}…` : oneLine;
68
+ this.log(`profile_addendum (${text.length} chars): ${preview}`);
69
+ if (oneLine.length > 160)
70
+ this.log(' (use --full to print the whole prompt)');
71
+ }
72
+ }
58
73
  }
59
74
  return data;
60
75
  }
@@ -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
+ }