autark-cli 0.1.4 → 0.1.5

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 +27 -21
  2. package/autark.mjs +298 -37
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,47 +18,53 @@ autark login send your@email.com
18
18
  autark login verify your@email.com --code 123456
19
19
  ```
20
20
 
21
- ## Use
21
+ ## Runbook use
22
22
 
23
23
  Autark is ID-first. Use `product list` / `context` to discover IDs, then pass IDs for writes. `slug/H01` remains a convenience alias.
24
24
 
25
25
  ```sh
26
- # products
27
26
  autark product upsert --slug chrome-relay --name "Chrome Relay" --tagline "..."
28
27
  autark product list # prints slug, visibility, id, name
29
28
  autark context chrome-relay
30
29
 
31
- autark context --product-id <product_id>
32
-
33
- # hypotheses
34
30
  autark hypothesis create --product-id <product_id> --md @./H01.md --code H01 --title "..."
35
- autark context --hypothesis-id <hypothesis_id>
36
- autark hypothesis status --hypothesis-id <hypothesis_id> --status active # active|inactive|dead
37
-
38
- # aliases still work
39
- autark hypothesis create --product chrome-relay --md @./H01.md --code H01
40
- autark context chrome-relay/H01
41
- autark hypothesis status chrome-relay/H01 --status inactive
42
-
43
- # runs
44
31
  autark run start --hypothesis-id <hypothesis_id>
32
+ autark log action --run-id <run_id> --channel github --title "..." --url https://github.com/...
45
33
  autark run finish --run-id <run_id> --narrative @./narrative.md
34
+ ```
46
35
 
47
- # actions (anything the agent did during a run)
48
- autark log action --run-id <run_id> --channel email --title "..." \
49
- --recipient person@example.com --agentmail-thread-id <thread_id>
50
- autark log action --run-id <run_id> --channel github --title "..." --url https://github.com/...
36
+ ## Mail use
51
37
 
52
- autark me
53
- autark logout
38
+ `autark mail` covers the AgentMail surface Autark needs. It does not require a separate AgentMail CLI login.
39
+
40
+ ```sh
41
+ autark mail setup --prefix laksh
42
+
43
+ autark mail send --run-id <run_id> \
44
+ --to person@example.com \
45
+ --subject "Subject" \
46
+ --text @./body.txt
47
+
48
+ autark mail reply --run-id <run_id> --message-id <message_id> --text @./reply.txt
49
+
50
+ autark mail threads --limit 20
51
+ autark mail thread <thread_id>
52
+ autark mail messages --limit 20
53
+ autark mail message <message_id>
54
+ autark mail raw <message_id>
55
+ autark mail attachment --message-id <message_id> --attachment-id <attachment_id> --out file.bin
54
56
  ```
55
57
 
58
+ Mail sends/replies call AgentMail directly with the user's inbox-scoped key from `~/.autark/credentials.json`, then log an Autark `email` action containing `agentmail_thread_id` and `agentmail_inbox_id`.
59
+
56
60
  ## ENV
57
61
 
58
62
  - `AUTARK_API_URL` — override the default Worker URL (`https://autark-api.kushalsokke.workers.dev`)
63
+ - `AGENTMAIL_API_URL` — override AgentMail API base
64
+ - `AGENTMAIL_API_KEY`, `AGENTMAIL_EMAIL`, `AGENTMAIL_INBOX_ID` — override local mail credentials for debugging
59
65
 
60
66
  ## Architecture
61
67
 
62
68
  The CLI is a thin HTTP client over a Cloudflare Worker that holds the InstantDB admin token. Magic-link auth via InstantDB. Token saved to `~/.autark/credentials.json` after login.
63
69
 
64
- The web at autark.kushalsm.com reads from InstantDB. Writes should go through the Worker/CLI path; dashboard direct writes are being phased out.
70
+ The Worker uses the AgentMail org key only for provisioning and dashboard thread reads. Local mail send/reply uses the user's inbox-scoped key.
package/autark.mjs CHANGED
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // autark CLI — thin HTTP client over the autark-api Worker.
3
3
  //
4
- // Auth: magic-link flow saves a refresh_token to ~/.autark/credentials.json.
5
- // Every subsequent command sends it as `Authorization: Bearer <token>`.
6
- //
7
- // The CLI is ID-first: once a product/hypothesis/run exists, use its id.
8
- // slug/Hxx remains as a convenience alias for humans.
4
+ // The runbook API is ID-first. slug/Hxx remains as a convenience alias.
5
+ // Mail commands call AgentMail directly with the user's inbox-scoped key;
6
+ // autark-api uses the org key only for provisioning and dashboard thread reads.
9
7
 
10
8
  import fs from 'node:fs'
11
9
  import os from 'node:os'
@@ -13,6 +11,7 @@ import path from 'node:path'
13
11
  import process from 'node:process'
14
12
 
15
13
  const API = process.env.AUTARK_API_URL || 'https://autark-api.kushalsokke.workers.dev'
14
+ const AGENTMAIL_API = process.env.AGENTMAIL_API_URL || 'https://api.agentmail.to/v0'
16
15
  const CREDS_PATH = path.join(os.homedir(), '.autark', 'credentials.json')
17
16
 
18
17
  const args = process.argv.slice(2)
@@ -47,6 +46,7 @@ async function main() {
47
46
  }
48
47
  if (group === 'log' && command === 'action') return logAction(rest)
49
48
  if (group === 'context') return context([command, ...rest].filter(Boolean))
49
+ if (group === 'mail') return mail(command, rest)
50
50
 
51
51
  usage()
52
52
  process.exit(1)
@@ -69,6 +69,7 @@ async function loginVerify(rest) {
69
69
  const result = await api('POST', '/v1/auth/login/verify', { email, code }, { auth: false })
70
70
  if (!result.token) throw new Error('verify succeeded but no token returned')
71
71
  saveCredentials({
72
+ ...loadCredentials(),
72
73
  user_id: result.user.id,
73
74
  email: result.user.email,
74
75
  token: result.token,
@@ -194,15 +195,235 @@ async function logAction(rest) {
194
195
  const result = await api('POST', `/v1/runs/${encodeURIComponent(run)}/actions`, {
195
196
  channel,
196
197
  title,
197
- url: opts.url || undefined,
198
- agentmail_thread_id: opts['agentmail-thread-id'] || opts['thread-id'] || opts.thread_id || undefined,
199
- recipient: opts.recipient || undefined,
200
- metadata: opts.metadata ? JSON.parse(readValue(opts.metadata)) : undefined,
201
- occurred_at: opts['occurred-at'] || opts.occurred_at || undefined,
198
+ url: opts.url || undefined,
199
+ agentmail_thread_id: opts['agentmail-thread-id'] || opts['thread-id'] || opts.thread_id || undefined,
200
+ agentmail_inbox_id: opts['agentmail-inbox-id'] || opts.inbox_id || opts.agentmail_inbox_id || undefined,
201
+ recipient: opts.recipient || undefined,
202
+ metadata: opts.metadata ? JSON.parse(readValue(opts.metadata)) : undefined,
203
+ occurred_at: opts['occurred-at'] || opts.occurred_at || undefined,
202
204
  })
203
205
  console.log(result.id)
204
206
  }
205
207
 
208
+ // ============================================================ mail
209
+
210
+ async function mail(command, rest) {
211
+ if (!command || command === '--help' || command === 'help') return mailUsage()
212
+ if (command === 'setup') return mailSetup(rest)
213
+ if (command === 'send') return mailSend(rest)
214
+ if (command === 'reply') return mailReply(rest, 'reply')
215
+ if (command === 'reply-all') return mailReply(rest, 'reply-all')
216
+ if (command === 'forward') return mailForward(rest)
217
+ if (command === 'threads') return mailThreads(rest)
218
+ if (command === 'thread') return mailThread(rest)
219
+ if (command === 'messages') return mailMessages(rest)
220
+ if (command === 'message') return mailMessage(rest)
221
+ if (command === 'raw') return mailRaw(rest)
222
+ if (command === 'attachment') return mailAttachment(rest)
223
+ if (command === 'request') return mailRequest(rest)
224
+ mailUsage()
225
+ process.exit(1)
226
+ }
227
+
228
+ async function mailSetup(rest) {
229
+ const opts = parseArgs(rest)
230
+ const existing = loadCredentials() || {}
231
+ if (existing.agentmail_token && existing.agentmail_email && !opts.force) {
232
+ console.log(existing.agentmail_email)
233
+ return
234
+ }
235
+ const prefix = required(opts.prefix || defaultInboxPrefix(existing.email), '--prefix')
236
+ const result = await api('POST', '/v1/onboard/agentmail', {
237
+ prefix,
238
+ display_name: opts['display-name'] || opts.display_name || existing.email || prefix,
239
+ purpose: opts.purpose || 'runner',
240
+ })
241
+ saveCredentials({
242
+ ...existing,
243
+ agentmail_email: result.agentmail_email || result.email,
244
+ agentmail_inbox_id: result.agentmail_inbox_id || result.inbox_id || result.email,
245
+ agentmail_api_key_id: result.agentmail_api_key_id || result.api_key_id,
246
+ agentmail_token: result.agentmail_token,
247
+ agentmail_saved_at: new Date().toISOString(),
248
+ })
249
+ console.log(result.agentmail_email || result.email)
250
+ }
251
+
252
+ async function mailSend(rest) {
253
+ const opts = parseArgs(rest)
254
+ const creds = requireAgentmailCredentials()
255
+ const to = listOpt(opts.to, '--to')
256
+ const subject = opts.subject || ''
257
+ const body = mailBody(opts, { to, subject })
258
+ const result = await agentmailRequest('POST', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/send`, body)
259
+ const action = await maybeLogMailAction(opts, {
260
+ kind: 'send',
261
+ defaultTitle: opts.title || `Email: ${subject || to.join(', ')}`,
262
+ recipient: to.join(','),
263
+ subject,
264
+ response: result,
265
+ })
266
+ printJson({ ...result, autark_action_id: action?.id })
267
+ }
268
+
269
+ async function mailReply(rest, mode) {
270
+ const opts = parseArgs(rest)
271
+ const creds = requireAgentmailCredentials()
272
+ const messageId = required(opts['message-id'] || opts.message_id || opts._[0], '--message-id')
273
+ const body = mailBody(opts)
274
+ const endpoint = mode === 'reply-all' ? 'reply-all' : 'reply'
275
+ const result = await agentmailRequest('POST', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/${encodeURIComponent(messageId)}/${endpoint}`, body)
276
+ const action = await maybeLogMailAction(opts, {
277
+ kind: mode,
278
+ defaultTitle: opts.title || `${mode === 'reply-all' ? 'Reply-all' : 'Reply'}: ${messageId}`,
279
+ recipient: listOpt(opts.to).join(','),
280
+ response: result,
281
+ metadata: { message_id: messageId },
282
+ })
283
+ printJson({ ...result, autark_action_id: action?.id })
284
+ }
285
+
286
+ async function mailForward(rest) {
287
+ const opts = parseArgs(rest)
288
+ const creds = requireAgentmailCredentials()
289
+ const messageId = required(opts['message-id'] || opts.message_id || opts._[0], '--message-id')
290
+ const to = listOpt(opts.to, '--to')
291
+ const body = mailBody(opts, { to, subject: opts.subject || '' })
292
+ const result = await agentmailRequest('POST', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/${encodeURIComponent(messageId)}/forward`, body)
293
+ const action = await maybeLogMailAction(opts, {
294
+ kind: 'forward',
295
+ defaultTitle: opts.title || `Forward: ${opts.subject || messageId}`,
296
+ recipient: to.join(','),
297
+ response: result,
298
+ metadata: { message_id: messageId },
299
+ })
300
+ printJson({ ...result, autark_action_id: action?.id })
301
+ }
302
+
303
+ async function mailThreads(rest) {
304
+ const opts = parseArgs(rest)
305
+ const creds = requireAgentmailCredentials()
306
+ const qs = queryString({ limit: opts.limit, page_token: opts['page-token'] || opts.page_token })
307
+ printJson(await agentmailRequest('GET', `/inboxes/${encodeURIComponent(creds.inboxId)}/threads${qs}`))
308
+ }
309
+
310
+ async function mailThread(rest) {
311
+ const opts = parseArgs(rest)
312
+ const creds = requireAgentmailCredentials()
313
+ const threadId = required(opts['thread-id'] || opts.thread_id || opts._[0], 'thread_id')
314
+ printJson(await agentmailRequest('GET', `/inboxes/${encodeURIComponent(creds.inboxId)}/threads/${encodeURIComponent(threadId)}`))
315
+ }
316
+
317
+ async function mailMessages(rest) {
318
+ const opts = parseArgs(rest)
319
+ const creds = requireAgentmailCredentials()
320
+ const qs = queryString({ limit: opts.limit, page_token: opts['page-token'] || opts.page_token })
321
+ printJson(await agentmailRequest('GET', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages${qs}`))
322
+ }
323
+
324
+ async function mailMessage(rest) {
325
+ const opts = parseArgs(rest)
326
+ const creds = requireAgentmailCredentials()
327
+ const messageId = required(opts['message-id'] || opts.message_id || opts._[0], 'message_id')
328
+ printJson(await agentmailRequest('GET', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/${encodeURIComponent(messageId)}`))
329
+ }
330
+
331
+ async function mailRaw(rest) {
332
+ const opts = parseArgs(rest)
333
+ const creds = requireAgentmailCredentials()
334
+ const messageId = required(opts['message-id'] || opts.message_id || opts._[0], 'message_id')
335
+ const raw = await agentmailRequest('GET', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/${encodeURIComponent(messageId)}/raw`, undefined, { parseJson: false })
336
+ process.stdout.write(raw)
337
+ }
338
+
339
+ async function mailAttachment(rest) {
340
+ const opts = parseArgs(rest)
341
+ const creds = requireAgentmailCredentials()
342
+ const messageId = required(opts['message-id'] || opts.message_id, '--message-id')
343
+ const attachmentId = required(opts['attachment-id'] || opts.attachment_id, '--attachment-id')
344
+ const body = await agentmailRequest('GET', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}`, undefined, { parseJson: false })
345
+ if (opts.out) fs.writeFileSync(opts.out, body)
346
+ else process.stdout.write(body)
347
+ }
348
+
349
+ async function mailRequest(rest) {
350
+ const opts = parseArgs(rest)
351
+ const method = (opts.method || opts._[0] || 'GET').toUpperCase()
352
+ const requestPath = required(opts.path || opts._[1], '--path')
353
+ const body = opts.body ? JSON.parse(readValue(opts.body)) : undefined
354
+ const parseJson = opts.raw ? false : true
355
+ const result = await agentmailRequest(method, requestPath.startsWith('/') ? requestPath : `/${requestPath}`, body, { parseJson })
356
+ if (parseJson) printJson(result)
357
+ else process.stdout.write(result)
358
+ }
359
+
360
+ function mailBody(opts, base = {}) {
361
+ const body = { ...base }
362
+ if (opts.text !== undefined) body.text = readValue(opts.text)
363
+ if (opts.html !== undefined) body.html = readValue(opts.html)
364
+ if (opts.subject !== undefined) body.subject = opts.subject
365
+ for (const key of ['cc', 'bcc', 'reply-to', 'label', 'attachment']) {
366
+ const snake = key.replaceAll('-', '_')
367
+ const value = opts[key] ?? opts[snake]
368
+ if (value !== undefined) body[snake] = listOpt(value)
369
+ }
370
+ if (opts.headers !== undefined) body.headers = JSON.parse(readValue(opts.headers))
371
+ return body
372
+ }
373
+
374
+ async function maybeLogMailAction(opts, { kind, defaultTitle, recipient, subject, response, metadata = {} }) {
375
+ const runId = opts['run-id'] || opts.run_id || opts.run
376
+ if (!runId) return null
377
+ const creds = requireAgentmailCredentials()
378
+ const threadId = response?.thread_id
379
+ if (!threadId) throw new Error('AgentMail response missing thread_id; refusing to log email action')
380
+ return api('POST', `/v1/runs/${encodeURIComponent(runId)}/actions`, {
381
+ channel: 'email',
382
+ title: opts.title || defaultTitle,
383
+ recipient: recipient || undefined,
384
+ agentmail_thread_id: threadId,
385
+ agentmail_inbox_id: creds.inboxId,
386
+ metadata: compact({
387
+ kind,
388
+ subject,
389
+ message_id: response?.message_id,
390
+ ...metadata,
391
+ }),
392
+ })
393
+ }
394
+
395
+ async function agentmailRequest(method, pathStr, body, { parseJson = true } = {}) {
396
+ const creds = requireAgentmailCredentials()
397
+ const res = await fetch(AGENTMAIL_API + pathStr, {
398
+ method,
399
+ headers: {
400
+ authorization: `Bearer ${creds.token}`,
401
+ 'content-type': 'application/json',
402
+ },
403
+ body: body === undefined ? undefined : JSON.stringify(body),
404
+ })
405
+ const text = await res.text()
406
+ let data = text
407
+ if (parseJson) {
408
+ try { data = text ? JSON.parse(text) : null } catch { data = { raw: text } }
409
+ }
410
+ if (!res.ok) {
411
+ const msg = parseJson ? (data?.error || data?.message || JSON.stringify(data)) : text
412
+ throw new Error(`AgentMail ${res.status}: ${msg}`)
413
+ }
414
+ return data
415
+ }
416
+
417
+ function requireAgentmailCredentials() {
418
+ const creds = loadCredentials()
419
+ const token = process.env.AGENTMAIL_API_KEY || creds?.agentmail_token
420
+ const email = process.env.AGENTMAIL_EMAIL || creds?.agentmail_email
421
+ const inboxId = process.env.AGENTMAIL_INBOX_ID || creds?.agentmail_inbox_id || email
422
+ if (!token) throw new Error('missing agentmail_token. run: autark mail setup --prefix <name>')
423
+ if (!inboxId) throw new Error('missing agentmail_inbox_id. run: autark mail setup --prefix <name>')
424
+ return { token, email, inboxId }
425
+ }
426
+
206
427
  // ============================================================ context
207
428
 
208
429
  async function context(rest) {
@@ -223,12 +444,8 @@ async function context(rest) {
223
444
  const productSlug = parts[0]
224
445
  const code = parts[1]
225
446
 
226
- if (!code) {
227
- return printProductContext(await api('GET', `/v1/context/${encodeURIComponent(productSlug)}`))
228
- }
229
-
230
- const result = await api('GET', `/v1/context/${encodeURIComponent(productSlug)}/${encodeURIComponent(code)}`)
231
- return printHypothesisContext(result)
447
+ if (!code) return printProductContext(await api('GET', `/v1/context/${encodeURIComponent(productSlug)}`))
448
+ return printHypothesisContext(await api('GET', `/v1/context/${encodeURIComponent(productSlug)}/${encodeURIComponent(code)}`))
232
449
  }
233
450
 
234
451
  function printProductContext(r) {
@@ -264,9 +481,7 @@ function printHypothesisContext(result) {
264
481
  console.log(` [${a.channel}] ${a.title}${pointer ? ` → ${pointer}` : ''}`)
265
482
  }
266
483
  }
267
- if (run.narrative_md) {
268
- console.log(`\n${run.narrative_md}`)
269
- }
484
+ if (run.narrative_md) console.log(`\n${run.narrative_md}`)
270
485
  }
271
486
  }
272
487
 
@@ -301,14 +516,10 @@ function saveCredentials(creds) {
301
516
 
302
517
  function loadCredentials() {
303
518
  if (!fs.existsSync(CREDS_PATH)) return null
304
- try {
305
- return JSON.parse(fs.readFileSync(CREDS_PATH, 'utf8'))
306
- } catch {
307
- return null
308
- }
519
+ try { return JSON.parse(fs.readFileSync(CREDS_PATH, 'utf8')) } catch { return null }
309
520
  }
310
521
 
311
- // ============================================================ arg parsing
522
+ // ============================================================ utilities
312
523
 
313
524
  function parseArgs(list) {
314
525
  const opts = { _: [] }
@@ -320,12 +531,11 @@ function parseArgs(list) {
320
531
  }
321
532
  const key = arg.slice(2)
322
533
  const next = list[i + 1]
323
- if (!next || next.startsWith('--')) {
324
- opts[key] = true
325
- } else {
326
- opts[key] = next
327
- i++
328
- }
534
+ const value = (!next || next.startsWith('--')) ? true : next
535
+ if (value !== true) i++
536
+ if (opts[key] === undefined) opts[key] = value
537
+ else if (Array.isArray(opts[key])) opts[key].push(value)
538
+ else opts[key] = [opts[key], value]
329
539
  }
330
540
  return opts
331
541
  }
@@ -338,12 +548,43 @@ function required(value, label) {
338
548
  }
339
549
 
340
550
  function readValue(value) {
341
- if (typeof value === 'string' && value.startsWith('@')) {
342
- return fs.readFileSync(value.slice(1), 'utf8')
343
- }
551
+ if (typeof value === 'string' && value.startsWith('@')) return fs.readFileSync(value.slice(1), 'utf8')
344
552
  return String(value)
345
553
  }
346
554
 
555
+ function listOpt(value, label) {
556
+ if (value === undefined || value === null || value === '') {
557
+ if (label) throw new Error(`${label} is required`)
558
+ return []
559
+ }
560
+ const values = Array.isArray(value) ? value : [value]
561
+ return values.flatMap(v => String(v).split(',')).map(v => v.trim()).filter(Boolean)
562
+ }
563
+
564
+ function queryString(params) {
565
+ const q = new URLSearchParams()
566
+ for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== null && v !== '') q.set(k, v)
567
+ const s = q.toString()
568
+ return s ? `?${s}` : ''
569
+ }
570
+
571
+ function defaultInboxPrefix(email) {
572
+ if (!email) return ''
573
+ return String(email).split('@')[0].toLowerCase().replace(/[^a-z0-9._-]/g, '').replace(/^[._-]+/, '').slice(0, 30)
574
+ }
575
+
576
+ function compact(obj) {
577
+ const out = {}
578
+ for (const [k, v] of Object.entries(obj || {})) {
579
+ if (v !== undefined && v !== null && v !== '' && !(Array.isArray(v) && !v.length)) out[k] = v
580
+ }
581
+ return out
582
+ }
583
+
584
+ function printJson(value) {
585
+ console.log(JSON.stringify(value, null, 2))
586
+ }
587
+
347
588
  function splitHypothesisRef(ref) {
348
589
  const m = String(ref).match(/^([\w.-]+)\/(H\d{2})$/)
349
590
  if (!m) throw new Error(`expected product/Hxx, got ${ref}`)
@@ -353,8 +594,26 @@ function splitHypothesisRef(ref) {
353
594
  // ============================================================ usage
354
595
 
355
596
  function loginUsage() {
356
- console.log(`autark login send <email>
357
- autark login verify <email> --code <6-digit-code>`)
597
+ console.log(`autark login send <email>\nautark login verify <email> --code <6-digit-code>`)
598
+ }
599
+
600
+ function mailUsage() {
601
+ console.log(`autark mail
602
+
603
+ setup --prefix <name> [--force]
604
+ send --to <email[,email]> --subject <s> --text @body.txt [--run-id <id>]
605
+ reply --message-id <id> --text @reply.txt [--run-id <id>]
606
+ reply-all --message-id <id> --text @reply.txt [--run-id <id>]
607
+ forward --message-id <id> --to <email> [--text @body.txt] [--run-id <id>]
608
+ threads [--limit N]
609
+ thread <thread_id>
610
+ messages [--limit N]
611
+ message <message_id>
612
+ raw <message_id>
613
+ attachment --message-id <id> --attachment-id <id> [--out file]
614
+ request <METHOD> <path> [--body @json] [--raw]
615
+
616
+ Mail uses the inbox-scoped token in ~/.autark/credentials.json.`)
358
617
  }
359
618
 
360
619
  function usage() {
@@ -378,9 +637,11 @@ function usage() {
378
637
  autark run finish --run-id <id> --narrative @./run.md
379
638
 
380
639
  autark log action --run-id <id> --channel <c> --title <t> [--url <u>]
381
- [--agentmail-thread-id <uuid>] [--recipient <email>]
640
+ [--agentmail-thread-id <uuid>] [--agentmail-inbox-id <inbox>] [--recipient <email>]
382
641
  [--metadata @./meta.json]
383
642
 
643
+ autark mail setup/send/reply/thread/messages/... AgentMail via autark credentials
644
+
384
645
  autark context --product-id <id>
385
646
  autark context --hypothesis-id <id>
386
647
  autark context <slug>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autark-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for autark — hypothesis-driven product runbooks. Track products, hypotheses, runs, and actions from the terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",