faces-cli 1.4.2 → 1.4.4

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/catalog.d.ts CHANGED
@@ -15,7 +15,7 @@ export interface FaceFrontmatter {
15
15
  }
16
16
  export interface FaceDataInput {
17
17
  name: string;
18
- id: string;
18
+ alias: string;
19
19
  uid?: string;
20
20
  basic_facts?: Record<string, string | {
21
21
  value: string;
package/dist/catalog.js CHANGED
@@ -82,7 +82,7 @@ export class CatalogService {
82
82
  if (!this.isEnabled())
83
83
  return;
84
84
  try {
85
- const username = faceData.id;
85
+ const username = faceData.alias;
86
86
  const dir = path.join(CATALOG_DIR, username);
87
87
  fs.mkdirSync(dir, { recursive: true });
88
88
  const filePath = path.join(dir, 'FACE.md');
package/dist/client.js CHANGED
@@ -21,16 +21,17 @@ export class FacesClient {
21
21
  authHeader(requireJwt = false) {
22
22
  if (requireJwt) {
23
23
  if (!this.token) {
24
- process.stderr.write('Error: JWT login required. Run: faces auth login\n');
24
+ process.stderr.write('Error: JWT login required. Run: faces auth:login\n');
25
25
  process.exit(1);
26
26
  }
27
27
  return { Authorization: `Bearer ${this.token}` };
28
28
  }
29
- if (this.token)
30
- return { Authorization: `Bearer ${this.token}` };
29
+ // API key takes priority over saved JWT (explicit key = deliberate choice)
31
30
  if (this.apiKey)
32
31
  return { Authorization: `Bearer ${this.apiKey}` };
33
- process.stderr.write('Error: Authentication required. Run: faces auth login or set FACES_API_KEY\n');
32
+ if (this.token)
33
+ return { Authorization: `Bearer ${this.token}` };
34
+ process.stderr.write('Error: Authentication required. Run: faces auth:login or set FACES_API_KEY\n');
34
35
  process.exit(1);
35
36
  }
36
37
  wrapNetworkError(err, path) {
@@ -24,10 +24,10 @@ export default class CatalogDoctor extends BaseCommand {
24
24
  // List endpoint returns minimal fields; fetch full details for each
25
25
  remoteFaces = await Promise.all(arr.map(async (f) => {
26
26
  try {
27
- return await client.get(`/v1/faces/${f.id}`);
27
+ return await client.get(`/v1/faces/${f.alias}`);
28
28
  }
29
29
  catch {
30
- return { ...f, uid: f.id };
30
+ return { ...f, uid: f.alias };
31
31
  }
32
32
  }));
33
33
  }
@@ -36,20 +36,20 @@ export default class CatalogDoctor extends BaseCommand {
36
36
  this.error(`Error (${err.statusCode}): ${err.message}`);
37
37
  throw err;
38
38
  }
39
- // Build map of remote faces by username
40
- const remoteByUsername = new Map();
39
+ // Build map of remote faces by alias
40
+ const remoteByAlias = new Map();
41
41
  for (const f of remoteFaces) {
42
- remoteByUsername.set(f.id, f);
42
+ remoteByAlias.set(f.alias, f);
43
43
  }
44
44
  // Read local catalog
45
- const localUsernames = new Set();
45
+ const localAliases = new Set();
46
46
  const localFrontmatters = new Map();
47
47
  try {
48
48
  if (fs.existsSync(CATALOG_DIR)) {
49
49
  for (const dirent of fs.readdirSync(CATALOG_DIR, { withFileTypes: true })) {
50
50
  if (!dirent.isDirectory())
51
51
  continue;
52
- localUsernames.add(dirent.name);
52
+ localAliases.add(dirent.name);
53
53
  const faceMdPath = path.join(CATALOG_DIR, dirent.name, 'FACE.md');
54
54
  if (fs.existsSync(faceMdPath)) {
55
55
  const content = fs.readFileSync(faceMdPath, 'utf8');
@@ -83,23 +83,23 @@ export default class CatalogDoctor extends BaseCommand {
83
83
  const stale = [];
84
84
  const noDescription = [];
85
85
  const orphaned = [];
86
- for (const [username, remote] of remoteByUsername) {
87
- if (!localUsernames.has(username)) {
86
+ for (const [alias, remote] of remoteByAlias) {
87
+ if (!localAliases.has(alias)) {
88
88
  missing.push(remote);
89
89
  }
90
90
  else {
91
- const local = localFrontmatters.get(username);
91
+ const local = localFrontmatters.get(alias);
92
92
  if (local && local.name !== remote.name) {
93
93
  stale.push(remote);
94
94
  }
95
95
  }
96
- const local = localFrontmatters.get(username);
96
+ const local = localFrontmatters.get(alias);
97
97
  if (!local?.description)
98
- noDescription.push(username);
98
+ noDescription.push(alias);
99
99
  }
100
- for (const username of localUsernames) {
101
- if (!remoteByUsername.has(username))
102
- orphaned.push(username);
100
+ for (const alias of localAliases) {
101
+ if (!remoteByAlias.has(alias))
102
+ orphaned.push(alias);
103
103
  }
104
104
  const doFix = flags.fix || flags.generate;
105
105
  if (!doFix) {
@@ -134,16 +134,16 @@ export default class CatalogDoctor extends BaseCommand {
134
134
  fixed++;
135
135
  }
136
136
  // Also refresh faces that exist but might have stale attributes
137
- for (const [username, remote] of remoteByUsername) {
138
- if (!missing.some((f) => f.id === username) && !stale.some((f) => f.id === username)) {
137
+ for (const [alias, remote] of remoteByAlias) {
138
+ if (!missing.some((f) => f.alias === alias) && !stale.some((f) => f.alias === alias)) {
139
139
  // Face exists locally and name matches — still refresh attributes
140
140
  catalog.writeFace(remote);
141
141
  }
142
142
  }
143
143
  // Remove orphans
144
144
  let removed = 0;
145
- for (const username of orphaned) {
146
- catalog.deleteFace(username);
145
+ for (const alias of orphaned) {
146
+ catalog.deleteFace(alias);
147
147
  removed++;
148
148
  }
149
149
  this.log(`Fixed ${fixed} catalog entries, removed ${removed} orphans.`);
@@ -153,13 +153,13 @@ export default class CatalogDoctor extends BaseCommand {
153
153
  const catalogModel = cfg.catalog_model ?? 'gpt-5-nano';
154
154
  this.log(`Generating descriptions for ${noDescription.length} face(s) via ${catalogModel}...`);
155
155
  let generated = 0;
156
- for (const username of noDescription) {
157
- const remote = remoteByUsername.get(username);
156
+ for (const alias of noDescription) {
157
+ const remote = remoteByAlias.get(alias);
158
158
  if (!remote)
159
159
  continue;
160
160
  try {
161
161
  const prompt = 'Describe yourself in one paragraph. Who are you, what do you care about, and what kind of questions are you best suited to answer?';
162
- const faceModel = `${username}@${catalogModel}`;
162
+ const faceModel = `${alias}@${catalogModel}`;
163
163
  const isAnthropic = catalogModel.startsWith('claude');
164
164
  let text;
165
165
  if (isAnthropic) {
@@ -177,15 +177,15 @@ export default class CatalogDoctor extends BaseCommand {
177
177
  if (text) {
178
178
  catalog.writeFace(remote, text.trim());
179
179
  generated++;
180
- this.log(` ${username}: done`);
180
+ this.log(` ${alias}: done`);
181
181
  }
182
182
  else {
183
- this.log(` ${username}: no response`);
183
+ this.log(` ${alias}: no response`);
184
184
  }
185
185
  }
186
186
  catch (err) {
187
187
  const msg = err instanceof FacesAPIError ? `Error (${err.statusCode}): ${err.message}` : String(err);
188
- this.log(` ${username}: failed — ${msg}`);
188
+ this.log(` ${alias}: failed — ${msg}`);
189
189
  }
190
190
  }
191
191
  this.log(`Generated ${generated} description(s).`);
@@ -22,7 +22,7 @@ export default class ChatChat extends BaseCommand {
22
22
  responses: Flags.boolean({ description: 'Use OpenAI Responses API instead of Chat Completions', default: false }),
23
23
  };
24
24
  static args = {
25
- face_username: Args.string({ description: 'Face username (or face@model)', required: true }),
25
+ face_username: Args.string({ description: 'Face alias (or alias@model)', required: true }),
26
26
  };
27
27
  async run() {
28
28
  const { args, flags } = await this.parse(ChatChat);
@@ -12,7 +12,7 @@ export default class CompileDocCreate extends BaseCommand {
12
12
  perspective: Flags.string({ description: 'Perspective (first-person or third-person)', options: ['first-person', 'third-person'], default: 'first-person' }),
13
13
  };
14
14
  static args = {
15
- face_id: Args.string({ description: 'Face ID or username', required: true }),
15
+ face_id: Args.string({ description: 'Face alias', required: true }),
16
16
  };
17
17
  async run() {
18
18
  const { args, flags } = await this.parse(CompileDocCreate);
@@ -25,7 +25,7 @@ export default class CompileDocCreate extends BaseCommand {
25
25
  }
26
26
  if (!content)
27
27
  this.error('Provide --content or --file.');
28
- const payload = { model: args.face_id, content };
28
+ const payload = { alias: args.face_id, content };
29
29
  if (flags.label)
30
30
  payload.label = flags.label;
31
31
  if (flags.perspective)
@@ -18,7 +18,7 @@ export default class CompileDoc extends BaseCommand {
18
18
  timeout: Flags.integer({ description: 'Compile timeout in seconds (default: 600)', default: 600 }),
19
19
  };
20
20
  static args = {
21
- face_id: Args.string({ description: 'Face ID or username', required: true }),
21
+ face_id: Args.string({ description: 'Face alias', required: true }),
22
22
  };
23
23
  async run() {
24
24
  const { args, flags } = await this.parse(CompileDoc);
@@ -38,7 +38,7 @@ export default class CompileDoc extends BaseCommand {
38
38
  process.stderr.write('Creating document... ');
39
39
  let doc;
40
40
  try {
41
- const payload = { model: args.face_id, content };
41
+ const payload = { alias: args.face_id, content };
42
42
  if (flags.label)
43
43
  payload.label = flags.label;
44
44
  if (flags.perspective)
@@ -7,14 +7,14 @@ export default class CompileDocList extends BaseCommand {
7
7
  ...BaseCommand.baseFlags,
8
8
  };
9
9
  static args = {
10
- face_id: Args.string({ description: 'Face ID or username', required: true }),
10
+ face_id: Args.string({ description: 'Face alias', required: true }),
11
11
  };
12
12
  async run() {
13
13
  const { args, flags } = await this.parse(CompileDocList);
14
14
  const client = this.makeClient(flags);
15
15
  let data;
16
16
  try {
17
- data = await client.get('/v1/compile/documents', { params: { model: args.face_id } });
17
+ data = await client.get('/v1/compile/documents', { params: { alias: args.face_id } });
18
18
  }
19
19
  catch (err) {
20
20
  if (err instanceof FacesAPIError)
@@ -24,7 +24,7 @@ export default class CompileImport extends BaseCommand {
24
24
  }),
25
25
  };
26
26
  static args = {
27
- face_id: Args.string({ description: 'Face ID or username', required: true }),
27
+ face_id: Args.string({ description: 'Face alias', required: true }),
28
28
  };
29
29
  async run() {
30
30
  const { args, flags } = await this.parse(CompileImport);
@@ -72,7 +72,7 @@ export default class CompileImport extends BaseCommand {
72
72
  this.log(`label: ${thread.label}`);
73
73
  this.log(``);
74
74
  this.log(`Next step:`);
75
- this.log(` faces compile:thread:sync ${thread.thread_id}`);
75
+ this.log(` faces compile:thread:make ${thread.thread_id}`);
76
76
  }
77
77
  }
78
78
  return data;
@@ -8,12 +8,12 @@ export default class CompileThreadCreate extends BaseCommand {
8
8
  label: Flags.string({ description: 'Thread label' }),
9
9
  };
10
10
  static args = {
11
- face_id: Args.string({ description: 'Face ID or username', required: true }),
11
+ face_id: Args.string({ description: 'Face alias', required: true }),
12
12
  };
13
13
  async run() {
14
14
  const { args, flags } = await this.parse(CompileThreadCreate);
15
15
  const client = this.makeClient(flags);
16
- const payload = { model: args.face_id };
16
+ const payload = { alias: args.face_id };
17
17
  if (flags.label)
18
18
  payload.label = flags.label;
19
19
  let data;
@@ -7,14 +7,14 @@ export default class CompileThreadList extends BaseCommand {
7
7
  ...BaseCommand.baseFlags,
8
8
  };
9
9
  static args = {
10
- face_id: Args.string({ description: 'Face ID or username', required: true }),
10
+ face_id: Args.string({ description: 'Face alias', required: true }),
11
11
  };
12
12
  async run() {
13
13
  const { args, flags } = await this.parse(CompileThreadList);
14
14
  const client = this.makeClient(flags);
15
15
  let data;
16
16
  try {
17
- data = await client.get('/v1/compile/threads', { params: { model: args.face_id } });
17
+ data = await client.get('/v1/compile/threads', { params: { alias: args.face_id } });
18
18
  }
19
19
  catch (err) {
20
20
  if (err instanceof FacesAPIError)
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class CompileThreadMake extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
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
+ thread_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,56 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../../base.js';
3
+ import { FacesAPIError } from '../../../client.js';
4
+ import { pollCompileProgress } from '../../../poll.js';
5
+ export default class CompileThreadMake extends BaseCommand {
6
+ static description = 'Compile an existing thread (prepare + sync in one step)';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ timeout: Flags.integer({ description: 'Compile timeout in seconds (default: 600)', default: 600 }),
10
+ };
11
+ static args = {
12
+ thread_id: Args.string({ description: 'Thread ID', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(CompileThreadMake);
16
+ const client = this.makeClient(flags);
17
+ const json = this.jsonEnabled();
18
+ let makeResponse;
19
+ try {
20
+ makeResponse = await client.post(`/v1/compile/threads/${args.thread_id}/make`);
21
+ }
22
+ catch (err) {
23
+ if (err instanceof FacesAPIError)
24
+ this.error(`Error (${err.statusCode}): ${err.message}`);
25
+ throw err;
26
+ }
27
+ const chunksTotal = makeResponse.chunks_total;
28
+ if (!json)
29
+ process.stderr.write(`Compiling${chunksTotal ? ` (${chunksTotal} chunks)` : ''}:\n`);
30
+ let result;
31
+ try {
32
+ result = await pollCompileProgress(client, args.thread_id, {
33
+ timeoutMs: flags.timeout * 1000,
34
+ endpoint: `/v1/compile/threads/${args.thread_id}`,
35
+ onProgress: (p) => {
36
+ if (!json) {
37
+ const c = p.current_counts;
38
+ const counts = c ? `ε=${c.epsilon} β=${c.beta} δ=${c.delta} α=${c.alpha}` : '';
39
+ const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
40
+ process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
41
+ }
42
+ },
43
+ });
44
+ }
45
+ catch (err) {
46
+ if (err instanceof Error)
47
+ this.error(err.message);
48
+ throw err;
49
+ }
50
+ if (!json)
51
+ process.stderr.write('Done.\n');
52
+ if (json)
53
+ this.log(JSON.stringify(result, null, 2));
54
+ return result;
55
+ }
56
+ }
@@ -3,7 +3,7 @@ export default class FaceCreate extends BaseCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
- username: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
+ alias: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
7
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  'default-model': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  formula: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -8,10 +8,10 @@ export default class FaceCreate extends BaseCommand {
8
8
  static flags = {
9
9
  ...BaseCommand.baseFlags,
10
10
  name: Flags.string({ description: 'Display name', required: true }),
11
- username: Flags.string({ description: 'Unique username slug', required: true }),
11
+ alias: Flags.string({ description: 'Unique alias slug', required: true }),
12
12
  description: Flags.string({ description: 'Description for local catalog' }),
13
13
  'default-model': Flags.string({ description: 'Default LLM for chat (e.g. gpt-4o-mini, claude-sonnet-4-6)' }),
14
- formula: Flags.string({ description: 'Boolean formula over owned concrete face usernames (e.g. "a | b", "(a | b) - c"). Creates a composite face.' }),
14
+ formula: Flags.string({ description: 'Boolean formula over owned concrete face aliases (e.g. "a | b", "(a | b) - c"). Creates a composite face.' }),
15
15
  attr: Flags.string({ description: 'Attribute KEY=VALUE (repeatable)', multiple: true }),
16
16
  tool: Flags.string({ description: 'Tool name to enable (repeatable)', multiple: true }),
17
17
  };
@@ -22,7 +22,7 @@ export default class FaceCreate extends BaseCommand {
22
22
  if (flags.formula && (flags.attr?.length || flags.tool?.length)) {
23
23
  this.error('--formula cannot be combined with --attr or --tool. Composite faces do not have compiled knowledge.');
24
24
  }
25
- const payload = { name: flags.name, username: flags.username };
25
+ const payload = { name: flags.name, alias: flags.alias };
26
26
  if (flags.formula) {
27
27
  payload.formula = flags.formula;
28
28
  }
@@ -10,7 +10,7 @@ export default class FaceDelete extends BaseCommand {
10
10
  yes: Flags.boolean({ description: 'Skip confirmation', default: false }),
11
11
  };
12
12
  static args = {
13
- face_id: Args.string({ description: 'Face ID or username', required: true }),
13
+ face_id: Args.string({ description: 'Face alias', required: true }),
14
14
  };
15
15
  async run() {
16
16
  const { args, flags } = await this.parse(FaceDelete);
@@ -6,7 +6,7 @@ export default class FaceDiff extends BaseCommand {
6
6
  static flags = {
7
7
  ...BaseCommand.baseFlags,
8
8
  face: Flags.string({
9
- description: 'Face username to include (repeatable, min 2)',
9
+ description: 'Face alias to include (repeatable, min 2)',
10
10
  multiple: true,
11
11
  required: true,
12
12
  }),
@@ -3,12 +3,12 @@ import { BaseCommand } from '../../base.js';
3
3
  import { FacesAPIError } from '../../client.js';
4
4
  import { flattenBasicFacts } from '../../utils.js';
5
5
  export default class FaceGet extends BaseCommand {
6
- static description = 'Get details for a face by ID or username';
6
+ static description = 'Get details for a face by alias';
7
7
  static flags = {
8
8
  ...BaseCommand.baseFlags,
9
9
  };
10
10
  static args = {
11
- face_id: Args.string({ description: 'Face ID or username', required: true }),
11
+ face_id: Args.string({ description: 'Face alias', required: true }),
12
12
  };
13
13
  async run() {
14
14
  const { args, flags } = await this.parse(FaceGet);
@@ -26,7 +26,7 @@ export default class FaceGet extends BaseCommand {
26
26
  if (f.basic_facts)
27
27
  f.basic_facts = flattenBasicFacts(f.basic_facts);
28
28
  if (!this.jsonEnabled()) {
29
- this.log(`id: ${f.id}`);
29
+ this.log(`alias: ${f.alias}`);
30
30
  this.log(`uid: ${f.uid}`);
31
31
  this.log(`name: ${f.name}`);
32
32
  this.log(`owned_by: ${f.owned_by}`);
@@ -31,7 +31,7 @@ export default class FaceList extends BaseCommand {
31
31
  this.log('(no faces)');
32
32
  }
33
33
  else {
34
- const idWidth = Math.max(...faces.map(f => f.id.length));
34
+ const aliasWidth = Math.max(...faces.map(f => f.alias.length));
35
35
  const nameWidth = Math.max(...faces.map(f => f.name.length));
36
36
  for (const f of faces) {
37
37
  let suffix = '';
@@ -44,7 +44,7 @@ export default class FaceList extends BaseCommand {
44
44
  const profile = f.profile_token_count ?? 0;
45
45
  suffix = ` [profile: ${profile} tok, components: ${total}]`;
46
46
  }
47
- this.log(`${f.id.padEnd(idWidth)} ${f.name.padEnd(nameWidth)}${suffix}`);
47
+ this.log(`${f.alias.padEnd(aliasWidth)} ${f.name.padEnd(nameWidth)}${suffix}`);
48
48
  }
49
49
  }
50
50
  }
@@ -23,7 +23,7 @@ export default class FaceNeighbors extends BaseCommand {
23
23
  }),
24
24
  };
25
25
  static args = {
26
- face_id: Args.string({ description: 'Face username', required: true }),
26
+ face_id: Args.string({ description: 'Face alias', required: true }),
27
27
  };
28
28
  async run() {
29
29
  const { args, flags } = await this.parse(FaceNeighbors);
@@ -15,7 +15,7 @@ export default class FaceUpdate extends BaseCommand {
15
15
  tool: Flags.string({ description: 'Tool names to set (replaces list, repeatable)', multiple: true }),
16
16
  };
17
17
  static args = {
18
- face_id: Args.string({ description: 'Face ID or username', required: true }),
18
+ face_id: Args.string({ description: 'Face alias', required: true }),
19
19
  };
20
20
  async run() {
21
21
  const { args, flags } = await this.parse(FaceUpdate);
@@ -23,7 +23,7 @@ export default class FaceUpload extends BaseCommand {
23
23
  }),
24
24
  };
25
25
  static args = {
26
- face_id: Args.string({ description: 'Face ID or username', required: true }),
26
+ face_id: Args.string({ description: 'Face alias', required: true }),
27
27
  };
28
28
  async run() {
29
29
  const { args, flags } = await this.parse(FaceUpload);
@@ -49,8 +49,18 @@ export default class FaceUpload extends BaseCommand {
49
49
  this.error(`Error (${err.statusCode}): ${err.message}`);
50
50
  throw err;
51
51
  }
52
- if (!this.jsonEnabled())
52
+ if (!this.jsonEnabled()) {
53
53
  this.printHuman(data);
54
+ const res = data;
55
+ if (flags.kind === 'thread' && res.thread_id) {
56
+ this.log(`\nNext step:`);
57
+ this.log(` faces compile:thread:make ${res.thread_id}`);
58
+ }
59
+ else if (flags.kind === 'document' && res.document_id) {
60
+ this.log(`\nNext step:`);
61
+ this.log(` faces compile:doc:make ${res.document_id}`);
62
+ }
63
+ }
54
64
  return data;
55
65
  }
56
66
  }
@@ -8,7 +8,7 @@ export default class KeysCreate extends BaseCommand {
8
8
  name: Flags.string({ description: 'Key name/label', required: true }),
9
9
  'expires-days': Flags.integer({ description: 'Expiry in days (omit for no expiry)' }),
10
10
  budget: Flags.string({ description: 'Spend budget in USD' }),
11
- face: Flags.string({ description: 'Allowed face username (repeatable)', multiple: true }),
11
+ face: Flags.string({ description: 'Allowed face alias (repeatable)', multiple: true }),
12
12
  model: Flags.string({ description: 'Allowed model name (repeatable)', multiple: true }),
13
13
  };
14
14
  async run() {
package/dist/poll.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Shared polling helper for document compile progress.
2
+ * Shared polling helper for compile progress (documents and threads).
3
3
  *
4
4
  * Works with both /make (preparing → syncing → synced) and
5
5
  * legacy /prepare (processing → ready) status flows.
@@ -20,11 +20,13 @@ export interface PollOptions {
20
20
  intervalMs?: number;
21
21
  timeoutMs?: number;
22
22
  onProgress?: (p: PollProgress) => void;
23
+ /** Override the GET endpoint path. Defaults to /v1/compile/documents/{id}. */
24
+ endpoint?: string;
23
25
  }
24
26
  /**
25
- * Poll GET /compile/documents/{docId} until a terminal status is reached.
27
+ * Poll a compile GET endpoint until a terminal status is reached.
26
28
  * Terminal statuses: "synced" (from /make), "ready" (from /prepare), "failed".
27
29
  * Returns the final GET response.
28
30
  * Throws on timeout or failure.
29
31
  */
30
- export declare function pollCompileProgress(client: FacesClient, docId: string, opts?: PollOptions): Promise<unknown>;
32
+ export declare function pollCompileProgress(client: FacesClient, id: string, opts?: PollOptions): Promise<unknown>;
package/dist/poll.js CHANGED
@@ -1,20 +1,21 @@
1
1
  /**
2
- * Poll GET /compile/documents/{docId} until a terminal status is reached.
2
+ * Poll a compile GET endpoint until a terminal status is reached.
3
3
  * Terminal statuses: "synced" (from /make), "ready" (from /prepare), "failed".
4
4
  * Returns the final GET response.
5
5
  * Throws on timeout or failure.
6
6
  */
7
- export async function pollCompileProgress(client, docId, opts = {}) {
7
+ export async function pollCompileProgress(client, id, opts = {}) {
8
8
  const interval = opts.intervalMs ?? 3000;
9
9
  const timeout = opts.timeoutMs ?? 600_000;
10
10
  const start = Date.now();
11
+ const endpoint = opts.endpoint ?? `/v1/compile/documents/${id}`;
11
12
  let lastChunks = -1;
12
13
  while (true) {
13
14
  if (Date.now() - start > timeout) {
14
15
  throw new Error(`Compile timed out after ${Math.round(timeout / 1000)}s`);
15
16
  }
16
17
  await sleep(interval);
17
- const data = await client.get(`/v1/compile/documents/${docId}`);
18
+ const data = await client.get(endpoint);
18
19
  const progress = {
19
20
  prepare_status: data.prepare_status ?? null,
20
21
  chunks_total: data.chunks_total ?? null,
@@ -1172,7 +1172,7 @@
1172
1172
  "aliases": [],
1173
1173
  "args": {
1174
1174
  "face_username": {
1175
- "description": "Face username (or face@model)",
1175
+ "description": "Face alias (or alias@model)",
1176
1176
  "name": "face_username",
1177
1177
  "required": true
1178
1178
  }
@@ -1457,7 +1457,7 @@
1457
1457
  "aliases": [],
1458
1458
  "args": {
1459
1459
  "face_id": {
1460
- "description": "Face ID or username",
1460
+ "description": "Face alias",
1461
1461
  "name": "face_id",
1462
1462
  "required": true
1463
1463
  }
@@ -1771,9 +1771,9 @@
1771
1771
  "multiple": false,
1772
1772
  "type": "option"
1773
1773
  },
1774
- "username": {
1775
- "description": "Unique username slug",
1776
- "name": "username",
1774
+ "alias": {
1775
+ "description": "Unique alias slug",
1776
+ "name": "alias",
1777
1777
  "required": true,
1778
1778
  "hasDynamicHelp": false,
1779
1779
  "multiple": false,
@@ -1794,7 +1794,7 @@
1794
1794
  "type": "option"
1795
1795
  },
1796
1796
  "formula": {
1797
- "description": "Boolean formula over owned concrete face usernames (e.g. \"a | b\", \"(a | b) - c\"). Creates a composite face.",
1797
+ "description": "Boolean formula over owned concrete face aliases (e.g. \"a | b\", \"(a | b) - c\"). Creates a composite face.",
1798
1798
  "name": "formula",
1799
1799
  "hasDynamicHelp": false,
1800
1800
  "multiple": false,
@@ -1835,7 +1835,7 @@
1835
1835
  "aliases": [],
1836
1836
  "args": {
1837
1837
  "face_id": {
1838
- "description": "Face ID or username",
1838
+ "description": "Face alias",
1839
1839
  "name": "face_id",
1840
1840
  "required": true
1841
1841
  }
@@ -1933,7 +1933,7 @@
1933
1933
  "type": "option"
1934
1934
  },
1935
1935
  "face": {
1936
- "description": "Face username to include (repeatable, min 2)",
1936
+ "description": "Face alias to include (repeatable, min 2)",
1937
1937
  "name": "face",
1938
1938
  "required": true,
1939
1939
  "hasDynamicHelp": false,
@@ -1961,12 +1961,12 @@
1961
1961
  "aliases": [],
1962
1962
  "args": {
1963
1963
  "face_id": {
1964
- "description": "Face ID or username",
1964
+ "description": "Face alias",
1965
1965
  "name": "face_id",
1966
1966
  "required": true
1967
1967
  }
1968
1968
  },
1969
- "description": "Get details for a face by ID or username",
1969
+ "description": "Get details for a face by alias",
1970
1970
  "flags": {
1971
1971
  "json": {
1972
1972
  "description": "Format output as json.",
@@ -2073,7 +2073,7 @@
2073
2073
  "aliases": [],
2074
2074
  "args": {
2075
2075
  "face_id": {
2076
- "description": "Face username",
2076
+ "description": "Face alias",
2077
2077
  "name": "face_id",
2078
2078
  "required": true
2079
2079
  }
@@ -2219,7 +2219,7 @@
2219
2219
  "aliases": [],
2220
2220
  "args": {
2221
2221
  "face_id": {
2222
- "description": "Face ID or username",
2222
+ "description": "Face alias",
2223
2223
  "name": "face_id",
2224
2224
  "required": true
2225
2225
  }
@@ -2320,7 +2320,7 @@
2320
2320
  "aliases": [],
2321
2321
  "args": {
2322
2322
  "face_id": {
2323
- "description": "Face ID or username",
2323
+ "description": "Face alias",
2324
2324
  "name": "face_id",
2325
2325
  "required": true
2326
2326
  }
@@ -2473,7 +2473,7 @@
2473
2473
  "type": "option"
2474
2474
  },
2475
2475
  "face": {
2476
- "description": "Allowed face username (repeatable)",
2476
+ "description": "Allowed face alias (repeatable)",
2477
2477
  "name": "face",
2478
2478
  "hasDynamicHelp": false,
2479
2479
  "multiple": true,
@@ -2704,7 +2704,7 @@
2704
2704
  "aliases": [],
2705
2705
  "args": {
2706
2706
  "face_id": {
2707
- "description": "Face ID or username",
2707
+ "description": "Face alias",
2708
2708
  "name": "face_id",
2709
2709
  "required": true
2710
2710
  }
@@ -3009,7 +3009,7 @@
3009
3009
  "aliases": [],
3010
3010
  "args": {
3011
3011
  "face_id": {
3012
- "description": "Face ID or username",
3012
+ "description": "Face alias",
3013
3013
  "name": "face_id",
3014
3014
  "required": true
3015
3015
  }
@@ -3110,7 +3110,7 @@
3110
3110
  "aliases": [],
3111
3111
  "args": {
3112
3112
  "face_id": {
3113
- "description": "Face ID or username",
3113
+ "description": "Face alias",
3114
3114
  "name": "face_id",
3115
3115
  "required": true
3116
3116
  }
@@ -3238,7 +3238,7 @@
3238
3238
  "aliases": [],
3239
3239
  "args": {
3240
3240
  "face_id": {
3241
- "description": "Face ID or username",
3241
+ "description": "Face alias",
3242
3242
  "name": "face_id",
3243
3243
  "required": true
3244
3244
  }
@@ -3499,7 +3499,7 @@
3499
3499
  "aliases": [],
3500
3500
  "args": {
3501
3501
  "face_id": {
3502
- "description": "Face ID or username",
3502
+ "description": "Face alias",
3503
3503
  "name": "face_id",
3504
3504
  "required": true
3505
3505
  }
@@ -3555,6 +3555,74 @@
3555
3555
  "list.js"
3556
3556
  ]
3557
3557
  },
3558
+ "compile:thread:make": {
3559
+ "aliases": [],
3560
+ "args": {
3561
+ "thread_id": {
3562
+ "description": "Thread ID",
3563
+ "name": "thread_id",
3564
+ "required": true
3565
+ }
3566
+ },
3567
+ "description": "Compile an existing thread (prepare + sync in one step)",
3568
+ "flags": {
3569
+ "json": {
3570
+ "description": "Format output as json.",
3571
+ "helpGroup": "GLOBAL",
3572
+ "name": "json",
3573
+ "allowNo": false,
3574
+ "type": "boolean"
3575
+ },
3576
+ "base-url": {
3577
+ "description": "API base URL",
3578
+ "env": "FACES_BASE_URL",
3579
+ "name": "base-url",
3580
+ "hasDynamicHelp": false,
3581
+ "multiple": false,
3582
+ "type": "option"
3583
+ },
3584
+ "token": {
3585
+ "description": "JWT bearer token",
3586
+ "env": "FACES_TOKEN",
3587
+ "name": "token",
3588
+ "hasDynamicHelp": false,
3589
+ "multiple": false,
3590
+ "type": "option"
3591
+ },
3592
+ "api-key": {
3593
+ "description": "API key",
3594
+ "env": "FACES_API_KEY",
3595
+ "name": "api-key",
3596
+ "hasDynamicHelp": false,
3597
+ "multiple": false,
3598
+ "type": "option"
3599
+ },
3600
+ "timeout": {
3601
+ "description": "Compile timeout in seconds (default: 600)",
3602
+ "name": "timeout",
3603
+ "default": 600,
3604
+ "hasDynamicHelp": false,
3605
+ "multiple": false,
3606
+ "type": "option"
3607
+ }
3608
+ },
3609
+ "hasDynamicHelp": false,
3610
+ "hiddenAliases": [],
3611
+ "id": "compile:thread:make",
3612
+ "pluginAlias": "faces-cli",
3613
+ "pluginName": "faces-cli",
3614
+ "pluginType": "core",
3615
+ "strict": true,
3616
+ "enableJsonFlag": true,
3617
+ "isESM": true,
3618
+ "relativePath": [
3619
+ "dist",
3620
+ "commands",
3621
+ "compile",
3622
+ "thread",
3623
+ "make.js"
3624
+ ]
3625
+ },
3558
3626
  "compile:thread:message": {
3559
3627
  "aliases": [],
3560
3628
  "args": {
@@ -3685,5 +3753,5 @@
3685
3753
  ]
3686
3754
  }
3687
3755
  },
3688
- "version": "1.4.2"
3756
+ "version": "1.4.4"
3689
3757
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "faces-cli",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "CLI for the Faces AI platform",
5
5
  "type": "module",
6
6
  "author": "sybileak <sybileak@proton.me>",