codeam-cli 2.60.64 → 2.60.66

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/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.60.65] — 2026-07-15
8
+
9
+ ### Added
10
+
11
+ - **cli:** At-least-once command delivery — dedupe + ack (bulletproof first-prompt loss)
12
+
13
+ ## [2.60.64] — 2026-07-15
14
+
15
+ ### Added
16
+
17
+ - **shared:** Add Slack search:read scope (agent can search the user's messages)
18
+
19
+ ### Documentation
20
+
21
+ - **shared:** Clarify Slack uses a USER token (agent acts as the user)
22
+
7
23
  ## [2.60.63] — 2026-07-15
8
24
 
9
25
  ### Added
package/dist/index.js CHANGED
@@ -663,6 +663,38 @@ var INTEGRATION_REGISTRY = {
663
663
  }
664
664
  }
665
665
  }
666
+ },
667
+ notion: {
668
+ id: "notion",
669
+ name: "Notion",
670
+ icon: "notion",
671
+ // LIVE — the Notion public OAuth integration is registered and
672
+ // NOTION_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager
673
+ // (prod+dev). The backend NotionOAuthProvider is config-gated (503 if env
674
+ // unset) so this is safe even mid-rollout before the secrets mount.
675
+ enabled: true,
676
+ auth: {
677
+ kind: "oauth_redirect",
678
+ // Notion does NOT use per-request OAuth scopes — access is governed by
679
+ // the integration's configured CAPABILITIES (read/update/insert content
680
+ // + read user info), set once on the Notion integration, not passed in
681
+ // the authorize URL. So there is no `scope` param to request here.
682
+ scopes: []
683
+ },
684
+ delivery: {
685
+ mcp: {
686
+ // Notion's OFFICIAL stdio MCP server (Node). BYO-token headless: the
687
+ // OAuth access token is fed via NOTION_TOKEN and the server sends it as
688
+ // `Authorization: Bearer` + `Notion-Version: 2022-06-28` (env only,
689
+ // never argv). No discriminator — the token alone authenticates its
690
+ // workspace. Version PINNED; bump only after re-verifying headless.
691
+ command: "npx",
692
+ args: ["-y", "@notionhq/notion-mcp-server@2.4.1"],
693
+ envMapping: {
694
+ NOTION_TOKEN: "accessToken"
695
+ }
696
+ }
697
+ }
666
698
  }
667
699
  };
668
700
  function getIntegration(id) {
@@ -5891,7 +5923,7 @@ function readAnonId() {
5891
5923
  }
5892
5924
  function superProperties() {
5893
5925
  return {
5894
- cliVersion: true ? "2.60.64" : "0.0.0-dev",
5926
+ cliVersion: true ? "2.60.66" : "0.0.0-dev",
5895
5927
  nodeVersion: process.version,
5896
5928
  platform: process.platform,
5897
5929
  arch: process.arch,
@@ -6072,7 +6104,7 @@ var os4 = __toESM(require("os"));
6072
6104
  // package.json
6073
6105
  var package_default = {
6074
6106
  name: "codeam-cli",
6075
- version: "2.60.64",
6107
+ version: "2.60.66",
6076
6108
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
6077
6109
  type: "commonjs",
6078
6110
  main: "dist/index.js",
@@ -6840,7 +6872,7 @@ function httpStatusOf(err) {
6840
6872
  var API_BASE2 = resolveApiBaseUrl();
6841
6873
  var SSE_LIVENESS_TIMEOUT_MS = 45e3;
6842
6874
  var SSE_WATCHDOG_INTERVAL_MS = 1e4;
6843
- var CommandRelayService = class {
6875
+ var CommandRelayService = class _CommandRelayService {
6844
6876
  constructor(pluginId, onCommand, agentMeta, agentsOverride) {
6845
6877
  this.pluginId = pluginId;
6846
6878
  this.onCommand = onCommand;
@@ -6862,6 +6894,16 @@ var CommandRelayService = class {
6862
6894
  agentsRegistered = false;
6863
6895
  /** SSE connection (null when on the polling fallback or stopped). */
6864
6896
  sseRequest = null;
6897
+ // At-least-once delivery: the backend now delivers commands NON-DESTRUCTIVELY
6898
+ // (peek) when we advertise `X-Codeam-Cmd-Ack: 1`, and drains the queue only
6899
+ // when we ack the ids. So a command can never be lost to a ghost/racing SSE
6900
+ // subscriber during codespace boot (the 2026-07-15 first-prompt loss). The
6901
+ // trade-off is possible REDELIVERY (reconnect, publish re-fire, ghost race),
6902
+ // so we dedupe by id here — a bounded FIFO set of ids we've already
6903
+ // dispatched. Cap keeps memory flat over a long session; the oldest ids are
6904
+ // evicted (a command that old is long gone from the server queue anyway).
6905
+ processedIds = /* @__PURE__ */ new Set();
6906
+ static PROCESSED_ID_CAP = 1e3;
6865
6907
  /** Polling backoff state (only used on the fallback). */
6866
6908
  pollTimer = null;
6867
6909
  pollFailures = 0;
@@ -7131,14 +7173,15 @@ var CommandRelayService = class {
7131
7173
  // as X-Plugin-Poll-Secret. Empty {} for legacy sessions / older
7132
7174
  // backends (which ignore it).
7133
7175
  pollSecretHeader() {
7176
+ const headers = { "X-Codeam-Cmd-Ack": "1" };
7134
7177
  try {
7135
7178
  const secret = loadCliConfig().sessions.find(
7136
7179
  (s) => s.pluginId === this.pluginId
7137
7180
  )?.pollSecret;
7138
- return secret ? { "X-Plugin-Poll-Secret": secret } : {};
7181
+ if (secret) headers["X-Plugin-Poll-Secret"] = secret;
7139
7182
  } catch {
7140
- return {};
7141
7183
  }
7184
+ return headers;
7142
7185
  }
7143
7186
  async pollOnce() {
7144
7187
  try {
@@ -7161,7 +7204,13 @@ var CommandRelayService = class {
7161
7204
  }
7162
7205
  }
7163
7206
  async dispatchCommands(commands) {
7207
+ this.ackCommands(commands.map((c2) => c2.id));
7164
7208
  for (const cmd of commands) {
7209
+ if (cmd.id && this.processedIds.has(cmd.id)) {
7210
+ log.trace("relay", `dedupe skip already-dispatched id=${cmd.id}`);
7211
+ continue;
7212
+ }
7213
+ if (cmd.id) this.rememberProcessed(cmd.id);
7165
7214
  try {
7166
7215
  log.trace("relay", `dispatch type=${cmd.type} id=${cmd.id}`);
7167
7216
  await this.onCommand(cmd);
@@ -7170,6 +7219,23 @@ var CommandRelayService = class {
7170
7219
  }
7171
7220
  }
7172
7221
  }
7222
+ /** Record a dispatched id, evicting the oldest when over the cap (FIFO). */
7223
+ rememberProcessed(id) {
7224
+ this.processedIds.add(id);
7225
+ if (this.processedIds.size > _CommandRelayService.PROCESSED_ID_CAP) {
7226
+ const oldest = this.processedIds.values().next().value;
7227
+ if (oldest !== void 0) this.processedIds.delete(oldest);
7228
+ }
7229
+ }
7230
+ /** Confirm receipt of command ids so the backend removes them from the queue
7231
+ * (the at-least-once delivery guarantee). Best-effort — never throws. */
7232
+ ackCommands(ids) {
7233
+ const commandIds = ids.filter((id) => typeof id === "string" && id.length > 0);
7234
+ if (commandIds.length === 0) return;
7235
+ void _postJson(`${API_BASE2}/api/commands/ack`, { pluginId: this.pluginId, commandIds }, {
7236
+ ...this.pollSecretHeader()
7237
+ }).catch((err) => log.trace("relay", "ack post failed (will redeliver+dedupe)", err));
7238
+ }
7173
7239
  // ─── Heartbeat + agents ──────────────────────────────────────────
7174
7240
  async sendHeartbeat(online) {
7175
7241
  await _postJson(`${API_BASE2}/api/plugin/heartbeat`, {
@@ -7181,7 +7247,7 @@ var CommandRelayService = class {
7181
7247
  // fresh + clear the "CLI update available" banner after a self-update
7182
7248
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
7183
7249
  // pair/reconnect). Older backends ignore the extra field.
7184
- ..."2.60.64" ? { ideVersion: "2.60.64" } : {}
7250
+ ..."2.60.66" ? { ideVersion: "2.60.66" } : {}
7185
7251
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
7186
7252
  }
7187
7253
  /**
@@ -17758,7 +17824,7 @@ async function autoUpgradeBeforeCriticalCommand() {
17758
17824
  if (process.env.NODE_ENV === "test") return;
17759
17825
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17760
17826
  if (process.env.CI) return;
17761
- const current = true ? "2.60.64" : null;
17827
+ const current = true ? "2.60.66" : null;
17762
17828
  if (!current) return;
17763
17829
  const cache = readCache();
17764
17830
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17775,7 +17841,7 @@ function checkForUpdates() {
17775
17841
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17776
17842
  if (process.env.CI) return;
17777
17843
  if (!process.stdout.isTTY) return;
17778
- const current = true ? "2.60.64" : null;
17844
+ const current = true ? "2.60.66" : null;
17779
17845
  if (!current) return;
17780
17846
  const cache = readCache();
17781
17847
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17795,7 +17861,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
17795
17861
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
17796
17862
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
17797
17863
  function currentCliVersion() {
17798
- return true ? "2.60.64" : null;
17864
+ return true ? "2.60.66" : null;
17799
17865
  }
17800
17866
  function runCmd(cmd, args2, timeoutMs) {
17801
17867
  return new Promise((resolve7) => {
@@ -35224,7 +35290,7 @@ function checkChokidar() {
35224
35290
  }
35225
35291
  async function doctor(args2 = []) {
35226
35292
  const json = args2.includes("--json");
35227
- const cliVersion = true ? "2.60.64" : "0.0.0-dev";
35293
+ const cliVersion = true ? "2.60.66" : "0.0.0-dev";
35228
35294
  const apiBase2 = resolveApiBaseUrl();
35229
35295
  const diagnosticId = (0, import_node_crypto12.randomUUID)();
35230
35296
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -35701,7 +35767,7 @@ async function mcpRun(args2) {
35701
35767
  // src/commands/version.ts
35702
35768
  var import_picocolors15 = __toESM(require("picocolors"));
35703
35769
  function version2() {
35704
- const v = true ? "2.60.64" : "unknown";
35770
+ const v = true ? "2.60.66" : "unknown";
35705
35771
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
35706
35772
  }
35707
35773
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.60.64",
3
+ "version": "2.60.66",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",