faces-cli 1.7.0 → 1.7.1

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.
@@ -19,7 +19,7 @@ export default class ChatChat extends BaseCommand {
19
19
  'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
20
20
  };
21
21
  static args = {
22
- face_username: Args.string({ description: 'Face alias (or alias@model)', required: true }),
22
+ face_username: Args.string({ description: 'Face alias (alias@model, or owner:alias@model for a published face)', required: true }),
23
23
  };
24
24
  async run() {
25
25
  const { args, flags } = await this.parse(ChatChat);
@@ -12,7 +12,7 @@ export default class ChatMessages extends BaseCommand {
12
12
  'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
13
13
  };
14
14
  static args = {
15
- face_model: Args.string({ description: 'Face model (e.g. myface or myface@claude-sonnet-4-6)', required: true }),
15
+ face_model: Args.string({ description: 'Face model (e.g. myface, myface@claude-sonnet-4-6, or head:judge@claude-sonnet-4-6)', required: true }),
16
16
  };
17
17
  async run() {
18
18
  const { args, flags } = await this.parse(ChatMessages);
@@ -11,7 +11,7 @@ export default class ChatResponses extends BaseCommand {
11
11
  'oauth-only': Flags.boolean({ description: 'Prevent fallback to paid system key (use OAuth only)', default: false }),
12
12
  };
13
13
  static args = {
14
- face_model: Args.string({ description: 'Face model (e.g. myface or myface@gpt-4o)', required: true }),
14
+ face_model: Args.string({ description: 'Face model (e.g. myface, myface@gpt-4o, or head:logician@gpt-5.4)', required: true }),
15
15
  };
16
16
  async run() {
17
17
  const { args, flags } = await this.parse(ChatResponses);
@@ -98,7 +98,7 @@ export default class ChatThread extends BaseCommand {
98
98
  };
99
99
  static args = {
100
100
  face_username: Args.string({
101
- description: 'Face alias (or alias@model) — required when starting a new thread',
101
+ description: 'Face alias (alias@model, or owner:alias@model for a published face) — required when starting a new thread',
102
102
  required: false,
103
103
  }),
104
104
  };
@@ -5,6 +5,10 @@ export default class FaceList extends BaseCommand {
5
5
  tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
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
+ public: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ system: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ 'from-users': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'not-from-users': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
12
  'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
13
  token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
14
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -2,17 +2,34 @@ import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base.js';
3
3
  import { FacesAPIError } from '../../client.js';
4
4
  import { renameFaceFields } from '../../utils.js';
5
+ /** Curated system faces are served under this owner account. */
6
+ const SYSTEM_OWNER = 'head';
5
7
  export default class FaceList extends BaseCommand {
6
- static description = 'List all owned faces';
8
+ static description = 'List faces. By default lists owned faces; use --public to include published faces from other accounts.';
7
9
  static flags = {
8
10
  ...BaseCommand.baseFlags,
9
11
  tag: Flags.string({ description: 'Filter by tag (repeatable, AND logic)', multiple: true }),
10
12
  team: Flags.string({ description: 'Filter by team ID (repeatable, OR logic)', multiple: true }),
11
13
  include: Flags.string({ description: 'Include extra fields: tags, teams (comma-separated)' }),
14
+ public: Flags.boolean({ description: 'Include published faces from other accounts' }),
15
+ system: Flags.boolean({ description: `Show only the curated system faces (published faces owned by '${SYSTEM_OWNER}')` }),
16
+ 'from-users': Flags.string({ description: 'Only published faces from these owners (repeatable/comma-separated)', multiple: true }),
17
+ 'not-from-users': Flags.string({ description: 'Exclude published faces from these owners (repeatable/comma-separated)', multiple: true }),
12
18
  };
13
19
  async run() {
14
20
  const { flags } = await this.parse(FaceList);
15
21
  const client = this.makeClient(flags);
22
+ // Split comma-separated owner lists into individual values.
23
+ const split = (vals) => (vals ?? []).flatMap(v => v.split(',')).map(v => v.trim()).filter(Boolean);
24
+ const fromUsers = split(flags['from-users']);
25
+ if (flags.system)
26
+ fromUsers.push(SYSTEM_OWNER);
27
+ const notFromUsers = split(flags['not-from-users']);
28
+ if (fromUsers.length > 0 && notFromUsers.length > 0) {
29
+ this.error('--from-users and --not-from-users are mutually exclusive');
30
+ }
31
+ // Any cross-account owner filter implies we want published faces.
32
+ const includePublished = flags.public || flags.system || fromUsers.length > 0 || notFromUsers.length > 0;
16
33
  // Build query string manually for repeated params
17
34
  const parts = [];
18
35
  if (flags.tag && flags.tag.length > 0) {
@@ -25,6 +42,12 @@ export default class FaceList extends BaseCommand {
25
42
  }
26
43
  if (flags.include)
27
44
  parts.push(`include=${encodeURIComponent(flags.include)}`);
45
+ if (includePublished)
46
+ parts.push('include_published=true');
47
+ for (const u of fromUsers)
48
+ parts.push(`from_users=${encodeURIComponent(u)}`);
49
+ for (const u of notFromUsers)
50
+ parts.push(`not_from_users=${encodeURIComponent(u)}`);
28
51
  let data;
29
52
  try {
30
53
  const path = parts.length > 0 ? `/v1/faces?${parts.join('&')}` : '/v1/faces';
@@ -40,13 +63,31 @@ export default class FaceList extends BaseCommand {
40
63
  for (const f of raw) {
41
64
  renameFaceFields(f);
42
65
  }
66
+ // The API's from_users/not_from_users only constrain the *appended published*
67
+ // set — the caller's own faces always come back. Filter client-side so
68
+ // --system / --from-users / --not-from-users actually mean "from these owners".
69
+ let faces = raw;
70
+ if (fromUsers.length > 0) {
71
+ const allow = new Set(fromUsers);
72
+ faces = faces.filter(f => f.owned_by != null && allow.has(f.owned_by));
73
+ }
74
+ else if (notFromUsers.length > 0) {
75
+ const block = new Set(notFromUsers);
76
+ faces = faces.filter(f => f.owned_by == null || !block.has(f.owned_by));
77
+ }
78
+ // Keep --json output consistent with what we display.
79
+ if (data.data !== undefined)
80
+ data.data = faces;
81
+ else
82
+ data = faces;
43
83
  if (!this.jsonEnabled()) {
44
- const faces = raw;
84
+ // Published faces are chatted as `owner:alias`; own faces stay bare.
85
+ const handle = (f) => f.published && f.owned_by ? `${f.owned_by}:${f.alias}` : f.alias;
45
86
  if (faces.length === 0) {
46
87
  this.log('(no faces)');
47
88
  }
48
89
  else {
49
- const aliasWidth = Math.max(...faces.map(f => f.alias.length));
90
+ const handleWidth = Math.max(...faces.map(f => handle(f).length));
50
91
  const nameWidth = Math.max(...faces.map(f => f.name.length));
51
92
  for (const f of faces) {
52
93
  let suffix = '';
@@ -59,7 +100,9 @@ export default class FaceList extends BaseCommand {
59
100
  const profile = f.profile_token_count ?? 0;
60
101
  suffix = ` [profile: ${profile} tok, components: ${total}]`;
61
102
  }
62
- this.log(`${f.alias.padEnd(aliasWidth)} ${f.name.padEnd(nameWidth)}${suffix}`);
103
+ if (f.published)
104
+ suffix += ' [public · requires @model]';
105
+ this.log(`${handle(f).padEnd(handleWidth)} ${f.name.padEnd(nameWidth)}${suffix}`);
63
106
  }
64
107
  }
65
108
  }