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,28 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../base.js';
3
+ import { FacesAPIError } from '../../client.js';
4
+ export default class TeamMembers extends BaseCommand {
5
+ static description = 'List members of a team';
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(TeamMembers);
14
+ const client = this.makeClient(flags);
15
+ let data;
16
+ try {
17
+ data = await client.get(`/v1/teams/${args.team_id}/members`);
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,14 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamRemove 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
+ alias: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,56 @@
1
+ import { Args } 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 TeamRemove extends BaseCommand {
6
+ static description = 'Remove a face from a team';
7
+ static flags = {
8
+ ...BaseCommand.baseFlags,
9
+ };
10
+ static args = {
11
+ team_id: Args.string({ description: 'Team ID', required: true }),
12
+ alias: Args.string({ description: 'Face alias to remove', required: true }),
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(TeamRemove);
16
+ const client = this.makeClient(flags);
17
+ // Resolve alias to iam_id
18
+ let iamId;
19
+ try {
20
+ const face = await client.get(`/v1/faces/${args.alias}`);
21
+ iamId = (face.uid ?? face.id ?? face.iam_id);
22
+ if (!iamId)
23
+ this.error(`Could not resolve iam_id for face '${args.alias}'`);
24
+ }
25
+ catch (err) {
26
+ if (err instanceof FacesAPIError)
27
+ this.error(`Error resolving '${args.alias}' (${err.statusCode}): ${err.message}`);
28
+ throw err;
29
+ }
30
+ let data;
31
+ try {
32
+ data = await client.delete(`/v1/teams/${args.team_id}/members/${iamId}`);
33
+ }
34
+ catch (err) {
35
+ if (err instanceof FacesAPIError)
36
+ this.error(`Error (${err.statusCode}): ${err.message}`);
37
+ throw err;
38
+ }
39
+ // Update local TEAM.md — find by team ID and remove alias
40
+ try {
41
+ const teamCatalog = new TeamCatalogService();
42
+ for (const name of teamCatalog.listTeams()) {
43
+ const team = teamCatalog.readTeam(name);
44
+ if (team?.frontmatter.id === args.team_id) {
45
+ team.frontmatter.members = (team.frontmatter.members ?? []).filter(m => m !== args.alias);
46
+ teamCatalog.writeTeam(name, team.frontmatter, team.body || undefined);
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ catch { /* non-fatal */ }
52
+ if (!this.jsonEnabled())
53
+ this.log(`Removed ${args.alias} from team.`);
54
+ return data;
55
+ }
56
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class TeamTagAdd 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
+ team_id: 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 TeamTagAdd extends BaseCommand {
5
+ static description = 'Add tags to a team (appends, idempotent)';
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ tag: Flags.string({ description: 'Tag to add (repeatable)', multiple: true, required: true }),
9
+ };
10
+ static args = {
11
+ team_id: Args.string({ description: 'Team ID', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(TeamTagAdd);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = await client.post(`/v1/teams/${args.team_id}/tags`, { body: { tags: flags.tag } });
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
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class TeamTagList 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,35 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../../base.js';
3
+ import { FacesAPIError } from '../../../client.js';
4
+ export default class TeamTagList extends BaseCommand {
5
+ static description = 'List tags on a team';
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(TeamTagList);
14
+ const client = this.makeClient(flags);
15
+ let data;
16
+ try {
17
+ data = await client.get(`/v1/teams/${args.team_id}/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 TeamTagRemove 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
+ tag: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<unknown>;
14
+ }
@@ -0,0 +1,29 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseCommand } from '../../../base.js';
3
+ import { FacesAPIError } from '../../../client.js';
4
+ export default class TeamTagRemove extends BaseCommand {
5
+ static description = 'Remove a tag from a team';
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ };
9
+ static args = {
10
+ team_id: Args.string({ description: 'Team ID', required: true }),
11
+ tag: Args.string({ description: 'Tag to remove', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(TeamTagRemove);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = await client.delete(`/v1/teams/${args.team_id}/tags/${encodeURIComponent(args.tag)}`);
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.log(`Removed tag '${args.tag}' from team.`);
27
+ return data;
28
+ }
29
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base.js';
2
+ export default class TeamTagSet 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
+ team_id: 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 TeamTagSet extends BaseCommand {
5
+ static description = 'Replace all tags on a team';
6
+ static flags = {
7
+ ...BaseCommand.baseFlags,
8
+ tag: Flags.string({ description: 'Tag to set (repeatable, replaces all)', multiple: true, required: true }),
9
+ };
10
+ static args = {
11
+ team_id: Args.string({ description: 'Team ID', required: true }),
12
+ };
13
+ async run() {
14
+ const { args, flags } = await this.parse(TeamTagSet);
15
+ const client = this.makeClient(flags);
16
+ let data;
17
+ try {
18
+ data = await client.put(`/v1/teams/${args.team_id}/tags`, { body: { tags: flags.tag } });
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
+ }
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../../base.js';
2
+ export default class TeamUpdate extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, 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
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static args: {
14
+ team_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
15
+ };
16
+ run(): Promise<unknown>;
17
+ }
@@ -0,0 +1,78 @@
1
+ import { Args, 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 TeamUpdate extends BaseCommand {
7
+ static description = 'Update a team';
8
+ static flags = {
9
+ ...BaseCommand.baseFlags,
10
+ name: Flags.string({ description: 'New team name' }),
11
+ description: Flags.string({ description: 'New description (max 1500 chars)' }),
12
+ protocol: Flags.string({ description: 'New protocol content' }),
13
+ 'protocol-file': Flags.string({ description: 'Read protocol from file' }),
14
+ };
15
+ static args = {
16
+ team_id: Args.string({ description: 'Team ID', required: true }),
17
+ };
18
+ async run() {
19
+ const { args, flags } = await this.parse(TeamUpdate);
20
+ const client = this.makeClient(flags);
21
+ let protocol = flags.protocol;
22
+ if (flags['protocol-file']) {
23
+ if (!fs.existsSync(flags['protocol-file']))
24
+ this.error(`File not found: ${flags['protocol-file']}`);
25
+ protocol = fs.readFileSync(flags['protocol-file'], 'utf8');
26
+ }
27
+ const payload = {};
28
+ if (flags.name)
29
+ payload.name = flags.name;
30
+ if (flags.description !== undefined)
31
+ payload.description = flags.description;
32
+ if (protocol !== undefined)
33
+ payload.protocol = protocol;
34
+ if (Object.keys(payload).length === 0)
35
+ this.error('Provide at least one field to update.');
36
+ let data;
37
+ try {
38
+ data = await client.patch(`/v1/teams/${args.team_id}`, { body: payload });
39
+ }
40
+ catch (err) {
41
+ if (err instanceof FacesAPIError)
42
+ this.error(`Error (${err.statusCode}): ${err.message}`);
43
+ throw err;
44
+ }
45
+ // Update local TEAM.md
46
+ try {
47
+ const team = data;
48
+ const teamCatalog = new TeamCatalogService();
49
+ const slug = String(team.name).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
50
+ // Fetch members for the local file
51
+ let members = [];
52
+ try {
53
+ const membersResp = await client.get(`/v1/teams/${args.team_id}/members`);
54
+ // Members come as iam_ids — we'd need to resolve, but for now store what we have
55
+ members = (membersResp.data ?? []).map(m => m.iam_id);
56
+ }
57
+ catch { /* proceed without members */ }
58
+ // Fetch tags
59
+ let tags = [];
60
+ try {
61
+ const tagResp = await client.get(`/v1/teams/${args.team_id}/tags`);
62
+ tags = tagResp.tags ?? [];
63
+ }
64
+ catch { /* proceed */ }
65
+ teamCatalog.writeTeam(slug, {
66
+ id: args.team_id,
67
+ name: team.name,
68
+ description: team.description ?? undefined,
69
+ tags,
70
+ members,
71
+ }, team.protocol ?? undefined);
72
+ }
73
+ catch { /* non-fatal */ }
74
+ if (!this.jsonEnabled())
75
+ this.printHuman(data);
76
+ return data;
77
+ }
78
+ }
@@ -0,0 +1,17 @@
1
+ export declare const TEAMS_DIR: string;
2
+ export interface TeamFrontmatter {
3
+ id?: string;
4
+ name: string;
5
+ description?: string;
6
+ tags?: string[];
7
+ members?: string[];
8
+ }
9
+ export declare class TeamCatalogService {
10
+ writeTeam(name: string, fm: TeamFrontmatter, protocol?: string | null): void;
11
+ readTeam(name: string): {
12
+ frontmatter: TeamFrontmatter;
13
+ body: string;
14
+ } | null;
15
+ listTeams(): string[];
16
+ deleteTeam(name: string): void;
17
+ }
@@ -0,0 +1,104 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ export const TEAMS_DIR = path.join(os.homedir(), '.faces', 'teams');
5
+ function quoteYaml(value) {
6
+ if (/[:#\[\]{}&*!|>'"%@`\n]/.test(value) || value !== value.trim() || value === '') {
7
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
8
+ }
9
+ return value;
10
+ }
11
+ function serializeFrontmatter(fm) {
12
+ const lines = ['---'];
13
+ if (fm.id)
14
+ lines.push(`id: ${fm.id}`);
15
+ lines.push(`name: ${quoteYaml(fm.name)}`);
16
+ if (fm.description)
17
+ lines.push(`description: ${quoteYaml(fm.description)}`);
18
+ if (fm.tags && fm.tags.length > 0) {
19
+ lines.push(`tags: [${fm.tags.join(', ')}]`);
20
+ }
21
+ if (fm.members && fm.members.length > 0) {
22
+ lines.push(`members: [${fm.members.join(', ')}]`);
23
+ }
24
+ lines.push('---');
25
+ return lines.join('\n');
26
+ }
27
+ function parseFrontmatter(content) {
28
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
29
+ if (!match)
30
+ return { frontmatter: null, body: content };
31
+ const yamlBlock = match[1];
32
+ const body = match[2];
33
+ const fm = {};
34
+ for (const line of yamlBlock.split('\n')) {
35
+ const colonIdx = line.indexOf(':');
36
+ if (colonIdx < 0)
37
+ continue;
38
+ const key = line.slice(0, colonIdx).trim();
39
+ const val = line.slice(colonIdx + 1).trim();
40
+ if (key === 'id') {
41
+ fm.id = val.replace(/^"(.*)"$/, '$1');
42
+ }
43
+ else if (key === 'name') {
44
+ fm.name = val.replace(/^"(.*)"$/, '$1');
45
+ }
46
+ else if (key === 'description') {
47
+ fm.description = val.replace(/^"(.*)"$/, '$1');
48
+ }
49
+ else if (key === 'tags') {
50
+ const inner = val.replace(/^\[/, '').replace(/\]$/, '');
51
+ fm.tags = inner ? inner.split(',').map(s => s.trim()).filter(Boolean) : [];
52
+ }
53
+ else if (key === 'members') {
54
+ const inner = val.replace(/^\[/, '').replace(/\]$/, '');
55
+ fm.members = inner ? inner.split(',').map(s => s.trim()).filter(Boolean) : [];
56
+ }
57
+ }
58
+ if (!fm.name)
59
+ return { frontmatter: null, body: content };
60
+ return { frontmatter: fm, body };
61
+ }
62
+ export class TeamCatalogService {
63
+ writeTeam(name, fm, protocol) {
64
+ try {
65
+ const dir = path.join(TEAMS_DIR, name);
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ const filePath = path.join(dir, 'TEAM.md');
68
+ const body = protocol ?? '';
69
+ const content = serializeFrontmatter(fm) + '\n' + body;
70
+ fs.writeFileSync(filePath, content);
71
+ }
72
+ catch (err) {
73
+ const msg = err instanceof Error ? err.message : String(err);
74
+ process.stderr.write(`warn: team catalog write failed: ${msg}\n`);
75
+ }
76
+ }
77
+ readTeam(name) {
78
+ const filePath = path.join(TEAMS_DIR, name, 'TEAM.md');
79
+ if (!fs.existsSync(filePath))
80
+ return null;
81
+ const content = fs.readFileSync(filePath, 'utf8');
82
+ const parsed = parseFrontmatter(content);
83
+ if (!parsed.frontmatter)
84
+ return null;
85
+ return { frontmatter: parsed.frontmatter, body: parsed.body };
86
+ }
87
+ listTeams() {
88
+ if (!fs.existsSync(TEAMS_DIR))
89
+ return [];
90
+ return fs.readdirSync(TEAMS_DIR, { withFileTypes: true })
91
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(TEAMS_DIR, d.name, 'TEAM.md')))
92
+ .map(d => d.name);
93
+ }
94
+ deleteTeam(name) {
95
+ try {
96
+ const dir = path.join(TEAMS_DIR, name);
97
+ fs.rmSync(dir, { recursive: true, force: true });
98
+ }
99
+ catch (err) {
100
+ const msg = err instanceof Error ? err.message : String(err);
101
+ process.stderr.write(`warn: team catalog delete failed: ${msg}\n`);
102
+ }
103
+ }
104
+ }
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  /**
2
- * Flatten basic_facts from API's nested {value, status, identification} format
2
+ * Flatten basic_facts/attributes from API's nested {value, status, identification} format
3
3
  * to simple {key: value} strings.
4
4
  */
5
- export declare function flattenBasicFacts(facts: Record<string, unknown>): Record<string, string>;
5
+ export declare function flattenAttributes(facts: Record<string, unknown>): Record<string, string>;
6
+ /** @deprecated Use flattenAttributes */
7
+ export declare const flattenBasicFacts: typeof flattenAttributes;
8
+ /**
9
+ * Rename basic_facts → attributes in a face response object.
10
+ * Mutates the object in place and returns it.
11
+ */
12
+ export declare function renameFaceFields(face: Record<string, unknown>): Record<string, unknown>;
package/dist/utils.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Flatten basic_facts from API's nested {value, status, identification} format
2
+ * Flatten basic_facts/attributes from API's nested {value, status, identification} format
3
3
  * to simple {key: value} strings.
4
4
  */
5
- export function flattenBasicFacts(facts) {
5
+ export function flattenAttributes(facts) {
6
6
  const flat = {};
7
7
  for (const [k, v] of Object.entries(facts)) {
8
8
  if (typeof v === 'object' && v !== null && 'value' in v) {
@@ -14,3 +14,18 @@ export function flattenBasicFacts(facts) {
14
14
  }
15
15
  return flat;
16
16
  }
17
+ /** @deprecated Use flattenAttributes */
18
+ export const flattenBasicFacts = flattenAttributes;
19
+ /**
20
+ * Rename basic_facts → attributes in a face response object.
21
+ * Mutates the object in place and returns it.
22
+ */
23
+ export function renameFaceFields(face) {
24
+ if (face.basic_facts !== undefined) {
25
+ face.attributes = typeof face.basic_facts === 'object' && face.basic_facts !== null
26
+ ? flattenAttributes(face.basic_facts)
27
+ : face.basic_facts;
28
+ delete face.basic_facts;
29
+ }
30
+ return face;
31
+ }