samoagent 0.4.0 → 0.4.1

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 CHANGED
@@ -14,13 +14,15 @@ Requirements:
14
14
  - `RECALL_API_KEY`.
15
15
  - `ngrok` installed and authenticated (free plan). `join` starts and manages ngrok automatically — you don't run it yourself.
16
16
 
17
+ Install the CLI from npm:
18
+
17
19
  ```bash
18
- bun install
20
+ npm install -g samoagent
19
21
  export RECALL_API_KEY=...
20
- bun run build
22
+ samoagent join "https://meet.google.com/..." --name Leo
21
23
  ```
22
24
 
23
- During development use `bun run samoagent ...`. After build or package install, use `samoagent ...`.
25
+ During development use `bun install`, `bun run build`, then `bun run samoagent ...`.
24
26
 
25
27
  ## What It Provides
26
28
 
@@ -35,6 +37,7 @@ samoagent gives an AI agent a small set of meeting tools:
35
37
  - `transcript` - print the transcript (local file, or post-call from Recall).
36
38
  - `screenshot` - capture the local Mac screen (fallback when no call frame is available).
37
39
  - `dicts` - list available Deepgram keyword dictionaries.
40
+ - `doctor` - check local prerequisites before joining a call.
38
41
 
39
42
  The agent still decides what to say, when to inspect a frame, and how to use the meeting context. samoagent is the local adapter that exposes those call capabilities.
40
43
 
package/dist/cli.js CHANGED
@@ -36,7 +36,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
36
36
  var require_package = __commonJS((exports, module) => {
37
37
  module.exports = {
38
38
  name: "samoagent",
39
- version: "0.4.0",
39
+ version: "0.4.1",
40
40
  description: "Let AI agents join Zoom and Google Meet calls as active participants.",
41
41
  type: "module",
42
42
  license: "Apache-2.0",
@@ -48,6 +48,7 @@ var require_package = __commonJS((exports, module) => {
48
48
  "dictionaries/",
49
49
  "LICENSE",
50
50
  "README.md",
51
+ "docs/",
51
52
  "avatar.html",
52
53
  "avatar.png",
53
54
  "logo.svg"
@@ -93,7 +94,7 @@ import { homedir } from "os";
93
94
  import { join, dirname } from "path";
94
95
  import { fileURLToPath } from "url";
95
96
  var RECALL_BASE = "https://us-east-1.recall.ai/api/v1";
96
- var AVATAR_URL = "https://nikolays.github.io/samoagent/avatar.html";
97
+ var AVATAR_URL = "https://samoagent.dev/avatar.html";
97
98
 
98
99
  class ExitError extends Error {
99
100
  code;
@@ -1437,13 +1438,81 @@ async function cmdServe(args) {
1437
1438
  await new Promise(() => {});
1438
1439
  }
1439
1440
 
1441
+ // src/commands/doctor.ts
1442
+ import { existsSync as existsSync10 } from "fs";
1443
+ function commandVersion(command, args = ["--version"]) {
1444
+ try {
1445
+ const proc = Bun.spawnSync([command, ...args]);
1446
+ if (proc.exitCode !== 0) {
1447
+ const stderr = new TextDecoder().decode(proc.stderr).trim();
1448
+ return {
1449
+ ok: false,
1450
+ detail: stderr || `${command} --version exited ${proc.exitCode}`
1451
+ };
1452
+ }
1453
+ const stdout = new TextDecoder().decode(proc.stdout).trim();
1454
+ return {
1455
+ ok: true,
1456
+ detail: stdout.split(/\r?\n/)[0] ?? ""
1457
+ };
1458
+ } catch (e) {
1459
+ return {
1460
+ ok: false,
1461
+ detail: e instanceof Error ? e.message : String(e)
1462
+ };
1463
+ }
1464
+ }
1465
+ async function cmdDoctor() {
1466
+ const bunVersion = commandVersion("bun");
1467
+ const ngrokVersion = commandVersion("ngrok");
1468
+ const ffmpegVersion = commandVersion("ffmpeg", ["-version"]);
1469
+ const checks = [
1470
+ {
1471
+ name: "Bun",
1472
+ ok: bunVersion.ok,
1473
+ detail: bunVersion.detail || "not found in PATH"
1474
+ },
1475
+ {
1476
+ name: "RECALL_API_KEY",
1477
+ ok: Boolean(process.env.RECALL_API_KEY),
1478
+ detail: process.env.RECALL_API_KEY ? "set" : "missing"
1479
+ },
1480
+ {
1481
+ name: "ngrok",
1482
+ ok: ngrokVersion.ok,
1483
+ detail: ngrokVersion.detail || "not found in PATH"
1484
+ },
1485
+ {
1486
+ name: "ffmpeg",
1487
+ ok: ffmpegVersion.ok,
1488
+ detail: ffmpegVersion.detail || "not found in PATH"
1489
+ },
1490
+ {
1491
+ name: "state",
1492
+ ok: true,
1493
+ detail: existsSync10(stateFile()) ? `active state at ${stateFile()}` : "no active bot state"
1494
+ }
1495
+ ];
1496
+ process.stdout.write(`samoagent doctor
1497
+
1498
+ `);
1499
+ for (const check of checks) {
1500
+ process.stdout.write(`${check.ok ? "OK" : "FAIL"} ${check.name}: ${check.detail}
1501
+ `);
1502
+ }
1503
+ if (checks.some((check) => !check.ok)) {
1504
+ process.exit(1);
1505
+ }
1506
+ }
1507
+
1440
1508
  // src/cli.ts
1441
1509
  var USAGE = `usage: samoagent <command> [options]
1442
1510
 
1443
- Meeting I/O helper for AI agents \u2014 join Zoom & Google Meet calls,
1444
- stream live transcripts, capture frames, and send chat messages.
1511
+ Put your AI agent in Zoom and Google Meet calls.
1512
+ samoagent joins through Recall.ai, streams live transcript lines,
1513
+ captures call frames on demand, and sends explicit chat messages.
1445
1514
 
1446
- Requires: RECALL_API_KEY env var (get one at recall.ai) and ngrok.
1515
+ Requires: Bun, RECALL_API_KEY env var (get one at recall.ai), and ngrok.
1447
1516
 
1448
1517
  commands:
1449
1518
  join <url> [--name N] [--dict D] [--port P] [--transcript-dir DIR] [--rtmp-url URL] [--rtmp] [--no-ws-video] [--frame-dir DIR]
@@ -1455,11 +1524,52 @@ commands:
1455
1524
  dicts
1456
1525
  watch
1457
1526
  frame [--out FILE] [--archive] [bot_id]
1527
+ doctor
1458
1528
 
1459
1529
  flags:
1460
1530
  -h, --help Show this help message
1461
1531
  -v, --version Show version number
1462
1532
  `;
1533
+ var COMMAND_HELP = {
1534
+ join: `usage: samoagent join <url> [options]
1535
+
1536
+ Join a Zoom or Google Meet call as a Recall.ai bot.
1537
+ By default, samoagent streams transcript events and receives call frames over WebSocket.
1538
+
1539
+ options:
1540
+ --name N Bot display name
1541
+ --dict D Deepgram keyword dictionary name
1542
+ --port P Local callback server port (default: 8080)
1543
+ --transcript-dir DIR Directory for transcript.txt
1544
+ --frame-dir DIR Directory for on-demand frame output
1545
+ --no-ws-video Disable WebSocket call-frame capture
1546
+ --rtmp Use local RTMP path through ngrok TCP
1547
+ --rtmp-url URL Use an existing RTMP endpoint
1548
+
1549
+ examples:
1550
+ samoagent join "https://meet.google.com/abc-defg-hij" --name Leo
1551
+ samoagent join "https://zoom.us/j/123" --dict postgresfm
1552
+ `,
1553
+ frame: `usage: samoagent frame [--out FILE] [--archive] [bot_id]
1554
+
1555
+ Write the latest call frame to disk.
1556
+ With the default WebSocket path, frames stay in memory until this command is run.
1557
+
1558
+ options:
1559
+ --out FILE Output path. Defaults to latest frame path from active state.
1560
+ --archive Also write a timestamped PNG+JSON archive copy.
1561
+
1562
+ examples:
1563
+ samoagent frame
1564
+ samoagent frame --out /tmp/current-call.png
1565
+ samoagent frame --archive
1566
+ `,
1567
+ doctor: `usage: samoagent doctor
1568
+
1569
+ Check local prerequisites for joining meetings:
1570
+ Bun, RECALL_API_KEY, ngrok, ffmpeg, and active samoagent state.
1571
+ `
1572
+ };
1463
1573
 
1464
1574
  class ArgError extends Error {
1465
1575
  }
@@ -1481,6 +1591,7 @@ function parseArgs(argv) {
1481
1591
  dicts: new Set,
1482
1592
  watch: new Set,
1483
1593
  frame: new Set(["--out"]),
1594
+ doctor: new Set,
1484
1595
  _serve: new Set(["--port", "--transcript-file", "--webhook-token", "--call-id-file", "--frame-token"])
1485
1596
  };
1486
1597
  const boolFlags = {
@@ -1493,6 +1604,7 @@ function parseArgs(argv) {
1493
1604
  dicts: new Set,
1494
1605
  watch: new Set,
1495
1606
  frame: new Set(["--archive"]),
1607
+ doctor: new Set,
1496
1608
  _serve: new Set
1497
1609
  };
1498
1610
  const knownCommands = Object.keys(valueFlags);
@@ -1586,6 +1698,7 @@ function parseArgs(argv) {
1586
1698
  }
1587
1699
  case "dicts":
1588
1700
  case "watch":
1701
+ case "doctor":
1589
1702
  break;
1590
1703
  case "_serve": {
1591
1704
  const rawPort2 = opts["--port"];
@@ -1630,6 +1743,8 @@ async function dispatch(args) {
1630
1743
  return cmdDicts();
1631
1744
  case "watch":
1632
1745
  return cmdWatch();
1746
+ case "doctor":
1747
+ return cmdDoctor();
1633
1748
  case "_serve":
1634
1749
  return cmdServe(args);
1635
1750
  default:
@@ -1638,10 +1753,15 @@ async function dispatch(args) {
1638
1753
  }
1639
1754
  async function main() {
1640
1755
  const argv = process.argv.slice(2);
1641
- if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv.includes("--help") || argv.includes("-h")) {
1756
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
1642
1757
  process.stdout.write(USAGE);
1643
1758
  process.exit(argv.length === 0 ? 2 : 0);
1644
1759
  }
1760
+ if (argv.length >= 2 && (argv[1] === "--help" || argv[1] === "-h")) {
1761
+ const help = COMMAND_HELP[argv[0]];
1762
+ process.stdout.write(help ?? USAGE);
1763
+ process.exit(help ? 0 : 2);
1764
+ }
1645
1765
  if (argv[0] === "--version" || argv[0] === "-v" || argv[0] === "-V") {
1646
1766
  const pkg = await Promise.resolve().then(() => __toESM(require_package(), 1));
1647
1767
  process.stdout.write(`samoagent ${pkg.version}
@@ -0,0 +1,56 @@
1
+ # Release Checklist
2
+
3
+ Use this checklist for each npm release.
4
+
5
+ ## Before Release
6
+
7
+ - Confirm `main` is clean and up to date.
8
+ - Run `bun test`.
9
+ - Run `bun run build`.
10
+ - Smoke-test the built CLI:
11
+
12
+ ```bash
13
+ ./dist/cli.js --version
14
+ ./dist/cli.js --help
15
+ ```
16
+
17
+ - Check package metadata:
18
+
19
+ ```bash
20
+ npm pack --dry-run
21
+ ```
22
+
23
+ - Confirm `package.json` has the intended version, description, homepage, license, files, and keywords.
24
+ - Confirm `README.md` installation and usage examples match the current CLI.
25
+
26
+ ## Publish
27
+
28
+ - Bump `package.json` version.
29
+ - Commit and push the version bump.
30
+ - Create and publish a GitHub release tag matching the package version, for example `v0.4.1`.
31
+ - Wait for the `Publish to npm` GitHub Actions workflow to pass.
32
+
33
+ ## After Publish
34
+
35
+ - Verify npm has the new version:
36
+
37
+ ```bash
38
+ npm view samoagent version
39
+ ```
40
+
41
+ - Smoke-test the registry package from a clean prefix:
42
+
43
+ ```bash
44
+ tmp="$(mktemp -d)"
45
+ npm_config_prefix="$tmp" npm install -g samoagent
46
+ PATH="$tmp/bin:$PATH" samoagent --version
47
+ rm -rf "$tmp"
48
+ ```
49
+
50
+ - Confirm the package page shows Apache-2.0 license, homepage, README, and provenance.
51
+ - Confirm GitHub Pages is healthy at `https://samoagent.dev/`.
52
+
53
+ ## Secret Hygiene
54
+
55
+ - Keep `NPM_TOKEN` only in GitHub Actions secrets.
56
+ - Rotate `NPM_TOKEN` immediately if it is pasted into chat, logs, issues, PRs, or local shell history.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samoagent",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Let AI agents join Zoom and Google Meet calls as active participants.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -12,6 +12,7 @@
12
12
  "dictionaries/",
13
13
  "LICENSE",
14
14
  "README.md",
15
+ "docs/",
15
16
  "avatar.html",
16
17
  "avatar.png",
17
18
  "logo.svg"