@instantkom/cli 3.129.2 → 3.131.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.
- package/README.md +29 -0
- package/bin/run.js +0 -0
- package/dist/api-client.js +28 -5
- package/dist/command-helpers/download.d.ts +10 -0
- package/dist/command-helpers/download.js +10 -0
- package/dist/command-helpers/json.d.ts +4 -0
- package/dist/command-helpers/json.js +37 -0
- package/dist/commands/account/get.d.ts +14 -0
- package/dist/commands/account/get.js +14 -0
- package/dist/commands/account/update.d.ts +23 -0
- package/dist/commands/account/update.js +37 -0
- package/dist/commands/auth/login.js +0 -2
- package/dist/commands/auth/logout.js +0 -2
- package/dist/commands/auth/tokens/create.js +0 -2
- package/dist/commands/auth/tokens/list.js +0 -2
- package/dist/commands/auth/tokens/revoke.js +0 -2
- package/dist/commands/bots/env-vars/create.d.ts +1 -1
- package/dist/commands/bots/env-vars/create.js +18 -11
- package/dist/commands/bots/filters/create.d.ts +5 -5
- package/dist/commands/bots/filters/create.js +33 -18
- package/dist/commands/broadcast/delete.d.ts +20 -0
- package/dist/commands/broadcast/delete.js +18 -0
- package/dist/commands/broadcast/get.d.ts +20 -0
- package/dist/commands/broadcast/get.js +18 -0
- package/dist/commands/broadcast/list.d.ts +21 -0
- package/dist/commands/broadcast/list.js +39 -0
- package/dist/commands/broadcast/send.d.ts +20 -0
- package/dist/commands/broadcast/send.js +18 -0
- package/dist/commands/broadcast/status.d.ts +20 -0
- package/dist/commands/broadcast/status.js +31 -0
- package/dist/commands/broadcast/update.d.ts +30 -0
- package/dist/commands/broadcast/update.js +47 -0
- package/dist/commands/chats/get.d.ts +20 -0
- package/dist/commands/chats/get.js +18 -0
- package/dist/commands/chats/list.d.ts +22 -0
- package/dist/commands/chats/list.js +40 -0
- package/dist/commands/config/get.js +0 -2
- package/dist/commands/config/set.js +0 -2
- package/dist/commands/config/unset.js +0 -2
- package/dist/commands/contacts/custom-fields/delete-value.d.ts +24 -0
- package/dist/commands/contacts/custom-fields/delete-value.js +32 -0
- package/dist/commands/contacts/custom-fields/list.d.ts +20 -0
- package/dist/commands/contacts/custom-fields/list.js +24 -0
- package/dist/commands/contacts/custom-fields/set-value.d.ts +25 -0
- package/dist/commands/contacts/custom-fields/set-value.js +32 -0
- package/dist/commands/contacts/custom-fields/set.d.ts +23 -0
- package/dist/commands/contacts/custom-fields/set.js +42 -0
- package/dist/commands/contacts/tags/add.d.ts +24 -0
- package/dist/commands/contacts/tags/add.js +30 -0
- package/dist/commands/contacts/tags/list.d.ts +20 -0
- package/dist/commands/contacts/tags/list.js +24 -0
- package/dist/commands/contacts/tags/remove.d.ts +24 -0
- package/dist/commands/contacts/tags/remove.js +32 -0
- package/dist/commands/custom-fields/bulk-delete.d.ts +15 -0
- package/dist/commands/custom-fields/bulk-delete.js +24 -0
- package/dist/commands/custom-fields/create.d.ts +21 -0
- package/dist/commands/custom-fields/create.js +47 -0
- package/dist/commands/custom-fields/delete.d.ts +20 -0
- package/dist/commands/custom-fields/delete.js +18 -0
- package/dist/commands/custom-fields/get.d.ts +20 -0
- package/dist/commands/custom-fields/get.js +18 -0
- package/dist/commands/custom-fields/list.d.ts +14 -0
- package/dist/commands/custom-fields/list.js +14 -0
- package/dist/commands/custom-fields/reorder.d.ts +15 -0
- package/dist/commands/custom-fields/reorder.js +22 -0
- package/dist/commands/custom-fields/update.d.ts +27 -0
- package/dist/commands/custom-fields/update.js +52 -0
- package/dist/commands/doctor.d.ts +14 -0
- package/dist/commands/doctor.js +56 -0
- package/dist/commands/features.d.ts +14 -0
- package/dist/commands/features.js +19 -0
- package/dist/commands/messages/bulk-spam.d.ts +16 -0
- package/dist/commands/messages/bulk-spam.js +30 -0
- package/dist/commands/messages/create-ticket.d.ts +23 -0
- package/dist/commands/messages/create-ticket.js +28 -0
- package/dist/commands/messages/delete.d.ts +20 -0
- package/dist/commands/messages/delete.js +18 -0
- package/dist/commands/messages/get.d.ts +20 -0
- package/dist/commands/messages/get.js +18 -0
- package/dist/commands/messages/list.d.ts +22 -0
- package/dist/commands/messages/list.js +35 -0
- package/dist/commands/messages/reactions.d.ts +20 -0
- package/dist/commands/messages/reactions.js +18 -0
- package/dist/commands/messages/spam.d.ts +21 -0
- package/dist/commands/messages/spam.js +27 -0
- package/dist/commands/messages/unread-count.d.ts +14 -0
- package/dist/commands/messages/unread-count.js +14 -0
- package/dist/commands/messages/update.d.ts +27 -0
- package/dist/commands/messages/update.js +43 -0
- package/dist/commands/permissions.d.ts +14 -0
- package/dist/commands/permissions.js +14 -0
- package/dist/commands/segments/create.d.ts +21 -0
- package/dist/commands/segments/create.js +33 -0
- package/dist/commands/segments/delete.d.ts +20 -0
- package/dist/commands/segments/delete.js +18 -0
- package/dist/commands/segments/get.d.ts +20 -0
- package/dist/commands/segments/get.js +18 -0
- package/dist/commands/segments/list.d.ts +20 -0
- package/dist/commands/segments/list.js +34 -0
- package/dist/commands/segments/tags/add.d.ts +24 -0
- package/dist/commands/segments/tags/add.js +21 -0
- package/dist/commands/segments/tags/list.d.ts +20 -0
- package/dist/commands/segments/tags/list.js +18 -0
- package/dist/commands/segments/tags/remove.d.ts +24 -0
- package/dist/commands/segments/tags/remove.js +23 -0
- package/dist/commands/segments/update.d.ts +26 -0
- package/dist/commands/segments/update.js +34 -0
- package/dist/commands/tags/create.d.ts +19 -0
- package/dist/commands/tags/create.js +29 -0
- package/dist/commands/tags/delete.d.ts +20 -0
- package/dist/commands/tags/delete.js +16 -0
- package/dist/commands/tags/export.d.ts +15 -0
- package/dist/commands/tags/export.js +27 -0
- package/dist/commands/tags/get.d.ts +20 -0
- package/dist/commands/tags/get.js +16 -0
- package/dist/commands/tags/import.d.ts +16 -0
- package/dist/commands/tags/import.js +29 -0
- package/dist/commands/tags/list.d.ts +18 -0
- package/dist/commands/tags/list.js +27 -0
- package/dist/commands/tags/recipients/add.d.ts +21 -0
- package/dist/commands/tags/recipients/add.js +27 -0
- package/dist/commands/tags/recipients/remove.d.ts +21 -0
- package/dist/commands/tags/recipients/remove.js +27 -0
- package/dist/commands/tags/update.d.ts +25 -0
- package/dist/commands/tags/update.js +30 -0
- package/dist/commands/tags/validate-import.d.ts +15 -0
- package/dist/commands/tags/validate-import.js +21 -0
- package/dist/commands/team/create.d.ts +23 -0
- package/dist/commands/team/create.js +37 -0
- package/dist/commands/team/delete.d.ts +20 -0
- package/dist/commands/team/delete.js +18 -0
- package/dist/commands/team/list.d.ts +14 -0
- package/dist/commands/team/list.js +14 -0
- package/dist/commands/templates/create.d.ts +24 -0
- package/dist/commands/templates/create.js +63 -0
- package/dist/commands/templates/delete.d.ts +20 -0
- package/dist/commands/templates/delete.js +18 -0
- package/dist/commands/templates/export.d.ts +16 -0
- package/dist/commands/templates/export.js +29 -0
- package/dist/commands/templates/import.d.ts +17 -0
- package/dist/commands/templates/import.js +34 -0
- package/dist/commands/templates/update.d.ts +30 -0
- package/dist/commands/templates/update.js +61 -0
- package/dist/commands/templates/validate-import.d.ts +16 -0
- package/dist/commands/templates/validate-import.js +28 -0
- package/dist/commands/tickets/reply.d.ts +22 -0
- package/dist/commands/tickets/reply.js +36 -0
- package/dist/commands/tickets/update.d.ts +2 -0
- package/dist/commands/tickets/update.js +16 -0
- package/dist/commands/webhooks/events.d.ts +14 -0
- package/dist/commands/webhooks/events.js +18 -0
- package/dist/commands/webhooks/logs.d.ts +14 -0
- package/dist/commands/webhooks/logs.js +11 -0
- package/dist/commands/webhooks/test.d.ts +17 -0
- package/dist/commands/webhooks/test.js +27 -0
- package/dist/commands/whoami.js +0 -2
- package/dist/crud/csv.js +2 -1
- package/dist/errors/api-error.d.ts +1 -1
- package/dist/errors/exit-codes.js +1 -0
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +8672 -1602
- package/package.json +38 -5
package/README.md
CHANGED
|
@@ -30,5 +30,34 @@ npm run --workspace services/cli build
|
|
|
30
30
|
```
|
|
31
31
|
npm run --workspace services/cli type-check
|
|
32
32
|
npm run --workspace services/cli check:all
|
|
33
|
+
npm run --workspace services/cli check:command-coverage
|
|
33
34
|
npm run --workspace services/cli test:unit
|
|
34
35
|
```
|
|
36
|
+
|
|
37
|
+
## Live Smoke Test
|
|
38
|
+
|
|
39
|
+
The live smoke test runs the built `ikm` binary against a real instantKOM API and validates the token lifecycle plus core read/write commands.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
cd services/cli
|
|
43
|
+
IKM_API_URL=http://localhost:3002 \
|
|
44
|
+
IKM_API_KEY=<admin-or-full-api-key> \
|
|
45
|
+
IKM_CLI_TEST_CHANNEL_ID=505 \
|
|
46
|
+
IKM_CLI_TEST_CONTACT_CHANNEL_ID=766 \
|
|
47
|
+
npm run test:live
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`IKM_CLI_TEST_CHANNEL_ID` is used for read checks. `IKM_CLI_TEST_CONTACT_CHANNEL_ID` is used for creating and deleting one temporary contact.
|
|
51
|
+
|
|
52
|
+
## Live Security Test
|
|
53
|
+
|
|
54
|
+
The cross-account security test requires two isolated test accounts. It creates a contact with account B, verifies account A cannot read or delete it, and then removes it with account B.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
cd services/cli
|
|
58
|
+
IKM_API_URL=http://localhost:3002 \
|
|
59
|
+
IKM_CLI_SECURITY_ACCOUNT_A_KEY=<account-a-api-key> \
|
|
60
|
+
IKM_CLI_SECURITY_ACCOUNT_B_KEY=<account-b-api-key> \
|
|
61
|
+
IKM_CLI_SECURITY_ACCOUNT_B_CHANNEL_ID=<account-b-channel-id> \
|
|
62
|
+
npm run test:security:live
|
|
63
|
+
```
|
package/bin/run.js
CHANGED
|
File without changes
|
package/dist/api-client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ApiError } from './errors/api-error.js';
|
|
1
2
|
export class ApiClient {
|
|
2
3
|
baseUrl;
|
|
3
4
|
apiKey;
|
|
@@ -22,10 +23,10 @@ export class ApiClient {
|
|
|
22
23
|
});
|
|
23
24
|
const data = await this.parseResponse(response);
|
|
24
25
|
if (!response.ok) {
|
|
25
|
-
throw new
|
|
26
|
+
throw new ApiError(httpStatusToKind(response.status), `JWT login failed (${response.status}): ${errorMessage(data, response.statusText)}`, response.status);
|
|
26
27
|
}
|
|
27
28
|
if (data?.requiresTwoFactor) {
|
|
28
|
-
throw new
|
|
29
|
+
throw new ApiError('unauthorized', 'JWT login requires 2FA - use a service account without 2FA enabled');
|
|
29
30
|
}
|
|
30
31
|
this.jwtAccessToken = data.accessToken;
|
|
31
32
|
this.jwtRefreshToken = data.refreshToken;
|
|
@@ -78,7 +79,7 @@ export class ApiClient {
|
|
|
78
79
|
});
|
|
79
80
|
const data = await this.parseResponse(response);
|
|
80
81
|
if (!response.ok) {
|
|
81
|
-
throw new
|
|
82
|
+
throw new ApiError(httpStatusToKind(response.status), `HTTP ${response.status}: ${errorMessage(data, response.statusText)}`, response.status);
|
|
82
83
|
}
|
|
83
84
|
return data;
|
|
84
85
|
}
|
|
@@ -100,7 +101,7 @@ export class ApiClient {
|
|
|
100
101
|
});
|
|
101
102
|
if (!response.ok) {
|
|
102
103
|
const data = await this.parseResponse(response);
|
|
103
|
-
throw new
|
|
104
|
+
throw new ApiError(httpStatusToKind(response.status), `HTTP ${response.status}: ${errorMessage(data, response.statusText)}`, response.status);
|
|
104
105
|
}
|
|
105
106
|
return {
|
|
106
107
|
buffer: Buffer.from(await response.arrayBuffer()),
|
|
@@ -143,7 +144,7 @@ export class ApiClient {
|
|
|
143
144
|
});
|
|
144
145
|
const data = await this.parseResponse(response);
|
|
145
146
|
if (!response.ok) {
|
|
146
|
-
throw new
|
|
147
|
+
throw new ApiError(httpStatusToKind(response.status), `HTTP ${response.status}: ${errorMessage(data, response.statusText)}`, response.status);
|
|
147
148
|
}
|
|
148
149
|
return data;
|
|
149
150
|
}
|
|
@@ -197,3 +198,25 @@ async function parseResponseBody(response) {
|
|
|
197
198
|
return text;
|
|
198
199
|
}
|
|
199
200
|
}
|
|
201
|
+
function httpStatusToKind(status) {
|
|
202
|
+
if (status === 401)
|
|
203
|
+
return 'unauthorized';
|
|
204
|
+
if (status === 403)
|
|
205
|
+
return 'forbidden';
|
|
206
|
+
if (status === 429)
|
|
207
|
+
return 'rate_limited';
|
|
208
|
+
if (status >= 500)
|
|
209
|
+
return 'server_error';
|
|
210
|
+
return 'user_error';
|
|
211
|
+
}
|
|
212
|
+
function errorMessage(data, fallback) {
|
|
213
|
+
if (typeof data === 'string' && data.trim())
|
|
214
|
+
return data;
|
|
215
|
+
if (typeof data?.message === 'string')
|
|
216
|
+
return data.message;
|
|
217
|
+
if (Array.isArray(data?.message))
|
|
218
|
+
return data.message.join('; ');
|
|
219
|
+
if (typeof data?.error === 'string')
|
|
220
|
+
return data.error;
|
|
221
|
+
return fallback;
|
|
222
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function writeDownloadResult(filePath: string, result: {
|
|
2
|
+
buffer: Buffer;
|
|
3
|
+
contentType: string | null;
|
|
4
|
+
contentDisposition: string | null;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
bytes: number;
|
|
7
|
+
contentType: string | null;
|
|
8
|
+
contentDisposition: string | null;
|
|
9
|
+
file: string;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
export async function writeDownloadResult(filePath, result) {
|
|
3
|
+
await writeFile(filePath, result.buffer);
|
|
4
|
+
return {
|
|
5
|
+
bytes: result.buffer.byteLength,
|
|
6
|
+
contentType: result.contentType,
|
|
7
|
+
contentDisposition: result.contentDisposition,
|
|
8
|
+
file: filePath,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function parseJsonValue<T = unknown>(value: string, label: string): T;
|
|
2
|
+
export declare function readJsonFile<T = unknown>(filePath: string, label: string): Promise<T>;
|
|
3
|
+
export declare function parseNumberList(value: string | undefined, label: string): number[];
|
|
4
|
+
export declare function parseStringList(value: string | undefined): string[] | undefined;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { ApiError } from '../errors/api-error.js';
|
|
3
|
+
export function parseJsonValue(value, label) {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(value);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9
|
+
throw new ApiError('user_error', `Invalid ${label} JSON: ${message}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export async function readJsonFile(filePath, label) {
|
|
13
|
+
const content = await readFile(filePath, 'utf8');
|
|
14
|
+
return parseJsonValue(content, label);
|
|
15
|
+
}
|
|
16
|
+
export function parseNumberList(value, label) {
|
|
17
|
+
if (!value) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const numbers = value
|
|
21
|
+
.split(',')
|
|
22
|
+
.map((item) => Number(item.trim()))
|
|
23
|
+
.filter((item) => Number.isFinite(item));
|
|
24
|
+
if (numbers.length === 0) {
|
|
25
|
+
throw new ApiError('user_error', `${label} must contain at least one numeric ID`);
|
|
26
|
+
}
|
|
27
|
+
return numbers;
|
|
28
|
+
}
|
|
29
|
+
export function parseStringList(value) {
|
|
30
|
+
if (!value) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return value
|
|
34
|
+
.split(',')
|
|
35
|
+
.map((item) => item.trim())
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class AccountGet extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
'no-color': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'api-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
import { createCliApiClient } from '../../send/api.js';
|
|
3
|
+
export default class AccountGet extends BaseCommand {
|
|
4
|
+
static description = 'Get account information';
|
|
5
|
+
static flags = { ...BaseCommand.baseFlags };
|
|
6
|
+
async run() {
|
|
7
|
+
const { flags } = await this.parse(AccountGet);
|
|
8
|
+
this.flags = flags;
|
|
9
|
+
const client = await createCliApiClient(flags);
|
|
10
|
+
const response = await client.get('/v1/account');
|
|
11
|
+
if (!flags.quiet)
|
|
12
|
+
this.log(this.toFormatted(response));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class AccountUpdate extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
email: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
'first-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
'last-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
company: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
phone: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
city: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
country: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
street: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
'no-color': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
19
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'api-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
};
|
|
22
|
+
run(): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import { mergeData, parseDataFlag } from '../../crud/data.js';
|
|
4
|
+
import { createCliApiClient } from '../../send/api.js';
|
|
5
|
+
export default class AccountUpdate extends BaseCommand {
|
|
6
|
+
static description = 'Update account information';
|
|
7
|
+
static flags = {
|
|
8
|
+
...BaseCommand.baseFlags,
|
|
9
|
+
email: Flags.string({ description: 'Email address' }),
|
|
10
|
+
'first-name': Flags.string({ description: 'First name' }),
|
|
11
|
+
'last-name': Flags.string({ description: 'Last name' }),
|
|
12
|
+
company: Flags.string({ description: 'Company name' }),
|
|
13
|
+
phone: Flags.string({ description: 'Phone number' }),
|
|
14
|
+
city: Flags.string({ description: 'City' }),
|
|
15
|
+
country: Flags.string({ description: 'Country' }),
|
|
16
|
+
street: Flags.string({ description: 'Street address' }),
|
|
17
|
+
data: Flags.string({ description: 'Additional JSON object payload' }),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(AccountUpdate);
|
|
21
|
+
this.flags = flags;
|
|
22
|
+
const payload = mergeData(parseDataFlag(flags.data), {
|
|
23
|
+
email: flags.email,
|
|
24
|
+
firstName: flags['first-name'],
|
|
25
|
+
lastName: flags['last-name'],
|
|
26
|
+
company: flags.company,
|
|
27
|
+
phone: flags.phone,
|
|
28
|
+
city: flags.city,
|
|
29
|
+
country: flags.country,
|
|
30
|
+
street: flags.street,
|
|
31
|
+
});
|
|
32
|
+
const client = await createCliApiClient(flags);
|
|
33
|
+
const response = await client.put('/v1/account', payload);
|
|
34
|
+
if (!flags.quiet)
|
|
35
|
+
this.log(this.toFormatted(response));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base-command.js';
|
|
3
|
-
import { EXIT_CODES } from '../../errors/exit-codes.js';
|
|
4
3
|
import * as tokenStore from '../../auth/token-store.js';
|
|
5
4
|
import * as deviceFlowClient from '../../auth/device-flow-client.js';
|
|
6
5
|
import { ApiClient } from '../../api-client.js';
|
|
@@ -32,6 +31,5 @@ export default class Login extends BaseCommand {
|
|
|
32
31
|
});
|
|
33
32
|
const me = await client.get('/v1/users/me');
|
|
34
33
|
this.log(`Logged in as ${me.email}. Scope: ${result.scope}.`);
|
|
35
|
-
this.exit(EXIT_CODES.OK);
|
|
36
34
|
}
|
|
37
35
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command.js';
|
|
2
|
-
import { EXIT_CODES } from '../../errors/exit-codes.js';
|
|
3
2
|
import * as tokenStore from '../../auth/token-store.js';
|
|
4
3
|
export default class Logout extends BaseCommand {
|
|
5
4
|
static description = 'Remove the stored API token for the current profile';
|
|
@@ -12,6 +11,5 @@ export default class Logout extends BaseCommand {
|
|
|
12
11
|
const profile = flags.profile ?? 'default';
|
|
13
12
|
await tokenStore.del(profile);
|
|
14
13
|
this.log(`Logged out of profile ${profile}.`);
|
|
15
|
-
this.exit(EXIT_CODES.OK);
|
|
16
14
|
}
|
|
17
15
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../../base-command.js';
|
|
3
|
-
import { EXIT_CODES } from '../../../errors/exit-codes.js';
|
|
4
3
|
import { ApiError } from '../../../errors/api-error.js';
|
|
5
4
|
import { resolveToken } from '../../../auth/token-resolver.js';
|
|
6
5
|
import { ApiClient } from '../../../api-client.js';
|
|
@@ -57,6 +56,5 @@ export default class TokensCreate extends BaseCommand {
|
|
|
57
56
|
created_at: created.createdAt,
|
|
58
57
|
expires_at: created.expiresAt,
|
|
59
58
|
}));
|
|
60
|
-
this.exit(EXIT_CODES.OK);
|
|
61
59
|
}
|
|
62
60
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base-command.js';
|
|
2
|
-
import { EXIT_CODES } from '../../../errors/exit-codes.js';
|
|
3
2
|
import { ApiError } from '../../../errors/api-error.js';
|
|
4
3
|
import { resolveToken } from '../../../auth/token-resolver.js';
|
|
5
4
|
import { ApiClient } from '../../../api-client.js';
|
|
@@ -36,6 +35,5 @@ export default class TokensList extends BaseCommand {
|
|
|
36
35
|
expires: item.expires_at ?? item.expiresAt ?? '',
|
|
37
36
|
}));
|
|
38
37
|
this.log(this.toFormatted(items));
|
|
39
|
-
this.exit(EXIT_CODES.OK);
|
|
40
38
|
}
|
|
41
39
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Args } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../../base-command.js';
|
|
3
|
-
import { EXIT_CODES } from '../../../errors/exit-codes.js';
|
|
4
3
|
import { ApiError } from '../../../errors/api-error.js';
|
|
5
4
|
import { resolveToken } from '../../../auth/token-resolver.js';
|
|
6
5
|
import { ApiClient } from '../../../api-client.js';
|
|
@@ -36,6 +35,5 @@ export default class TokensRevoke extends BaseCommand {
|
|
|
36
35
|
});
|
|
37
36
|
await client.delete(`/v1/api-keys/${args.id}`);
|
|
38
37
|
this.log(`Token ${args.id} revoked.`);
|
|
39
|
-
this.exit(EXIT_CODES.OK);
|
|
40
38
|
}
|
|
41
39
|
}
|
|
@@ -1,27 +1,34 @@
|
|
|
1
|
-
import { Flags } from
|
|
2
|
-
import { BaseCommand } from
|
|
3
|
-
import { mergeData, parseDataFlag } from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../../base-command.js";
|
|
3
|
+
import { mergeData, parseDataFlag } from "../../../crud/data.js";
|
|
4
|
+
import { ApiError } from "../../../errors/api-error.js";
|
|
5
|
+
import { createResource } from "../../../crud/request.js";
|
|
6
|
+
import { createCliApiClient } from "../../../send/api.js";
|
|
6
7
|
export default class BotsEnvVarsCreate extends BaseCommand {
|
|
7
|
-
static description =
|
|
8
|
+
static description = "Create a bot environment variable";
|
|
8
9
|
static flags = {
|
|
9
10
|
...BaseCommand.baseFlags,
|
|
10
|
-
key: Flags.string({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
key: Flags.string({
|
|
12
|
+
description: "Variable key (max 16 chars, letters, digits, underscore)",
|
|
13
|
+
required: true,
|
|
14
|
+
}),
|
|
15
|
+
description: Flags.string({ description: "Variable description" }),
|
|
16
|
+
color: Flags.string({ description: "Variable color as HEX" }),
|
|
17
|
+
data: Flags.string({ description: "Additional JSON object payload" }),
|
|
14
18
|
};
|
|
15
19
|
async run() {
|
|
16
20
|
const { flags } = await this.parse(BotsEnvVarsCreate);
|
|
17
21
|
this.flags = flags;
|
|
22
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(flags.key) || flags.key.length > 16) {
|
|
23
|
+
throw new ApiError("user_error", "Variable key must start with a letter or underscore and contain only letters, digits, and underscores. Maximum length is 16 characters.");
|
|
24
|
+
}
|
|
18
25
|
const payload = mergeData(parseDataFlag(flags.data), {
|
|
19
26
|
key: flags.key,
|
|
20
27
|
description: flags.description,
|
|
21
28
|
color: flags.color,
|
|
22
29
|
});
|
|
23
30
|
const client = await createCliApiClient(flags);
|
|
24
|
-
const envVar = await createResource(client,
|
|
31
|
+
const envVar = await createResource(client, "/v1/bot-env-vars", payload);
|
|
25
32
|
if (!flags.quiet)
|
|
26
33
|
this.log(this.toFormatted(envVar));
|
|
27
34
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseCommand } from
|
|
1
|
+
import { BaseCommand } from "../../../base-command.js";
|
|
2
2
|
export default class BotsFiltersCreate extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static args: {
|
|
@@ -10,10 +10,10 @@ export default class BotsFiltersCreate extends BaseCommand {
|
|
|
10
10
|
static flags: {
|
|
11
11
|
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
operator: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"filter-object": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
"filter-attribute": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
"filter-comparator": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
"filter-value": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
status: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
18
|
data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,22 +1,37 @@
|
|
|
1
|
-
import { Args, Flags } from
|
|
2
|
-
import { BaseCommand } from
|
|
3
|
-
import { mergeData, parseDataFlag } from
|
|
4
|
-
import { createCliApiClient } from
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../../base-command.js";
|
|
3
|
+
import { mergeData, parseDataFlag } from "../../../crud/data.js";
|
|
4
|
+
import { createCliApiClient } from "../../../send/api.js";
|
|
5
5
|
export default class BotsFiltersCreate extends BaseCommand {
|
|
6
|
-
static description =
|
|
6
|
+
static description = "Create a bot filter";
|
|
7
7
|
static args = {
|
|
8
|
-
botId: Args.integer({ description:
|
|
8
|
+
botId: Args.integer({ description: "Bot ID", required: true }),
|
|
9
9
|
};
|
|
10
10
|
static flags = {
|
|
11
11
|
...BaseCommand.baseFlags,
|
|
12
|
-
name: Flags.string({ description:
|
|
13
|
-
operator: Flags.string({ description:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
name: Flags.string({ description: "Filter name", required: true }),
|
|
13
|
+
operator: Flags.string({ description: "Logical operator", default: "and" }),
|
|
14
|
+
"filter-object": Flags.string({
|
|
15
|
+
description: "Filter object type",
|
|
16
|
+
default: "msg",
|
|
17
|
+
}),
|
|
18
|
+
"filter-attribute": Flags.string({
|
|
19
|
+
description: "Filter attribute",
|
|
20
|
+
default: "text",
|
|
21
|
+
}),
|
|
22
|
+
"filter-comparator": Flags.string({
|
|
23
|
+
description: "Filter comparator",
|
|
24
|
+
default: "contains",
|
|
25
|
+
}),
|
|
26
|
+
"filter-value": Flags.string({
|
|
27
|
+
description: "Filter value",
|
|
28
|
+
required: true,
|
|
29
|
+
}),
|
|
30
|
+
status: Flags.integer({
|
|
31
|
+
description: "Status: 0 inactive, 1 active",
|
|
32
|
+
options: ["0", "1"],
|
|
33
|
+
}),
|
|
34
|
+
data: Flags.string({ description: "Additional JSON object payload" }),
|
|
20
35
|
};
|
|
21
36
|
async run() {
|
|
22
37
|
const { args, flags } = await this.parse(BotsFiltersCreate);
|
|
@@ -24,10 +39,10 @@ export default class BotsFiltersCreate extends BaseCommand {
|
|
|
24
39
|
const payload = mergeData(parseDataFlag(flags.data), {
|
|
25
40
|
name: flags.name,
|
|
26
41
|
operator: flags.operator,
|
|
27
|
-
objectType: flags[
|
|
28
|
-
attribute: flags[
|
|
29
|
-
comparator: flags[
|
|
30
|
-
value: flags[
|
|
42
|
+
objectType: flags["filter-object"],
|
|
43
|
+
attribute: flags["filter-attribute"],
|
|
44
|
+
comparator: flags["filter-comparator"],
|
|
45
|
+
value: flags["filter-value"],
|
|
31
46
|
status: flags.status,
|
|
32
47
|
});
|
|
33
48
|
const client = await createCliApiClient(flags);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class BroadcastDelete extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
id: import("@oclif/core/interfaces").Arg<number, {
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
'no-color': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'api-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import { createCliApiClient } from '../../send/api.js';
|
|
4
|
+
export default class BroadcastDelete extends BaseCommand {
|
|
5
|
+
static description = 'Delete broadcast';
|
|
6
|
+
static args = {
|
|
7
|
+
id: Args.integer({ description: 'broadcast ID', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static flags = { ...BaseCommand.baseFlags };
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(BroadcastDelete);
|
|
12
|
+
this.flags = flags;
|
|
13
|
+
const client = await createCliApiClient(flags);
|
|
14
|
+
await client.delete(`/v1/broadcasts/${args.id}`);
|
|
15
|
+
if (!flags.quiet)
|
|
16
|
+
this.log(this.toFormatted({ deleted: true, id: args.id }));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class BroadcastGet extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
id: import("@oclif/core/interfaces").Arg<number, {
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
'no-color': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'api-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import { createCliApiClient } from '../../send/api.js';
|
|
4
|
+
export default class BroadcastGet extends BaseCommand {
|
|
5
|
+
static description = 'Get broadcast by ID';
|
|
6
|
+
static args = {
|
|
7
|
+
id: Args.integer({ description: 'broadcast ID', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static flags = { ...BaseCommand.baseFlags };
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(BroadcastGet);
|
|
12
|
+
this.flags = flags;
|
|
13
|
+
const client = await createCliApiClient(flags);
|
|
14
|
+
const response = await client.get(`/v1/broadcasts/${args.id}`);
|
|
15
|
+
if (!flags.quiet)
|
|
16
|
+
this.log(this.toFormatted(response));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class BroadcastList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
channel: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
'send-status': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
test: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
'no-color': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'api-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
};
|
|
20
|
+
run(): Promise<void>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import { cleanQuery } from '../../crud/data.js';
|
|
4
|
+
import { createCliApiClient } from '../../send/api.js';
|
|
5
|
+
export default class BroadcastList extends BaseCommand {
|
|
6
|
+
static description = 'List broadcasts';
|
|
7
|
+
static flags = {
|
|
8
|
+
...BaseCommand.baseFlags,
|
|
9
|
+
page: Flags.integer({ description: 'Page number', default: 1 }),
|
|
10
|
+
limit: Flags.integer({ description: 'Items per page', default: 20 }),
|
|
11
|
+
channel: Flags.integer({ description: 'Filter by channel ID' }),
|
|
12
|
+
'send-status': Flags.string({ description: 'Filter by send status' }),
|
|
13
|
+
type: Flags.string({
|
|
14
|
+
description: 'Broadcast type',
|
|
15
|
+
options: ['text', 'image', 'video', 'audio', 'document'],
|
|
16
|
+
}),
|
|
17
|
+
search: Flags.string({ description: 'Search broadcasts' }),
|
|
18
|
+
test: Flags.integer({
|
|
19
|
+
description: 'Filter test flag: 0 or 1',
|
|
20
|
+
options: ['0', '1'],
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
async run() {
|
|
24
|
+
const { flags } = await this.parse(BroadcastList);
|
|
25
|
+
this.flags = flags;
|
|
26
|
+
const client = await createCliApiClient(flags);
|
|
27
|
+
const response = await client.get('/v1/broadcasts', cleanQuery({
|
|
28
|
+
page: flags.page,
|
|
29
|
+
limit: flags.limit,
|
|
30
|
+
channelId: flags.channel,
|
|
31
|
+
sendStatus: flags['send-status'],
|
|
32
|
+
broadcastType: flags.type,
|
|
33
|
+
search: flags.search,
|
|
34
|
+
isTest: flags.test,
|
|
35
|
+
}));
|
|
36
|
+
if (!flags.quiet)
|
|
37
|
+
this.log(this.toFormatted(response));
|
|
38
|
+
}
|
|
39
|
+
}
|