faces-cli 1.3.2 → 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.
- package/dist/catalog.d.ts +36 -0
- package/dist/catalog.js +167 -0
- package/dist/client.js +2 -1
- package/dist/commands/auth/whoami.js +1 -1
- package/dist/commands/catalog/doctor.d.ts +12 -0
- package/dist/commands/catalog/doctor.js +196 -0
- package/dist/commands/catalog/list.d.ts +10 -0
- package/dist/commands/catalog/list.js +35 -0
- package/dist/commands/chat/chat.d.ts +4 -0
- package/dist/commands/chat/chat.js +146 -39
- package/dist/commands/chat/messages.js +1 -1
- package/dist/commands/chat/responses.js +1 -1
- package/dist/commands/compile/doc/create.d.ts +1 -0
- package/dist/commands/compile/doc/create.js +5 -2
- package/dist/commands/compile/doc/edit.d.ts +17 -0
- package/dist/commands/compile/doc/edit.js +48 -0
- package/dist/commands/compile/doc/index.d.ts +18 -0
- package/dist/commands/compile/doc/index.js +95 -0
- package/dist/commands/compile/doc/list.js +1 -1
- package/dist/commands/compile/doc/make.d.ts +14 -0
- package/dist/commands/compile/doc/make.js +55 -0
- package/dist/commands/compile/import.js +2 -3
- package/dist/commands/compile/thread/create.js +2 -2
- package/dist/commands/compile/{doc/sync.d.ts → thread/delete.d.ts} +2 -2
- package/dist/commands/compile/{doc/sync.js → thread/delete.js} +7 -7
- package/dist/commands/compile/thread/edit.d.ts +14 -0
- package/dist/commands/compile/thread/edit.js +29 -0
- package/dist/commands/compile/{doc/prepare.d.ts → thread/get.d.ts} +2 -2
- package/dist/commands/compile/{doc/prepare.js → thread/get.js} +5 -5
- package/dist/commands/compile/thread/list.js +1 -1
- package/dist/commands/compile/thread/message.js +1 -1
- package/dist/commands/face/create.d.ts +2 -0
- package/dist/commands/face/create.js +17 -2
- package/dist/commands/face/delete.js +6 -0
- package/dist/commands/face/get.js +5 -3
- package/dist/commands/face/list.js +20 -3
- package/dist/commands/face/update.d.ts +2 -0
- package/dist/commands/face/update.js +39 -9
- package/dist/config.d.ts +2 -0
- package/dist/poll.d.ts +30 -0
- package/dist/poll.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +16 -0
- package/oclif.manifest.json +1154 -661
- 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 = '
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
44
|
+
userMessages.push(fs.readFileSync(flags.file, 'utf8'));
|
|
30
45
|
}
|
|
31
46
|
else if (flags.message && flags.message.length > 0) {
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
64
|
-
return {};
|
|
135
|
+
catch { /* ignore */ }
|
|
65
136
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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 (
|
|
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(
|
|
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(
|
|
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
|
|
66
|
-
this.log(` faces compile:doc:
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
6
|
-
static description = '
|
|
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
|
|
9
|
+
yes: Flags.boolean({ description: 'Skip confirmation', default: false }),
|
|
10
10
|
};
|
|
11
11
|
static args = {
|
|
12
|
-
|
|
12
|
+
thread_id: Args.string({ description: 'Thread ID', required: true }),
|
|
13
13
|
};
|
|
14
14
|
async run() {
|
|
15
|
-
const { args, flags } = await this.parse(
|
|
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(
|
|
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.
|
|
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
|
+
}
|