@letterstory/cli 0.5.0 → 0.5.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
@@ -273,19 +273,28 @@ letterstory seers apply --file seers.json --dry-run # print the create/update/
273
273
  letterstory seers apply --file seers.json # apply it
274
274
  ```
275
275
 
276
- Connections, and one honest gap:
276
+ Connections both providers connect headlessly (no browser required):
277
277
 
278
278
  ```bash
279
279
  letterstory seers connections # GitHub + Notion status
280
280
  letterstory seers connect github --token <pat> # org-wide token; unlocks private repos
281
- letterstory seers connect notion # prints instructions see below
281
+ letterstory seers connect notion # no token: prints how to mint one + which pages to share
282
+ letterstory seers connect notion --token ntn_xxx # store a Notion internal-integration secret
282
283
  ```
283
284
 
284
- > **Known gaps.** Notion connects via browser OAuth in the app only the CLI/API can
285
- > read connection status but cannot complete the OAuth flow. `seers run` executes
286
- > synchronously server-side (up to ~5 minutes for web-scan providers); there is no
287
- > start-then-poll variant yet. And `create`'s config takes raw JSON `seers providers`
288
- > is the schema reference the server validates against.
285
+ **Connecting Notion.** Notion uses an _internal integration_ (the direct analogue of a
286
+ GitHub PAT), so it's fully scriptable:
287
+
288
+ 1. Create one at [notion.so/my-integrations](https://www.notion.so/my-integrations) **New integration Internal**, with the **Read content** capability.
289
+ 2. Copy the **Internal Integration Secret** (`ntn_…` / `secret_…`).
290
+ 3. In Notion, open each page/database the seer should watch → **••• → Connections →** add your integration. **A Notion integration only sees pages explicitly shared with it** — this step is required, or the seer connects but detects nothing.
291
+ 4. `letterstory seers connect notion --token ntn_…`
292
+
293
+ The app's browser OAuth flow (Seers tab → Connect Notion) is an equivalent alternative, not a requirement.
294
+
295
+ > **Remaining gap.** `seers run` executes synchronously server-side (up to ~5 minutes for
296
+ > web-scan providers); there is no start-then-poll variant yet. And `create`'s config takes
297
+ > raw JSON — `seers providers` is the schema reference the server validates against.
289
298
 
290
299
  Content templates (the compose structures seers bind via `config.template_key`):
291
300
 
package/lib/cli.mjs CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  } from "./commands.mjs";
36
36
 
37
37
  // Keep in sync with cli/package.json.
38
- export const VERSION = "0.5.0";
38
+ export const VERSION = "0.5.1";
39
39
 
40
40
  // Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
41
41
  // can't accidentally swallow the id as --json's value.
@@ -245,7 +245,8 @@ Seers (event-driven signals -> drafts):
245
245
  seers release <article-id> | seers discard <article-id>
246
246
  seers connections GitHub/Notion connection status
247
247
  seers connect github --token <pat> Store an org-wide GitHub token
248
- seers connect notion (browser-only OAuth prints instructions)
248
+ seers connect notion --token <secret> Store a Notion internal-integration token
249
+ seers connect notion (no token: prints how to mint one + share pages)
249
250
  seers export [--file <path>] Dump the org's seers as a manifest
250
251
  seers apply --file <path> [--dry-run] Idempotent, name-keyed upsert from a manifest
251
252
  (never deletes; --dry-run prints the plan)
@@ -4,11 +4,11 @@
4
4
  // `seers export` (dump the org's seers as a manifest) and `seers apply` (idempotent,
5
5
  // name-keyed diff-then-upsert from a manifest, with --dry-run).
6
6
  //
7
- // Known surface gaps, stated here because this is where operators hit them:
8
- // - Notion connects via browser OAuth in the app only. `seers connect notion`
9
- // prints instructions; there is no headless flow.
10
- // - `seers run` executes synchronously server-side (news/regulation web scans can
11
- // take minutes) and spends model budget — it is usage-gated per org.
7
+ // Connections are both headless: `seers connect github --token` (a PAT) and
8
+ // `seers connect notion --token` (a Notion internal-integration secret; run
9
+ // `seers connect notion` with no token for how to mint one and which pages to share).
10
+ // One thing to know: `seers run` executes synchronously server-side (news/regulation
11
+ // web scans can take minutes) and spends model budget — it is usage-gated per org.
12
12
 
13
13
  import { readFileSync, writeFileSync } from "node:fs";
14
14
  import { CliError } from "../client.mjs";
@@ -292,7 +292,7 @@ async function seersConnections(ctx) {
292
292
  : `GitHub: not connected${r.github.configured ? " — connect with `seers connect github --token <pat>`" : " (token storage not configured on this deployment)"}`;
293
293
  const notion = r.notion.connected
294
294
  ? `Notion: connected to ${r.notion.workspace_name} (${r.notion.connected_at})`
295
- : "Notion: not connected — connect via browser in the app (Seers tab Connect Notion); there is no headless flow";
295
+ : `Notion: not connected${r.notion.configured ? " — connect with `seers connect notion --token <secret>` (run `seers connect notion` for how to get one)" : " (token storage not configured on this deployment)"}`;
296
296
  return `${gh}\n${notion}`;
297
297
  });
298
298
  return 0;
@@ -310,11 +310,29 @@ async function seersConnect(ctx) {
310
310
  return 0;
311
311
  }
312
312
  if (which === "notion") {
313
- // Deliberate gap: Notion's OAuth flow needs a browser session; the integrations
314
- // surface can read status but can't complete the dance.
315
- rest.io.log("Notion connects via browser OAuth and can't be completed from the CLI.");
316
- rest.io.log(`Open ${rest.config.url.replace(/\/$/, "")}/seers and use "Connect Notion", then verify with:`);
317
- rest.io.log(` ${rest.bin} seers connections`);
313
+ const token = flagStr(rest.flags.token);
314
+ if (!token) {
315
+ // No token yet print how to mint one and which pages to share. Notion
316
+ // internal integrations only see pages explicitly shared with them, so the
317
+ // share step is load-bearing, not optional.
318
+ rest.io.log("Connect Notion with an internal-integration secret (headless — no browser needed):");
319
+ rest.io.log("");
320
+ rest.io.log(" 1. Go to https://www.notion.so/my-integrations → New integration → Internal.");
321
+ rest.io.log(" 2. Give it the 'Read content' capability, then copy the Internal Integration Secret");
322
+ rest.io.log(" (starts with ntn_ or secret_).");
323
+ rest.io.log(" 3. In Notion, open each page/database the seer should watch → ••• → Connections →");
324
+ rest.io.log(" add your integration. (An integration only sees pages shared with it.)");
325
+ rest.io.log(` 4. Run: ${rest.bin} seers connect notion --token ntn_your_secret`);
326
+ rest.io.log("");
327
+ rest.io.log(
328
+ "The app's browser OAuth flow (Seers tab → Connect Notion) is an alternative, not a requirement."
329
+ );
330
+ return 0;
331
+ }
332
+ const result = await rest.client.callTool("set_notion_connection", { access_token: token });
333
+ ok(rest, `Notion connected${result.workspace_name ? ` to ${result.workspace_name}` : ""}.`);
334
+ ok(rest, "Reminder: the integration only watches pages you've shared with it in Notion.");
335
+ printResult(rest.io, rest.flags, result);
318
336
  return 0;
319
337
  }
320
338
  throw new CliError(`Unknown connect target: ${which ?? "(none)"}. Try: github, notion`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",