@inboxychat/sdk 0.0.0-stage → 0.1.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 +22 -2
- package/dist/capabilities.d.ts +24 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +39 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/client.d.ts +75 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +291 -0
- package/dist/client.js.map +1 -0
- package/dist/credentials.d.ts +76 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +323 -0
- package/dist/credentials.js.map +1 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +78 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/redact.d.ts +6 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +25 -0
- package/dist/redact.js.map +1 -0
- package/dist/routes.d.ts +60 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +24 -0
- package/dist/routes.js.map +1 -0
- package/dist/types.d.ts +295 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +69 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -3
package/README.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @inboxychat/sdk — read-only client for the Inboxy public API v1
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Used by `@inboxychat/mcp` and `@inboxychat/cli`. Local package, not published.
|
|
4
|
+
|
|
5
|
+
- `InboxyClient`: one GET method per route in `PUBLIC_ROUTES` (the same list as
|
|
6
|
+
`PUBLIC_API_V1_ROUTES` in `apps/api/src/modules/api-public/api-public.contract.ts`).
|
|
7
|
+
No write methods, no company parameter, HTTPS only (http for localhost),
|
|
8
|
+
redirects refused, per-request timeout and `AbortSignal`.
|
|
9
|
+
- `InboxyError`: `kind` (`auth`, `forbidden`, `not_found`, `rate_limited`,
|
|
10
|
+
`invalid_request`, `server`, `timeout`, `cancelled`, `network`,
|
|
11
|
+
`invalid_response`, `config`), `status`, `code`, `details`,
|
|
12
|
+
`retryAfterSeconds`; the message is redacted when the error is created.
|
|
13
|
+
- `resolveCredentials`: `INBOXY_API_KEY` → OS keychain → `0600` config file.
|
|
14
|
+
- `collectPages`: follows `meta.nextCursor` up to a page cap.
|
|
15
|
+
|
|
16
|
+
Types in `src/types.ts` are written by hand to match the zod contract.
|
|
17
|
+
`test/contract-drift.test.ts` imports the server's zod schemas and checks the
|
|
18
|
+
route list, the query enums, the query field names and the test fixtures
|
|
19
|
+
against them, so a change on either side fails a test.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm --filter @inboxychat/sdk test
|
|
23
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public capabilities the MCP server and the CLI offer, as this SDK knows
|
|
3
|
+
* them. The API's capability registry is the authority
|
|
4
|
+
* (apps/api/src/lib/capabilities/public-capabilities.ts); this is its mirror
|
|
5
|
+
* for processes that run outside the API, and `test/capabilities-parity.test.ts`
|
|
6
|
+
* fails when the two differ (id, path, scope, MCP tool, CLI command).
|
|
7
|
+
*
|
|
8
|
+
* The MCP server derives each tool's required scope from here, and the CLI
|
|
9
|
+
* derives the scopes `doctor` checks: neither keeps its own copy.
|
|
10
|
+
*/
|
|
11
|
+
import { type PublicRoutePath } from './routes.js';
|
|
12
|
+
export interface PublicCapability {
|
|
13
|
+
id: string;
|
|
14
|
+
kind: 'read';
|
|
15
|
+
path: PublicRoutePath;
|
|
16
|
+
/** Scope the key / OAuth grant must carry; null = any valid credential. */
|
|
17
|
+
scope: string | null;
|
|
18
|
+
mcpTool?: string;
|
|
19
|
+
cliCommand?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const PUBLIC_CAPABILITIES: readonly PublicCapability[];
|
|
22
|
+
export declare function capabilityForMcpTool(tool: string): PublicCapability | undefined;
|
|
23
|
+
export declare function capabilityForCliCommand(command: string): PublicCapability | undefined;
|
|
24
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EAiB1D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE/E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAErF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public capabilities the MCP server and the CLI offer, as this SDK knows
|
|
3
|
+
* them. The API's capability registry is the authority
|
|
4
|
+
* (apps/api/src/lib/capabilities/public-capabilities.ts); this is its mirror
|
|
5
|
+
* for processes that run outside the API, and `test/capabilities-parity.test.ts`
|
|
6
|
+
* fails when the two differ (id, path, scope, MCP tool, CLI command).
|
|
7
|
+
*
|
|
8
|
+
* The MCP server derives each tool's required scope from here, and the CLI
|
|
9
|
+
* derives the scopes `doctor` checks: neither keeps its own copy.
|
|
10
|
+
*/
|
|
11
|
+
import { scopeFor } from './routes.js';
|
|
12
|
+
function cap(id, path, adapters) {
|
|
13
|
+
return { id, kind: 'read', path, scope: scopeFor(path), ...adapters };
|
|
14
|
+
}
|
|
15
|
+
export const PUBLIC_CAPABILITIES = [
|
|
16
|
+
cap('public.company.get', '/company', { mcpTool: 'company_get', cliCommand: 'whoami' }),
|
|
17
|
+
cap('public.usage.get', '/usage', { mcpTool: 'usage_get', cliCommand: 'usage' }),
|
|
18
|
+
// No stores route exists: `stores list` is the connected-store COUNT from usage.
|
|
19
|
+
cap('public.stores.count', '/usage', { cliCommand: 'stores list' }),
|
|
20
|
+
cap('public.orders.list', '/orders', { mcpTool: 'orders_list', cliCommand: 'orders list' }),
|
|
21
|
+
cap('public.orders.get', '/orders/:id', { mcpTool: 'order_get', cliCommand: 'orders get' }),
|
|
22
|
+
cap('public.contacts.search', '/contacts', {
|
|
23
|
+
mcpTool: 'contacts_search',
|
|
24
|
+
cliCommand: 'contacts search',
|
|
25
|
+
}),
|
|
26
|
+
cap('public.contacts.get', '/contacts/:id', { mcpTool: 'contact_get' }),
|
|
27
|
+
cap('public.conversations.list', '/conversations', { mcpTool: 'conversations_list' }),
|
|
28
|
+
cap('public.conversations.get', '/conversations/:id', { mcpTool: 'conversation_get' }),
|
|
29
|
+
cap('public.messages.list', '/conversations/:id/messages', { mcpTool: 'conversation_messages' }),
|
|
30
|
+
cap('public.products.search', '/products', { mcpTool: 'products_search' }),
|
|
31
|
+
cap('public.products.get', '/products/:id', { mcpTool: 'product_get' }),
|
|
32
|
+
];
|
|
33
|
+
export function capabilityForMcpTool(tool) {
|
|
34
|
+
return PUBLIC_CAPABILITIES.find((c) => c.mcpTool === tool);
|
|
35
|
+
}
|
|
36
|
+
export function capabilityForCliCommand(command) {
|
|
37
|
+
return PUBLIC_CAPABILITIES.find((c) => c.cliCommand === command);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAY7D,SAAS,GAAG,CACV,EAAU,EACV,IAAqB,EACrB,QAAmD;IAEnD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAgC;IAC9D,GAAG,CAAC,oBAAoB,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvF,GAAG,CAAC,kBAAkB,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IAChF,iFAAiF;IACjF,GAAG,CAAC,qBAAqB,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;IACnE,GAAG,CAAC,oBAAoB,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;IAC3F,GAAG,CAAC,mBAAmB,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3F,GAAG,CAAC,wBAAwB,EAAE,WAAW,EAAE;QACzC,OAAO,EAAE,iBAAiB;QAC1B,UAAU,EAAE,iBAAiB;KAC9B,CAAC;IACF,GAAG,CAAC,qBAAqB,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IACvE,GAAG,CAAC,2BAA2B,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IACrF,GAAG,CAAC,0BAA0B,EAAE,oBAAoB,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IACtF,GAAG,CAAC,sBAAsB,EAAE,6BAA6B,EAAE,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IAChG,GAAG,CAAC,wBAAwB,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC1E,GAAG,CAAC,qBAAqB,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;CACxE,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC;AACnE,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { CompanyContext, Contact, ContactsQuery, Conversation, ConversationsQuery, CursorMeta, CursorQuery, ItemEnvelope, ListEnvelope, Message, MessagesQuery, Order, OrdersQuery, Product, ProductsQuery, Usage } from './types.js';
|
|
2
|
+
export declare const DEFAULT_API_URL = "https://api.inboxy.chat";
|
|
3
|
+
export declare const DEFAULT_TIMEOUT_MS = 20000;
|
|
4
|
+
export declare const SDK_VERSION = "0.1.0";
|
|
5
|
+
export interface InboxyClientOptions {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** Origin of the Inboxy API, e.g. https://api.inboxy.chat. */
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
/** Per-request timeout. Default 20 s. */
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
/** Injected for tests; defaults to the global fetch. */
|
|
12
|
+
fetch?: typeof fetch;
|
|
13
|
+
userAgent?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RequestOptions {
|
|
16
|
+
/** Aborts the request (MCP cancellation, Ctrl-C). */
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
export interface ResponseInfo {
|
|
20
|
+
status: number;
|
|
21
|
+
/** The server's Date header, used by `inboxy doctor` for clock skew. */
|
|
22
|
+
date: string | null;
|
|
23
|
+
retryAfterSeconds: number | null;
|
|
24
|
+
rateLimit: {
|
|
25
|
+
limit: number | null;
|
|
26
|
+
remaining: number | null;
|
|
27
|
+
reset: number | null;
|
|
28
|
+
};
|
|
29
|
+
latencyMs: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validates and normalizes the API origin. Throws a `config` error; nothing is
|
|
33
|
+
* sent with a URL that fails here.
|
|
34
|
+
*/
|
|
35
|
+
export declare function normalizeBaseUrl(raw: string): string;
|
|
36
|
+
export declare class InboxyClient {
|
|
37
|
+
#private;
|
|
38
|
+
readonly baseUrl: string;
|
|
39
|
+
readonly timeoutMs: number;
|
|
40
|
+
constructor(options: InboxyClientOptions);
|
|
41
|
+
/** Never reveal the key through inspection or JSON.stringify. */
|
|
42
|
+
toJSON(): {
|
|
43
|
+
baseUrl: string;
|
|
44
|
+
timeoutMs: number;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* One GET under `/api/v1/public`, returning the parsed envelope and the
|
|
48
|
+
* response metadata. `path` must already be encoded.
|
|
49
|
+
*/
|
|
50
|
+
request<T>(path: string, query?: object, options?: RequestOptions): Promise<{
|
|
51
|
+
body: T;
|
|
52
|
+
info: ResponseInfo;
|
|
53
|
+
}>;
|
|
54
|
+
getCompany(options?: RequestOptions): Promise<ItemEnvelope<CompanyContext>>;
|
|
55
|
+
getUsage(options?: RequestOptions): Promise<ItemEnvelope<Usage>>;
|
|
56
|
+
listContacts(query?: ContactsQuery, options?: RequestOptions): Promise<ListEnvelope<Contact>>;
|
|
57
|
+
getContact(id: string, options?: RequestOptions): Promise<ItemEnvelope<Contact>>;
|
|
58
|
+
listConversations(query?: ConversationsQuery, options?: RequestOptions): Promise<ListEnvelope<Conversation>>;
|
|
59
|
+
getConversation(id: string, options?: RequestOptions): Promise<ItemEnvelope<Conversation>>;
|
|
60
|
+
listMessages(conversationId: string, query?: MessagesQuery, options?: RequestOptions): Promise<ListEnvelope<Message>>;
|
|
61
|
+
listOrders(query?: OrdersQuery, options?: RequestOptions): Promise<ListEnvelope<Order>>;
|
|
62
|
+
getOrder(id: string, options?: RequestOptions): Promise<ItemEnvelope<Order>>;
|
|
63
|
+
listProducts(query?: ProductsQuery, options?: RequestOptions): Promise<ListEnvelope<Product>>;
|
|
64
|
+
getProduct(id: string, options?: RequestOptions): Promise<ItemEnvelope<Product>>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Walks a cursor-paginated list. Stops at the last page or after `maxPages`
|
|
68
|
+
* (default 10), whichever comes first, and reports whether more remained.
|
|
69
|
+
*/
|
|
70
|
+
export declare function collectPages<T, Q extends CursorQuery>(fetchPage: (query: Q) => Promise<ListEnvelope<T>>, query: Q, maxPages?: number): Promise<{
|
|
71
|
+
items: T[];
|
|
72
|
+
pages: number;
|
|
73
|
+
meta: CursorMeta;
|
|
74
|
+
}>;
|
|
75
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InboxyClient — a small, read-only client for `/api/v1/public`.
|
|
3
|
+
*
|
|
4
|
+
* Safety properties (tested in test/client.test.ts):
|
|
5
|
+
* - GET only. There is no method that writes, so nothing built on this client
|
|
6
|
+
* (the MCP server, the CLI) can change merchant data.
|
|
7
|
+
* - The company is decided by the key. No method takes a company id; ids are
|
|
8
|
+
* validated and percent-encoded into one path segment, so `../usage` or
|
|
9
|
+
* `a/b` cannot reach another route.
|
|
10
|
+
* - The key is sent only in the Authorization header, only over HTTPS (plain
|
|
11
|
+
* HTTP is accepted for localhost alone), and redirects are refused so the
|
|
12
|
+
* header never follows a redirect to another host.
|
|
13
|
+
* - Every error is an `InboxyError` with a redacted message.
|
|
14
|
+
*/
|
|
15
|
+
import { InboxyError, kindForStatus } from './errors.js';
|
|
16
|
+
import { PUBLIC_API_PREFIX } from './routes.js';
|
|
17
|
+
export const DEFAULT_API_URL = 'https://api.inboxy.chat';
|
|
18
|
+
export const DEFAULT_TIMEOUT_MS = 20_000;
|
|
19
|
+
export const SDK_VERSION = '0.1.0';
|
|
20
|
+
const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
|
|
21
|
+
const ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
22
|
+
function isLocalHost(hostname) {
|
|
23
|
+
return LOCAL_HOSTS.has(hostname) || hostname.endsWith('.localhost');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates and normalizes the API origin. Throws a `config` error; nothing is
|
|
27
|
+
* sent with a URL that fails here.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeBaseUrl(raw) {
|
|
30
|
+
let url;
|
|
31
|
+
try {
|
|
32
|
+
url = new URL(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new InboxyError({
|
|
36
|
+
kind: 'config',
|
|
37
|
+
code: 'INVALID_API_URL',
|
|
38
|
+
message: `INBOXY_API_URL is not a valid URL.`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (url.username || url.password) {
|
|
42
|
+
throw new InboxyError({
|
|
43
|
+
kind: 'config',
|
|
44
|
+
code: 'INVALID_API_URL',
|
|
45
|
+
message: 'INBOXY_API_URL must not contain credentials.',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (url.search || url.hash) {
|
|
49
|
+
throw new InboxyError({
|
|
50
|
+
kind: 'config',
|
|
51
|
+
code: 'INVALID_API_URL',
|
|
52
|
+
message: 'INBOXY_API_URL must not contain a query string or fragment.',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (url.protocol === 'http:' && !isLocalHost(url.hostname)) {
|
|
56
|
+
throw new InboxyError({
|
|
57
|
+
kind: 'config',
|
|
58
|
+
code: 'INSECURE_API_URL',
|
|
59
|
+
message: 'INBOXY_API_URL must use https:// (plain http is allowed for localhost only).',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
63
|
+
throw new InboxyError({
|
|
64
|
+
kind: 'config',
|
|
65
|
+
code: 'INVALID_API_URL',
|
|
66
|
+
message: 'INBOXY_API_URL must be an http(s) URL.',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return `${url.origin}${url.pathname.replace(/\/+$/, '')}`;
|
|
70
|
+
}
|
|
71
|
+
function idSegment(id, label) {
|
|
72
|
+
if (typeof id !== 'string' || !ID_PATTERN.test(id)) {
|
|
73
|
+
throw new InboxyError({
|
|
74
|
+
kind: 'invalid_request',
|
|
75
|
+
code: 'INVALID_ID',
|
|
76
|
+
message: `${label} must be 1-64 characters of letters, digits, "_" or "-".`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return encodeURIComponent(id);
|
|
80
|
+
}
|
|
81
|
+
function toSearchParams(query) {
|
|
82
|
+
if (!query)
|
|
83
|
+
return '';
|
|
84
|
+
const params = new URLSearchParams();
|
|
85
|
+
for (const [key, value] of Object.entries(query)) {
|
|
86
|
+
if (value === undefined || value === null || value === '')
|
|
87
|
+
continue;
|
|
88
|
+
params.set(key, String(value));
|
|
89
|
+
}
|
|
90
|
+
const s = params.toString();
|
|
91
|
+
return s ? `?${s}` : '';
|
|
92
|
+
}
|
|
93
|
+
function intHeader(headers, name) {
|
|
94
|
+
const raw = headers.get(name);
|
|
95
|
+
if (raw === null)
|
|
96
|
+
return null;
|
|
97
|
+
const n = Number(raw);
|
|
98
|
+
return Number.isFinite(n) ? n : null;
|
|
99
|
+
}
|
|
100
|
+
export class InboxyClient {
|
|
101
|
+
baseUrl;
|
|
102
|
+
timeoutMs;
|
|
103
|
+
#apiKey;
|
|
104
|
+
#fetch;
|
|
105
|
+
#userAgent;
|
|
106
|
+
constructor(options) {
|
|
107
|
+
if (!options.apiKey || typeof options.apiKey !== 'string') {
|
|
108
|
+
throw new InboxyError({
|
|
109
|
+
kind: 'config',
|
|
110
|
+
code: 'API_KEY_MISSING',
|
|
111
|
+
message: 'No Inboxy API key. Set INBOXY_API_KEY or run `inboxy auth login` to store one in the OS keychain.',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (/\s/.test(options.apiKey)) {
|
|
115
|
+
throw new InboxyError({
|
|
116
|
+
kind: 'config',
|
|
117
|
+
code: 'API_KEY_INVALID_FORMAT',
|
|
118
|
+
message: 'The Inboxy API key contains whitespace; copy it again from the dashboard.',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
this.#apiKey = options.apiKey;
|
|
122
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_API_URL);
|
|
123
|
+
const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
124
|
+
this.timeoutMs = Math.min(Math.max(Math.trunc(timeout) || DEFAULT_TIMEOUT_MS, 100), 120_000);
|
|
125
|
+
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
126
|
+
this.#userAgent = options.userAgent ?? `inboxy-sdk/${SDK_VERSION}`;
|
|
127
|
+
}
|
|
128
|
+
/** Never reveal the key through inspection or JSON.stringify. */
|
|
129
|
+
toJSON() {
|
|
130
|
+
return { baseUrl: this.baseUrl, timeoutMs: this.timeoutMs };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* One GET under `/api/v1/public`, returning the parsed envelope and the
|
|
134
|
+
* response metadata. `path` must already be encoded.
|
|
135
|
+
*/
|
|
136
|
+
async request(path, query, options = {}) {
|
|
137
|
+
const url = `${this.baseUrl}${PUBLIC_API_PREFIX}${path}${toSearchParams(query)}`;
|
|
138
|
+
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
139
|
+
const signal = options.signal
|
|
140
|
+
? AbortSignal.any([options.signal, timeoutSignal])
|
|
141
|
+
: timeoutSignal;
|
|
142
|
+
if (options.signal?.aborted) {
|
|
143
|
+
throw new InboxyError({
|
|
144
|
+
kind: 'cancelled',
|
|
145
|
+
code: 'CANCELLED',
|
|
146
|
+
message: 'Request cancelled.',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const started = Date.now();
|
|
150
|
+
let response;
|
|
151
|
+
let text;
|
|
152
|
+
try {
|
|
153
|
+
response = await this.#fetch(url, {
|
|
154
|
+
method: 'GET',
|
|
155
|
+
headers: {
|
|
156
|
+
Authorization: `Bearer ${this.#apiKey}`,
|
|
157
|
+
Accept: 'application/json',
|
|
158
|
+
'User-Agent': this.#userAgent,
|
|
159
|
+
},
|
|
160
|
+
redirect: 'error',
|
|
161
|
+
signal,
|
|
162
|
+
});
|
|
163
|
+
text = await response.text();
|
|
164
|
+
}
|
|
165
|
+
catch (cause) {
|
|
166
|
+
if (options.signal?.aborted) {
|
|
167
|
+
throw new InboxyError({
|
|
168
|
+
kind: 'cancelled',
|
|
169
|
+
code: 'CANCELLED',
|
|
170
|
+
message: 'Request cancelled.',
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (timeoutSignal.aborted) {
|
|
174
|
+
throw new InboxyError({
|
|
175
|
+
kind: 'timeout',
|
|
176
|
+
code: 'TIMEOUT',
|
|
177
|
+
message: `no response within ${this.timeoutMs} ms`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const reason = cause instanceof Error
|
|
181
|
+
? [cause.message, cause.cause?.code]
|
|
182
|
+
.filter(Boolean)
|
|
183
|
+
.join(': ')
|
|
184
|
+
: 'request failed';
|
|
185
|
+
throw new InboxyError({ kind: 'network', code: 'NETWORK_ERROR', message: reason });
|
|
186
|
+
}
|
|
187
|
+
const info = {
|
|
188
|
+
status: response.status,
|
|
189
|
+
date: response.headers.get('date'),
|
|
190
|
+
retryAfterSeconds: intHeader(response.headers, 'retry-after'),
|
|
191
|
+
rateLimit: {
|
|
192
|
+
limit: intHeader(response.headers, 'x-ratelimit-limit'),
|
|
193
|
+
remaining: intHeader(response.headers, 'x-ratelimit-remaining'),
|
|
194
|
+
reset: intHeader(response.headers, 'x-ratelimit-reset'),
|
|
195
|
+
},
|
|
196
|
+
latencyMs: Date.now() - started,
|
|
197
|
+
};
|
|
198
|
+
let body;
|
|
199
|
+
try {
|
|
200
|
+
body = text ? JSON.parse(text) : undefined;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
body = undefined;
|
|
204
|
+
}
|
|
205
|
+
if (!response.ok) {
|
|
206
|
+
const envelope = body;
|
|
207
|
+
const err = envelope && envelope.success === false ? envelope.error : undefined;
|
|
208
|
+
throw new InboxyError({
|
|
209
|
+
kind: kindForStatus(response.status),
|
|
210
|
+
status: response.status,
|
|
211
|
+
code: typeof err?.code === 'string' ? err.code : `HTTP_${response.status}`,
|
|
212
|
+
message: typeof err?.message === 'string'
|
|
213
|
+
? err.message
|
|
214
|
+
: `Inboxy answered HTTP ${response.status}`,
|
|
215
|
+
details: err?.details && typeof err.details === 'object'
|
|
216
|
+
? err.details
|
|
217
|
+
: {},
|
|
218
|
+
retryAfterSeconds: info.retryAfterSeconds,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (!body || typeof body !== 'object' || body.success !== true) {
|
|
222
|
+
throw new InboxyError({
|
|
223
|
+
kind: 'invalid_response',
|
|
224
|
+
status: response.status,
|
|
225
|
+
code: 'INVALID_RESPONSE',
|
|
226
|
+
message: 'Inboxy answered with something that is not the public API envelope.',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return { body: body, info };
|
|
230
|
+
}
|
|
231
|
+
async #get(path, query, options) {
|
|
232
|
+
return (await this.request(path, query, options)).body;
|
|
233
|
+
}
|
|
234
|
+
// ------------------------------------------------------------------
|
|
235
|
+
// Resources — one method per route in PUBLIC_ROUTES
|
|
236
|
+
// ------------------------------------------------------------------
|
|
237
|
+
async getCompany(options) {
|
|
238
|
+
return this.#get('/company', undefined, options);
|
|
239
|
+
}
|
|
240
|
+
async getUsage(options) {
|
|
241
|
+
return this.#get('/usage', undefined, options);
|
|
242
|
+
}
|
|
243
|
+
async listContacts(query, options) {
|
|
244
|
+
return this.#get('/contacts', query, options);
|
|
245
|
+
}
|
|
246
|
+
async getContact(id, options) {
|
|
247
|
+
return this.#get(`/contacts/${idSegment(id, 'Contact id')}`, undefined, options);
|
|
248
|
+
}
|
|
249
|
+
async listConversations(query, options) {
|
|
250
|
+
return this.#get('/conversations', query, options);
|
|
251
|
+
}
|
|
252
|
+
async getConversation(id, options) {
|
|
253
|
+
return this.#get(`/conversations/${idSegment(id, 'Conversation id')}`, undefined, options);
|
|
254
|
+
}
|
|
255
|
+
async listMessages(conversationId, query, options) {
|
|
256
|
+
return this.#get(`/conversations/${idSegment(conversationId, 'Conversation id')}/messages`, query, options);
|
|
257
|
+
}
|
|
258
|
+
async listOrders(query, options) {
|
|
259
|
+
return this.#get('/orders', query, options);
|
|
260
|
+
}
|
|
261
|
+
async getOrder(id, options) {
|
|
262
|
+
return this.#get(`/orders/${idSegment(id, 'Order id')}`, undefined, options);
|
|
263
|
+
}
|
|
264
|
+
async listProducts(query, options) {
|
|
265
|
+
return this.#get('/products', query, options);
|
|
266
|
+
}
|
|
267
|
+
async getProduct(id, options) {
|
|
268
|
+
return this.#get(`/products/${idSegment(id, 'Product id')}`, undefined, options);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Walks a cursor-paginated list. Stops at the last page or after `maxPages`
|
|
273
|
+
* (default 10), whichever comes first, and reports whether more remained.
|
|
274
|
+
*/
|
|
275
|
+
export async function collectPages(fetchPage, query, maxPages = 10) {
|
|
276
|
+
const items = [];
|
|
277
|
+
let cursor = query.cursor;
|
|
278
|
+
let pages = 0;
|
|
279
|
+
let meta = { limit: 0, hasMore: false, nextCursor: null };
|
|
280
|
+
while (pages < Math.max(1, maxPages)) {
|
|
281
|
+
const page = await fetchPage({ ...query, cursor });
|
|
282
|
+
pages += 1;
|
|
283
|
+
items.push(...page.data);
|
|
284
|
+
meta = page.meta;
|
|
285
|
+
if (!page.meta.hasMore || !page.meta.nextCursor)
|
|
286
|
+
break;
|
|
287
|
+
cursor = page.meta.nextCursor;
|
|
288
|
+
}
|
|
289
|
+
return { items, pages, meta };
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export declare const KEYCHAIN_SERVICE = "inboxy-cli";
|
|
2
|
+
export declare const CONFIG_FILE_NAME = "config.json";
|
|
3
|
+
export type KeySource = 'env' | 'keychain' | 'config-file' | 'none';
|
|
4
|
+
export type UrlSource = 'env' | 'config' | 'default';
|
|
5
|
+
export interface ProfileConfig {
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
/** Where `inboxy auth login` stored the key. */
|
|
8
|
+
keyStorage?: 'keychain' | 'file';
|
|
9
|
+
/** Only with keyStorage "file" (no keychain on this machine). */
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface InboxyConfigFile {
|
|
13
|
+
version: 1;
|
|
14
|
+
defaultProfile?: string;
|
|
15
|
+
profiles: Record<string, ProfileConfig>;
|
|
16
|
+
}
|
|
17
|
+
export interface ResolvedCredentials {
|
|
18
|
+
profile: string;
|
|
19
|
+
apiKey: string | null;
|
|
20
|
+
keySource: KeySource;
|
|
21
|
+
apiUrl: string;
|
|
22
|
+
urlSource: UrlSource;
|
|
23
|
+
configPath: string;
|
|
24
|
+
/** Non-fatal observations for `inboxy doctor` (never contain the key). */
|
|
25
|
+
warnings: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function validateProfileName(profile: string): string;
|
|
28
|
+
export declare function configDir(env?: NodeJS.ProcessEnv): string;
|
|
29
|
+
export declare function configPath(env?: NodeJS.ProcessEnv): string;
|
|
30
|
+
export interface LoadedConfig {
|
|
31
|
+
path: string;
|
|
32
|
+
exists: boolean;
|
|
33
|
+
/** File mode bits (e.g. 0o600); null when missing or on Windows. */
|
|
34
|
+
mode: number | null;
|
|
35
|
+
config: InboxyConfigFile;
|
|
36
|
+
}
|
|
37
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv): Promise<LoadedConfig>;
|
|
38
|
+
/** True when group or others have any access to the file. */
|
|
39
|
+
export declare function isModeTooOpen(mode: number | null): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Writes the config with 0700 on the directory and 0600 on the file. The
|
|
42
|
+
* temporary file is created 0600 before any byte is written, then renamed, so
|
|
43
|
+
* there is no moment where the content is world-readable.
|
|
44
|
+
*/
|
|
45
|
+
export declare function saveConfig(config: InboxyConfigFile, env?: NodeJS.ProcessEnv): Promise<string>;
|
|
46
|
+
export interface CommandResult {
|
|
47
|
+
code: number | null;
|
|
48
|
+
stdout: string;
|
|
49
|
+
stderr: string;
|
|
50
|
+
}
|
|
51
|
+
/** Runs a program with an argument array — never through a shell. */
|
|
52
|
+
export type CommandRunner = (command: string, args: readonly string[], input?: string) => Promise<CommandResult>;
|
|
53
|
+
export declare const spawnRunner: CommandRunner;
|
|
54
|
+
export interface Keychain {
|
|
55
|
+
readonly name: string;
|
|
56
|
+
get(account: string): Promise<string | null>;
|
|
57
|
+
set(account: string, secret: string): Promise<void>;
|
|
58
|
+
delete(account: string): Promise<boolean>;
|
|
59
|
+
}
|
|
60
|
+
/** macOS Keychain through /usr/bin/security. */
|
|
61
|
+
export declare function macosKeychain(run?: CommandRunner): Keychain;
|
|
62
|
+
/** Linux Secret Service (GNOME Keyring, KWallet) through `secret-tool`. */
|
|
63
|
+
export declare function secretServiceKeychain(run?: CommandRunner): Keychain;
|
|
64
|
+
/**
|
|
65
|
+
* The keychain for this machine, or null when there is none (Windows, a Linux
|
|
66
|
+
* box without secret-tool, or INBOXY_KEYCHAIN=off).
|
|
67
|
+
*/
|
|
68
|
+
export declare function systemKeychain(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, run?: CommandRunner): Promise<Keychain | null>;
|
|
69
|
+
export interface ResolveOptions {
|
|
70
|
+
env?: NodeJS.ProcessEnv;
|
|
71
|
+
profile?: string;
|
|
72
|
+
/** Pass `null` to skip the keychain; omit to detect it. */
|
|
73
|
+
keychain?: Keychain | null;
|
|
74
|
+
}
|
|
75
|
+
export declare function resolveCredentials(options?: ResolveOptions): Promise<ResolvedCredentials>;
|
|
76
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
|