autark-cli 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/autark.mjs +41 -0
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # autark-cli
2
2
 
3
- CLI for [autark](https://autark.kushalsm.com) — hypothesis-driven product runbooks.
3
+ CLI for [autark](https://autark.sh) — hypothesis-driven product runbooks.
4
4
 
5
5
  A product has hypotheses. Each hypothesis has runs. Each run logs actions (emails sent, posts made, replies received). The dashboard shows it all.
6
6
 
package/autark.mjs CHANGED
@@ -27,6 +27,37 @@ const SKILL_NAMES = ['autark', 'plumcake', 'chrome-relay', 'email', 'outreach',
27
27
  const ECOSYSTEM_CLIS = ['autark-cli', 'plumcake-cli', 'chrome-relay']
28
28
  const PROGRAM_FILES = ['start.md', 'double-down.md', 'check.md', 'followup.md']
29
29
 
30
+ // ============================================================ address reject
31
+ // Two address shapes are bad enough to refuse at the CLI boundary, regardless
32
+ // of what the agent prompted: RFC 2606 reserved domains (test@example.com — pure
33
+ // bounce, hurts SES rep) and role-accounts (sales@, info@, hello@ — almost never
34
+ // reach a human, waste of a thread). Reject mode is hard — surfaces an error so
35
+ // the operator sees and the agent picks a different target. Lives up here, not
36
+ // next to mailSend, because mailSend gets invoked via main() synchronously and
37
+ // would TDZ on the Sets if they were declared after main()'s call site.
38
+ const RFC2606_DOMAINS = new Set(['example.com', 'example.net', 'example.org', 'example', 'test', 'invalid', 'localhost'])
39
+ const PLACEHOLDER_USERS = new Set(['test', 'foo', 'bar', 'x', 'y', 'sample', 'placeholder'])
40
+ const ROLE_ACCOUNT_USERS = new Set([
41
+ 'sales', 'info', 'hello', 'team', 'contact', 'support', 'press', 'marketing',
42
+ 'admin', 'billing', 'careers', 'jobs', 'webmaster', 'postmaster', 'help',
43
+ 'office', 'media', 'partners', 'enquiries', 'inquiries', 'general',
44
+ 'noreply', 'no-reply', 'donotreply', 'do-not-reply',
45
+ ])
46
+
47
+ function rejectUnsendableAddress(addresses) {
48
+ for (const raw of addresses || []) {
49
+ const addr = String(raw || '').trim().toLowerCase()
50
+ if (!addr) continue
51
+ const [user, domain] = addr.split('@')
52
+ if (!user || !domain) throw new Error(`malformed address: ${raw}`)
53
+ if (RFC2606_DOMAINS.has(domain)) throw new Error(`refusing to send to RFC2606 reserved domain: ${raw}`)
54
+ if (PLACEHOLDER_USERS.has(user)) throw new Error(`refusing to send to placeholder address: ${raw}`)
55
+ if (ROLE_ACCOUNT_USERS.has(user)) {
56
+ throw new Error(`refusing to send to role-account: ${raw}. Find a named person, or skip this target — role accounts almost never reply.`)
57
+ }
58
+ }
59
+ }
60
+
30
61
  const args = process.argv.slice(2)
31
62
  main()
32
63
  .then(() => maybeNudge())
@@ -805,6 +836,10 @@ function deliverabilityUsage() {
805
836
  bounces don't tank your domain rep.`)
806
837
  }
807
838
 
839
+ // rejectUnsendableAddress is defined near the top of the file (before main()
840
+ // runs) so the const Sets it depends on are out of TDZ when mailSend invokes
841
+ // it. See block tagged "address reject" up top.
842
+
808
843
  // ============================================================ mail
809
844
 
810
845
  async function mail(command, rest) {
@@ -853,8 +888,14 @@ async function mailSend(rest) {
853
888
  const opts = parseArgs(rest)
854
889
  const creds = requireAgentmailCredentials()
855
890
  const to = listOpt(opts.to, '--to')
891
+ rejectUnsendableAddress(to)
856
892
  const subject = opts.subject || ''
857
893
  const body = mailBody(opts, { to, subject })
894
+ const dryRun = opts['dry-run'] === true || opts.dry_run === true
895
+ if (dryRun) {
896
+ printJson({ dry_run: true, would_send: { inbox: creds.inboxId, to, subject, body } })
897
+ return
898
+ }
858
899
  const result = await agentmailRequest('POST', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/send`, body)
859
900
  const action = await maybeLogMailAction(opts, {
860
901
  kind: 'send',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autark-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "CLI for autark \u2014 hypothesis-driven product runbooks. Track products, hypotheses, runs, and actions from the terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -11,7 +11,7 @@
11
11
  "autark.mjs",
12
12
  "README.md"
13
13
  ],
14
- "homepage": "https://autark.kushalsm.com",
14
+ "homepage": "https://autark.sh",
15
15
  "repository": {
16
16
  "type": "git",
17
17
  "url": "git+https://github.com/kiluazen/autark.git"