@agentisend/sdk-node 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 +97 -0
- package/dist/cli/args.d.ts +24 -0
- package/dist/cli/args.js +71 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands.d.ts +29 -0
- package/dist/cli/commands.js +147 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/config.d.ts +31 -0
- package/dist/cli/config.js +73 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/doctor.d.ts +36 -0
- package/dist/cli/doctor.js +161 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/listen.d.ts +28 -0
- package/dist/cli/listen.js +140 -0
- package/dist/cli/listen.js.map +1 -0
- package/dist/cli/main.d.ts +14 -0
- package/dist/cli/main.js +369 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +40 -0
- package/dist/cli/output.js +35 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/scheduled-at.d.ts +19 -0
- package/dist/cli/scheduled-at.js +108 -0
- package/dist/cli/scheduled-at.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +426 -0
- package/dist/client.js +542 -0
- package/dist/client.js.map +1 -0
- package/dist/compat-resend.d.ts +194 -0
- package/dist/compat-resend.js +233 -0
- package/dist/compat-resend.js.map +1 -0
- package/dist/errors.d.ts +82 -0
- package/dist/errors.js +92 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +499 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/dist/webhook-signing.d.ts +64 -0
- package/dist/webhook-signing.js +125 -0
- package/dist/webhook-signing.js.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<img src="https://agentisend.com/brand/lockup-horizontal-light.png#gh-light-mode-only" alt="AgentiSend" width="200" />
|
|
2
|
+
<img src="https://agentisend.com/brand/lockup-horizontal-dark.png#gh-dark-mode-only" alt="AgentiSend" width="200" />
|
|
3
|
+
|
|
4
|
+
# @agentisend/sdk-node
|
|
5
|
+
|
|
6
|
+
Typed Node client for the [AgentiSend](https://agentisend.com) API. Node 20 or newer,
|
|
7
|
+
ESM, no runtime dependencies.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @agentisend/sdk-node
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Send
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { AgentiSend } from '@agentisend/sdk-node';
|
|
17
|
+
|
|
18
|
+
const agentisend = new AgentiSend(process.env.AGENTISEND_API_KEY);
|
|
19
|
+
const { id } = await agentisend.emails.send({
|
|
20
|
+
from: 'you@yourdomain.com',
|
|
21
|
+
to: 'someone@example.com',
|
|
22
|
+
subject: 'Hello from AgentiSend',
|
|
23
|
+
text: 'Ten minutes, start to sent.',
|
|
24
|
+
});
|
|
25
|
+
console.log(id);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That block is executed against a running API server on every build of this
|
|
29
|
+
repository, so a snippet that stops working fails the build rather than your
|
|
30
|
+
first send.
|
|
31
|
+
|
|
32
|
+
Every mutating call takes an idempotency key. Pass one whenever a retry is
|
|
33
|
+
possible: `agentisend.emails.send(payload, { idempotencyKey: idempotencyKey('welcome-email', 'user_123') })`.
|
|
34
|
+
The same key with the same body replays the first response instead of sending twice.
|
|
35
|
+
|
|
36
|
+
Errors throw `AgentiSendError`, which carries `code`, `message`, `fix` and
|
|
37
|
+
`docsUrl`. `fix` names what to do and which endpoint to call; on a 429 the
|
|
38
|
+
error also carries `retryAfterSeconds`.
|
|
39
|
+
|
|
40
|
+
## Coming from Resend
|
|
41
|
+
|
|
42
|
+
`@agentisend/sdk-node/compat-resend` exports a `Resend` class with every method
|
|
43
|
+
the Resend SDK has and its `{ data, error }` return shape, so the migration is
|
|
44
|
+
the import line:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import { Resend } from '@agentisend/sdk-node/compat-resend';
|
|
48
|
+
|
|
49
|
+
const resend = new Resend(process.env.AGENTISEND_API_KEY);
|
|
50
|
+
const { data, error } = await resend.emails.send({ from, to, subject, html });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Where AgentiSend is stricter than Resend, the shim warns on stderr instead of
|
|
54
|
+
changing what it does.
|
|
55
|
+
|
|
56
|
+
## CLI
|
|
57
|
+
|
|
58
|
+
The package installs an `agentisend` binary.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx @agentisend/sdk-node emails send --to someone@example.com \
|
|
62
|
+
--from you@yourdomain.com --subject Hi --text "Sent from the CLI"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Command | What it does |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `emails send` / `get` / `list` / `cancel` | The send path from a shell. |
|
|
68
|
+
| `emails explain <id>` | Everything that happened to one message, in order. |
|
|
69
|
+
| `domains list` / `verify` | Sending domains and a re-check of their DNS. |
|
|
70
|
+
| `webhooks listen --forward-to <url>` | Verify deliveries locally and forward the raw bytes to your app. |
|
|
71
|
+
| `receiving listen` | The same, for inbound mail. |
|
|
72
|
+
| `limits kill` / `resume` | Stop a key sending, and undo it. |
|
|
73
|
+
| `trust standing` | This account's standing and the reasons behind it. |
|
|
74
|
+
| `doctor` | Check the key, the domains and the DNS in one pass. |
|
|
75
|
+
| `profile list` / `use` / `set` | Named key and base-URL profiles. |
|
|
76
|
+
| `completions <shell>` | bash, zsh or fish. |
|
|
77
|
+
|
|
78
|
+
`AGENTISEND_API_KEY` is read from the environment. Piped or redirected output is
|
|
79
|
+
JSON; a terminal gets the table.
|
|
80
|
+
|
|
81
|
+
## Verifying webhooks
|
|
82
|
+
|
|
83
|
+
`@agentisend/sdk-node/webhook-signing` is the same implementation that signs the
|
|
84
|
+
delivery, so a signature that verifies here verified on the wire.
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
import { verifyWebhookSignature, SIGNATURE_HEADER } from '@agentisend/sdk-node/webhook-signing';
|
|
88
|
+
|
|
89
|
+
const ok = verifyWebhookSignature(secret, req.headers[SIGNATURE_HEADER], rawBody);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pass the raw request body, byte for byte. Re-serialising parsed JSON changes the
|
|
93
|
+
bytes and therefore the HMAC.
|
|
94
|
+
|
|
95
|
+
## Docs
|
|
96
|
+
|
|
97
|
+
[agentisend.com/docs](https://agentisend.com/docs). Problems: hello@agentisend.com.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4.16 — one argument parser for every command.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately small and deliberately strict about one thing: an unknown flag
|
|
5
|
+
* is an ERROR. The old parser ignored them "so the CLI stays scriptable",
|
|
6
|
+
* which means `--idempotency-key` typed as `--idempotency_key` sent the email
|
|
7
|
+
* twice and said nothing. Silence is not scriptability.
|
|
8
|
+
*/
|
|
9
|
+
export interface ParsedArgs {
|
|
10
|
+
/** Positional words, in order: `webhooks listen` → ['webhooks', 'listen']. */
|
|
11
|
+
positional: string[];
|
|
12
|
+
/** Repeated flags keep every value; a bare flag records ''. */
|
|
13
|
+
flags: Map<string, string[]>;
|
|
14
|
+
}
|
|
15
|
+
export declare class UsageError extends Error {
|
|
16
|
+
readonly fix: string;
|
|
17
|
+
constructor(message: string, fix: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function parseArgs(argv: string[], known?: Set<string>): ParsedArgs;
|
|
20
|
+
export declare function flag(args: ParsedArgs, name: string): string | undefined;
|
|
21
|
+
export declare function flagAll(args: ParsedArgs, name: string): string[];
|
|
22
|
+
export declare function has(args: ParsedArgs, name: string): boolean;
|
|
23
|
+
/** Comma-or-repetition, because both are what people actually type. */
|
|
24
|
+
export declare function list(args: ParsedArgs, name: string): string[];
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export class UsageError extends Error {
|
|
2
|
+
fix;
|
|
3
|
+
constructor(message, fix) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'UsageError';
|
|
6
|
+
this.fix = fix;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Flags that never take a value. Everything else consumes the next token. */
|
|
10
|
+
const BOOLEAN_FLAGS = new Set([
|
|
11
|
+
'json',
|
|
12
|
+
'help',
|
|
13
|
+
'version',
|
|
14
|
+
'no-color',
|
|
15
|
+
'verify-only',
|
|
16
|
+
'follow',
|
|
17
|
+
'once',
|
|
18
|
+
]);
|
|
19
|
+
export function parseArgs(argv, known) {
|
|
20
|
+
const positional = [];
|
|
21
|
+
const flags = new Map();
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
const token = argv[i];
|
|
24
|
+
if (!token.startsWith('--')) {
|
|
25
|
+
positional.push(token);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const body = token.slice(2);
|
|
29
|
+
const eq = body.indexOf('=');
|
|
30
|
+
const name = eq === -1 ? body : body.slice(0, eq);
|
|
31
|
+
if (known && !known.has(name)) {
|
|
32
|
+
throw new UsageError(`Unknown option --${name}.`, 'Run the command with --help to see what it accepts. Nothing was sent.');
|
|
33
|
+
}
|
|
34
|
+
let value;
|
|
35
|
+
if (eq !== -1)
|
|
36
|
+
value = body.slice(eq + 1);
|
|
37
|
+
else if (BOOLEAN_FLAGS.has(name))
|
|
38
|
+
value = '';
|
|
39
|
+
else {
|
|
40
|
+
const next = argv[i + 1];
|
|
41
|
+
if (next === undefined || next.startsWith('--')) {
|
|
42
|
+
throw new UsageError(`--${name} needs a value.`, `Pass it as --${name} <value>.`);
|
|
43
|
+
}
|
|
44
|
+
value = next;
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
const existing = flags.get(name);
|
|
48
|
+
if (existing)
|
|
49
|
+
existing.push(value);
|
|
50
|
+
else
|
|
51
|
+
flags.set(name, [value]);
|
|
52
|
+
}
|
|
53
|
+
return { positional, flags };
|
|
54
|
+
}
|
|
55
|
+
export function flag(args, name) {
|
|
56
|
+
return args.flags.get(name)?.[0];
|
|
57
|
+
}
|
|
58
|
+
export function flagAll(args, name) {
|
|
59
|
+
return args.flags.get(name) ?? [];
|
|
60
|
+
}
|
|
61
|
+
export function has(args, name) {
|
|
62
|
+
return args.flags.has(name);
|
|
63
|
+
}
|
|
64
|
+
/** Comma-or-repetition, because both are what people actually type. */
|
|
65
|
+
export function list(args, name) {
|
|
66
|
+
return flagAll(args, name)
|
|
67
|
+
.flatMap((value) => value.split(','))
|
|
68
|
+
.map((value) => value.trim())
|
|
69
|
+
.filter((value) => value.length > 0);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4.16 — the command surface, as data.
|
|
3
|
+
*
|
|
4
|
+
* One table drives the help text, the shell completions and the flag
|
|
5
|
+
* validation. Three hand-maintained lists is how a CLI ends up completing a
|
|
6
|
+
* flag it no longer accepts, and the person typing it trusts the completion
|
|
7
|
+
* more than the docs.
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandSpec {
|
|
10
|
+
/** e.g. "emails send" */
|
|
11
|
+
name: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
/** Flags this command accepts, without the leading dashes. */
|
|
14
|
+
flags: string[];
|
|
15
|
+
}
|
|
16
|
+
/** Flags every command takes. */
|
|
17
|
+
export declare const GLOBAL_FLAGS: string[];
|
|
18
|
+
export declare const COMMANDS: CommandSpec[];
|
|
19
|
+
export declare function flagsFor(command: string): Set<string>;
|
|
20
|
+
export declare function helpText(): string;
|
|
21
|
+
declare const SHELLS: readonly ["bash", "zsh", "fish"];
|
|
22
|
+
export type CompletionShell = (typeof SHELLS)[number];
|
|
23
|
+
export declare function isShell(value: string): value is CompletionShell;
|
|
24
|
+
/**
|
|
25
|
+
* Completions generated from the same table — a completion that offers a flag
|
|
26
|
+
* the command rejects is worse than none, because it is trusted.
|
|
27
|
+
*/
|
|
28
|
+
export declare function completionScript(shell: CompletionShell): string;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** Flags every command takes. */
|
|
2
|
+
export const GLOBAL_FLAGS = ['json', 'help', 'version', 'profile', 'api-key', 'base-url'];
|
|
3
|
+
export const COMMANDS = [
|
|
4
|
+
{
|
|
5
|
+
name: 'emails send',
|
|
6
|
+
summary: 'Send one email. Use - with --text-file/--html-file to read the body from stdin.',
|
|
7
|
+
flags: [
|
|
8
|
+
'from',
|
|
9
|
+
'to',
|
|
10
|
+
'cc',
|
|
11
|
+
'bcc',
|
|
12
|
+
'reply-to',
|
|
13
|
+
'subject',
|
|
14
|
+
'html',
|
|
15
|
+
'text',
|
|
16
|
+
'html-file',
|
|
17
|
+
'text-file',
|
|
18
|
+
'tag',
|
|
19
|
+
'scheduled-at',
|
|
20
|
+
'idempotency-key',
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{ name: 'emails get', summary: 'Fetch one message by id.', flags: [] },
|
|
24
|
+
{ name: 'emails list', summary: 'List messages, newest first.', flags: ['status', 'limit', 'to'] },
|
|
25
|
+
{ name: 'emails cancel', summary: 'Cancel a scheduled or queued message.', flags: [] },
|
|
26
|
+
{
|
|
27
|
+
name: 'emails explain',
|
|
28
|
+
summary: 'Why a message ended the way it did, with the fix as callable steps.',
|
|
29
|
+
flags: [],
|
|
30
|
+
},
|
|
31
|
+
{ name: 'domains list', summary: 'List sending domains and their status.', flags: [] },
|
|
32
|
+
{ name: 'domains verify', summary: 'Re-check a domain\'s DNS records.', flags: [] },
|
|
33
|
+
{
|
|
34
|
+
name: 'webhooks listen',
|
|
35
|
+
summary: 'Verify deliveries locally and forward them to your app with the same headers and bytes.',
|
|
36
|
+
flags: ['secret', 'forward-to', 'port', 'verify-only'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'receiving listen',
|
|
40
|
+
summary: 'Stream inbound messages as NDJSON, one JSON object per line.',
|
|
41
|
+
flags: ['interval', 'limit', 'to', 'once'],
|
|
42
|
+
},
|
|
43
|
+
{ name: 'limits kill', summary: 'Stop a key sending, immediately.', flags: ['reason'] },
|
|
44
|
+
{
|
|
45
|
+
name: 'limits resume',
|
|
46
|
+
// W4.1: the command stays, because the refusal is the useful answer — it
|
|
47
|
+
// names who can. An API key is refused here however it is scoped.
|
|
48
|
+
summary: 'Undo a kill switch. A person signed in to the console does this; a key is refused.',
|
|
49
|
+
flags: [],
|
|
50
|
+
},
|
|
51
|
+
{ name: 'trust standing', summary: 'This account\'s standing and the reasons behind it.', flags: [] },
|
|
52
|
+
{
|
|
53
|
+
name: 'doctor',
|
|
54
|
+
summary: 'Check credentials, reachability, kill switch, budget, domains and standing.',
|
|
55
|
+
flags: [],
|
|
56
|
+
},
|
|
57
|
+
{ name: 'profile list', summary: 'Show configured profiles and which is current.', flags: [] },
|
|
58
|
+
{ name: 'profile use', summary: 'Switch the current profile.', flags: [] },
|
|
59
|
+
{
|
|
60
|
+
name: 'profile set',
|
|
61
|
+
summary: 'Create or update a profile. The key is stored 0600 and never printed back.',
|
|
62
|
+
flags: ['api-key', 'base-url'],
|
|
63
|
+
},
|
|
64
|
+
{ name: 'completions', summary: 'Print a completion script for bash, zsh or fish.', flags: [] },
|
|
65
|
+
];
|
|
66
|
+
export function flagsFor(command) {
|
|
67
|
+
const spec = COMMANDS.find((entry) => entry.name === command);
|
|
68
|
+
return new Set([...GLOBAL_FLAGS, ...(spec?.flags ?? [])]);
|
|
69
|
+
}
|
|
70
|
+
export function helpText() {
|
|
71
|
+
const width = Math.max(...COMMANDS.map((entry) => entry.name.length));
|
|
72
|
+
return [
|
|
73
|
+
'agentisend — the AgentiSend CLI.',
|
|
74
|
+
'',
|
|
75
|
+
'Usage: agentisend <command> [options]',
|
|
76
|
+
'',
|
|
77
|
+
'Commands:',
|
|
78
|
+
...COMMANDS.map((entry) => ` ${entry.name.padEnd(width)} ${entry.summary}`),
|
|
79
|
+
'',
|
|
80
|
+
'Global options:',
|
|
81
|
+
' --json Print JSON. On by default when stdout is not a terminal.',
|
|
82
|
+
' --profile <name> Use a saved profile instead of the current one.',
|
|
83
|
+
' --api-key <key> Use this key for one command.',
|
|
84
|
+
' --base-url <url> Point at a different API.',
|
|
85
|
+
'',
|
|
86
|
+
'Credentials: --api-key, then AGENTISEND_API_KEY, then the profile.',
|
|
87
|
+
].join('\n');
|
|
88
|
+
}
|
|
89
|
+
const SHELLS = ['bash', 'zsh', 'fish'];
|
|
90
|
+
export function isShell(value) {
|
|
91
|
+
return SHELLS.includes(value);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Completions generated from the same table — a completion that offers a flag
|
|
95
|
+
* the command rejects is worse than none, because it is trusted.
|
|
96
|
+
*/
|
|
97
|
+
export function completionScript(shell) {
|
|
98
|
+
const groups = [...new Set(COMMANDS.map((entry) => entry.name.split(' ')[0]))];
|
|
99
|
+
const subcommands = new Map();
|
|
100
|
+
for (const entry of COMMANDS) {
|
|
101
|
+
const [group, sub] = entry.name.split(' ');
|
|
102
|
+
if (sub)
|
|
103
|
+
subcommands.set(group, [...(subcommands.get(group) ?? []), sub]);
|
|
104
|
+
}
|
|
105
|
+
const allFlags = [...new Set([...GLOBAL_FLAGS, ...COMMANDS.flatMap((entry) => entry.flags)])]
|
|
106
|
+
.map((name) => `--${name}`)
|
|
107
|
+
.join(' ');
|
|
108
|
+
if (shell === 'fish') {
|
|
109
|
+
return [
|
|
110
|
+
'# agentisend completions (fish). Add with: agentisend completions fish | source',
|
|
111
|
+
...groups.map((group) => `complete -c agentisend -n "__fish_use_subcommand" -a "${group}"`),
|
|
112
|
+
...[...subcommands.entries()].map(([group, subs]) => `complete -c agentisend -n "__fish_seen_subcommand_from ${group}" -a "${subs.join(' ')}"`),
|
|
113
|
+
...allFlags
|
|
114
|
+
.split(' ')
|
|
115
|
+
.map((flag) => `complete -c agentisend -l "${flag.slice(2)}"`),
|
|
116
|
+
'',
|
|
117
|
+
].join('\n');
|
|
118
|
+
}
|
|
119
|
+
const caseArms = [...subcommands.entries()]
|
|
120
|
+
.map(([group, subs]) => ` ${group}) words="${subs.join(' ')}" ;;`)
|
|
121
|
+
.join('\n');
|
|
122
|
+
return [
|
|
123
|
+
`# agentisend completions (${shell}). Add with: agentisend completions ${shell} >> ~/.${shell}rc`,
|
|
124
|
+
shell === 'zsh' ? 'autoload -U +X bashcompinit && bashcompinit' : '',
|
|
125
|
+
'_agentisend_complete() {',
|
|
126
|
+
' local cur prev words',
|
|
127
|
+
' cur="${COMP_WORDS[COMP_CWORD]}"',
|
|
128
|
+
' prev="${COMP_WORDS[COMP_CWORD-1]}"',
|
|
129
|
+
` words="${groups.join(' ')}"`,
|
|
130
|
+
' if [ "$COMP_CWORD" -gt 1 ]; then',
|
|
131
|
+
' case "${COMP_WORDS[1]}" in',
|
|
132
|
+
caseArms,
|
|
133
|
+
` *) words="${allFlags}" ;;`,
|
|
134
|
+
' esac',
|
|
135
|
+
' fi',
|
|
136
|
+
' case "$cur" in',
|
|
137
|
+
` -*) words="${allFlags}" ;;`,
|
|
138
|
+
' esac',
|
|
139
|
+
' COMPREPLY=( $(compgen -W "$words" -- "$cur") )',
|
|
140
|
+
'}',
|
|
141
|
+
'complete -F _agentisend_complete agentisend',
|
|
142
|
+
'',
|
|
143
|
+
]
|
|
144
|
+
.filter((line) => line !== '')
|
|
145
|
+
.join('\n');
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAgBA,iCAAiC;AACjC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAE1F,MAAM,CAAC,MAAM,QAAQ,GAAkB;IACrC;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,iFAAiF;QAC1F,KAAK,EAAE;YACL,MAAM;YACN,IAAI;YACJ,IAAI;YACJ,KAAK;YACL,UAAU;YACV,SAAS;YACT,MAAM;YACN,MAAM;YACN,WAAW;YACX,WAAW;YACX,KAAK;YACL,cAAc;YACd,iBAAiB;SAClB;KACF;IACD,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,0BAA0B,EAAE,KAAK,EAAE,EAAE,EAAE;IACtE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,8BAA8B,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;IAClG,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,uCAAuC,EAAE,KAAK,EAAE,EAAE,EAAE;IACtF;QACE,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,qEAAqE;QAC9E,KAAK,EAAE,EAAE;KACV;IACD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,wCAAwC,EAAE,KAAK,EAAE,EAAE,EAAE;IACtF,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,mCAAmC,EAAE,KAAK,EAAE,EAAE,EAAE;IACnF;QACE,IAAI,EAAE,iBAAiB;QACvB,OAAO,EACL,yFAAyF;QAC3F,KAAK,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC;KACvD;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,8DAA8D;QACvE,KAAK,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC;KAC3C;IACD,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE;IACvF;QACE,IAAI,EAAE,eAAe;QACrB,yEAAyE;QACzE,kEAAkE;QAClE,OAAO,EAAE,oFAAoF;QAC7F,KAAK,EAAE,EAAE;KACV;IACD,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,qDAAqD,EAAE,KAAK,EAAE,EAAE,EAAE;IACrG;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,6EAA6E;QACtF,KAAK,EAAE,EAAE;KACV;IACD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,gDAAgD,EAAE,KAAK,EAAE,EAAE,EAAE;IAC9F,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,6BAA6B,EAAE,KAAK,EAAE,EAAE,EAAE;IAC1E;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,4EAA4E;QACrF,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;KAC/B;IACD,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,kDAAkD,EAAE,KAAK,EAAE,EAAE,EAAE;CAChG,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAC9D,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,OAAO;QACL,kCAAkC;QAClC,EAAE;QACF,uCAAuC;QACvC,EAAE;QACF,WAAW;QACX,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7E,EAAE;QACF,iBAAiB;QACjB,6EAA6E;QAC7E,oEAAoE;QACpE,kDAAkD;QAClD,8CAA8C;QAC9C,EAAE;QACF,oEAAoE;KACrE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAU,CAAC;AAGhD,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAQ,MAA4B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC;IAChF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAChD,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,GAAG;YAAE,WAAW,CAAC,GAAG,CAAC,KAAM,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,KAAM,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAC1F,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO;YACL,iFAAiF;YACjF,GAAG,MAAM,CAAC,GAAG,CACX,CAAC,KAAK,EAAE,EAAE,CAAC,yDAAyD,KAAK,GAAG,CAC7E;YACD,GAAG,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAC/B,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAChB,0DAA0D,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAC5F;YACD,GAAG,QAAQ;iBACR,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,8BAA8B,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;YAChE,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC;SACxC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,SAAS,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;SACtE,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,6BAA6B,KAAK,uCAAuC,KAAK,UAAU,KAAK,IAAI;QACjG,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,6CAA6C,CAAC,CAAC,CAAC,EAAE;QACpE,0BAA0B;QAC1B,wBAAwB;QACxB,mCAAmC;QACnC,sCAAsC;QACtC,YAAY,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG;QAC/B,oCAAoC;QACpC,gCAAgC;QAChC,QAAQ;QACR,mBAAmB,QAAQ,MAAM;QACjC,UAAU;QACV,MAAM;QACN,kBAAkB;QAClB,kBAAkB,QAAQ,MAAM;QAChC,QAAQ;QACR,kDAAkD;QAClD,GAAG;QACH,6CAA6C;QAC7C,EAAE;KACH;SACE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;SAC7B,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface Profile {
|
|
2
|
+
api_key: string;
|
|
3
|
+
base_url?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface CliConfig {
|
|
6
|
+
current: string;
|
|
7
|
+
profiles: Record<string, Profile>;
|
|
8
|
+
}
|
|
9
|
+
export declare function configPath(env?: NodeJS.ProcessEnv): string;
|
|
10
|
+
export declare function readConfig(path?: string): CliConfig;
|
|
11
|
+
export declare function writeConfig(config: CliConfig, path?: string): void;
|
|
12
|
+
export interface ResolvedCredentials {
|
|
13
|
+
apiKey: string | null;
|
|
14
|
+
baseUrl: string | undefined;
|
|
15
|
+
/** Where the key came from, for `doctor` to say. */
|
|
16
|
+
source: 'flag' | 'env' | 'profile' | 'none';
|
|
17
|
+
profile: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Precedence: an explicit flag, then the environment, then the named profile.
|
|
21
|
+
* Explicit beats ambient, always — the whole point of typing it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveCredentials(input: {
|
|
24
|
+
apiKeyFlag?: string | undefined;
|
|
25
|
+
baseUrlFlag?: string | undefined;
|
|
26
|
+
profileFlag?: string | undefined;
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
config?: CliConfig;
|
|
29
|
+
}): ResolvedCredentials;
|
|
30
|
+
/** Never print a key. The prefix is enough to tell two keys apart. */
|
|
31
|
+
export declare function keyPreview(apiKey: string): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4.16 — profiles.
|
|
3
|
+
*
|
|
4
|
+
* One machine, several accounts: a test account, a customer's, production.
|
|
5
|
+
* The failure this prevents is the obvious one — sending live mail because the
|
|
6
|
+
* environment still had last week's key in it. A profile names the key AND the
|
|
7
|
+
* base URL together, so switching is one word and cannot half-apply.
|
|
8
|
+
*
|
|
9
|
+
* Keys are stored in a file the CLI creates with 0600, and never printed back:
|
|
10
|
+
* `profile show` prints the prefix only.
|
|
11
|
+
*/
|
|
12
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
const EMPTY = { current: 'default', profiles: {} };
|
|
16
|
+
export function configPath(env = process.env) {
|
|
17
|
+
const override = env['AGENTISEND_CONFIG'];
|
|
18
|
+
if (override)
|
|
19
|
+
return override;
|
|
20
|
+
return join(env['AGENTISEND_CONFIG_HOME'] ?? homedir(), '.agentisend', 'config.json');
|
|
21
|
+
}
|
|
22
|
+
export function readConfig(path = configPath()) {
|
|
23
|
+
if (!existsSync(path))
|
|
24
|
+
return { ...EMPTY, profiles: {} };
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
27
|
+
return {
|
|
28
|
+
current: parsed.current ?? 'default',
|
|
29
|
+
profiles: parsed.profiles ?? {},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// A corrupt config must not make the CLI unusable — it makes it
|
|
34
|
+
// unconfigured, which `doctor` can then explain.
|
|
35
|
+
return { ...EMPTY, profiles: {} };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function writeConfig(config, path = configPath()) {
|
|
39
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
40
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
41
|
+
try {
|
|
42
|
+
chmodSync(path, 0o600);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Some filesystems refuse chmod; the file is still written with the mode
|
|
46
|
+
// above where the platform honours it.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Precedence: an explicit flag, then the environment, then the named profile.
|
|
51
|
+
* Explicit beats ambient, always — the whole point of typing it.
|
|
52
|
+
*/
|
|
53
|
+
export function resolveCredentials(input) {
|
|
54
|
+
const env = input.env ?? process.env;
|
|
55
|
+
const config = input.config ?? readConfig(configPath(env));
|
|
56
|
+
const name = input.profileFlag ?? env['AGENTISEND_PROFILE'] ?? config.current;
|
|
57
|
+
const profile = config.profiles[name];
|
|
58
|
+
const baseUrl = input.baseUrlFlag ?? env['AGENTISEND_BASE_URL'] ?? profile?.base_url;
|
|
59
|
+
if (input.apiKeyFlag)
|
|
60
|
+
return { apiKey: input.apiKeyFlag, baseUrl, source: 'flag', profile: name };
|
|
61
|
+
const fromEnv = env['AGENTISEND_API_KEY'];
|
|
62
|
+
if (fromEnv)
|
|
63
|
+
return { apiKey: fromEnv, baseUrl, source: 'env', profile: name };
|
|
64
|
+
if (profile?.api_key) {
|
|
65
|
+
return { apiKey: profile.api_key, baseUrl, source: 'profile', profile: name };
|
|
66
|
+
}
|
|
67
|
+
return { apiKey: null, baseUrl, source: 'none', profile: name };
|
|
68
|
+
}
|
|
69
|
+
/** Never print a key. The prefix is enough to tell two keys apart. */
|
|
70
|
+
export function keyPreview(apiKey) {
|
|
71
|
+
return `${apiKey.slice(0, 11)}…`;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M4.16 — `agentisend doctor`.
|
|
3
|
+
*
|
|
4
|
+
* The command you run when something is wrong and you do not yet know what.
|
|
5
|
+
* It answers, in order, the questions that actually block a first send: is
|
|
6
|
+
* there a key, does it reach the API, is it paused, is there budget left, is
|
|
7
|
+
* any domain verified. Each check reports pass/fail AND what to do — a
|
|
8
|
+
* diagnostic that says "domains: 0" and nothing else has told you the symptom
|
|
9
|
+
* you already knew.
|
|
10
|
+
*
|
|
11
|
+
* It makes only read calls. `doctor` must be safe to run on production while
|
|
12
|
+
* panicking.
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentiSend } from '../client.js';
|
|
15
|
+
export interface DoctorCheck {
|
|
16
|
+
name: string;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
detail: string;
|
|
19
|
+
fix?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface DoctorReport {
|
|
22
|
+
ok: boolean;
|
|
23
|
+
base_url: string;
|
|
24
|
+
profile: string;
|
|
25
|
+
key_source: string;
|
|
26
|
+
checks: DoctorCheck[];
|
|
27
|
+
}
|
|
28
|
+
export interface DoctorInput {
|
|
29
|
+
client: AgentiSend | null;
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
profile: string;
|
|
32
|
+
keySource: string;
|
|
33
|
+
configPath: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function runDoctor(input: DoctorInput): Promise<DoctorReport>;
|
|
36
|
+
export declare function renderDoctor(report: DoctorReport): string;
|