faces-cli 1.5.3 → 1.5.5

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/base.d.ts CHANGED
@@ -12,5 +12,8 @@ export declare abstract class BaseCommand extends Command {
12
12
  token?: string;
13
13
  'api-key'?: string;
14
14
  }, _requireJwt?: boolean): FacesClient;
15
+ catch(err: Error & {
16
+ exitCode?: number;
17
+ }): Promise<unknown>;
15
18
  protected printHuman(data: unknown, indent?: number): void;
16
19
  }
package/dist/base.js CHANGED
@@ -23,6 +23,16 @@ export class BaseCommand extends Command {
23
23
  const apiKey = resolveApiKey(flags['api-key']);
24
24
  return new FacesClient(baseUrl, token, apiKey);
25
25
  }
26
+ async catch(err) {
27
+ // When --json is enabled, oclif swallows error messages. Override to
28
+ // include the message in structured JSON output so agents can parse it.
29
+ if (this.jsonEnabled()) {
30
+ const code = err.exitCode ?? 1;
31
+ process.stdout.write(JSON.stringify({ error: { message: err.message, code } }) + '\n');
32
+ process.exit(code);
33
+ }
34
+ throw err;
35
+ }
26
36
  printHuman(data, indent = 0) {
27
37
  const prefix = ' '.repeat(indent);
28
38
  if (Array.isArray(data)) {
@@ -7,6 +7,7 @@ export default class AuthRegister extends BaseCommand {
7
7
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  username: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
9
  'invite-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ plan: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
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>;
12
13
  'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -1,6 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base.js';
3
- import { FacesAPIError } from '../../client.js';
3
+ import { FacesAPIError, FacesClient } from '../../client.js';
4
4
  import { saveConfig, resolveBaseUrl } from '../../config.js';
5
5
  export default class AuthRegister extends BaseCommand {
6
6
  static description = 'Create a new Faces account';
@@ -11,11 +11,15 @@ export default class AuthRegister extends BaseCommand {
11
11
  name: Flags.string({ description: 'Display name (defaults to username)' }),
12
12
  username: Flags.string({ description: 'Username (lowercase, dashes, numbers)', required: true }),
13
13
  'invite-key': Flags.string({ description: 'Invite key (if required)' }),
14
+ plan: Flags.string({
15
+ description: 'Plan to subscribe to: "free" ($5 activation) or "connect" ($17/mo, no activation fee)',
16
+ options: ['free', 'connect'],
17
+ default: 'free',
18
+ }),
14
19
  };
15
20
  async run() {
16
21
  const { flags } = await this.parse(AuthRegister);
17
22
  const baseUrl = resolveBaseUrl(flags['base-url']);
18
- const { FacesClient } = await import('../../client.js');
19
23
  const client = new FacesClient(baseUrl);
20
24
  const payload = {
21
25
  email: flags.email,
@@ -44,10 +48,45 @@ export default class AuthRegister extends BaseCommand {
44
48
  const result = {
45
49
  status: 'registered',
46
50
  user_id: inner.user_id,
47
- activation_required: inner.activation_required ?? false,
48
51
  };
49
- if (inner.activation_checkout_url) {
50
- result.activation_checkout_url = inner.activation_checkout_url;
52
+ // Get the appropriate checkout URL based on plan
53
+ if (token) {
54
+ const authedClient = new FacesClient(baseUrl, token);
55
+ if (flags.plan === 'connect') {
56
+ // Connect plan: $17/mo subscription, no $5 activation needed
57
+ try {
58
+ const checkoutResp = await authedClient.post(`/v1/billing/checkout?plan=connect`);
59
+ result.checkout_url = checkoutResp.checkout_url;
60
+ result.plan = 'connect';
61
+ result.amount = '$17/mo';
62
+ }
63
+ catch (err) {
64
+ if (err instanceof FacesAPIError) {
65
+ result.checkout_error = `Failed to get Connect checkout URL: ${err.message}`;
66
+ }
67
+ }
68
+ }
69
+ else {
70
+ // Free plan: $5 activation
71
+ if (inner.activation_checkout_url) {
72
+ result.checkout_url = inner.activation_checkout_url;
73
+ result.plan = 'free';
74
+ result.amount = '$5 (added as API credits)';
75
+ }
76
+ else {
77
+ try {
78
+ const activate = await authedClient.post('/v1/billing/activate');
79
+ result.checkout_url = activate.checkout_url;
80
+ result.plan = 'free';
81
+ result.amount = '$5 (added as API credits)';
82
+ }
83
+ catch (err) {
84
+ if (err instanceof FacesAPIError) {
85
+ result.checkout_error = `Failed to get activation URL: ${err.message}`;
86
+ }
87
+ }
88
+ }
89
+ }
51
90
  }
52
91
  if (!this.jsonEnabled())
53
92
  this.printHuman(result);
@@ -37,10 +37,11 @@ export default class CompileThreadEdit extends BaseCommand {
37
37
  if (!messages || messages.length === 0) {
38
38
  this.error('Thread has no messages to remap');
39
39
  }
40
- // Remap roles
40
+ // Remap roles — case-insensitive exact match against speaker label
41
41
  let remapped = 0;
42
+ const speakerLower = speaker.toLowerCase();
42
43
  const updated = messages.map((m) => {
43
- const isFace = m.name && m.name.toLowerCase() === speaker.toLowerCase();
44
+ const isFace = (m.name ?? '').toLowerCase() === speakerLower;
44
45
  const newRole = isFace ? 'user' : 'assistant';
45
46
  if (m.role !== newRole)
46
47
  remapped++;