faces-cli 1.0.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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.js +5 -0
  5. package/dist/base.d.ts +16 -0
  6. package/dist/base.js +53 -0
  7. package/dist/client.d.ts +36 -0
  8. package/dist/client.js +191 -0
  9. package/dist/commands/account/state.d.ts +10 -0
  10. package/dist/commands/account/state.js +24 -0
  11. package/dist/commands/auth/login.d.ts +12 -0
  12. package/dist/commands/auth/login.js +40 -0
  13. package/dist/commands/auth/logout.d.ts +10 -0
  14. package/dist/commands/auth/logout.js +14 -0
  15. package/dist/commands/auth/refresh.d.ts +10 -0
  16. package/dist/commands/auth/refresh.js +30 -0
  17. package/dist/commands/auth/register.d.ts +15 -0
  18. package/dist/commands/auth/register.js +45 -0
  19. package/dist/commands/auth/whoami.d.ts +10 -0
  20. package/dist/commands/auth/whoami.js +24 -0
  21. package/dist/commands/billing/balance.d.ts +10 -0
  22. package/dist/commands/billing/balance.js +24 -0
  23. package/dist/commands/billing/card-setup.d.ts +10 -0
  24. package/dist/commands/billing/card-setup.js +24 -0
  25. package/dist/commands/billing/checkout.d.ts +11 -0
  26. package/dist/commands/billing/checkout.js +30 -0
  27. package/dist/commands/billing/llm-costs.d.ts +11 -0
  28. package/dist/commands/billing/llm-costs.js +29 -0
  29. package/dist/commands/billing/quota.d.ts +10 -0
  30. package/dist/commands/billing/quota.js +24 -0
  31. package/dist/commands/billing/subscription.d.ts +10 -0
  32. package/dist/commands/billing/subscription.js +24 -0
  33. package/dist/commands/billing/topup.d.ts +12 -0
  34. package/dist/commands/billing/topup.js +33 -0
  35. package/dist/commands/billing/usage.d.ts +13 -0
  36. package/dist/commands/billing/usage.js +38 -0
  37. package/dist/commands/chat/chat.d.ts +20 -0
  38. package/dist/commands/chat/chat.js +81 -0
  39. package/dist/commands/chat/messages.d.ts +17 -0
  40. package/dist/commands/chat/messages.js +68 -0
  41. package/dist/commands/chat/responses.d.ts +16 -0
  42. package/dist/commands/chat/responses.js +67 -0
  43. package/dist/commands/compile/doc/create.d.ts +16 -0
  44. package/dist/commands/compile/doc/create.js +43 -0
  45. package/dist/commands/compile/doc/delete.d.ts +13 -0
  46. package/dist/commands/compile/doc/delete.js +28 -0
  47. package/dist/commands/compile/doc/get.d.ts +13 -0
  48. package/dist/commands/compile/doc/get.js +28 -0
  49. package/dist/commands/compile/doc/list.d.ts +13 -0
  50. package/dist/commands/compile/doc/list.js +28 -0
  51. package/dist/commands/compile/doc/prepare.d.ts +13 -0
  52. package/dist/commands/compile/doc/prepare.js +28 -0
  53. package/dist/commands/compile/doc/sync.d.ts +15 -0
  54. package/dist/commands/compile/doc/sync.js +44 -0
  55. package/dist/commands/compile/thread/create.d.ts +14 -0
  56. package/dist/commands/compile/thread/create.js +32 -0
  57. package/dist/commands/compile/thread/list.d.ts +13 -0
  58. package/dist/commands/compile/thread/list.js +28 -0
  59. package/dist/commands/compile/thread/message.d.ts +14 -0
  60. package/dist/commands/compile/thread/message.js +31 -0
  61. package/dist/commands/compile/thread/sync.d.ts +13 -0
  62. package/dist/commands/compile/thread/sync.js +28 -0
  63. package/dist/commands/config/clear.d.ts +12 -0
  64. package/dist/commands/config/clear.js +31 -0
  65. package/dist/commands/config/set.d.ts +14 -0
  66. package/dist/commands/config/set.js +19 -0
  67. package/dist/commands/config/show.d.ts +10 -0
  68. package/dist/commands/config/show.js +19 -0
  69. package/dist/commands/face/create.d.ts +15 -0
  70. package/dist/commands/face/create.js +42 -0
  71. package/dist/commands/face/delete.d.ts +15 -0
  72. package/dist/commands/face/delete.js +44 -0
  73. package/dist/commands/face/get.d.ts +13 -0
  74. package/dist/commands/face/get.js +28 -0
  75. package/dist/commands/face/list.d.ts +10 -0
  76. package/dist/commands/face/list.js +24 -0
  77. package/dist/commands/face/stats.d.ts +10 -0
  78. package/dist/commands/face/stats.js +24 -0
  79. package/dist/commands/face/update.d.ts +16 -0
  80. package/dist/commands/face/update.js +48 -0
  81. package/dist/commands/face/upload.d.ts +15 -0
  82. package/dist/commands/face/upload.js +43 -0
  83. package/dist/commands/keys/create.d.ts +15 -0
  84. package/dist/commands/keys/create.js +39 -0
  85. package/dist/commands/keys/list.d.ts +10 -0
  86. package/dist/commands/keys/list.js +24 -0
  87. package/dist/commands/keys/revoke.d.ts +15 -0
  88. package/dist/commands/keys/revoke.js +44 -0
  89. package/dist/commands/keys/update.d.ts +16 -0
  90. package/dist/commands/keys/update.js +40 -0
  91. package/dist/config.d.ts +16 -0
  92. package/dist/config.js +65 -0
  93. package/oclif.manifest.json +2719 -0
  94. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Headwaters Lab Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # faces-cli
2
+
3
+ Command-line interface for the [Faces](https://faces.sh) platform.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g faces-cli
9
+ ```
10
+
11
+ Requires Node.js ≥ 18.
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ faces auth login
17
+ faces face list
18
+ faces chat chat myface -m "Hello"
19
+ ```
20
+
21
+ ## Commands
22
+
23
+ ```
24
+ faces auth login / logout / register / whoami / refresh
25
+ faces face create / list / get / update / delete / stats / upload
26
+ faces chat chat / messages / responses
27
+ faces compile doc create / list / get / prepare / sync / delete
28
+ faces compile thread create / list / message / sync
29
+ faces keys create / list / revoke / update
30
+ faces billing balance / subscription / quota / usage / topup / checkout / card-setup / llm-costs
31
+ faces account state
32
+ faces config set / show / clear
33
+ ```
34
+
35
+ Run `faces <command> --help` for details on any command.
36
+
37
+ ## Authentication
38
+
39
+ Login stores a JWT at `~/.faces/config.json` (shared with the Python `faces-cli`).
40
+ You can also authenticate via environment variables:
41
+
42
+ ```bash
43
+ export FACES_API_KEY=your_api_key
44
+ export FACES_TOKEN=your_jwt
45
+ export FACES_BASE_URL=https://api.faces.sh # optional override
46
+ ```
47
+
48
+ ## License
49
+
50
+ MIT — Copyright (c) 2026 Headwaters Lab Inc.
package/bin/dev.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({development: true, dir: import.meta.url})
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({development: false, dir: import.meta.url})
package/dist/base.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ import { FacesClient } from './client.js';
3
+ export declare abstract class BaseCommand extends Command {
4
+ static enableJsonFlag: boolean;
5
+ static baseFlags: {
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
+ protected makeClient(flags: {
11
+ 'base-url'?: string;
12
+ token?: string;
13
+ 'api-key'?: string;
14
+ }, _requireJwt?: boolean): FacesClient;
15
+ protected printHuman(data: unknown, indent?: number): void;
16
+ }
package/dist/base.js ADDED
@@ -0,0 +1,53 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { FacesClient } from './client.js';
3
+ import { resolveBaseUrl, resolveToken, resolveApiKey } from './config.js';
4
+ export class BaseCommand extends Command {
5
+ static enableJsonFlag = true;
6
+ static baseFlags = {
7
+ 'base-url': Flags.string({
8
+ description: 'API base URL',
9
+ env: 'FACES_BASE_URL',
10
+ }),
11
+ token: Flags.string({
12
+ description: 'JWT bearer token',
13
+ env: 'FACES_TOKEN',
14
+ }),
15
+ 'api-key': Flags.string({
16
+ description: 'API key',
17
+ env: 'FACES_API_KEY',
18
+ }),
19
+ };
20
+ makeClient(flags, _requireJwt = false) {
21
+ const baseUrl = resolveBaseUrl(flags['base-url']);
22
+ const token = resolveToken(flags.token);
23
+ const apiKey = resolveApiKey(flags['api-key']);
24
+ return new FacesClient(baseUrl, token, apiKey);
25
+ }
26
+ printHuman(data, indent = 0) {
27
+ const prefix = ' '.repeat(indent);
28
+ if (Array.isArray(data)) {
29
+ if (data.length === 0) {
30
+ this.log(`${prefix}(empty)`);
31
+ return;
32
+ }
33
+ for (const [i, item] of data.entries()) {
34
+ this.log(`${prefix}[${i}]`);
35
+ this.printHuman(item, indent + 1);
36
+ }
37
+ }
38
+ else if (data !== null && typeof data === 'object') {
39
+ for (const [k, v] of Object.entries(data)) {
40
+ if (v !== null && typeof v === 'object') {
41
+ this.log(`${prefix}${k}:`);
42
+ this.printHuman(v, indent + 1);
43
+ }
44
+ else {
45
+ this.log(`${prefix}${k}: ${v}`);
46
+ }
47
+ }
48
+ }
49
+ else {
50
+ this.log(`${prefix}${data}`);
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,36 @@
1
+ export declare class FacesAPIError extends Error {
2
+ statusCode: number;
3
+ constructor(statusCode: number, message: string);
4
+ }
5
+ export declare class FacesClient {
6
+ private baseUrl;
7
+ private token?;
8
+ private apiKey?;
9
+ constructor(baseUrl: string, token?: string, apiKey?: string);
10
+ private url;
11
+ authHeader(requireJwt?: boolean): Record<string, string>;
12
+ private wrapNetworkError;
13
+ private parseError;
14
+ get(path: string, opts?: {
15
+ requireJwt?: boolean;
16
+ params?: Record<string, string>;
17
+ }): Promise<unknown>;
18
+ post(path: string, opts?: {
19
+ requireJwt?: boolean;
20
+ body?: unknown;
21
+ }): Promise<unknown>;
22
+ postNoAuth(path: string, body: unknown): Promise<unknown>;
23
+ patch(path: string, opts?: {
24
+ requireJwt?: boolean;
25
+ body?: unknown;
26
+ }): Promise<unknown>;
27
+ delete(path: string, opts?: {
28
+ requireJwt?: boolean;
29
+ }): Promise<unknown>;
30
+ postForm(path: string, formData: FormData, opts?: {
31
+ requireJwt?: boolean;
32
+ }): Promise<unknown>;
33
+ stream(path: string, body: unknown, opts?: {
34
+ requireJwt?: boolean;
35
+ }): AsyncGenerator<string>;
36
+ }
package/dist/client.js ADDED
@@ -0,0 +1,191 @@
1
+ export class FacesAPIError extends Error {
2
+ statusCode;
3
+ constructor(statusCode, message) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.name = 'FacesAPIError';
7
+ }
8
+ }
9
+ export class FacesClient {
10
+ baseUrl;
11
+ token;
12
+ apiKey;
13
+ constructor(baseUrl, token, apiKey) {
14
+ this.baseUrl = baseUrl.replace(/\/$/, '');
15
+ this.token = token;
16
+ this.apiKey = apiKey;
17
+ }
18
+ url(p) {
19
+ return `${this.baseUrl}${p}`;
20
+ }
21
+ authHeader(requireJwt = false) {
22
+ if (requireJwt) {
23
+ if (!this.token) {
24
+ process.stderr.write('Error: JWT login required. Run: faces auth login\n');
25
+ process.exit(1);
26
+ }
27
+ return { Authorization: `Bearer ${this.token}` };
28
+ }
29
+ if (this.token)
30
+ return { Authorization: `Bearer ${this.token}` };
31
+ if (this.apiKey)
32
+ return { Authorization: `Bearer ${this.apiKey}` };
33
+ process.stderr.write('Error: Authentication required. Run: faces auth login or set FACES_API_KEY\n');
34
+ process.exit(1);
35
+ }
36
+ wrapNetworkError(err, path) {
37
+ const msg = err instanceof Error ? err.message : String(err);
38
+ throw new FacesAPIError(0, `Connection failed to ${this.baseUrl}${path}: ${msg}`);
39
+ }
40
+ async parseError(resp) {
41
+ let msg = resp.statusText;
42
+ try {
43
+ const body = await resp.json();
44
+ msg = String(body.detail ?? body.error ?? body.message ?? msg);
45
+ }
46
+ catch {
47
+ // use statusText
48
+ }
49
+ if (resp.status === 402)
50
+ msg = `Payment required: ${msg}`;
51
+ if (resp.status === 403)
52
+ msg = `Forbidden: ${msg}`;
53
+ return new FacesAPIError(resp.status, msg);
54
+ }
55
+ async get(path, opts = {}) {
56
+ let url = this.url(path);
57
+ if (opts.params && Object.keys(opts.params).length > 0) {
58
+ url += '?' + new URLSearchParams(opts.params).toString();
59
+ }
60
+ let resp;
61
+ try {
62
+ resp = await fetch(url, { headers: { ...this.authHeader(opts.requireJwt) } });
63
+ }
64
+ catch (err) {
65
+ this.wrapNetworkError(err, path);
66
+ }
67
+ if (!resp.ok)
68
+ throw await this.parseError(resp);
69
+ return resp.json();
70
+ }
71
+ async post(path, opts = {}) {
72
+ let resp;
73
+ try {
74
+ resp = await fetch(this.url(path), {
75
+ method: 'POST',
76
+ headers: { ...this.authHeader(opts.requireJwt), 'Content-Type': 'application/json' },
77
+ body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
78
+ });
79
+ }
80
+ catch (err) {
81
+ this.wrapNetworkError(err, path);
82
+ }
83
+ if (!resp.ok)
84
+ throw await this.parseError(resp);
85
+ return resp.json();
86
+ }
87
+ async postNoAuth(path, body) {
88
+ let resp;
89
+ try {
90
+ resp = await fetch(this.url(path), {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ body: JSON.stringify(body),
94
+ });
95
+ }
96
+ catch (err) {
97
+ this.wrapNetworkError(err, path);
98
+ }
99
+ if (!resp.ok)
100
+ throw await this.parseError(resp);
101
+ return resp.json();
102
+ }
103
+ async patch(path, opts = {}) {
104
+ let resp;
105
+ try {
106
+ resp = await fetch(this.url(path), {
107
+ method: 'PATCH',
108
+ headers: { ...this.authHeader(opts.requireJwt), 'Content-Type': 'application/json' },
109
+ body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
110
+ });
111
+ }
112
+ catch (err) {
113
+ this.wrapNetworkError(err, path);
114
+ }
115
+ if (!resp.ok)
116
+ throw await this.parseError(resp);
117
+ return resp.json();
118
+ }
119
+ async delete(path, opts = {}) {
120
+ let resp;
121
+ try {
122
+ resp = await fetch(this.url(path), {
123
+ method: 'DELETE',
124
+ headers: { ...this.authHeader(opts.requireJwt) },
125
+ });
126
+ }
127
+ catch (err) {
128
+ this.wrapNetworkError(err, path);
129
+ }
130
+ if (!resp.ok)
131
+ throw await this.parseError(resp);
132
+ // DELETE may return 204 with no body
133
+ const text = await resp.text();
134
+ if (!text)
135
+ return {};
136
+ try {
137
+ return JSON.parse(text);
138
+ }
139
+ catch {
140
+ return {};
141
+ }
142
+ }
143
+ async postForm(path, formData, opts = {}) {
144
+ let resp;
145
+ try {
146
+ resp = await fetch(this.url(path), {
147
+ method: 'POST',
148
+ headers: { ...this.authHeader(opts.requireJwt) },
149
+ body: formData,
150
+ });
151
+ }
152
+ catch (err) {
153
+ this.wrapNetworkError(err, path);
154
+ }
155
+ if (!resp.ok)
156
+ throw await this.parseError(resp);
157
+ return resp.json();
158
+ }
159
+ async *stream(path, body, opts = {}) {
160
+ let resp;
161
+ try {
162
+ resp = await fetch(this.url(path), {
163
+ method: 'POST',
164
+ headers: { ...this.authHeader(opts.requireJwt), 'Content-Type': 'application/json' },
165
+ body: JSON.stringify(body),
166
+ });
167
+ }
168
+ catch (err) {
169
+ this.wrapNetworkError(err, path);
170
+ }
171
+ if (!resp.ok || !resp.body) {
172
+ throw await this.parseError(resp);
173
+ }
174
+ const reader = resp.body.getReader();
175
+ const dec = new TextDecoder();
176
+ let buf = '';
177
+ while (true) {
178
+ const { done, value } = await reader.read();
179
+ if (done)
180
+ break;
181
+ buf += dec.decode(value, { stream: true });
182
+ const lines = buf.split('\n');
183
+ buf = lines.pop() ?? '';
184
+ for (const line of lines)
185
+ if (line.trim())
186
+ yield line;
187
+ }
188
+ if (buf.trim())
189
+ yield buf;
190
+ }
191
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class AccountState extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<unknown>;
10
+ }
@@ -0,0 +1,24 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ import { FacesAPIError } from '../../client.js';
3
+ export default class AccountState extends BaseCommand {
4
+ static description = 'Full account routing state (plan, balance, onboarding)';
5
+ static flags = {
6
+ ...BaseCommand.baseFlags,
7
+ };
8
+ async run() {
9
+ const { flags } = await this.parse(AccountState);
10
+ const client = this.makeClient(flags);
11
+ let data;
12
+ try {
13
+ data = await client.get('/v1/account/state');
14
+ }
15
+ catch (err) {
16
+ if (err instanceof FacesAPIError)
17
+ this.error(`Error (${err.statusCode}): ${err.message}`);
18
+ throw err;
19
+ }
20
+ if (!this.jsonEnabled())
21
+ this.printHuman(data);
22
+ return data;
23
+ }
24
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class AuthLogin extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ email: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
+ password: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<unknown>;
12
+ }
@@ -0,0 +1,40 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { saveConfig, resolveBaseUrl } from '../../config.js';
5
+ export default class AuthLogin extends BaseCommand {
6
+ static description = 'Login and save JWT credentials';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ email: Flags.string({ description: 'Account email', required: true }),
10
+ password: Flags.string({ description: 'Account password', required: true }),
11
+ };
12
+ async run() {
13
+ const { flags } = await this.parse(AuthLogin);
14
+ const baseUrl = resolveBaseUrl(flags['base-url']);
15
+ const { FacesClient } = await import('../../client.js');
16
+ const client = new FacesClient(baseUrl);
17
+ let data;
18
+ try {
19
+ data = (await client.postNoAuth('/v1/auth/login', {
20
+ email: flags.email,
21
+ password: flags.password,
22
+ }));
23
+ }
24
+ catch (err) {
25
+ if (err instanceof FacesAPIError) {
26
+ this.error(`Login failed (${err.statusCode}): ${err.message}`);
27
+ }
28
+ throw err;
29
+ }
30
+ const token = (data.access_token ?? data.token);
31
+ const userId = data.user_id ?? data.id ?? data.user?.id;
32
+ if (!token)
33
+ this.error(`Unexpected response: ${JSON.stringify(data)}`);
34
+ saveConfig({ token, user_id: userId ? String(userId) : undefined });
35
+ const result = { status: 'logged_in', user_id: userId };
36
+ if (!this.jsonEnabled())
37
+ this.printHuman(result);
38
+ return result;
39
+ }
40
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class AuthLogout extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<unknown>;
10
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ import { clearConfig } from '../../config.js';
3
+ export default class AuthLogout extends BaseCommand {
4
+ static description = 'Clear saved credentials';
5
+ static flags = {
6
+ ...BaseCommand.baseFlags,
7
+ };
8
+ async run() {
9
+ await this.parse(AuthLogout);
10
+ clearConfig();
11
+ this.log('Logged out. Credentials cleared.');
12
+ return { status: 'logged_out' };
13
+ }
14
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class AuthRefresh extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<unknown>;
10
+ }
@@ -0,0 +1,30 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ import { FacesAPIError } from '../../client.js';
3
+ import { saveConfig } from '../../config.js';
4
+ export default class AuthRefresh extends BaseCommand {
5
+ static description = 'Exchange current JWT for a fresh one';
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ };
9
+ async run() {
10
+ const { flags } = await this.parse(AuthRefresh);
11
+ const client = this.makeClient(flags);
12
+ let data;
13
+ try {
14
+ data = (await client.post('/v1/auth/refresh', { requireJwt: true }));
15
+ }
16
+ catch (err) {
17
+ if (err instanceof FacesAPIError)
18
+ this.error(`Refresh failed (${err.statusCode}): ${err.message}`);
19
+ throw err;
20
+ }
21
+ const token = (data.access_token ?? data.token);
22
+ if (token) {
23
+ saveConfig({ token });
24
+ this.log('Token refreshed and saved.');
25
+ }
26
+ if (!this.jsonEnabled())
27
+ this.printHuman(data);
28
+ return data;
29
+ }
30
+ }
@@ -0,0 +1,15 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class AuthRegister extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ email: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
+ password: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ username: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'invite-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, 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
+ run(): Promise<unknown>;
15
+ }
@@ -0,0 +1,45 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { saveConfig, resolveBaseUrl } from '../../config.js';
5
+ export default class AuthRegister extends BaseCommand {
6
+ static description = 'Create a new Faces account';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ email: Flags.string({ description: 'Email address', required: true }),
10
+ password: Flags.string({ description: 'Password', required: true }),
11
+ name: Flags.string({ description: 'Display name', required: true }),
12
+ username: Flags.string({ description: 'Username (lowercase, dashes, numbers)', required: true }),
13
+ 'invite-key': Flags.string({ description: 'Invite key (if required)' }),
14
+ };
15
+ async run() {
16
+ const { flags } = await this.parse(AuthRegister);
17
+ const baseUrl = resolveBaseUrl(flags['base-url']);
18
+ const { FacesClient } = await import('../../client.js');
19
+ const client = new FacesClient(baseUrl);
20
+ const payload = {
21
+ email: flags.email,
22
+ password: flags.password,
23
+ name: flags.name,
24
+ username: flags.username,
25
+ };
26
+ if (flags['invite-key'])
27
+ payload.invite_key = flags['invite-key'];
28
+ let data;
29
+ try {
30
+ data = (await client.postNoAuth('/v1/auth/register', payload));
31
+ }
32
+ catch (err) {
33
+ if (err instanceof FacesAPIError) {
34
+ this.error(`Registration failed (${err.statusCode}): ${err.message}`);
35
+ }
36
+ throw err;
37
+ }
38
+ const token = (data.access_token ?? data.token);
39
+ if (token)
40
+ saveConfig({ token });
41
+ if (!this.jsonEnabled())
42
+ this.printHuman(data);
43
+ return data;
44
+ }
45
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class AuthWhoami extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<unknown>;
10
+ }
@@ -0,0 +1,24 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ import { FacesAPIError } from '../../client.js';
3
+ export default class AuthWhoami extends BaseCommand {
4
+ static description = 'Print current user profile';
5
+ static flags = {
6
+ ...BaseCommand.baseFlags,
7
+ };
8
+ async run() {
9
+ const { flags } = await this.parse(AuthWhoami);
10
+ const client = this.makeClient(flags);
11
+ let data;
12
+ try {
13
+ data = await client.get('/v1/auth/me', { requireJwt: true });
14
+ }
15
+ catch (err) {
16
+ if (err instanceof FacesAPIError)
17
+ this.error(`Error (${err.statusCode}): ${err.message}`);
18
+ throw err;
19
+ }
20
+ if (!this.jsonEnabled())
21
+ this.printHuman(data);
22
+ return data;
23
+ }
24
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class BillingBalance extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<unknown>;
10
+ }