faces-cli 1.3.1 → 1.4.0

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 (46) hide show
  1. package/dist/catalog.d.ts +36 -0
  2. package/dist/catalog.js +167 -0
  3. package/dist/client.js +2 -1
  4. package/dist/commands/auth/register.js +1 -0
  5. package/dist/commands/auth/whoami.js +1 -1
  6. package/dist/commands/catalog/doctor.d.ts +12 -0
  7. package/dist/commands/catalog/doctor.js +196 -0
  8. package/dist/commands/catalog/list.d.ts +10 -0
  9. package/dist/commands/catalog/list.js +35 -0
  10. package/dist/commands/chat/chat.d.ts +4 -0
  11. package/dist/commands/chat/chat.js +146 -39
  12. package/dist/commands/chat/messages.js +1 -1
  13. package/dist/commands/chat/responses.js +1 -1
  14. package/dist/commands/compile/doc/create.d.ts +1 -0
  15. package/dist/commands/compile/doc/create.js +5 -2
  16. package/dist/commands/compile/doc/edit.d.ts +17 -0
  17. package/dist/commands/compile/doc/edit.js +48 -0
  18. package/dist/commands/compile/doc/index.d.ts +18 -0
  19. package/dist/commands/compile/doc/index.js +95 -0
  20. package/dist/commands/compile/doc/list.js +1 -1
  21. package/dist/commands/compile/doc/make.d.ts +14 -0
  22. package/dist/commands/compile/doc/make.js +55 -0
  23. package/dist/commands/compile/import.js +2 -3
  24. package/dist/commands/compile/thread/create.js +2 -2
  25. package/dist/commands/compile/{doc/sync.d.ts → thread/delete.d.ts} +2 -2
  26. package/dist/commands/compile/{doc/sync.js → thread/delete.js} +7 -7
  27. package/dist/commands/compile/thread/edit.d.ts +14 -0
  28. package/dist/commands/compile/thread/edit.js +29 -0
  29. package/dist/commands/compile/{doc/prepare.d.ts → thread/get.d.ts} +2 -2
  30. package/dist/commands/compile/{doc/prepare.js → thread/get.js} +5 -5
  31. package/dist/commands/compile/thread/list.js +1 -1
  32. package/dist/commands/compile/thread/message.js +1 -1
  33. package/dist/commands/face/create.d.ts +2 -0
  34. package/dist/commands/face/create.js +17 -2
  35. package/dist/commands/face/delete.js +6 -0
  36. package/dist/commands/face/get.js +5 -3
  37. package/dist/commands/face/list.js +20 -3
  38. package/dist/commands/face/update.d.ts +2 -0
  39. package/dist/commands/face/update.js +39 -9
  40. package/dist/config.d.ts +2 -0
  41. package/dist/poll.d.ts +30 -0
  42. package/dist/poll.js +40 -0
  43. package/dist/utils.d.ts +5 -0
  44. package/dist/utils.js +16 -0
  45. package/oclif.manifest.json +970 -477
  46. package/package.json +4 -1
@@ -2,80 +2,187 @@ import { Args, Flags } from '@oclif/core';
2
2
  import fs from 'node:fs';
3
3
  import { BaseCommand } from '../../base.js';
4
4
  import { FacesAPIError } from '../../client.js';
5
+ function isAnthropicModel(model) {
6
+ // Extract model name from face@model format
7
+ const parts = model.split('@');
8
+ const llm = parts.length > 1 ? parts[parts.length - 1] : '';
9
+ return llm.startsWith('claude');
10
+ }
5
11
  export default class ChatChat extends BaseCommand {
6
- static description = 'OpenAI-compatible chat completion via a face';
12
+ static description = 'Chat via a face. Auto-routes to the correct API endpoint based on model provider.';
7
13
  static flags = {
8
14
  ...BaseCommand.baseFlags,
9
15
  message: Flags.string({ char: 'm', description: 'User message (repeatable)', multiple: true, required: false }),
10
- llm: Flags.string({ description: 'LLM override (e.g. claude-sonnet-4-6)' }),
11
- system: Flags.string({ description: 'System prompt override' }),
16
+ llm: Flags.string({ description: 'LLM override (e.g. gpt-4o-mini, claude-sonnet-4-6)' }),
17
+ system: Flags.string({ description: 'System prompt / instructions' }),
12
18
  stream: Flags.boolean({ description: 'Stream the response', default: false }),
13
19
  'max-tokens': Flags.integer({ description: 'Max tokens' }),
14
20
  temperature: Flags.string({ description: 'Temperature (0.0-2.0)' }),
15
21
  file: Flags.string({ description: 'Read message from file' }),
22
+ responses: Flags.boolean({ description: 'Use OpenAI Responses API instead of Chat Completions', default: false }),
16
23
  };
17
24
  static args = {
18
- face_username: Args.string({ description: 'Face username', required: true }),
25
+ face_username: Args.string({ description: 'Face username (or face@model)', required: true }),
19
26
  };
20
27
  async run() {
21
28
  const { args, flags } = await this.parse(ChatChat);
22
29
  const client = this.makeClient(flags);
23
- const messages = [];
24
- if (flags.system)
25
- messages.push({ role: 'system', content: flags.system });
30
+ // Build model string
31
+ let model;
32
+ if (flags.llm) {
33
+ const base = args.face_username.split('@')[0];
34
+ model = `${base}@${flags.llm}`;
35
+ }
36
+ else {
37
+ model = args.face_username;
38
+ }
39
+ // Collect user messages
40
+ const userMessages = [];
26
41
  if (flags.file) {
27
42
  if (!fs.existsSync(flags.file))
28
43
  this.error(`File not found: ${flags.file}`);
29
- messages.push({ role: 'user', content: fs.readFileSync(flags.file, 'utf8') });
44
+ userMessages.push(fs.readFileSync(flags.file, 'utf8'));
30
45
  }
31
46
  else if (flags.message && flags.message.length > 0) {
32
- for (const m of flags.message)
33
- messages.push({ role: 'user', content: m });
47
+ userMessages.push(...flags.message);
34
48
  }
35
49
  else {
36
50
  this.error('Provide at least one --message/-m or --file');
37
51
  }
38
- const model = flags.llm ? `${args.face_username}@${flags.llm}` : args.face_username;
52
+ // Route based on model provider
53
+ const anthropic = isAnthropicModel(model);
54
+ const useResponses = flags.responses && !anthropic;
55
+ try {
56
+ if (anthropic) {
57
+ return await this.runMessages(client, model, userMessages, flags);
58
+ }
59
+ else if (useResponses) {
60
+ return await this.runResponses(client, model, userMessages[0], flags);
61
+ }
62
+ else {
63
+ return await this.runChatCompletions(client, model, userMessages, flags);
64
+ }
65
+ }
66
+ catch (err) {
67
+ if (err instanceof FacesAPIError)
68
+ this.error(`Error (${err.statusCode}): ${err.message}`);
69
+ throw err;
70
+ }
71
+ }
72
+ async runChatCompletions(client, model, userMessages, flags) {
73
+ const messages = [];
74
+ if (flags.system)
75
+ messages.push({ role: 'system', content: flags.system });
76
+ for (const m of userMessages)
77
+ messages.push({ role: 'user', content: m });
39
78
  const payload = { model, messages, stream: flags.stream };
40
79
  if (flags['max-tokens'])
41
80
  payload.max_tokens = flags['max-tokens'];
42
81
  if (flags.temperature !== undefined)
43
82
  payload.temperature = Number.parseFloat(flags.temperature);
44
- try {
45
- if (flags.stream) {
46
- for await (const line of client.stream('/v1/chat/completions', payload)) {
47
- if (!line.startsWith('data: '))
48
- continue;
49
- const chunk = line.slice(6);
50
- if (chunk.trim() === '[DONE]')
51
- break;
52
- try {
53
- const parsed = JSON.parse(chunk);
54
- const choices = parsed?.choices;
55
- const delta = choices?.[0]?.delta?.content ?? '';
83
+ if (flags.stream) {
84
+ for await (const line of client.stream('/v1/chat/completions', payload)) {
85
+ if (!line.startsWith('data: '))
86
+ continue;
87
+ const chunk = line.slice(6);
88
+ if (chunk.trim() === '[DONE]')
89
+ break;
90
+ try {
91
+ const parsed = JSON.parse(chunk);
92
+ const choices = parsed?.choices;
93
+ const delta = choices?.[0]?.delta?.content ?? '';
94
+ if (delta)
95
+ process.stdout.write(String(delta));
96
+ }
97
+ catch { /* ignore parse errors */ }
98
+ }
99
+ process.stdout.write('\n');
100
+ return {};
101
+ }
102
+ const data = (await client.post('/v1/chat/completions', { body: payload }));
103
+ if (this.jsonEnabled())
104
+ return data;
105
+ const choices = data.choices;
106
+ const message = choices?.[0]?.message;
107
+ this.log(String(message?.content ?? ''));
108
+ return data;
109
+ }
110
+ async runMessages(client, model, userMessages, flags) {
111
+ const msgs = userMessages.map((m) => ({ role: 'user', content: m }));
112
+ const payload = {
113
+ model,
114
+ messages: msgs,
115
+ max_tokens: flags['max-tokens'] ?? 1024,
116
+ stream: flags.stream,
117
+ };
118
+ if (flags.system)
119
+ payload.system = flags.system;
120
+ if (flags.stream) {
121
+ for await (const line of client.stream('/v1/messages', payload)) {
122
+ if (!line.startsWith('data: '))
123
+ continue;
124
+ const chunk = line.slice(6);
125
+ if (chunk.trim() === '[DONE]' || chunk.trim() === '')
126
+ continue;
127
+ try {
128
+ const obj = JSON.parse(chunk);
129
+ if (obj.type === 'content_block_delta') {
130
+ const delta = obj.delta?.text ?? '';
56
131
  if (delta)
57
132
  process.stdout.write(String(delta));
58
133
  }
59
- catch {
60
- // ignore parse errors
61
- }
62
134
  }
63
- process.stdout.write('\n');
64
- return {};
135
+ catch { /* ignore */ }
65
136
  }
66
- const data = (await client.post('/v1/chat/completions', { body: payload }));
67
- if (this.jsonEnabled())
68
- return data;
69
- const choices = data.choices;
70
- const message = choices?.[0]?.message;
71
- const content = message?.content ?? '';
72
- this.log(String(content));
137
+ process.stdout.write('\n');
138
+ return {};
139
+ }
140
+ const data = (await client.post('/v1/messages', { body: payload }));
141
+ if (this.jsonEnabled())
73
142
  return data;
143
+ let content = '';
144
+ for (const block of data.content ?? []) {
145
+ if (block.type === 'text')
146
+ content += String(block.text ?? '');
74
147
  }
75
- catch (err) {
76
- if (err instanceof FacesAPIError)
77
- this.error(`Error (${err.statusCode}): ${err.message}`);
78
- throw err;
148
+ this.log(content);
149
+ return data;
150
+ }
151
+ async runResponses(client, model, input, flags) {
152
+ const payload = { model, input, stream: flags.stream };
153
+ if (flags.system)
154
+ payload.instructions = flags.system;
155
+ if (flags.stream) {
156
+ for await (const line of client.stream('/v1/responses', payload)) {
157
+ if (!line.startsWith('data: '))
158
+ continue;
159
+ const chunk = line.slice(6);
160
+ if (chunk.trim() === '[DONE]' || chunk.trim() === '')
161
+ continue;
162
+ try {
163
+ const obj = JSON.parse(chunk);
164
+ if (obj.type === 'response.output_text.delta') {
165
+ const delta = obj.delta ?? '';
166
+ if (delta)
167
+ process.stdout.write(String(delta));
168
+ }
169
+ }
170
+ catch { /* ignore */ }
171
+ }
172
+ process.stdout.write('\n');
173
+ return {};
174
+ }
175
+ const data = (await client.post('/v1/responses', { body: payload }));
176
+ if (this.jsonEnabled())
177
+ return data;
178
+ let outputText = '';
179
+ for (const item of data.output ?? []) {
180
+ for (const block of item.content ?? []) {
181
+ if (block.type === 'output_text')
182
+ outputText += String(block.text ?? '');
183
+ }
79
184
  }
185
+ this.log(outputText);
186
+ return data;
80
187
  }
81
188
  }
@@ -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
  export default class ChatMessages extends BaseCommand {
5
- static description = 'Anthropic Messages API proxy via a face (e.g. face@claude-sonnet-4-6)';
5
+ static description = 'Anthropic Messages API proxy via a face (alias: chat:chat auto-routes Anthropic models here)';
6
6
  static flags = {
7
7
  ...BaseCommand.baseFlags,
8
8
  message: Flags.string({ char: 'm', description: 'User message (repeatable)', multiple: true, required: true }),
@@ -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
  export default class ChatResponses extends BaseCommand {
5
- static description = 'OpenAI Responses API proxy via a face';
5
+ static description = 'OpenAI Responses API proxy via a face (alias: chat:chat --responses)';
6
6
  static flags = {
7
7
  ...BaseCommand.baseFlags,
8
8
  message: Flags.string({ char: 'm', description: 'User input message', required: true }),
@@ -5,6 +5,7 @@ export default class CompileDocCreate extends BaseCommand {
5
5
  label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
6
  content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
7
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ perspective: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
9
  'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -9,6 +9,7 @@ export default class CompileDocCreate extends BaseCommand {
9
9
  label: Flags.string({ description: 'Document label/title' }),
10
10
  content: Flags.string({ description: 'Inline text content' }),
11
11
  file: Flags.string({ description: 'Read content from file' }),
12
+ perspective: Flags.string({ description: 'Perspective (first-person or third-person)', options: ['first-person', 'third-person'], default: 'first-person' }),
12
13
  };
13
14
  static args = {
14
15
  face_id: Args.string({ description: 'Face ID or username', required: true }),
@@ -24,12 +25,14 @@ export default class CompileDocCreate extends BaseCommand {
24
25
  }
25
26
  if (!content)
26
27
  this.error('Provide --content or --file.');
27
- const payload = { content };
28
+ const payload = { model: args.face_id, content };
28
29
  if (flags.label)
29
30
  payload.label = flags.label;
31
+ if (flags.perspective)
32
+ payload.perspective = flags.perspective;
30
33
  let data;
31
34
  try {
32
- data = await client.post(`/v1/faces/${args.face_id}/compile/documents`, { body: payload });
35
+ data = await client.post('/v1/compile/documents', { body: payload });
33
36
  }
34
37
  catch (err) {
35
38
  if (err instanceof FacesAPIError)
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class CompileDocEdit extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ perspective: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static args: {
14
+ doc_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
15
+ };
16
+ run(): Promise<unknown>;
17
+ }
@@ -0,0 +1,48 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import fs from 'node:fs';
3
+ import { BaseCommand } from '../../../base.js';
4
+ import { FacesAPIError } from '../../../client.js';
5
+ export default class CompileDocEdit extends BaseCommand {
6
+ static description = 'Edit a document (label, content, or perspective)';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ label: Flags.string({ description: 'New label/title' }),
10
+ content: Flags.string({ description: 'New inline text content' }),
11
+ file: Flags.string({ description: 'Read new content from file' }),
12
+ perspective: Flags.string({ description: 'Perspective (first-person or third-person)', options: ['first-person', 'third-person'] }),
13
+ };
14
+ static args = {
15
+ doc_id: Args.string({ description: 'Document ID', required: true }),
16
+ };
17
+ async run() {
18
+ const { args, flags } = await this.parse(CompileDocEdit);
19
+ const client = this.makeClient(flags);
20
+ const payload = {};
21
+ if (flags.label)
22
+ payload.label = flags.label;
23
+ if (flags.perspective)
24
+ payload.perspective = flags.perspective;
25
+ if (flags.file) {
26
+ if (!fs.existsSync(flags.file))
27
+ this.error(`File not found: ${flags.file}`);
28
+ payload.content = fs.readFileSync(flags.file, 'utf8');
29
+ }
30
+ else if (flags.content) {
31
+ payload.content = flags.content;
32
+ }
33
+ if (Object.keys(payload).length === 0)
34
+ this.error('Provide at least one field to edit.');
35
+ let data;
36
+ try {
37
+ data = await client.patch(`/v1/compile/documents/${args.doc_id}`, { body: payload });
38
+ }
39
+ catch (err) {
40
+ if (err instanceof FacesAPIError)
41
+ this.error(`Error (${err.statusCode}): ${err.message}`);
42
+ throw err;
43
+ }
44
+ if (!this.jsonEnabled())
45
+ this.printHuman(data);
46
+ return data;
47
+ }
48
+ }
@@ -0,0 +1,18 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class CompileDoc extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ perspective: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ static args: {
15
+ face_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
16
+ };
17
+ run(): Promise<unknown>;
18
+ }
@@ -0,0 +1,95 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import fs from 'node:fs';
3
+ import { BaseCommand } from '../../../base.js';
4
+ import { FacesAPIError } from '../../../client.js';
5
+ import { pollCompileProgress } from '../../../poll.js';
6
+ export default class CompileDoc extends BaseCommand {
7
+ static description = 'Compile a document into a face (create → make in one step)';
8
+ static flags = {
9
+ ...BaseCommand.baseFlags,
10
+ label: Flags.string({ description: 'Document label/title' }),
11
+ content: Flags.string({ description: 'Inline text content' }),
12
+ file: Flags.string({ description: 'Read content from file' }),
13
+ perspective: Flags.string({
14
+ description: 'Perspective',
15
+ options: ['first-person', 'third-person'],
16
+ default: 'first-person',
17
+ }),
18
+ timeout: Flags.integer({ description: 'Compile timeout in seconds (default: 600)', default: 600 }),
19
+ };
20
+ static args = {
21
+ face_id: Args.string({ description: 'Face ID or username', required: true }),
22
+ };
23
+ async run() {
24
+ const { args, flags } = await this.parse(CompileDoc);
25
+ const client = this.makeClient(flags);
26
+ const json = this.jsonEnabled();
27
+ // Resolve content
28
+ let content = flags.content;
29
+ if (flags.file) {
30
+ if (!fs.existsSync(flags.file))
31
+ this.error(`File not found: ${flags.file}`);
32
+ content = fs.readFileSync(flags.file, 'utf8');
33
+ }
34
+ if (!content)
35
+ this.error('Provide --content or --file.');
36
+ // Step 1: Create document
37
+ if (!json)
38
+ process.stderr.write('Creating document... ');
39
+ let doc;
40
+ try {
41
+ const payload = { model: args.face_id, content };
42
+ if (flags.label)
43
+ payload.label = flags.label;
44
+ if (flags.perspective)
45
+ payload.perspective = flags.perspective;
46
+ doc = await client.post('/v1/compile/documents', { body: payload });
47
+ }
48
+ catch (err) {
49
+ if (err instanceof FacesAPIError)
50
+ this.error(`Error (${err.statusCode}): ${err.message}`);
51
+ throw err;
52
+ }
53
+ const docId = (doc.document_id ?? doc.id);
54
+ if (!json)
55
+ process.stderr.write(`done (${docId})\n`);
56
+ // Step 2: Make (prepare + sync in one call, returns 202)
57
+ let makeResponse;
58
+ try {
59
+ makeResponse = await client.post(`/v1/compile/documents/${docId}/make`);
60
+ }
61
+ catch (err) {
62
+ if (err instanceof FacesAPIError)
63
+ this.error(`Error (${err.statusCode}): ${err.message}`);
64
+ throw err;
65
+ }
66
+ const chunksTotal = makeResponse.chunks_total;
67
+ if (!json)
68
+ process.stderr.write(`Compiling${chunksTotal ? ` (${chunksTotal} chunks)` : ''}:\n`);
69
+ // Step 3: Poll until synced
70
+ let result;
71
+ try {
72
+ result = await pollCompileProgress(client, docId, {
73
+ timeoutMs: flags.timeout * 1000,
74
+ onProgress: (p) => {
75
+ if (!json) {
76
+ const c = p.current_counts;
77
+ const counts = c ? `ε=${c.epsilon} β=${c.beta} δ=${c.delta} α=${c.alpha}` : '';
78
+ const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
79
+ process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
80
+ }
81
+ },
82
+ });
83
+ }
84
+ catch (err) {
85
+ if (err instanceof Error)
86
+ this.error(err.message);
87
+ throw err;
88
+ }
89
+ if (!json)
90
+ process.stderr.write('Done.\n');
91
+ if (json)
92
+ this.log(JSON.stringify(result, null, 2));
93
+ return result;
94
+ }
95
+ }
@@ -14,7 +14,7 @@ export default class CompileDocList extends BaseCommand {
14
14
  const client = this.makeClient(flags);
15
15
  let data;
16
16
  try {
17
- data = await client.get(`/v1/faces/${args.face_id}/compile/documents`);
17
+ data = await client.get('/v1/compile/documents', { params: { model: 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 CompileDocMake 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
+ doc_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,55 @@
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 CompileDocMake extends BaseCommand {
6
+ static description = 'Compile an existing document (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
+ doc_id: Args.string({ description: 'Document ID', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(CompileDocMake);
16
+ const client = this.makeClient(flags);
17
+ const json = this.jsonEnabled();
18
+ let makeResponse;
19
+ try {
20
+ makeResponse = await client.post(`/v1/compile/documents/${args.doc_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.doc_id, {
33
+ timeoutMs: flags.timeout * 1000,
34
+ onProgress: (p) => {
35
+ if (!json) {
36
+ const c = p.current_counts;
37
+ const counts = c ? `ε=${c.epsilon} β=${c.beta} δ=${c.delta} α=${c.alpha}` : '';
38
+ const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
39
+ process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
40
+ }
41
+ },
42
+ });
43
+ }
44
+ catch (err) {
45
+ if (err instanceof Error)
46
+ this.error(err.message);
47
+ throw err;
48
+ }
49
+ if (!json)
50
+ process.stderr.write('Done.\n');
51
+ if (json)
52
+ this.log(JSON.stringify(result, null, 2));
53
+ return result;
54
+ }
55
+ }
@@ -62,9 +62,8 @@ export default class CompileImport extends BaseCommand {
62
62
  this.log(`label: ${doc.label}`);
63
63
  this.log(`tokens: ${doc.token_count}`);
64
64
  this.log(``);
65
- this.log(`Next steps:`);
66
- this.log(` faces compile:doc:prepare ${doc.document_id}`);
67
- this.log(` faces compile:doc:sync ${doc.document_id} --yes`);
65
+ this.log(`Next step:`);
66
+ this.log(` faces compile:doc:make ${doc.document_id}`);
68
67
  }
69
68
  else {
70
69
  const thread = res;
@@ -13,12 +13,12 @@ export default class CompileThreadCreate extends BaseCommand {
13
13
  async run() {
14
14
  const { args, flags } = await this.parse(CompileThreadCreate);
15
15
  const client = this.makeClient(flags);
16
- const payload = {};
16
+ const payload = { model: args.face_id };
17
17
  if (flags.label)
18
18
  payload.label = flags.label;
19
19
  let data;
20
20
  try {
21
- data = await client.post(`/v1/faces/${args.face_id}/compile/threads`, { body: payload });
21
+ data = await client.post('/v1/compile/threads', { body: payload });
22
22
  }
23
23
  catch (err) {
24
24
  if (err instanceof FacesAPIError)
@@ -1,5 +1,5 @@
1
1
  import { BaseCommand } from '../../../base.js';
2
- export default class CompileDocSync extends BaseCommand {
2
+ export default class CompileThreadDelete extends BaseCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -8,7 +8,7 @@ export default class CompileDocSync extends BaseCommand {
8
8
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  };
10
10
  static args: {
11
- doc_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
11
+ thread_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
12
  };
13
13
  run(): Promise<unknown>;
14
14
  private confirm;
@@ -2,26 +2,26 @@ import { Args, Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../../base.js';
3
3
  import { FacesAPIError } from '../../../client.js';
4
4
  import { createInterface } from 'node:readline';
5
- export default class CompileDocSync extends BaseCommand {
6
- static description = 'Sync a document into a face (charges compile quota)';
5
+ export default class CompileThreadDelete extends BaseCommand {
6
+ static description = 'Delete a thread';
7
7
  static flags = {
8
8
  ...BaseCommand.baseFlags,
9
- yes: Flags.boolean({ description: 'Skip confirmation (quota will be charged)', default: false }),
9
+ yes: Flags.boolean({ description: 'Skip confirmation', default: false }),
10
10
  };
11
11
  static args = {
12
- doc_id: Args.string({ description: 'Document ID', required: true }),
12
+ thread_id: Args.string({ description: 'Thread ID', required: true }),
13
13
  };
14
14
  async run() {
15
- const { args, flags } = await this.parse(CompileDocSync);
15
+ const { args, flags } = await this.parse(CompileThreadDelete);
16
16
  const client = this.makeClient(flags);
17
17
  if (!flags.yes) {
18
- const confirmed = await this.confirm('Syncing into a face charges compile quota. Continue?');
18
+ const confirmed = await this.confirm(`Delete thread '${args.thread_id}'? This cannot be undone.`);
19
19
  if (!confirmed)
20
20
  this.error('Aborted.');
21
21
  }
22
22
  let data;
23
23
  try {
24
- data = await client.post(`/v1/compile/documents/${args.doc_id}/sync`);
24
+ data = await client.delete(`/v1/compile/threads/${args.thread_id}`);
25
25
  }
26
26
  catch (err) {
27
27
  if (err instanceof FacesAPIError)
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class CompileThreadEdit extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ label: import("@oclif/core/interfaces").OptionFlag<string, 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
+ }