faces-cli 1.6.1 → 1.6.3

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 (58) hide show
  1. package/README.md +50 -17
  2. package/dist/catalog.d.ts +13 -1
  3. package/dist/catalog.js +78 -9
  4. package/dist/client.d.ts +4 -0
  5. package/dist/client.js +16 -0
  6. package/dist/commands/catalog/backup.js +64 -18
  7. package/dist/commands/catalog/doctor.js +72 -16
  8. package/dist/commands/catalog/restore.js +64 -11
  9. package/dist/commands/face/create.d.ts +1 -0
  10. package/dist/commands/face/create.js +27 -5
  11. package/dist/commands/face/get.d.ts +1 -0
  12. package/dist/commands/face/get.js +13 -9
  13. package/dist/commands/face/list.d.ts +3 -0
  14. package/dist/commands/face/list.js +21 -6
  15. package/dist/commands/face/tag/add.d.ts +14 -0
  16. package/dist/commands/face/tag/add.js +40 -0
  17. package/dist/commands/face/tag/all.d.ts +10 -0
  18. package/dist/commands/face/tag/all.js +31 -0
  19. package/dist/commands/face/tag/list.d.ts +13 -0
  20. package/dist/commands/face/tag/list.js +35 -0
  21. package/dist/commands/face/tag/remove.d.ts +14 -0
  22. package/dist/commands/face/tag/remove.js +40 -0
  23. package/dist/commands/face/tag/set.d.ts +14 -0
  24. package/dist/commands/face/tag/set.js +40 -0
  25. package/dist/commands/face/teams.d.ts +14 -0
  26. package/dist/commands/face/teams.js +29 -0
  27. package/dist/commands/face/update.d.ts +1 -0
  28. package/dist/commands/face/update.js +34 -11
  29. package/dist/commands/team/add.d.ts +14 -0
  30. package/dist/commands/team/add.js +68 -0
  31. package/dist/commands/team/create.d.ts +15 -0
  32. package/dist/commands/team/create.js +69 -0
  33. package/dist/commands/team/delete.d.ts +15 -0
  34. package/dist/commands/team/delete.js +44 -0
  35. package/dist/commands/team/get.d.ts +13 -0
  36. package/dist/commands/team/get.js +28 -0
  37. package/dist/commands/team/list.d.ts +10 -0
  38. package/dist/commands/team/list.js +40 -0
  39. package/dist/commands/team/members.d.ts +13 -0
  40. package/dist/commands/team/members.js +28 -0
  41. package/dist/commands/team/remove.d.ts +14 -0
  42. package/dist/commands/team/remove.js +56 -0
  43. package/dist/commands/team/tag/add.d.ts +14 -0
  44. package/dist/commands/team/tag/add.js +29 -0
  45. package/dist/commands/team/tag/list.d.ts +13 -0
  46. package/dist/commands/team/tag/list.js +35 -0
  47. package/dist/commands/team/tag/remove.d.ts +14 -0
  48. package/dist/commands/team/tag/remove.js +29 -0
  49. package/dist/commands/team/tag/set.d.ts +14 -0
  50. package/dist/commands/team/tag/set.js +29 -0
  51. package/dist/commands/team/update.d.ts +17 -0
  52. package/dist/commands/team/update.js +78 -0
  53. package/dist/team-catalog.d.ts +17 -0
  54. package/dist/team-catalog.js +104 -0
  55. package/dist/utils.d.ts +9 -2
  56. package/dist/utils.js +17 -2
  57. package/oclif.manifest.json +1537 -309
  58. package/package.json +1 -1
@@ -0,0 +1,35 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../../base.js';
3
+ import { FacesAPIError } from '../../../client.js';
4
+ export default class FaceTagList extends BaseCommand {
5
+ static description = 'List tags on a face';
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ };
9
+ static args = {
10
+ alias: Args.string({ description: 'Face alias', required: true }),
11
+ };
12
+ async run() {
13
+ const { args, flags } = await this.parse(FaceTagList);
14
+ const client = this.makeClient(flags);
15
+ let data;
16
+ try {
17
+ data = await client.get(`/v1/iam/${args.alias}/tags`);
18
+ }
19
+ catch (err) {
20
+ if (err instanceof FacesAPIError)
21
+ this.error(`Error (${err.statusCode}): ${err.message}`);
22
+ throw err;
23
+ }
24
+ if (!this.jsonEnabled()) {
25
+ const tags = data.tags ?? [];
26
+ if (tags.length === 0) {
27
+ this.log('(no tags)');
28
+ }
29
+ else {
30
+ this.log(tags.join(', '));
31
+ }
32
+ }
33
+ return data;
34
+ }
35
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class FaceTagRemove 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
+ static args: {
10
+ alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
11
+ tag: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,40 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../../base.js';
3
+ import { FacesAPIError } from '../../../client.js';
4
+ import { CatalogService } from '../../../catalog.js';
5
+ export default class FaceTagRemove extends BaseCommand {
6
+ static description = 'Remove a tag from a face';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ };
10
+ static args = {
11
+ alias: Args.string({ description: 'Face alias', required: true }),
12
+ tag: Args.string({ description: 'Tag to remove', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(FaceTagRemove);
16
+ const client = this.makeClient(flags);
17
+ let data;
18
+ try {
19
+ data = await client.delete(`/v1/iam/${args.alias}/tags/${encodeURIComponent(args.tag)}`);
20
+ }
21
+ catch (err) {
22
+ if (err instanceof FacesAPIError) {
23
+ if (err.statusCode === 403) {
24
+ this.error(`Forbidden: face '${args.alias}' is not owned by the current account. Run \`faces catalog:doctor --fix\` to sync.`);
25
+ }
26
+ this.error(`Error (${err.statusCode}): ${err.message}`);
27
+ }
28
+ throw err;
29
+ }
30
+ // Refresh tags in local catalog
31
+ try {
32
+ const tagResp = await client.get(`/v1/iam/${args.alias}/tags`);
33
+ new CatalogService().updateTags(args.alias, tagResp.tags ?? []);
34
+ }
35
+ catch { /* non-fatal */ }
36
+ if (!this.jsonEnabled())
37
+ this.log(`Removed tag '${args.tag}' from ${args.alias}`);
38
+ return data;
39
+ }
40
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class FaceTagSet extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ tag: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
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
+ static args: {
11
+ alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,40 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../../base.js';
3
+ import { FacesAPIError } from '../../../client.js';
4
+ import { CatalogService } from '../../../catalog.js';
5
+ export default class FaceTagSet extends BaseCommand {
6
+ static description = 'Replace all tags on a face';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ tag: Flags.string({ description: 'Tag to set (repeatable, replaces all)', multiple: true, required: true }),
10
+ };
11
+ static args = {
12
+ alias: Args.string({ description: 'Face alias', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(FaceTagSet);
16
+ const client = this.makeClient(flags);
17
+ let data;
18
+ try {
19
+ data = await client.put(`/v1/iam/${args.alias}/tags`, { body: { tags: flags.tag } });
20
+ }
21
+ catch (err) {
22
+ if (err instanceof FacesAPIError) {
23
+ if (err.statusCode === 403) {
24
+ this.error(`Forbidden: face '${args.alias}' is not owned by the current account. Run \`faces catalog:doctor --fix\` to sync.`);
25
+ }
26
+ this.error(`Error (${err.statusCode}): ${err.message}`);
27
+ }
28
+ throw err;
29
+ }
30
+ // Sync tags to local catalog
31
+ const tags = data.tags ?? [];
32
+ try {
33
+ new CatalogService().updateTags(args.alias, tags);
34
+ }
35
+ catch { /* non-fatal */ }
36
+ if (!this.jsonEnabled())
37
+ this.printHuman(data);
38
+ return data;
39
+ }
40
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class FaceTeams extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ team: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
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
+ static args: {
11
+ alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,29 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class FaceTeams extends BaseCommand {
5
+ static description = "Set a face's team memberships (replaces all)";
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ team: Flags.string({ description: 'Team ID (repeatable, replaces all memberships)', multiple: true, required: true }),
9
+ };
10
+ static args = {
11
+ alias: Args.string({ description: 'Face alias', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(FaceTeams);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = await client.put(`/v1/faces/${args.alias}/teams`, { body: { team_ids: flags.team } });
19
+ }
20
+ catch (err) {
21
+ if (err instanceof FacesAPIError)
22
+ this.error(`Error (${err.statusCode}): ${err.message}`);
23
+ throw err;
24
+ }
25
+ if (!this.jsonEnabled())
26
+ this.printHuman(data);
27
+ return data;
28
+ }
29
+ }
@@ -4,6 +4,7 @@ export default class FaceUpdate extends BaseCommand {
4
4
  static flags: {
5
5
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
6
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
8
  'default-model': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  formula: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  attr: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -2,13 +2,14 @@ import { Args, Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base.js';
3
3
  import { FacesAPIError } from '../../client.js';
4
4
  import { CatalogService } from '../../catalog.js';
5
- import { flattenBasicFacts } from '../../utils.js';
5
+ import { renameFaceFields } from '../../utils.js';
6
6
  export default class FaceUpdate extends BaseCommand {
7
7
  static description = "Update a face's metadata";
8
8
  static flags = {
9
9
  ...BaseCommand.baseFlags,
10
10
  name: Flags.string({ description: 'New display name' }),
11
- description: Flags.string({ description: 'Description for local catalog' }),
11
+ description: Flags.string({ description: 'Plain-text description / bio (max 1500 chars)' }),
12
+ tag: Flags.string({ description: 'Replace all tags (repeatable, lowercase)', multiple: true }),
12
13
  'default-model': Flags.string({ description: 'Default LLM for chat (e.g. gpt-4o-mini, claude-sonnet-4-6)' }),
13
14
  formula: Flags.string({ description: 'New boolean formula (synthetic faces only)' }),
14
15
  attr: Flags.string({ description: 'Update attribute KEY=VALUE (repeatable)', multiple: true }),
@@ -39,12 +40,13 @@ export default class FaceUpdate extends BaseCommand {
39
40
  payload.default_tools = flags.tool;
40
41
  if (flags['default-model'])
41
42
  payload.default_model = flags['default-model'];
42
- const hasApiFields = Object.keys(payload).length > 0;
43
- const hasDescription = Boolean(flags.description);
44
- if (!hasApiFields && !hasDescription)
43
+ if (flags.description !== undefined)
44
+ payload.description = flags.description;
45
+ const hasTags = flags.tag && flags.tag.length > 0;
46
+ if (Object.keys(payload).length === 0 && !hasTags)
45
47
  this.error('Provide at least one field to update.');
46
48
  let data;
47
- if (hasApiFields) {
49
+ if (Object.keys(payload).length > 0) {
48
50
  try {
49
51
  data = await client.patch(`/v1/faces/${args.face_id}`, { body: payload });
50
52
  }
@@ -55,7 +57,6 @@ export default class FaceUpdate extends BaseCommand {
55
57
  }
56
58
  }
57
59
  else {
58
- // description-only update: fetch current face data for catalog
59
60
  try {
60
61
  data = await client.get(`/v1/faces/${args.face_id}`);
61
62
  }
@@ -65,17 +66,39 @@ export default class FaceUpdate extends BaseCommand {
65
66
  throw err;
66
67
  }
67
68
  }
69
+ // Replace tags if provided
70
+ let currentTags;
71
+ if (hasTags) {
72
+ try {
73
+ const tagResp = await client.put(`/v1/iam/${args.face_id}/tags`, { body: { tags: flags.tag } });
74
+ currentTags = tagResp.tags ?? flags.tag;
75
+ }
76
+ catch (err) {
77
+ if (err instanceof FacesAPIError)
78
+ this.warn(`Tags failed (${err.statusCode}): ${err.message}`);
79
+ }
80
+ }
68
81
  const face = data;
69
82
  if (Array.isArray(face.warnings) && face.warnings.length > 0) {
70
83
  for (const w of face.warnings)
71
84
  this.warn(w);
72
85
  }
73
- if (face.basic_facts && typeof face.basic_facts === 'object') {
74
- face.basic_facts = flattenBasicFacts(face.basic_facts);
75
- }
86
+ // Add tags to response if we set them
87
+ if (currentTags)
88
+ face.tags = currentTags;
89
+ // Rename basic_facts → attributes
90
+ renameFaceFields(face);
91
+ // Write to local catalog
76
92
  try {
77
93
  const catalog = new CatalogService();
78
- catalog.writeFace(face, flags.description);
94
+ catalog.writeFace({
95
+ ...face,
96
+ basic_facts: face.attributes,
97
+ tags: face.tags ?? undefined,
98
+ description: face.description ?? undefined,
99
+ default_model: face.default_model,
100
+ formula: face.formula,
101
+ });
79
102
  }
80
103
  catch { /* catalog errors are non-fatal */ }
81
104
  if (!this.jsonEnabled())
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamAdd extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ face: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
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
+ static args: {
11
+ team_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,68 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ import { TeamCatalogService } from '../../team-catalog.js';
5
+ export default class TeamAdd extends BaseCommand {
6
+ static description = 'Add face(s) to a team';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ face: Flags.string({ description: 'Face alias to add (repeatable)', multiple: true, required: true }),
10
+ };
11
+ static args = {
12
+ team_id: Args.string({ description: 'Team ID', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(TeamAdd);
16
+ const client = this.makeClient(flags);
17
+ // Resolve aliases to iam_ids
18
+ const iamIds = [];
19
+ for (const alias of flags.face) {
20
+ try {
21
+ const face = await client.get(`/v1/faces/${alias}`);
22
+ const id = (face.uid ?? face.id ?? face.iam_id);
23
+ if (!id)
24
+ this.error(`Could not resolve iam_id for face '${alias}'`);
25
+ iamIds.push(id);
26
+ }
27
+ catch (err) {
28
+ if (err instanceof FacesAPIError)
29
+ this.error(`Error resolving '${alias}' (${err.statusCode}): ${err.message}`);
30
+ throw err;
31
+ }
32
+ }
33
+ let data;
34
+ try {
35
+ if (iamIds.length === 1) {
36
+ data = await client.post(`/v1/teams/${args.team_id}/members`, { body: { iam_id: iamIds[0] } });
37
+ }
38
+ else {
39
+ data = await client.post(`/v1/teams/${args.team_id}/members`, { body: { iam_ids: iamIds } });
40
+ }
41
+ }
42
+ catch (err) {
43
+ if (err instanceof FacesAPIError)
44
+ this.error(`Error (${err.statusCode}): ${err.message}`);
45
+ throw err;
46
+ }
47
+ // Update local TEAM.md — find by team ID and add aliases
48
+ try {
49
+ const teamCatalog = new TeamCatalogService();
50
+ for (const name of teamCatalog.listTeams()) {
51
+ const team = teamCatalog.readTeam(name);
52
+ if (team?.frontmatter.id === args.team_id) {
53
+ const members = new Set(team.frontmatter.members ?? []);
54
+ for (const alias of flags.face)
55
+ members.add(alias);
56
+ team.frontmatter.members = [...members];
57
+ teamCatalog.writeTeam(name, team.frontmatter, team.body || undefined);
58
+ break;
59
+ }
60
+ }
61
+ }
62
+ catch { /* non-fatal */ }
63
+ if (!this.jsonEnabled()) {
64
+ this.log(`Added ${flags.face.join(', ')} to team.`);
65
+ }
66
+ return data;
67
+ }
68
+ }
@@ -0,0 +1,15 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamCreate extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ protocol: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'protocol-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ tag: 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,69 @@
1
+ import { Flags } from '@oclif/core';
2
+ import fs from 'node:fs';
3
+ import { BaseCommand } from '../../base.js';
4
+ import { FacesAPIError } from '../../client.js';
5
+ import { TeamCatalogService } from '../../team-catalog.js';
6
+ export default class TeamCreate extends BaseCommand {
7
+ static description = 'Create a new team';
8
+ static flags = {
9
+ ...BaseCommand.baseFlags,
10
+ name: Flags.string({ description: 'Team name', required: true }),
11
+ description: Flags.string({ description: 'Plain-text team description (max 1500 chars)' }),
12
+ protocol: Flags.string({ description: 'Protocol content (mermaid diagram or markdown, max 1000 words)' }),
13
+ 'protocol-file': Flags.string({ description: 'Read protocol from file' }),
14
+ tag: Flags.string({ description: 'Tag label (repeatable, lowercase)', multiple: true }),
15
+ };
16
+ async run() {
17
+ const { flags } = await this.parse(TeamCreate);
18
+ const client = this.makeClient(flags);
19
+ let protocol = flags.protocol;
20
+ if (flags['protocol-file']) {
21
+ if (!fs.existsSync(flags['protocol-file']))
22
+ this.error(`File not found: ${flags['protocol-file']}`);
23
+ protocol = fs.readFileSync(flags['protocol-file'], 'utf8');
24
+ }
25
+ const payload = { name: flags.name };
26
+ if (flags.description)
27
+ payload.description = flags.description;
28
+ if (protocol)
29
+ payload.protocol = protocol;
30
+ let data;
31
+ try {
32
+ data = await client.post('/v1/teams', { body: payload });
33
+ }
34
+ catch (err) {
35
+ if (err instanceof FacesAPIError)
36
+ this.error(`Error (${err.statusCode}): ${err.message}`);
37
+ throw err;
38
+ }
39
+ const team = data;
40
+ // Post tags if provided
41
+ const tags = flags.tag ?? [];
42
+ if (tags.length > 0) {
43
+ try {
44
+ await client.post(`/v1/teams/${team.id}/tags`, { body: { tags } });
45
+ team.tags = tags;
46
+ }
47
+ catch (err) {
48
+ if (err instanceof FacesAPIError)
49
+ this.warn(`Tags failed (${err.statusCode}): ${err.message}`);
50
+ }
51
+ }
52
+ // Write local TEAM.md
53
+ try {
54
+ const teamCatalog = new TeamCatalogService();
55
+ const slug = String(team.name).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
56
+ teamCatalog.writeTeam(slug, {
57
+ id: team.id,
58
+ name: team.name,
59
+ description: team.description ?? undefined,
60
+ tags: team.tags ?? undefined,
61
+ members: [],
62
+ }, team.protocol ?? protocol ?? undefined);
63
+ }
64
+ catch { /* non-fatal */ }
65
+ if (!this.jsonEnabled())
66
+ this.printHuman(data);
67
+ return data;
68
+ }
69
+ }
@@ -0,0 +1,15 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamDelete extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
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
+ static args: {
11
+ team_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ private confirm;
15
+ }
@@ -0,0 +1,44 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { createInterface } from 'node:readline';
3
+ import { BaseCommand } from '../../base.js';
4
+ import { FacesAPIError } from '../../client.js';
5
+ export default class TeamDelete extends BaseCommand {
6
+ static description = 'Delete a team';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ yes: Flags.boolean({ description: 'Skip confirmation', default: false }),
10
+ };
11
+ static args = {
12
+ team_id: Args.string({ description: 'Team ID', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(TeamDelete);
16
+ const client = this.makeClient(flags);
17
+ if (!flags.yes) {
18
+ const confirmed = await this.confirm(`Delete team '${args.team_id}'?`);
19
+ if (!confirmed)
20
+ this.error('Aborted.');
21
+ }
22
+ let data;
23
+ try {
24
+ data = await client.delete(`/v1/teams/${args.team_id}`);
25
+ }
26
+ catch (err) {
27
+ if (err instanceof FacesAPIError)
28
+ this.error(`Error (${err.statusCode}): ${err.message}`);
29
+ throw err;
30
+ }
31
+ if (!this.jsonEnabled())
32
+ this.printHuman(data);
33
+ return data;
34
+ }
35
+ confirm(message) {
36
+ return new Promise((resolve) => {
37
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
38
+ rl.question(`${message} [y/N] `, (answer) => {
39
+ rl.close();
40
+ resolve(answer.toLowerCase() === 'y');
41
+ });
42
+ });
43
+ }
44
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamGet 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
+ static args: {
10
+ team_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
11
+ };
12
+ run(): Promise<unknown>;
13
+ }
@@ -0,0 +1,28 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class TeamGet extends BaseCommand {
5
+ static description = 'Get a team by ID';
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ };
9
+ static args = {
10
+ team_id: Args.string({ description: 'Team ID', required: true }),
11
+ };
12
+ async run() {
13
+ const { args, flags } = await this.parse(TeamGet);
14
+ const client = this.makeClient(flags);
15
+ let data;
16
+ try {
17
+ data = await client.get(`/v1/teams/${args.team_id}`);
18
+ }
19
+ catch (err) {
20
+ if (err instanceof FacesAPIError)
21
+ this.error(`Error (${err.statusCode}): ${err.message}`);
22
+ throw err;
23
+ }
24
+ if (!this.jsonEnabled())
25
+ this.printHuman(data);
26
+ return data;
27
+ }
28
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamList 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,40 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ import { FacesAPIError } from '../../client.js';
3
+ export default class TeamList extends BaseCommand {
4
+ static description = 'List your teams';
5
+ static flags = {
6
+ ...BaseCommand.baseFlags,
7
+ };
8
+ async run() {
9
+ const { flags } = await this.parse(TeamList);
10
+ const client = this.makeClient(flags);
11
+ let data;
12
+ try {
13
+ data = await client.get('/v1/me/teams');
14
+ }
15
+ catch (err) {
16
+ if (err instanceof FacesAPIError)
17
+ this.error(`Error (${err.statusCode}): ${err.message}`);
18
+ throw err;
19
+ }
20
+ const resp = data;
21
+ let teams = resp.data ?? data;
22
+ // Filter out personal Guild using kind field
23
+ teams = teams.filter(t => t.kind !== 'personal');
24
+ if (this.jsonEnabled())
25
+ return teams;
26
+ if (teams.length === 0) {
27
+ this.log('(no teams)');
28
+ }
29
+ else {
30
+ const nameWidth = Math.max(...teams.map(t => String(t.name ?? '').length));
31
+ for (const t of teams) {
32
+ const name = String(t.name ?? '').padEnd(nameWidth);
33
+ const members = t.member_count != null ? ` [${t.member_count} members]` : '';
34
+ const desc = t.description ? ` ${String(t.description).slice(0, 50)}` : '';
35
+ this.log(`${t.id} ${name}${members}${desc}`);
36
+ }
37
+ }
38
+ return teams;
39
+ }
40
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamMembers 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
+ static args: {
10
+ team_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
11
+ };
12
+ run(): Promise<unknown>;
13
+ }