faces-cli 1.8.7 → 1.8.8
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/commands/chat/chat.d.ts +1 -0
- package/dist/commands/chat/chat.js +38 -2
- package/dist/commands/face/lock.js +1 -1
- package/dist/commands/face/unlock.js +1 -1
- package/dist/commands/style/delete.js +1 -18
- package/dist/commands/style/make.js +5 -1
- package/dist/style.d.ts +3 -3
- package/dist/style.js +6 -8
- package/oclif.manifest.json +1012 -1004
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@ export default class ChatChat extends BaseCommand {
|
|
|
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
15
|
medium: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
'best-of': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
17
|
'oauth-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
18
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -5,9 +5,12 @@ import { FacesAPIError } from '../../client.js';
|
|
|
5
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
|
-
static description = 'Chat via a face. Auto-routes to the correct API endpoint based on the model catalog.'
|
|
8
|
+
static description = 'Chat via a face. Auto-routes to the correct API endpoint based on the model catalog. ' +
|
|
9
|
+
'Prefix the alias with + to answer in the style the face captured, e.g. +alice; without it the face ' +
|
|
10
|
+
'uses its ordinary voice.';
|
|
9
11
|
static examples = [
|
|
10
12
|
'<%= config.bin %> <%= command.id %> socrates -m "What is virtue?"',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> +alice -m "Reply to the note below." --medium email',
|
|
11
14
|
'<%= config.bin %> <%= command.id %> "(socrates | nietzsche)@claude-sonnet-4-6" -m "What is virtue?"',
|
|
12
15
|
'<%= config.bin %> <%= command.id %> --formula "socrates | nietzsche" --llm claude-sonnet-4-6 -m "What is virtue?"',
|
|
13
16
|
];
|
|
@@ -23,11 +26,16 @@ export default class ChatChat extends BaseCommand {
|
|
|
23
26
|
file: Flags.string({ description: 'Read message from file' }),
|
|
24
27
|
responses: Flags.boolean({ description: 'Force the OpenAI Responses API endpoint (escape hatch; routing is automatic otherwise)', default: false }),
|
|
25
28
|
medium: Flags.string({ description: MEDIUM_FLAG_DESCRIPTION }),
|
|
29
|
+
'best-of': Flags.integer({
|
|
30
|
+
description: 'Write N replies and serve the one closest to the captured style (1-5, default 1). Only ' +
|
|
31
|
+
'meaningful with a + alias, and it multiplies the cost of the request by N. Cannot be streamed.',
|
|
32
|
+
}),
|
|
26
33
|
'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
|
|
27
34
|
};
|
|
28
35
|
static args = {
|
|
29
36
|
face_username: Args.string({
|
|
30
|
-
description: 'Face alias (alias@model, owner:alias@model for a published face, or a composite formula like
|
|
37
|
+
description: 'Face alias (alias@model, owner:alias@model for a published face, or a composite formula like ' +
|
|
38
|
+
'"(a | b)@model"). Prefix with + to use the face\'s captured style. Omit when using --formula.',
|
|
31
39
|
required: false,
|
|
32
40
|
}),
|
|
33
41
|
};
|
|
@@ -59,6 +67,20 @@ export default class ChatChat extends BaseCommand {
|
|
|
59
67
|
this.error(`Composite face '${spec}' has no default model. Specify one explicitly, ` +
|
|
60
68
|
`e.g. '${spec}@claude-sonnet-4-6', or pass --llm <model>.`);
|
|
61
69
|
}
|
|
70
|
+
// best_of_n picks between finished drafts, so there is nothing to stream.
|
|
71
|
+
// The server answers 400 deepself_streaming_unsupported; say so first.
|
|
72
|
+
if (flags['best-of'] !== undefined) {
|
|
73
|
+
if (flags['best-of'] < 1 || flags['best-of'] > 5) {
|
|
74
|
+
this.error(`--best-of must be between 1 and 5 (got ${flags['best-of']}).`);
|
|
75
|
+
}
|
|
76
|
+
if (flags.stream) {
|
|
77
|
+
this.error('--best-of cannot be streamed: choosing between drafts needs them finished. Drop --stream or --best-of.');
|
|
78
|
+
}
|
|
79
|
+
if (!model.startsWith('+')) {
|
|
80
|
+
this.error(`--best-of only applies to a face's captured style, so the alias needs a + prefix: '+${model}'.\n` +
|
|
81
|
+
'Without it the face answers in its ordinary voice and there is nothing to choose between.');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
62
84
|
// Collect user messages
|
|
63
85
|
const userMessages = [];
|
|
64
86
|
if (flags.file) {
|
|
@@ -90,6 +112,14 @@ export default class ChatChat extends BaseCommand {
|
|
|
90
112
|
if (err instanceof FacesAPIError) {
|
|
91
113
|
if (err.errorCode === 'oauth_rejected')
|
|
92
114
|
this.error(`OAuth failed: ${err.message}\n${this.oauthHint(err, { ...route, endpoint })}`);
|
|
115
|
+
// A + request on a face with no captured style is refused, not quietly
|
|
116
|
+
// downgraded. The same request without the + works.
|
|
117
|
+
if (err.statusCode === 409 && model.startsWith('+')) {
|
|
118
|
+
const bare = faceSpecOf(model).replace(/^\+/, '');
|
|
119
|
+
this.error(`Error (409): ${err.message}\n` +
|
|
120
|
+
`Capture one with: faces style:make ${bare} --all\n` +
|
|
121
|
+
`Or drop the + to use ${bare}'s ordinary voice.`);
|
|
122
|
+
}
|
|
93
123
|
this.error(`Error (${err.statusCode}): ${err.message}`);
|
|
94
124
|
}
|
|
95
125
|
throw err;
|
|
@@ -143,6 +173,8 @@ export default class ChatChat extends BaseCommand {
|
|
|
143
173
|
payload.temperature = Number.parseFloat(flags.temperature);
|
|
144
174
|
if (flags.medium)
|
|
145
175
|
payload.medium = flags.medium;
|
|
176
|
+
if (flags['best-of'] !== undefined)
|
|
177
|
+
payload.best_of_n = flags['best-of'];
|
|
146
178
|
if (flags['oauth-only'])
|
|
147
179
|
payload.oauth_only = true;
|
|
148
180
|
if (flags.stream) {
|
|
@@ -189,6 +221,8 @@ export default class ChatChat extends BaseCommand {
|
|
|
189
221
|
payload.system = flags.system;
|
|
190
222
|
if (flags.medium)
|
|
191
223
|
payload.medium = flags.medium;
|
|
224
|
+
if (flags['best-of'] !== undefined)
|
|
225
|
+
payload.best_of_n = flags['best-of'];
|
|
192
226
|
if (flags['oauth-only'])
|
|
193
227
|
payload.oauth_only = true;
|
|
194
228
|
if (flags.stream) {
|
|
@@ -234,6 +268,8 @@ export default class ChatChat extends BaseCommand {
|
|
|
234
268
|
payload.temperature = Number.parseFloat(flags.temperature);
|
|
235
269
|
if (flags.medium)
|
|
236
270
|
payload.medium = flags.medium;
|
|
271
|
+
if (flags['best-of'] !== undefined)
|
|
272
|
+
payload.best_of_n = flags['best-of'];
|
|
237
273
|
if (flags['oauth-only'])
|
|
238
274
|
payload.oauth_only = true;
|
|
239
275
|
if (flags.stream) {
|
|
@@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
|
|
|
2
2
|
import { BaseCommand } from '../../base.js';
|
|
3
3
|
import { FacesAPIError } from '../../client.js';
|
|
4
4
|
export default class FaceLock extends BaseCommand {
|
|
5
|
-
static description = 'Lock a face (read-only). A locked face still reads and chats normally, but it, and everything it owns (documents, threads,
|
|
5
|
+
static description = 'Lock a face (read-only). A locked face still reads and chats normally, but it, and everything it owns (documents, threads, captured style), cannot be changed until unlocked.';
|
|
6
6
|
static examples = ['<%= config.bin %> <%= command.id %> socrates'];
|
|
7
7
|
static flags = {
|
|
8
8
|
...BaseCommand.baseFlags,
|
|
@@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
|
|
|
2
2
|
import { BaseCommand } from '../../base.js';
|
|
3
3
|
import { FacesAPIError } from '../../client.js';
|
|
4
4
|
export default class FaceUnlock extends BaseCommand {
|
|
5
|
-
static description = 'Unlock a face (make it writable again). Thaws the face and everything it owns (documents, threads,
|
|
5
|
+
static description = 'Unlock a face (make it writable again). Thaws the face and everything it owns (documents, threads, captured style).';
|
|
6
6
|
static examples = ['<%= config.bin %> <%= command.id %> socrates'];
|
|
7
7
|
static flags = {
|
|
8
8
|
...BaseCommand.baseFlags,
|
|
@@ -3,23 +3,6 @@ import { createInterface } from 'node:readline';
|
|
|
3
3
|
import { BaseCommand } from '../../base.js';
|
|
4
4
|
import { FacesAPIError } from '../../client.js';
|
|
5
5
|
import { STYLE_FACES_PATH } from '../../style.js';
|
|
6
|
-
/**
|
|
7
|
-
* The API takes a scope. This CLI only ever sends `map`.
|
|
8
|
-
*
|
|
9
|
-
* `map` removes the installed style. `all` removes it and everything derived
|
|
10
|
-
* from it — versions, jobs, checkpoints, style exemplars. **Neither touches the
|
|
11
|
-
* uploaded material.** The API reference said `all` destroyed it; that
|
|
12
|
-
* documentation was wrong and has been corrected, and the behaviour is not
|
|
13
|
-
* going to change (backend memo, 2026-08-28 pm).
|
|
14
|
-
*
|
|
15
|
-
* So the reason for sending only `map` is no longer that `all` is dangerous to
|
|
16
|
-
* source text. It is that one command should do one thing: this one forgets a
|
|
17
|
-
* style, and a deeper purge of derived artefacts is a different request nobody
|
|
18
|
-
* has asked for. Source text is deleted through the commands that own it,
|
|
19
|
-
* compile:doc:delete and compile:thread:delete, and a face's uploaded corpus
|
|
20
|
-
* through the corpus route — never as a side effect of a flag value here.
|
|
21
|
-
*/
|
|
22
|
-
const SCOPE = 'map';
|
|
23
6
|
export default class StyleDelete extends BaseCommand {
|
|
24
7
|
static description = "Delete a face's captured style. The material it was learned from is kept, so the style can be " +
|
|
25
8
|
'captured again without re-uploading. This never deletes documents or threads: remove those with ' +
|
|
@@ -48,7 +31,7 @@ export default class StyleDelete extends BaseCommand {
|
|
|
48
31
|
}
|
|
49
32
|
let data;
|
|
50
33
|
try {
|
|
51
|
-
data = await client.delete(`${STYLE_FACES_PATH}/${encodeURIComponent(args.alias)}
|
|
34
|
+
data = await client.delete(`${STYLE_FACES_PATH}/${encodeURIComponent(args.alias)}`);
|
|
52
35
|
}
|
|
53
36
|
catch (err) {
|
|
54
37
|
if (err instanceof FacesAPIError) {
|
|
@@ -104,7 +104,11 @@ export default class StyleMake extends BaseCommand {
|
|
|
104
104
|
for (const line of formatBuildReport(final))
|
|
105
105
|
this.log(line);
|
|
106
106
|
this.log('');
|
|
107
|
-
|
|
107
|
+
// Capturing a style does not apply it. The plain alias still answers in the
|
|
108
|
+
// face's ordinary voice; the + prefix is how a caller asks for the style.
|
|
109
|
+
this.log(`'${args.alias}' has captured a style. Use it by prefixing the alias with +:`);
|
|
110
|
+
this.log(` faces chat:chat +${args.alias} -m "..."`);
|
|
111
|
+
this.log(`Without the +, the face answers in its ordinary voice. Both are valid.`);
|
|
108
112
|
this.log(`Go back to the previous style: faces style:revert ${args.alias} --yes`);
|
|
109
113
|
return final;
|
|
110
114
|
}
|
package/dist/style.d.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* other from what the list endpoints already report.
|
|
12
12
|
*/
|
|
13
13
|
import { FacesClient } from './client.js';
|
|
14
|
-
export declare const BUILDS_PATH = "/v1/
|
|
15
|
-
export declare const VERSIONS_PATH = "/v1/
|
|
16
|
-
export declare const STYLE_FACES_PATH = "/v1/
|
|
14
|
+
export declare const BUILDS_PATH = "/v1/style/builds";
|
|
15
|
+
export declare const VERSIONS_PATH = "/v1/style/versions";
|
|
16
|
+
export declare const STYLE_FACES_PATH = "/v1/style/faces";
|
|
17
17
|
/**
|
|
18
18
|
* Analyst model used when the caller does not choose one.
|
|
19
19
|
*
|
package/dist/style.js
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* other from what the list endpoints already report.
|
|
12
12
|
*/
|
|
13
13
|
import { FacesAPIError } from './client.js';
|
|
14
|
-
export const BUILDS_PATH = '/v1/
|
|
15
|
-
export const VERSIONS_PATH = '/v1/
|
|
16
|
-
export const STYLE_FACES_PATH = '/v1/
|
|
14
|
+
export const BUILDS_PATH = '/v1/style/builds';
|
|
15
|
+
export const VERSIONS_PATH = '/v1/style/versions';
|
|
16
|
+
export const STYLE_FACES_PATH = '/v1/style/faces';
|
|
17
17
|
/**
|
|
18
18
|
* Analyst model used when the caller does not choose one.
|
|
19
19
|
*
|
|
@@ -85,7 +85,7 @@ function mediumOf(r, sourceType) {
|
|
|
85
85
|
return !declared || declared === UNIDENTIFIED_CORPUS ? undefined : declared;
|
|
86
86
|
}
|
|
87
87
|
function rowToSelectable(r, sourceType) {
|
|
88
|
-
const stamp = r.
|
|
88
|
+
const stamp = r.style ?? undefined;
|
|
89
89
|
return {
|
|
90
90
|
id: String(r.document_id ?? r.thread_id ?? ''),
|
|
91
91
|
sourceType,
|
|
@@ -97,10 +97,8 @@ function rowToSelectable(r, sourceType) {
|
|
|
97
97
|
isCorpus: sourceType === 'document' || Boolean(r.corpus_medium?.trim()),
|
|
98
98
|
isLiveThread: sourceType === 'room' && !r.corpus_medium?.trim(),
|
|
99
99
|
authored: r.authored_message_count ?? undefined,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
printedMedium: stamp?.medium ?? stamp?.kind,
|
|
103
|
-
printedAt: stamp?.printed_at,
|
|
100
|
+
printedMedium: stamp?.medium,
|
|
101
|
+
printedAt: stamp?.captured_at,
|
|
104
102
|
};
|
|
105
103
|
}
|
|
106
104
|
function rowsOf(data) {
|