faces-cli 1.7.9 → 1.7.14
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 +2 -12
- package/dist/catalog.js +2 -3
- package/dist/commands/chat/chat.d.ts +1 -0
- package/dist/commands/chat/chat.js +36 -3
- package/dist/commands/chat/messages.d.ts +1 -0
- package/dist/commands/chat/messages.js +5 -1
- package/dist/commands/chat/responses.d.ts +1 -0
- package/dist/commands/chat/responses.js +5 -1
- package/dist/commands/chat/thread.d.ts +1 -0
- package/dist/commands/chat/thread.js +31 -3
- package/dist/commands/compile/doc/create.d.ts +1 -0
- package/dist/commands/compile/doc/create.js +4 -0
- package/dist/commands/compile/doc/edit.d.ts +1 -0
- package/dist/commands/compile/doc/edit.js +4 -0
- package/dist/commands/compile/doc/index.d.ts +1 -0
- package/dist/commands/compile/doc/index.js +7 -3
- package/dist/commands/compile/doc/make.js +1 -1
- package/dist/commands/compile/thread/make.js +1 -1
- package/dist/commands/compile/upload.d.ts +1 -0
- package/dist/commands/compile/upload.js +10 -1
- package/dist/commands/face/diff.js +8 -6
- package/dist/commands/face/list.d.ts +1 -0
- package/dist/commands/face/list.js +16 -3
- package/dist/commands/face/neighbors.js +20 -4
- package/dist/commands/face/publish.d.ts +15 -0
- package/dist/commands/face/publish.js +41 -0
- package/dist/commands/face/share.d.ts +21 -0
- package/dist/commands/face/share.js +106 -0
- package/dist/commands/face/unpublish.d.ts +13 -0
- package/dist/commands/face/unpublish.js +30 -0
- package/dist/commands/face/unshare.d.ts +17 -0
- package/dist/commands/face/unshare.js +67 -0
- package/dist/poll.d.ts +1 -6
- package/dist/utils.d.ts +34 -0
- package/dist/utils.js +57 -0
- package/oclif.manifest.json +920 -540
- package/package.json +1 -1
package/dist/catalog.d.ts
CHANGED
|
@@ -8,12 +8,7 @@ export interface FaceFrontmatter {
|
|
|
8
8
|
tags?: string[];
|
|
9
9
|
default_model?: string;
|
|
10
10
|
formula?: string | null;
|
|
11
|
-
component_counts?:
|
|
12
|
-
alpha: number;
|
|
13
|
-
beta: number;
|
|
14
|
-
delta: number;
|
|
15
|
-
epsilon: number;
|
|
16
|
-
};
|
|
11
|
+
component_counts?: number[];
|
|
17
12
|
compiled_tokens?: number;
|
|
18
13
|
profile_token_count?: number;
|
|
19
14
|
[key: string]: unknown;
|
|
@@ -29,12 +24,7 @@ export interface FaceDataInput {
|
|
|
29
24
|
tags?: string[] | null;
|
|
30
25
|
default_model?: string | null;
|
|
31
26
|
formula?: string | null;
|
|
32
|
-
component_counts?:
|
|
33
|
-
alpha: number;
|
|
34
|
-
beta: number;
|
|
35
|
-
delta: number;
|
|
36
|
-
epsilon: number;
|
|
37
|
-
} | null;
|
|
27
|
+
component_counts?: number[] | null;
|
|
38
28
|
profile_token_count?: number | null;
|
|
39
29
|
total_tokens_saved?: number | null;
|
|
40
30
|
}
|
package/dist/catalog.js
CHANGED
|
@@ -29,9 +29,8 @@ function serializeFrontmatter(fm) {
|
|
|
29
29
|
if (fm.tags && fm.tags.length > 0) {
|
|
30
30
|
lines.push(`tags: [${fm.tags.join(', ')}]`);
|
|
31
31
|
}
|
|
32
|
-
if (fm.component_counts) {
|
|
33
|
-
|
|
34
|
-
lines.push(`component_counts: ${cc.alpha + cc.beta + cc.delta + cc.epsilon}`);
|
|
32
|
+
if (fm.component_counts?.length) {
|
|
33
|
+
lines.push(`component_counts: ${fm.component_counts.reduce((a, b) => a + b, 0)}`);
|
|
35
34
|
}
|
|
36
35
|
if (fm.compiled_tokens) {
|
|
37
36
|
lines.push(`compiled_tokens: ${fm.compiled_tokens}`);
|
|
@@ -12,6 +12,7 @@ export default class ChatChat extends BaseCommand {
|
|
|
12
12
|
temperature: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
14
|
responses: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
17
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
18
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -2,7 +2,7 @@ import { Args, Flags } from '@oclif/core';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import { BaseCommand } from '../../base.js';
|
|
4
4
|
import { FacesAPIError } from '../../client.js';
|
|
5
|
-
import { extractStreamDelta } from '../../utils.js';
|
|
5
|
+
import { extractStreamDelta, isMaxTokensRenameError, MIN_MAX_OUTPUT_TOKENS, MEDIUM_FLAG_DESCRIPTION, warnDroppedParams, CHAT_PARAM_FLAGS } from '../../utils.js';
|
|
6
6
|
import { resolveEndpoint, MESSAGES_ENDPOINT, RESPONSES_ENDPOINT, isCompositeFormula, faceSpecOf, llmFromArg } from '../../routing.js';
|
|
7
7
|
export default class ChatChat extends BaseCommand {
|
|
8
8
|
static description = 'Chat via a face. Auto-routes to the correct API endpoint based on the model catalog.';
|
|
@@ -22,6 +22,7 @@ export default class ChatChat extends BaseCommand {
|
|
|
22
22
|
temperature: Flags.string({ description: 'Temperature (0.0-2.0)' }),
|
|
23
23
|
file: Flags.string({ description: 'Read message from file' }),
|
|
24
24
|
responses: Flags.boolean({ description: 'Force the OpenAI Responses API endpoint (escape hatch; routing is automatic otherwise)', default: false }),
|
|
25
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
25
26
|
'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
|
|
26
27
|
};
|
|
27
28
|
static args = {
|
|
@@ -140,13 +141,32 @@ export default class ChatChat extends BaseCommand {
|
|
|
140
141
|
payload.max_tokens = flags['max-tokens'];
|
|
141
142
|
if (flags.temperature !== undefined)
|
|
142
143
|
payload.temperature = Number.parseFloat(flags.temperature);
|
|
144
|
+
if (flags.medium)
|
|
145
|
+
payload.medium = flags.medium;
|
|
143
146
|
if (flags['oauth-only'])
|
|
144
147
|
payload.oauth_only = true;
|
|
145
148
|
if (flags.stream) {
|
|
146
149
|
await this.streamDeltas(client, '/v1/chat/completions', payload);
|
|
147
150
|
return {};
|
|
148
151
|
}
|
|
149
|
-
|
|
152
|
+
let data;
|
|
153
|
+
let headers;
|
|
154
|
+
try {
|
|
155
|
+
;
|
|
156
|
+
({ data, headers } = await client.postWithHeaders('/v1/chat/completions', { body: payload }));
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (err instanceof FacesAPIError && isMaxTokensRenameError(err.message) && payload.max_tokens !== undefined) {
|
|
160
|
+
payload.max_completion_tokens = payload.max_tokens;
|
|
161
|
+
delete payload.max_tokens;
|
|
162
|
+
({ data, headers } = await client.postWithHeaders('/v1/chat/completions', { body: payload }));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!this.jsonEnabled())
|
|
169
|
+
warnDroppedParams(headers, CHAT_PARAM_FLAGS);
|
|
150
170
|
const body = data;
|
|
151
171
|
const choices = body.choices;
|
|
152
172
|
const text = String(choices?.[0]?.message?.content ?? '');
|
|
@@ -167,6 +187,8 @@ export default class ChatChat extends BaseCommand {
|
|
|
167
187
|
};
|
|
168
188
|
if (flags.system)
|
|
169
189
|
payload.system = flags.system;
|
|
190
|
+
if (flags.medium)
|
|
191
|
+
payload.medium = flags.medium;
|
|
170
192
|
if (flags['oauth-only'])
|
|
171
193
|
payload.oauth_only = true;
|
|
172
194
|
if (flags.stream) {
|
|
@@ -174,6 +196,8 @@ export default class ChatChat extends BaseCommand {
|
|
|
174
196
|
return {};
|
|
175
197
|
}
|
|
176
198
|
const { data, headers } = await client.postWithHeaders('/v1/messages', { body: payload });
|
|
199
|
+
if (!this.jsonEnabled())
|
|
200
|
+
warnDroppedParams(headers, CHAT_PARAM_FLAGS);
|
|
177
201
|
const body = data;
|
|
178
202
|
if (this.jsonEnabled()) {
|
|
179
203
|
body._meta = this.metaFor(headers, '/v1/messages');
|
|
@@ -199,10 +223,17 @@ export default class ChatChat extends BaseCommand {
|
|
|
199
223
|
// `temperature` is accepted per-model (gpt-5.1/5.2/5.4 yes; gpt-5.5/5.6-*/o3
|
|
200
224
|
// no) with no capability flag to query, so we always send what the user asked
|
|
201
225
|
// for — the backend strips the param and retries when a model 400s on it.
|
|
202
|
-
if (flags['max-tokens'])
|
|
226
|
+
if (flags['max-tokens']) {
|
|
227
|
+
if (flags['max-tokens'] < MIN_MAX_OUTPUT_TOKENS) {
|
|
228
|
+
this.error(`--max-tokens must be at least ${MIN_MAX_OUTPUT_TOKENS} for this model (got ${flags['max-tokens']}). ` +
|
|
229
|
+
'The Responses API rejects a smaller output cap.');
|
|
230
|
+
}
|
|
203
231
|
payload.max_output_tokens = flags['max-tokens'];
|
|
232
|
+
}
|
|
204
233
|
if (flags.temperature !== undefined)
|
|
205
234
|
payload.temperature = Number.parseFloat(flags.temperature);
|
|
235
|
+
if (flags.medium)
|
|
236
|
+
payload.medium = flags.medium;
|
|
206
237
|
if (flags['oauth-only'])
|
|
207
238
|
payload.oauth_only = true;
|
|
208
239
|
if (flags.stream) {
|
|
@@ -210,6 +241,8 @@ export default class ChatChat extends BaseCommand {
|
|
|
210
241
|
return {};
|
|
211
242
|
}
|
|
212
243
|
const { data, headers } = await client.postWithHeaders('/v1/responses', { body: payload });
|
|
244
|
+
if (!this.jsonEnabled())
|
|
245
|
+
warnDroppedParams(headers, CHAT_PARAM_FLAGS);
|
|
213
246
|
const body = data;
|
|
214
247
|
if (this.jsonEnabled()) {
|
|
215
248
|
body._meta = this.metaFor(headers, '/v1/responses');
|
|
@@ -6,6 +6,7 @@ export default class ChatMessages extends BaseCommand {
|
|
|
6
6
|
system: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
stream: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
8
|
'max-tokens': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base.js';
|
|
3
|
+
import { MEDIUM_FLAG_DESCRIPTION } from '../../utils.js';
|
|
3
4
|
import { FacesAPIError } from '../../client.js';
|
|
4
5
|
export default class ChatMessages extends BaseCommand {
|
|
5
6
|
static description = 'Anthropic Messages API proxy via a face (alias: chat:chat auto-routes Anthropic models here)';
|
|
@@ -9,10 +10,11 @@ export default class ChatMessages extends BaseCommand {
|
|
|
9
10
|
system: Flags.string({ description: 'System prompt' }),
|
|
10
11
|
stream: Flags.boolean({ description: 'Stream the response', default: false }),
|
|
11
12
|
'max-tokens': Flags.integer({ description: 'Max tokens', default: 1024 }),
|
|
13
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
12
14
|
'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
|
|
13
15
|
};
|
|
14
16
|
static args = {
|
|
15
|
-
face_model: Args.string({ description: 'Face model (e.g. myface, myface@claude-sonnet-4-6, or head:
|
|
17
|
+
face_model: Args.string({ description: 'Face model (e.g. myface, myface@claude-sonnet-4-6, or head:socrates@claude-sonnet-4-6)', required: true }),
|
|
16
18
|
};
|
|
17
19
|
async run() {
|
|
18
20
|
const { args, flags } = await this.parse(ChatMessages);
|
|
@@ -26,6 +28,8 @@ export default class ChatMessages extends BaseCommand {
|
|
|
26
28
|
};
|
|
27
29
|
if (flags.system)
|
|
28
30
|
payload.system = flags.system;
|
|
31
|
+
if (flags.medium)
|
|
32
|
+
payload.medium = flags.medium;
|
|
29
33
|
if (flags['oauth-only'])
|
|
30
34
|
payload.oauth_only = true;
|
|
31
35
|
try {
|
|
@@ -5,6 +5,7 @@ export default class ChatResponses extends BaseCommand {
|
|
|
5
5
|
message: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
6
|
instructions: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
stream: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base.js';
|
|
3
|
+
import { MEDIUM_FLAG_DESCRIPTION } from '../../utils.js';
|
|
3
4
|
import { FacesAPIError } from '../../client.js';
|
|
4
5
|
export default class ChatResponses extends BaseCommand {
|
|
5
6
|
static description = 'OpenAI Responses API proxy via a face (alias: chat:chat --responses)';
|
|
@@ -8,10 +9,11 @@ export default class ChatResponses extends BaseCommand {
|
|
|
8
9
|
message: Flags.string({ char: 'm', description: 'User input message', required: true }),
|
|
9
10
|
instructions: Flags.string({ description: 'System instructions' }),
|
|
10
11
|
stream: Flags.boolean({ description: 'Stream the response', default: false }),
|
|
12
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
11
13
|
'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
|
|
12
14
|
};
|
|
13
15
|
static args = {
|
|
14
|
-
face_model: Args.string({ description: 'Face model (e.g. myface, myface@gpt-4o, or head:
|
|
16
|
+
face_model: Args.string({ description: 'Face model (e.g. myface, myface@gpt-4o, or head:socrates@gpt-5.4)', required: true }),
|
|
15
17
|
};
|
|
16
18
|
async run() {
|
|
17
19
|
const { args, flags } = await this.parse(ChatResponses);
|
|
@@ -23,6 +25,8 @@ export default class ChatResponses extends BaseCommand {
|
|
|
23
25
|
};
|
|
24
26
|
if (flags.instructions)
|
|
25
27
|
payload.instructions = flags.instructions;
|
|
28
|
+
if (flags.medium)
|
|
29
|
+
payload.medium = flags.medium;
|
|
26
30
|
if (flags['oauth-only'])
|
|
27
31
|
payload.oauth_only = true;
|
|
28
32
|
try {
|
|
@@ -11,6 +11,7 @@ export default class ChatThread extends BaseCommand {
|
|
|
11
11
|
'max-tokens': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
temperature: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
stream: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
16
|
list: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
17
|
show: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { BaseCommand } from '../../base.js';
|
|
7
7
|
import { FacesAPIError } from '../../client.js';
|
|
8
|
-
import { extractStreamDelta } from '../../utils.js';
|
|
8
|
+
import { extractStreamDelta, isMaxTokensRenameError, MIN_MAX_OUTPUT_TOKENS, MEDIUM_FLAG_DESCRIPTION } from '../../utils.js';
|
|
9
9
|
import { resolveEndpoint, MESSAGES_ENDPOINT, RESPONSES_ENDPOINT, CHAT_COMPLETIONS_ENDPOINT } from '../../routing.js';
|
|
10
10
|
const THREADS_DIR = path.join(os.homedir(), '.faces', 'threads');
|
|
11
11
|
/**
|
|
@@ -91,6 +91,7 @@ export default class ChatThread extends BaseCommand {
|
|
|
91
91
|
'max-tokens': Flags.integer({ description: 'Max tokens' }),
|
|
92
92
|
temperature: Flags.string({ description: 'Temperature (0.0-2.0, OpenAI/Chat Completions only)' }),
|
|
93
93
|
stream: Flags.boolean({ description: 'Stream the response', default: false }),
|
|
94
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
94
95
|
'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key', default: false }),
|
|
95
96
|
list: Flags.boolean({ description: 'List saved threads', default: false }),
|
|
96
97
|
show: Flags.boolean({ description: 'Show the thread transcript (requires --id)', default: false }),
|
|
@@ -279,12 +280,30 @@ export default class ChatThread extends BaseCommand {
|
|
|
279
280
|
payload.max_tokens = flags['max-tokens'];
|
|
280
281
|
if (flags.temperature !== undefined)
|
|
281
282
|
payload.temperature = Number.parseFloat(flags.temperature);
|
|
283
|
+
if (flags.medium)
|
|
284
|
+
payload.medium = flags.medium;
|
|
282
285
|
if (flags['oauth-only'])
|
|
283
286
|
payload.oauth_only = true;
|
|
284
287
|
if (flags.stream) {
|
|
285
288
|
return this.streamAndAccumulate(client, CHAT_COMPLETIONS_ENDPOINT, payload);
|
|
286
289
|
}
|
|
287
|
-
|
|
290
|
+
let data;
|
|
291
|
+
try {
|
|
292
|
+
;
|
|
293
|
+
({ data } = await client.postWithHeaders(CHAT_COMPLETIONS_ENDPOINT, { body: payload }));
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
// Newer OpenAI models require max_completion_tokens; retry on the provider's
|
|
297
|
+
// own rejection rather than keeping a model list that goes stale.
|
|
298
|
+
if (err instanceof FacesAPIError && isMaxTokensRenameError(err.message) && payload.max_tokens !== undefined) {
|
|
299
|
+
payload.max_completion_tokens = payload.max_tokens;
|
|
300
|
+
delete payload.max_tokens;
|
|
301
|
+
({ data } = await client.postWithHeaders(CHAT_COMPLETIONS_ENDPOINT, { body: payload }));
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
288
307
|
return extractAssistantText(data, CHAT_COMPLETIONS_ENDPOINT);
|
|
289
308
|
}
|
|
290
309
|
async sendResponses(client, thread, flags) {
|
|
@@ -294,8 +313,15 @@ export default class ChatThread extends BaseCommand {
|
|
|
294
313
|
const payload = { model: thread.model, input, stream: flags.stream };
|
|
295
314
|
if (thread.system)
|
|
296
315
|
payload.instructions = thread.system;
|
|
297
|
-
if (flags['max-tokens'])
|
|
316
|
+
if (flags['max-tokens']) {
|
|
317
|
+
if (flags['max-tokens'] < MIN_MAX_OUTPUT_TOKENS) {
|
|
318
|
+
throw new Error(`--max-tokens must be at least ${MIN_MAX_OUTPUT_TOKENS} for this model (got ${flags['max-tokens']}). ` +
|
|
319
|
+
'The Responses API rejects a smaller output cap.');
|
|
320
|
+
}
|
|
298
321
|
payload.max_output_tokens = flags['max-tokens'];
|
|
322
|
+
}
|
|
323
|
+
if (flags.medium)
|
|
324
|
+
payload.medium = flags.medium;
|
|
299
325
|
if (flags['oauth-only'])
|
|
300
326
|
payload.oauth_only = true;
|
|
301
327
|
if (flags.stream) {
|
|
@@ -314,6 +340,8 @@ export default class ChatThread extends BaseCommand {
|
|
|
314
340
|
};
|
|
315
341
|
if (thread.system)
|
|
316
342
|
payload.system = thread.system;
|
|
343
|
+
if (flags.medium)
|
|
344
|
+
payload.medium = flags.medium;
|
|
317
345
|
if (flags['oauth-only'])
|
|
318
346
|
payload.oauth_only = true;
|
|
319
347
|
if (flags.stream) {
|
|
@@ -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
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
perspective: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import { BaseCommand } from '../../../base.js';
|
|
4
|
+
import { MEDIUM_FLAG_DESCRIPTION } from '../../../utils.js';
|
|
4
5
|
import { FacesAPIError } from '../../../client.js';
|
|
5
6
|
export default class CompileDocCreate extends BaseCommand {
|
|
6
7
|
static description = 'Submit a document to a face';
|
|
@@ -9,6 +10,7 @@ export default class CompileDocCreate extends BaseCommand {
|
|
|
9
10
|
label: Flags.string({ description: 'Document label/title' }),
|
|
10
11
|
content: Flags.string({ description: 'Inline text content' }),
|
|
11
12
|
file: Flags.string({ description: 'Read content from file' }),
|
|
13
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
12
14
|
perspective: Flags.string({ description: 'Perspective (first-person or third-person)', options: ['first-person', 'third-person'], default: 'first-person' }),
|
|
13
15
|
};
|
|
14
16
|
static args = {
|
|
@@ -28,6 +30,8 @@ export default class CompileDocCreate extends BaseCommand {
|
|
|
28
30
|
const payload = { alias: args.face_id, content };
|
|
29
31
|
if (flags.label)
|
|
30
32
|
payload.label = flags.label;
|
|
33
|
+
if (flags.medium)
|
|
34
|
+
payload.medium = flags.medium;
|
|
31
35
|
if (flags.perspective)
|
|
32
36
|
payload.perspective = flags.perspective;
|
|
33
37
|
let data;
|
|
@@ -5,6 +5,7 @@ export default class CompileDocEdit 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
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
perspective: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import { BaseCommand } from '../../../base.js';
|
|
4
|
+
import { MEDIUM_FLAG_DESCRIPTION } from '../../../utils.js';
|
|
4
5
|
import { FacesAPIError } from '../../../client.js';
|
|
5
6
|
export default class CompileDocEdit extends BaseCommand {
|
|
6
7
|
static description = 'Edit a document (label, content, or perspective)';
|
|
@@ -9,6 +10,7 @@ export default class CompileDocEdit extends BaseCommand {
|
|
|
9
10
|
label: Flags.string({ description: 'New label/title' }),
|
|
10
11
|
content: Flags.string({ description: 'New inline text content' }),
|
|
11
12
|
file: Flags.string({ description: 'Read new content from file' }),
|
|
13
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
12
14
|
perspective: Flags.string({ description: 'Perspective (first-person or third-person)', options: ['first-person', 'third-person'] }),
|
|
13
15
|
};
|
|
14
16
|
static args = {
|
|
@@ -20,6 +22,8 @@ export default class CompileDocEdit extends BaseCommand {
|
|
|
20
22
|
const payload = {};
|
|
21
23
|
if (flags.label)
|
|
22
24
|
payload.label = flags.label;
|
|
25
|
+
if (flags.medium)
|
|
26
|
+
payload.medium = flags.medium;
|
|
23
27
|
if (flags.perspective)
|
|
24
28
|
payload.perspective = flags.perspective;
|
|
25
29
|
if (flags.file) {
|
|
@@ -6,6 +6,7 @@ export default class CompileDoc extends BaseCommand {
|
|
|
6
6
|
label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
file: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
perspective: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -2,6 +2,7 @@ import { Args, Flags } from '@oclif/core';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { BaseCommand } from '../../../base.js';
|
|
5
|
+
import { MEDIUM_FLAG_DESCRIPTION } from '../../../utils.js';
|
|
5
6
|
import { FacesAPIError } from '../../../client.js';
|
|
6
7
|
import { pollCompileProgress } from '../../../poll.js';
|
|
7
8
|
export default class CompileDoc extends BaseCommand {
|
|
@@ -17,6 +18,7 @@ export default class CompileDoc extends BaseCommand {
|
|
|
17
18
|
label: Flags.string({ description: 'Document label/title (single document only; with several files each is labelled by its filename)' }),
|
|
18
19
|
content: Flags.string({ description: 'Inline text content', exclusive: ['file'] }),
|
|
19
20
|
file: Flags.string({ description: 'Read content from file (repeatable — each file becomes its own document)', multiple: true }),
|
|
21
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
20
22
|
perspective: Flags.string({
|
|
21
23
|
description: 'Perspective',
|
|
22
24
|
options: ['first-person', 'third-person'],
|
|
@@ -71,6 +73,8 @@ export default class CompileDoc extends BaseCommand {
|
|
|
71
73
|
payload.label = flags.label;
|
|
72
74
|
else if (multi)
|
|
73
75
|
payload.label = path.basename(source.name);
|
|
76
|
+
if (flags.medium)
|
|
77
|
+
payload.medium = flags.medium;
|
|
74
78
|
if (flags.perspective)
|
|
75
79
|
payload.perspective = flags.perspective;
|
|
76
80
|
if (!json)
|
|
@@ -123,7 +127,7 @@ export default class CompileDoc extends BaseCommand {
|
|
|
123
127
|
if (json)
|
|
124
128
|
return;
|
|
125
129
|
const c = p.current_counts;
|
|
126
|
-
const counts = c ?
|
|
130
|
+
const counts = c?.length ? `${c.reduce((a, b) => a + b, 0)} components` : '';
|
|
127
131
|
const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
|
|
128
132
|
process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
|
|
129
133
|
},
|
|
@@ -174,8 +178,8 @@ export default class CompileDoc extends BaseCommand {
|
|
|
174
178
|
/** Extensions the compile-documents endpoint takes as inline text. */
|
|
175
179
|
const BINARY_HINTS = {
|
|
176
180
|
'.pdf': 'PDF is supported, but not by this command — use: faces compile:upload <alias> --file <path> --kind document',
|
|
177
|
-
'.docx': 'Word documents are
|
|
178
|
-
'.doc': '
|
|
181
|
+
'.docx': 'Word documents are supported, but not by this command — use: faces compile:upload <alias> --file <path> --kind document',
|
|
182
|
+
'.doc': "Legacy '.doc' is not supported. Open it in Word or Pages and save as .docx, then upload that with: faces compile:upload <alias> --file <path> --kind document",
|
|
179
183
|
'.pages': 'Pages documents are not supported. Export it as text or PDF first.',
|
|
180
184
|
'.rtf': 'RTF is not supported. Convert it to plain text first (e.g. `textutil -convert txt file.rtf`).',
|
|
181
185
|
'.key': 'Keynote files are not supported. Export the text first.',
|
|
@@ -45,7 +45,7 @@ export default class CompileDocMake extends BaseCommand {
|
|
|
45
45
|
onProgress: (p) => {
|
|
46
46
|
if (!json) {
|
|
47
47
|
const c = p.current_counts;
|
|
48
|
-
const counts = c ?
|
|
48
|
+
const counts = c?.length ? `${c.reduce((a, b) => a + b, 0)} components` : '';
|
|
49
49
|
const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
|
|
50
50
|
process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
|
|
51
51
|
}
|
|
@@ -46,7 +46,7 @@ export default class CompileThreadMake extends BaseCommand {
|
|
|
46
46
|
onProgress: (p) => {
|
|
47
47
|
if (!json) {
|
|
48
48
|
const c = p.current_counts;
|
|
49
|
-
const counts = c ?
|
|
49
|
+
const counts = c?.length ? `${c.reduce((a, b) => a + b, 0)} components` : '';
|
|
50
50
|
const phase = p.prepare_status === 'syncing' ? ' (syncing)' : '';
|
|
51
51
|
process.stderr.write(` [${p.chunks_completed ?? '?'}/${p.chunks_total ?? '?'}] ${counts}${phase}\n`);
|
|
52
52
|
}
|
|
@@ -5,6 +5,7 @@ export default class CompileUpload extends BaseCommand {
|
|
|
5
5
|
file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
6
|
kind: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
perspective: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
'face-speaker': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -2,12 +2,13 @@ import { Args, Flags } from '@oclif/core';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { BaseCommand } from '../../base.js';
|
|
5
|
+
import { MEDIUM_FLAG_DESCRIPTION } from '../../utils.js';
|
|
5
6
|
import { FacesAPIError } from '../../client.js';
|
|
6
7
|
function sleep(ms) {
|
|
7
8
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
9
|
}
|
|
9
10
|
export default class CompileUpload extends BaseCommand {
|
|
10
|
-
static description = 'Upload a file (text/PDF/audio/video) to compile into a face';
|
|
11
|
+
static description = 'Upload a file (text/PDF/Word .docx/audio/video) to compile into a face';
|
|
11
12
|
static flags = {
|
|
12
13
|
...BaseCommand.baseFlags,
|
|
13
14
|
file: Flags.string({ description: 'File to upload', required: true }),
|
|
@@ -21,6 +22,7 @@ export default class CompileUpload extends BaseCommand {
|
|
|
21
22
|
options: ['first-person', 'third-person'],
|
|
22
23
|
default: 'third-person',
|
|
23
24
|
}),
|
|
25
|
+
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION + ' Only valid with --kind document.' }),
|
|
24
26
|
'face-speaker': Flags.string({
|
|
25
27
|
description: 'Speaker name to map to the face (thread only). If omitted, auto-detected from face name.',
|
|
26
28
|
}),
|
|
@@ -42,8 +44,15 @@ export default class CompileUpload extends BaseCommand {
|
|
|
42
44
|
const fileBlob = new Blob([fs.readFileSync(flags.file)], { type: 'application/octet-stream' });
|
|
43
45
|
const form = new FormData();
|
|
44
46
|
form.append('file', fileBlob, filename);
|
|
47
|
+
// The API accepts medium for document uploads only — a thread carries a kind
|
|
48
|
+
// per message, derived from the file. Refuse here rather than on a 422.
|
|
49
|
+
if (flags.medium && flags.kind !== 'document') {
|
|
50
|
+
this.error(`--medium applies to --kind document only (got --kind ${flags.kind}); a thread carries a kind per message.`);
|
|
51
|
+
}
|
|
45
52
|
const params = new URLSearchParams();
|
|
46
53
|
params.set('type', flags.kind);
|
|
54
|
+
if (flags.medium)
|
|
55
|
+
params.set('medium', flags.medium);
|
|
47
56
|
params.set('perspective', flags.perspective);
|
|
48
57
|
if (flags['face-speaker'])
|
|
49
58
|
params.set('face_speaker', flags['face-speaker']);
|
|
@@ -30,16 +30,18 @@ export default class FaceDiff extends BaseCommand {
|
|
|
30
30
|
if (!this.jsonEnabled()) {
|
|
31
31
|
const res = data;
|
|
32
32
|
const faces = res.faces;
|
|
33
|
-
const
|
|
33
|
+
const fmt = (v) => (v === null || v === undefined ? '—' : v.toFixed(2));
|
|
34
34
|
for (let i = 0; i < faces.length; i++) {
|
|
35
35
|
for (let j = i + 1; j < faces.length; j++) {
|
|
36
36
|
const a = faces[i];
|
|
37
37
|
const b = faces[j];
|
|
38
|
-
const
|
|
39
|
-
const parts =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
const { face: overall, components } = res.similarity[a][b];
|
|
39
|
+
const parts = [
|
|
40
|
+
`overall: ${fmt(overall)}`,
|
|
41
|
+
// Label by true index, never by ordinal-among-non-null: the position
|
|
42
|
+
// is the identity, and it is the same vocabulary the API uses.
|
|
43
|
+
...(components ?? []).map((v, n) => `component ${n}: ${fmt(v)}`),
|
|
44
|
+
];
|
|
43
45
|
this.log(`${a} ↔ ${b} ${parts.join(' ')}`);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -6,6 +6,7 @@ export default class FaceList extends BaseCommand {
|
|
|
6
6
|
team: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
public: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
shared: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
system: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
'from-users': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
'not-from-users': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -11,7 +11,8 @@ export default class FaceList extends BaseCommand {
|
|
|
11
11
|
tag: Flags.string({ description: 'Filter by tag (repeatable, AND logic)', multiple: true }),
|
|
12
12
|
team: Flags.string({ description: 'Filter by team ID (repeatable, OR logic)', multiple: true }),
|
|
13
13
|
include: Flags.string({ description: 'Include extra fields: tags, teams (comma-separated)' }),
|
|
14
|
-
public: Flags.boolean({ description: 'Include published faces from other accounts' }),
|
|
14
|
+
public: Flags.boolean({ description: 'Include published faces from other accounts (open to everybody)' }),
|
|
15
|
+
shared: Flags.boolean({ description: 'Include faces other accounts have shared with YOU in particular' }),
|
|
15
16
|
system: Flags.boolean({ description: `Show only the curated system faces (published faces owned by '${SYSTEM_OWNER}')` }),
|
|
16
17
|
'from-users': Flags.string({ description: 'Only published faces from these owners (repeatable/comma-separated)', multiple: true }),
|
|
17
18
|
'not-from-users': Flags.string({ description: 'Exclude published faces from these owners (repeatable/comma-separated)', multiple: true }),
|
|
@@ -44,6 +45,8 @@ export default class FaceList extends BaseCommand {
|
|
|
44
45
|
parts.push(`include=${encodeURIComponent(flags.include)}`);
|
|
45
46
|
if (includePublished)
|
|
46
47
|
parts.push('include_published=true');
|
|
48
|
+
if (flags.shared)
|
|
49
|
+
parts.push('include_shared=true');
|
|
47
50
|
for (const u of fromUsers)
|
|
48
51
|
parts.push(`from_users=${encodeURIComponent(u)}`);
|
|
49
52
|
for (const u of notFromUsers)
|
|
@@ -82,7 +85,11 @@ export default class FaceList extends BaseCommand {
|
|
|
82
85
|
data = faces;
|
|
83
86
|
if (!this.jsonEnabled()) {
|
|
84
87
|
// Published faces are chatted as `owner:alias`; own faces stay bare.
|
|
85
|
-
|
|
88
|
+
// A face you do not own needs the owner prefix to resolve at all. Both
|
|
89
|
+
// published and shared faces are someone else's, and only those two carry
|
|
90
|
+
// `published` / `via`, so either marks the alias as needing qualifying.
|
|
91
|
+
const isForeign = (f) => Boolean(f.published || f.via?.length);
|
|
92
|
+
const handle = (f) => isForeign(f) && f.owned_by ? `${f.owned_by}:${f.alias}` : f.alias;
|
|
86
93
|
if (faces.length === 0) {
|
|
87
94
|
this.log('(no faces)');
|
|
88
95
|
}
|
|
@@ -96,10 +103,16 @@ export default class FaceList extends BaseCommand {
|
|
|
96
103
|
}
|
|
97
104
|
else if (f.component_counts || f.profile_token_count) {
|
|
98
105
|
const cc = f.component_counts;
|
|
99
|
-
const total = cc
|
|
106
|
+
const total = cc?.reduce((a, b) => a + b, 0) ?? 0;
|
|
100
107
|
const profile = f.profile_token_count ?? 0;
|
|
101
108
|
suffix = ` [profile: ${profile} tok, components: ${total}]`;
|
|
102
109
|
}
|
|
110
|
+
if (f.via?.length) {
|
|
111
|
+
const how = f.via.includes('shared') && f.via.includes('workspace')
|
|
112
|
+
? 'shared with you, and via a workspace'
|
|
113
|
+
: f.via.includes('workspace') ? 'via a workspace' : 'shared with you';
|
|
114
|
+
suffix += ` [${how} · requires @model]`;
|
|
115
|
+
}
|
|
103
116
|
if (f.published)
|
|
104
117
|
suffix += ' [public · requires @model]';
|
|
105
118
|
if (f.read_only)
|