@zeegme/cli 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -78,7 +78,13 @@ zeeg crm upsert people --match emails --data '{"firstName":"Sophie","emails":["s
78
78
 
79
79
  ## JSON output
80
80
 
81
- In a terminal the CLI prints tables. When its output goes to a pipe or a file, or with `--json`, it prints the API's JSON unchanged, so you can hand it to `jq` or an agent:
81
+ In a terminal the CLI prints tables. When its output goes to a pipe or a file, or with `--json`, it prints JSON you can hand to `jq` or an agent. Every command answers in one of these shapes:
82
+
83
+ - **Lists** print `{ "collection": [...], "pagination": {...} }`. `pagination` is `null` when the command has no pages, as for `notes list`.
84
+ - **One record** prints `{ "resource": {...} }`. That covers `get` commands and the record a write command created or changed. `resource` is `null` when the API returns no record, as for `bookings cancel`.
85
+ - **Login and logout** print their own small status objects.
86
+
87
+ For example:
82
88
 
83
89
  ```sh
84
90
  zeeg bookings list --status confirmed | jq -r '.collection[].uuid'
@@ -1,5 +1,5 @@
1
1
  import { unwrap } from '../api/client.js';
2
- import { asJson, pageFooter, positiveInt } from './util.js';
2
+ import { asJson, positiveInt } from './util.js';
3
3
  export function registerAgents(program, context) {
4
4
  const agents = program.command('agents').description('AI phone agents and their calls');
5
5
  agents
@@ -10,13 +10,13 @@ export function registerAgents(program, context) {
10
10
  .action(async (options) => {
11
11
  const ctx = context();
12
12
  const data = asJson(await unwrap((await ctx.api()).GET('/agents', { params: { query: options } })));
13
- ctx.print.list(data, (data.data?.agents ?? []), [
13
+ ctx.print.list((data.data?.agents ?? []), [
14
14
  { header: 'id', value: (row) => row.id },
15
15
  { header: 'title', value: (row) => row.title },
16
16
  { header: 'active', value: (row) => (row.active ? 'yes' : 'no') },
17
17
  { header: 'phone', value: (row) => row.phoneNumber },
18
18
  { header: 'calls', value: (row) => row.totalCalls },
19
- ], pageFooter(data.data?.pagination));
19
+ ], data.data?.pagination);
20
20
  });
21
21
  agents
22
22
  .command('calls <agentUuid>')
@@ -31,14 +31,14 @@ export function registerAgents(program, context) {
31
31
  const data = asJson(await unwrap((await ctx.api()).GET('/agents/{agentUuid}/calls', {
32
32
  params: { path: { agentUuid }, query: options },
33
33
  })));
34
- ctx.print.list(data, (data.data?.calls ?? []), [
34
+ ctx.print.list((data.data?.calls ?? []), [
35
35
  { header: 'id', value: (row) => row.id },
36
36
  { header: 'started', value: (row) => row.startedAt },
37
37
  { header: 'direction', value: (row) => row.direction },
38
38
  { header: 'status', value: (row) => row.status },
39
39
  { header: 'seconds', value: (row) => row.durationSeconds },
40
40
  { header: 'summary', value: (row) => row.callSummaryTitle },
41
- ], pageFooter(data.data?.pagination));
41
+ ], data.data?.pagination);
42
42
  });
43
43
  agents
44
44
  .command('call <callId>')
@@ -48,6 +48,6 @@ export function registerAgents(program, context) {
48
48
  const data = asJson(await unwrap((await ctx.api()).GET('/agent-calls/{callId}', {
49
49
  params: { path: { callId } },
50
50
  })));
51
- ctx.print.record(data, asJson(data.call));
51
+ ctx.print.record(data.call);
52
52
  });
53
53
  }
@@ -55,6 +55,6 @@ export function registerAuth(program, context) {
55
55
  const ctx = context();
56
56
  const me = await unwrap((await ctx.api()).GET('/whoami'));
57
57
  const tokens = await (await ctx.store()).load();
58
- ctx.print.record({ email: me.email, baseUrl: ctx.baseUrl, scopes: tokens?.scopes ?? [] }, { email: me.email, api: ctx.baseUrl, scopes: tokens?.scopes ?? [] });
58
+ ctx.print.record({ email: me.email, baseUrl: ctx.baseUrl, scopes: tokens?.scopes ?? [] });
59
59
  });
60
60
  }
@@ -1,6 +1,6 @@
1
1
  import { unwrap } from '../api/client.js';
2
2
  import { UsageError } from '../errors.js';
3
- import { asJson, clockTime, collect, isoDate, localTimeZone, pageFooter, positiveInt } from './util.js';
3
+ import { asJson, clockTime, collect, isoDate, localTimeZone, positiveInt } from './util.js';
4
4
  const inviteeNames = (row) => (row.invitees ?? []).map((invitee) => invitee.fullName ?? invitee.email);
5
5
  export function registerBookings(program, context) {
6
6
  const bookings = program.command('bookings').description('Scheduled events (bookings)');
@@ -38,13 +38,13 @@ export function registerBookings(program, context) {
38
38
  },
39
39
  },
40
40
  })));
41
- ctx.print.list(data, (data.collection ?? []), [
41
+ ctx.print.list((data.collection ?? []), [
42
42
  { header: 'uuid', value: (row) => row.uuid },
43
43
  { header: 'start', value: (row) => row.startTime },
44
44
  { header: 'title', value: (row) => row.title },
45
45
  { header: 'status', value: (row) => row.status },
46
46
  { header: 'invitees', value: inviteeNames },
47
- ], pageFooter(data.pagination));
47
+ ], data.pagination);
48
48
  });
49
49
  bookings
50
50
  .command('get <uuid>')
@@ -52,7 +52,7 @@ export function registerBookings(program, context) {
52
52
  .action(async (uuid) => {
53
53
  const ctx = context();
54
54
  const data = asJson(await unwrap((await ctx.api()).GET('/scheduled-events/{uuid}', { params: { path: { uuid } } })));
55
- ctx.print.record(data, asJson(data.resource));
55
+ ctx.print.record(data.resource);
56
56
  });
57
57
  bookings
58
58
  .command('create <eventTypeUuid>')
@@ -87,7 +87,7 @@ export function registerBookings(program, context) {
87
87
  locationKey: options.locationKey,
88
88
  },
89
89
  })));
90
- ctx.print.message(data, `Booked: ${data.title ?? 'meeting'} at ${data.startTime ?? `${options.date} ${options.time}`} (${data.uuid}).`);
90
+ ctx.print.record(data, `Booked: ${data.title ?? 'meeting'} at ${data.startTime ?? `${options.date} ${options.time}`} (${data.uuid}).`);
91
91
  });
92
92
  bookings
93
93
  .command('cancel <uuid>')
@@ -97,11 +97,11 @@ export function registerBookings(program, context) {
97
97
  .action(async (uuid, options) => {
98
98
  const ctx = context();
99
99
  await ctx.confirm(`Cancel booking ${uuid}? The invitees are notified.`, options.yes);
100
- const data = await unwrap((await ctx.api()).PUT('/scheduled-events/{uuid}/cancel', {
100
+ await unwrap((await ctx.api()).PUT('/scheduled-events/{uuid}/cancel', {
101
101
  params: { path: { uuid } },
102
102
  body: { cancellationReason: options.reason },
103
103
  }));
104
- ctx.print.message(data, `Cancelled ${uuid}.`);
104
+ ctx.print.record(null, `Cancelled ${uuid}.`);
105
105
  });
106
106
  bookings
107
107
  .command('reschedule <uuid>')
@@ -119,7 +119,7 @@ export function registerBookings(program, context) {
119
119
  params: { path: { uuid } },
120
120
  body: { date: options.date, start: options.time, timeZone: options.tz, duration: options.duration, reason: options.reason },
121
121
  })));
122
- ctx.print.message(data, `Rescheduled ${uuid} to ${options.date} ${options.time}.`);
122
+ ctx.print.record(data.resource, `Rescheduled ${uuid} to ${options.date} ${options.time}.`);
123
123
  });
124
124
  bookings
125
125
  .command('no-show <inviteeUuid>')
@@ -131,12 +131,12 @@ export function registerBookings(program, context) {
131
131
  const params = { params: { path: { uuid: inviteeUuid } } };
132
132
  if (options.undo) {
133
133
  const data = asJson(await unwrap((await ctx.api()).DELETE('/scheduled-invitees/{uuid}/no-show', params)));
134
- ctx.print.message(data, `${inviteeUuid} is no longer marked as a no-show.`);
134
+ ctx.print.record(data.resource, `${inviteeUuid} is no longer marked as a no-show.`);
135
135
  return;
136
136
  }
137
137
  await ctx.confirm(`Mark invitee ${inviteeUuid} as a no-show? Your no-show workflows may email them.`, options.yes);
138
138
  const data = asJson(await unwrap((await ctx.api()).POST('/scheduled-invitees/{uuid}/no-show', params)));
139
- ctx.print.message(data, `Marked ${inviteeUuid} as a no-show.`);
139
+ ctx.print.record(data.resource, `Marked ${inviteeUuid} as a no-show.`);
140
140
  });
141
141
  bookings
142
142
  .command('hand-over <uuid>')
@@ -156,6 +156,6 @@ export function registerBookings(program, context) {
156
156
  fallbackToReschedule: options.fallbackToReschedule,
157
157
  },
158
158
  }));
159
- ctx.print.message(data, `Handed over ${uuid}.`);
159
+ ctx.print.record(asJson(data).resource, `Handed over ${uuid}.`);
160
160
  });
161
161
  }
@@ -27,7 +27,7 @@ export function registerCalls(program, context) {
27
27
  scheduledTime: options.at,
28
28
  },
29
29
  })));
30
- ctx.print.message(data, `Call ${options.at ? 'scheduled' : 'started'} (${asJson(data.element).status ?? 'queued'}).`);
30
+ ctx.print.record(data.element, `Call ${options.at ? 'scheduled' : 'started'} (${asJson(data.element).status ?? 'queued'}).`);
31
31
  });
32
32
  }
33
33
  /** The API takes the agent's extra context as one string, so a file is sent as its text. */
@@ -1,6 +1,6 @@
1
1
  import { unwrap } from '../api/client.js';
2
2
  import { UsageError } from '../errors.js';
3
- import { asJson, jsonArgument, pageFooter, positiveInt } from './util.js';
3
+ import { asJson, jsonArgument, positiveInt } from './util.js';
4
4
  /** `people` and `companies` have their own endpoints; anything else is a custom object slug. */
5
5
  async function list(api, object, query) {
6
6
  if (object === 'people') {
@@ -66,12 +66,12 @@ export function registerCrm(program, context) {
66
66
  }
67
67
  const ctx = context();
68
68
  const { data, rows } = await list(await ctx.api(), object, options);
69
- ctx.print.list(data, rows, [
69
+ ctx.print.list(rows, [
70
70
  { header: 'id', value: (row) => row.id },
71
71
  { header: 'name', value: displayName },
72
72
  { header: 'email', value: (row) => row.emails ?? row.domain },
73
73
  { header: 'updated', value: (row) => row.updatedAt },
74
- ], pageFooter(data.data?.pagination));
74
+ ], data.data?.pagination);
75
75
  });
76
76
  crm
77
77
  .command('get <object> <id>')
@@ -79,7 +79,7 @@ export function registerCrm(program, context) {
79
79
  .action(async (object, id) => {
80
80
  const ctx = context();
81
81
  const data = await get(await ctx.api(), object, id);
82
- ctx.print.record(data, recordOf(data));
82
+ ctx.print.record(recordOf(data));
83
83
  });
84
84
  crm
85
85
  .command('upsert <object>')
@@ -91,6 +91,6 @@ export function registerCrm(program, context) {
91
91
  const ctx = context();
92
92
  const data = await upsert(await ctx.api(), object, options.match, body);
93
93
  const record = recordOf(data);
94
- ctx.print.message(data, `Saved ${object} record ${record.id ?? ''}.`.replace(' .', '.'));
94
+ ctx.print.record(record, `Saved ${object} record ${record.id ?? ''}.`.replace(' .', '.'));
95
95
  });
96
96
  }
@@ -1,5 +1,6 @@
1
1
  import { unwrap } from '../api/client.js';
2
- import { addDays, asJson, isoDate, localTimeZone, pageFooter, positiveInt } from './util.js';
2
+ import { renderTable } from '../output.js';
3
+ import { addDays, asJson, isoDate, localTimeZone, positiveInt } from './util.js';
3
4
  export function registerEventTypes(program, context) {
4
5
  const eventTypes = program.command('event-types').description('Scheduling pages (event types)');
5
6
  eventTypes
@@ -14,13 +15,13 @@ export function registerEventTypes(program, context) {
14
15
  const data = asJson(await unwrap((await ctx.api()).GET('/event-types', {
15
16
  params: { query: { userSlug: options.userSlug, email: options.email, page: options.page, count: options.count } },
16
17
  })));
17
- ctx.print.list(data, (data.collection ?? []), [
18
+ ctx.print.list((data.collection ?? []), [
18
19
  { header: 'uuid', value: (row) => row.uuid },
19
20
  { header: 'title', value: (row) => row.title },
20
21
  { header: 'minutes', value: (row) => row.duration },
21
22
  { header: 'active', value: (row) => (row.isActive ? 'yes' : 'no') },
22
23
  { header: 'url', value: (row) => row.schedulingUrl },
23
- ], pageFooter(data.pagination));
24
+ ], data.pagination);
24
25
  });
25
26
  eventTypes
26
27
  .command('get <uuid>')
@@ -28,7 +29,7 @@ export function registerEventTypes(program, context) {
28
29
  .action(async (uuid) => {
29
30
  const ctx = context();
30
31
  const data = asJson(await unwrap((await ctx.api()).GET('/event-types/{uuid}', { params: { path: { uuid } } })));
31
- ctx.print.record(data, asJson(data.resource));
32
+ ctx.print.record(data.resource);
32
33
  });
33
34
  program
34
35
  .command('availability <eventTypeUuid>')
@@ -47,10 +48,11 @@ export function registerEventTypes(program, context) {
47
48
  query: { startDate: from, endDate: to, timeZone: options.tz ?? localTimeZone(), duration: options.duration },
48
49
  },
49
50
  })));
51
+ // One record, since the time zone and date range belong with the days; a terminal shows only the days with free times.
50
52
  const days = (data.availability ?? []).filter((day) => Array.isArray(day.slots) && day.slots.length > 0);
51
- ctx.print.list(data, days, [
53
+ ctx.print.record(data, days.length === 0 ? 'Nothing found.' : renderTable(days, [
52
54
  { header: 'date', value: (day) => day.date },
53
55
  { header: `times (${data.timeZone ?? options.tz ?? localTimeZone()})`, full: true, value: (day) => day.slots.map((slot) => (typeof slot === 'string' ? slot : asJson(slot).time)).join(' ') },
54
- ]);
56
+ ]));
55
57
  });
56
58
  }
@@ -1,5 +1,5 @@
1
1
  import { unwrap } from '../api/client.js';
2
- import { asJson, pageFooter, positiveInt } from './util.js';
2
+ import { asJson, positiveInt } from './util.js';
3
3
  export function registerForms(program, context) {
4
4
  const forms = program.command('forms').description('Routing forms');
5
5
  forms
@@ -10,12 +10,12 @@ export function registerForms(program, context) {
10
10
  .action(async (options) => {
11
11
  const ctx = context();
12
12
  const data = asJson(await unwrap((await ctx.api()).GET('/routing-forms', { params: { query: options } })));
13
- ctx.print.list(data, (data.data?.routingForms ?? []), [
13
+ ctx.print.list((data.data?.routingForms ?? []), [
14
14
  { header: 'id', value: (row) => row.id },
15
15
  { header: 'name', value: (row) => row.name },
16
16
  { header: 'active', value: (row) => (row.isActive ? 'yes' : 'no') },
17
17
  { header: 'updated', value: (row) => row.updatedAt },
18
- ], pageFooter(data.data?.pagination));
18
+ ], data.data?.pagination);
19
19
  });
20
20
  forms
21
21
  .command('submissions')
@@ -39,11 +39,11 @@ export function registerForms(program, context) {
39
39
  },
40
40
  },
41
41
  })));
42
- ctx.print.list(data, (data.data?.submissions ?? []), [
42
+ ctx.print.list((data.data?.submissions ?? []), [
43
43
  { header: 'id', value: (row) => row.id },
44
44
  { header: 'created', value: (row) => row.createdAt },
45
45
  { header: 'form', value: (row) => row.routingForm?.name ?? row.routingFormId },
46
46
  { header: 'route', value: (row) => row.routeType ?? row.route?.type },
47
- ], pageFooter(data.data?.pagination));
47
+ ], data.data?.pagination);
48
48
  });
49
49
  }
@@ -8,7 +8,7 @@ export function registerNotes(program, context) {
8
8
  .action(async (uuid) => {
9
9
  const ctx = context();
10
10
  const data = asJson(await unwrap((await ctx.api()).GET('/notes/event/{uuid}', { params: { path: { uuid } } })));
11
- ctx.print.list(data, (data.list ?? []), [
11
+ ctx.print.list((data.list ?? []), [
12
12
  { header: 'id', value: (row) => row.id },
13
13
  { header: 'created', value: (row) => row.createdAt },
14
14
  { header: 'by', value: (row) => row.creator?.fullName ?? row.creator?.email },
@@ -23,6 +23,6 @@ export function registerNotes(program, context) {
23
23
  const data = await unwrap((await ctx.api()).POST('/notes', {
24
24
  body: { relation: 'event', uuid, note: text.join(' ') },
25
25
  }));
26
- ctx.print.message(data, `Note added to ${uuid}.`);
26
+ ctx.print.record(asJson(data).data, `Note added to ${uuid}.`);
27
27
  });
28
28
  }
@@ -1,6 +1,6 @@
1
1
  import { unwrap } from '../api/client.js';
2
2
  import { UsageError } from '../errors.js';
3
- import { asJson, collect, isoDate, pageFooter, positiveInt } from './util.js';
3
+ import { asJson, collect, isoDate, positiveInt } from './util.js';
4
4
  async function ownEmail(api) {
5
5
  return (await unwrap(api.GET('/whoami'))).email;
6
6
  }
@@ -18,12 +18,12 @@ export function registerSchedules(program, context) {
18
18
  const data = asJson(await unwrap(api.GET('/schedules', {
19
19
  params: { query: { email: options.email ?? await ownEmail(api), page: options.page, count: options.count } },
20
20
  })));
21
- ctx.print.list(data, (data.collection ?? []), [
21
+ ctx.print.list((data.collection ?? []), [
22
22
  { header: 'uuid', value: (row) => row.uuid },
23
23
  { header: 'title', value: (row) => row.title },
24
24
  { header: 'time zone', value: (row) => row.timezone },
25
25
  { header: 'default', value: (row) => (row.isDefault ? 'yes' : 'no') },
26
- ], pageFooter(data.pagination));
26
+ ], data.pagination);
27
27
  });
28
28
  const timeOff = program.command('time-off').description('Time off (vacation, out of office)');
29
29
  timeOff
@@ -51,6 +51,6 @@ export function registerSchedules(program, context) {
51
51
  note: options.note,
52
52
  },
53
53
  }));
54
- ctx.print.message(data, `Time off added from ${options.start} to ${options.end}.`);
54
+ ctx.print.record(data, `Time off added from ${options.start} to ${options.end}.`);
55
55
  });
56
56
  }
@@ -55,14 +55,3 @@ export function addDays(date, days) {
55
55
  copy.setUTCDate(copy.getUTCDate() + days);
56
56
  return copy.toISOString().slice(0, 10);
57
57
  }
58
- /** "Page 2 of 5 (93 in total)" from either pagination shape the v2 API uses. */
59
- export function pageFooter(pagination) {
60
- const p = asJson(pagination);
61
- const current = p.currentPage;
62
- const last = p.totalPages ?? p.lastPage;
63
- const total = p.total ?? p.totalItems;
64
- if (current === undefined || last === undefined) {
65
- return undefined;
66
- }
67
- return `Page ${current} of ${last}${total !== undefined ? ` (${total} in total)` : ''}. Use --page to see more.`;
68
- }
package/dist/config.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import { UsageError } from './errors.js';
2
2
  export const DEFAULT_BASE_URL = 'https://api.zeeg.me';
3
- /**
4
- * The CLI's OAuth client. The backend inserts it with this id in every environment
5
- * (config('mcp.cli.client_id')), so one build reaches local, staging and production.
6
- */
3
+ /** The CLI's public OAuth client, which has the same id on every Zeeg server it can sign in to. */
7
4
  export const DEFAULT_CLIENT_ID = '1000001';
8
5
  /** The v2 scopes the commands need. The server narrows them to what the plan allows. */
9
6
  export const SCOPES = [
package/dist/errors.js CHANGED
@@ -43,15 +43,19 @@ export function exitCodeFor(error) {
43
43
  return ExitCode.Usage;
44
44
  }
45
45
  /**
46
- * The v2 API answers errors as `{ message, errors? }`; validation errors list messages per
47
- * field. A 401 means the token was revoked or expired server-side, so it asks for a new login.
46
+ * The API answers errors in two shapes: controllers and validation send `{ message, errors? }`,
47
+ * with validation messages listed per field, and the shared exception handler sends
48
+ * `{ error: { type, message } }` for not found, forbidden and similar. A 401 means the token was
49
+ * revoked or expired server-side, so it asks for a new login.
48
50
  */
49
51
  export function apiErrorFrom(status, body) {
50
52
  if (status === 401) {
51
53
  return new AuthError('Your Zeeg session is no longer valid. Run `zeeg login` to sign in again.');
52
54
  }
53
55
  const record = typeof body === 'object' && body !== null ? body : {};
54
- const message = typeof record.message === 'string' && record.message !== '' ? record.message : `The Zeeg API answered HTTP ${status}.`;
56
+ const nested = typeof record.error === 'object' && record.error !== null ? record.error : {};
57
+ const text = [record.message, nested.message].find((value) => typeof value === 'string' && value !== '');
58
+ const message = text ?? `The Zeeg API answered HTTP ${status}.`;
55
59
  const details = record.errors && typeof record.errors === 'object'
56
60
  ? Object.values(record.errors).flat().filter((item) => typeof item === 'string')
57
61
  : [];
package/dist/output.js CHANGED
@@ -38,23 +38,36 @@ export function renderRecord(record) {
38
38
  const width = Math.max(0, ...lines.map(([key]) => key.length));
39
39
  return lines.map(([key, value]) => `${key.padEnd(width)} ${value}`).join('\n');
40
40
  }
41
+ /** "Page 2 of 5 (93 in total)" from either pagination shape the API uses. */
42
+ export function pageFooter(pagination) {
43
+ const p = pagination !== null && typeof pagination === 'object' ? pagination : {};
44
+ const current = p.currentPage;
45
+ const last = p.totalPages ?? p.lastPage;
46
+ const total = p.total ?? p.totalItems;
47
+ if (current === undefined || last === undefined) {
48
+ return undefined;
49
+ }
50
+ return `Page ${String(current)} of ${String(last)}${total !== undefined ? ` (${String(total)} in total)` : ''}. Use --page to see more.`;
51
+ }
41
52
  export function createPrinter(mode, write = (text) => process.stdout.write(text)) {
42
53
  const json = (data) => write(`${JSON.stringify(data ?? null, null, 2)}\n`);
43
54
  return {
44
- list(data, rows, columns, footer) {
55
+ list(rows, columns, pagination) {
45
56
  if (mode === 'json') {
46
- return json(data);
57
+ return json({ collection: rows, pagination: pagination ?? null });
47
58
  }
48
59
  write(rows.length === 0 ? 'Nothing found.\n' : `${renderTable(rows, columns)}\n`);
60
+ const footer = pageFooter(pagination);
49
61
  if (footer) {
50
62
  write(`${footer}\n`);
51
63
  }
52
64
  },
53
- record(data, record) {
65
+ record(resource, text) {
54
66
  if (mode === 'json') {
55
- return json(data);
67
+ return json({ resource: resource ?? null });
56
68
  }
57
- write(`${renderRecord(record)}\n`);
69
+ const record = resource !== null && typeof resource === 'object' ? resource : {};
70
+ write(`${text ?? renderRecord(record)}\n`);
58
71
  },
59
72
  message(data, text) {
60
73
  if (mode === 'json') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeegme/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "The Zeeg command line: bookings, availability, notes, CRM, routing forms and AI phone calls from your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "access": "public"
18
18
  },
19
19
  "scripts": {
20
- "build": "tsc -p tsconfig.build.json",
20
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
21
21
  "prepublishOnly": "npm run build",
22
22
  "check-types": "tsc --noEmit",
23
23
  "generate:api": "openapi-typescript ../../apps/backend/tools/api-docs/openapi.yaml -o src/api/schema.d.ts",