@creator-notes/cnotes 0.24.3 → 0.25.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 (37) hide show
  1. package/dist/commands/auth.d.ts +1 -1
  2. package/dist/commands/auth.d.ts.map +1 -1
  3. package/dist/commands/auth.js +72 -8
  4. package/dist/commands/auth.js.map +1 -1
  5. package/dist/commands/canvas.d.ts.map +1 -1
  6. package/dist/commands/canvas.js +122 -20
  7. package/dist/commands/canvas.js.map +1 -1
  8. package/dist/commands/config.d.ts.map +1 -1
  9. package/dist/commands/config.js +10 -3
  10. package/dist/commands/config.js.map +1 -1
  11. package/dist/commands/operations.d.ts.map +1 -1
  12. package/dist/commands/operations.js +21 -19
  13. package/dist/commands/operations.js.map +1 -1
  14. package/dist/commands/workspace.d.ts.map +1 -1
  15. package/dist/commands/workspace.js +171 -1
  16. package/dist/commands/workspace.js.map +1 -1
  17. package/dist/lib/api-client.d.ts +35 -9
  18. package/dist/lib/api-client.d.ts.map +1 -1
  19. package/dist/lib/api-client.js +60 -24
  20. package/dist/lib/api-client.js.map +1 -1
  21. package/dist/lib/auth-store.d.ts +50 -11
  22. package/dist/lib/auth-store.d.ts.map +1 -1
  23. package/dist/lib/auth-store.js +47 -12
  24. package/dist/lib/auth-store.js.map +1 -1
  25. package/dist/lib/install-mcp.d.ts.map +1 -1
  26. package/dist/lib/install-mcp.js +19 -1
  27. package/dist/lib/install-mcp.js.map +1 -1
  28. package/dist/lib/output.d.ts +7 -0
  29. package/dist/lib/output.d.ts.map +1 -1
  30. package/dist/lib/output.js +16 -0
  31. package/dist/lib/output.js.map +1 -1
  32. package/dist/lib/server-url.d.ts +33 -0
  33. package/dist/lib/server-url.d.ts.map +1 -0
  34. package/dist/lib/server-url.js +106 -0
  35. package/dist/lib/server-url.js.map +1 -0
  36. package/package.json +3 -3
  37. package/skills/cnotes/SKILL.md +59 -4
@@ -4,7 +4,7 @@ export interface LoginResult {
4
4
  email: string;
5
5
  serverUrl: string;
6
6
  }
7
- export declare function browserLogin(serverUrl: string, json: boolean, options?: {
7
+ export declare function browserLogin(rawServerUrl: string, json: boolean, options?: {
8
8
  quiet?: boolean;
9
9
  }): Promise<LoginResult>;
10
10
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyG3D;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B,OAAO,CAAC,WAAW,CAAC,CAuGtB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyI3D;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B,OAAO,CAAC,WAAW,CAAC,CA6ItB"}
@@ -1,5 +1,8 @@
1
1
  import { createServer } from "node:http";
2
+ import { randomBytes } from "node:crypto";
2
3
  import { loadConfig, saveConfig, setServerConfig, removeServerConfig, getServerConfig, } from "../lib/auth-store.js";
4
+ import { validateServerUrl, unofficialServerWarning, allowedCallbackOrigins, } from "../lib/server-url.js";
5
+ import { readStdin } from "../lib/stdin.js";
3
6
  import { handleError } from "../lib/errors.js";
4
7
  import { outputJson } from "../lib/output.js";
5
8
  export function registerAuthCommands(program) {
@@ -10,15 +13,42 @@ export function registerAuthCommands(program) {
10
13
  .command("login")
11
14
  .description("Authenticate with CreatorNotes via browser OAuth")
12
15
  .option("--token <token>", "Paste an existing API key instead of browser flow")
16
+ .option("--token-stdin", "Read the API key from stdin (safer than --token, which is visible in `ps` and shell history)")
13
17
  .option("--server <url>", "Server URL to authenticate with")
14
18
  .action(async (opts) => {
15
19
  const json = program.opts().json ?? false;
16
20
  try {
17
21
  const config = loadConfig();
18
- const serverUrl = opts.server || process.env.CNOTES_SERVER || config.currentServer;
19
- if (opts.token) {
20
- // Direct token input (for agents or manual paste)
21
- const token = opts.token;
22
+ // Reject malformed/insecure server URLs before we ever send a key there.
23
+ const serverUrl = validateServerUrl(opts.server || process.env.CNOTES_SERVER || config.currentServer);
24
+ // Resolve a directly-supplied token from the safe source first.
25
+ let token;
26
+ if (opts.tokenStdin) {
27
+ // On a TTY there is nothing piped in, so readStdin() blocks on EOF with
28
+ // no output — indistinguishable from a hang. Say what we're waiting for.
29
+ if (process.stdin.isTTY && !json) {
30
+ console.error("Reading the API key from stdin — paste it and press Ctrl-D, " +
31
+ "or pipe it in (e.g. `pbpaste | cnotes auth login --token-stdin`).");
32
+ }
33
+ token = (await readStdin()).trim();
34
+ if (!token)
35
+ throw new Error("No token received on stdin.");
36
+ }
37
+ else if (opts.token) {
38
+ token = opts.token;
39
+ // argv is world-visible via `ps` and lands in shell history — the key
40
+ // is a bearer credential, so nudge toward the stdin path (HIGH-2).
41
+ if (!json) {
42
+ console.error("Warning: --token is visible in `ps` output and your shell history. " +
43
+ "Prefer `--token-stdin` (pipe the key in) for a credential.");
44
+ }
45
+ }
46
+ if (token) {
47
+ // The key is about to be sent to serverUrl to validate — warn if that
48
+ // host isn't official/localhost, the phishing moment (MEDIUM-2).
49
+ const warning = unofficialServerWarning(serverUrl);
50
+ if (warning && !json)
51
+ console.error(warning);
22
52
  // Verify the token works by calling a simple endpoint
23
53
  const res = await fetch(new URL("/api/workspaces", serverUrl).toString(), {
24
54
  headers: { Authorization: `Bearer ${token}` },
@@ -104,13 +134,38 @@ export function registerAuthCommands(program) {
104
134
  }
105
135
  });
106
136
  }
107
- export async function browserLogin(serverUrl, json, options) {
137
+ export async function browserLogin(rawServerUrl, json, options) {
108
138
  const quiet = options?.quiet ?? false;
139
+ // Validate HERE, not only at the call sites: `cnotes init` (and any future
140
+ // caller) feeds `config.currentServer` straight in, so a URL stored before
141
+ // set-server validated anything — a trailing slash, or a cleartext http remote
142
+ // — would otherwise skip every check and, if malformed, die on `new URL(...)`
143
+ // below with a far less legible error. Validating at the entry point makes
144
+ // every caller inherit it, and normalizes what we then persist.
145
+ const serverUrl = validateServerUrl(rawServerUrl);
146
+ // Single-use nonce tying the callback to THIS login attempt. Without it, any
147
+ // web page open in the browser during the ~2-minute login window could scan
148
+ // localhost ports and POST its own token to our /callback — a login-CSRF that
149
+ // would silently authenticate the CLI as the ATTACKER's account (so the
150
+ // user's next `cnotes notes create` writes into the attacker's workspace).
151
+ // The auth page echoes this value back; a mismatch is rejected (HIGH-1).
152
+ const expectedState = randomBytes(16).toString("hex");
153
+ const allowedOrigins = allowedCallbackOrigins(serverUrl);
109
154
  return new Promise((resolve, reject) => {
110
155
  const server = createServer((req, res) => {
111
156
  const url = new URL(req.url || "/", `http://localhost`);
112
- // CORS headers so the browser auth page can call us via fetch
113
- res.setHeader("Access-Control-Allow-Origin", "*");
157
+ // Scope CORS to the server origin (not "*") so no other site can read this
158
+ // callback's response or drive it cross-origin (HIGH-1). Echo back the
159
+ // request's own Origin when it's one we allow — for a local dev server the
160
+ // page may arrive from the loopback SIBLING (the /auth/cli page rewrites
161
+ // 127.0.0.1 -> localhost so OAuth cookies resolve), and a single hard-coded
162
+ // origin would mismatch it. Login would still complete (CORS gates reading
163
+ // the response, not sending the GET), but the page would show the manual
164
+ // screen for no reason.
165
+ const reqOrigin = req.headers.origin;
166
+ const originToAllow = reqOrigin && allowedOrigins.has(reqOrigin) ? reqOrigin : allowedOrigins.values().next().value;
167
+ res.setHeader("Access-Control-Allow-Origin", originToAllow);
168
+ res.setHeader("Vary", "Origin");
114
169
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
115
170
  if (req.method === "OPTIONS") {
116
171
  res.writeHead(204);
@@ -124,6 +179,15 @@ export async function browserLogin(serverUrl, json, options) {
124
179
  }
125
180
  const token = url.searchParams.get("token");
126
181
  const email = url.searchParams.get("email");
182
+ const state = url.searchParams.get("state");
183
+ // Reject a callback that doesn't carry our nonce. An older auth page that
184
+ // doesn't echo `state` fails here too, but the page's own fallback then
185
+ // shows the manual `--token` command, so the user is never hard-blocked.
186
+ if (!state || state !== expectedState) {
187
+ res.writeHead(400);
188
+ res.end("Invalid or missing state");
189
+ return;
190
+ }
127
191
  if (!token) {
128
192
  res.writeHead(400);
129
193
  res.end("Missing token");
@@ -161,7 +225,7 @@ export async function browserLogin(serverUrl, json, options) {
161
225
  return;
162
226
  }
163
227
  const port = addr.port;
164
- const authUrl = `${serverUrl}/auth/cli?callbackPort=${port}`;
228
+ const authUrl = `${serverUrl}/auth/cli?callbackPort=${port}&state=${expectedState}`;
165
229
  if (!quiet && !json) {
166
230
  console.log("Opening browser for authentication...");
167
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EACL,UAAU,EACV,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,uBAAuB,CAAC,CAAC;IAExC,IAAI;SACD,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,kDAAkD,CAAC;SAC/D,MAAM,CAAC,iBAAiB,EAAE,mDAAmD,CAAC;SAC9E,MAAM,CAAC,gBAAgB,EAAE,iCAAiC,CAAC;SAC3D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,KAAK,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC;YAEnF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,kDAAkD;gBAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;gBACnC,sDAAsD;gBACtD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE;oBACxE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;iBAC9C,CAAC,CAAC;gBAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;gBACjE,CAAC;gBAED,2EAA2E;gBAC3E,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;gBAChE,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC;gBACjC,UAAU,CAAC,MAAM,CAAC,CAAC;gBAEnB,IAAI,IAAI,EAAE,CAAC;oBACT,UAAU,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC7D,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC;gBACjD,CAAC;gBACD,OAAO;YACT,CAAC;YAED,qBAAqB;YACrB,MAAM,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2BAA2B,CAAC;SACxC,MAAM,CAAC,gBAAgB,EAAE,2CAA2C,CAAC;SACrE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,KAAK,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC;YACtD,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEtC,IAAI,IAAI,EAAE,CAAC;gBACT,UAAU,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAC1D,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,mBAAmB,SAAS,EAAE,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,oCAAoC,CAAC;SACjD,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,KAAK,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC;YACvC,MAAM,EAAE,GAAG,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAE9C,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,IAAI,IAAI,EAAE,CAAC;oBACT,UAAU,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC1D,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,0BAA0B,SAAS,EAAE,CAAC,CAAC;oBACnD,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO;YACT,CAAC;YAED,IAAI,IAAI,EAAE,CAAC;gBACT,UAAU,CAAC;oBACT,aAAa,EAAE,IAAI;oBACnB,MAAM,EAAE,SAAS;oBACjB,KAAK,EAAE,EAAE,CAAC,KAAK;oBACf,WAAW,EAAE,EAAE,CAAC,WAAW,IAAI,IAAI;iBACpC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,EAAE,CAAC,CAAC;gBACvC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;gBACtC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,WAAW,IAAI,gBAAgB,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,IAAa,EACb,OAA6B;IAE7B,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,KAAK,CAAC;IAEtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACvC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;YAExD,8DAA8D;YAC9D,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;YAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;YAE9D,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACjC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YAED,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAE5C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;gBACzB,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;gBACpD,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;YAED,kBAAkB;YAClB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE;gBACjC,KAAK;gBACL,KAAK,EAAE,KAAK,IAAI,SAAS;aAC1B,CAAC,CAAC;YACH,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC;YACjC,UAAU,CAAC,MAAM,CAAC,CAAC;YAEnB,2DAA2D;YAC3D,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAEtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,IAAI,IAAI,EAAE,CAAC;oBACT,UAAU,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpE,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,IAAI,SAAS,OAAO,SAAS,EAAE,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;YAED,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,2BAA2B;QAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,MAAM,OAAO,GAAG,GAAG,SAAS,0BAA0B,IAAI,EAAE,CAAC;YAE7D,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACvD,CAAC;YAED,wEAAwE;YACxE,uEAAuE;YACvE,yEAAyE;YACzE,wEAAwE;YACxE,wEAAwE;YACxE,uEAAuE;YACvE,0EAA0E;YAC1E,wEAAwE;YACxE,uDAAuD;YACvD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;gBACrD,OAAO,CAAC,GAAG,CAAC,OAAO,OAAO,EAAE,CAAC,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;YAC9E,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC5C,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;YACpE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,0BAA0B;QAC1B,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,CAAC;QACnE,CAAC,EAAE,OAAO,CAAC,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/commands/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAkF5C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoxD7D"}
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/commands/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAwF5C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA44D7D"}
@@ -1,9 +1,10 @@
1
1
  import { Option } from "commander";
2
- import { ApiClient } from "../lib/api-client.js";
2
+ import { ApiClient, resolveActiveAgentRun } from "../lib/api-client.js";
3
3
  import { resolveConfig } from "../lib/config.js";
4
- import { EXIT_CODES, handleError, ValidationError } from "../lib/errors.js";
4
+ import { CliError, EXIT_CODES, handleError, ValidationError } from "../lib/errors.js";
5
5
  import { parseJsonOption, readFileSafe } from "../lib/fs-utils.js";
6
- import { outputJson, outputTable, outputQuiet, truncate, timeAgo } from "../lib/output.js";
6
+ import { loadConfig, setActiveAgentRunId, clearActiveAgentRunId, getActiveAgentRunId, } from "../lib/auth-store.js";
7
+ import { outputJson, outputTable, outputQuiet, truncate, timeAgo, formatAgo } from "../lib/output.js";
7
8
  import { readCanvasMarkdown, renderCanvasMarkdown } from "../lib/canvas-read.js";
8
9
  import { resolveAgentSession } from "../lib/claude-session.js";
9
10
  import { WARN, dim } from "../lib/style.js";
@@ -1520,33 +1521,88 @@ Examples:
1520
1521
  // Bracket a sequence of canvas commands with auto-checkpoints so the
1521
1522
  // server treats them as a single agent operation. The pattern:
1522
1523
  //
1523
- // eval "$(npx cnotes canvas agent-run begin --canvas <id> --prompt "Reorganize")"
1524
+ // npx cnotes canvas agent-run begin --canvas <id> --prompt "Reorganize"
1524
1525
  // npx cnotes canvas add-node ...
1525
1526
  // npx cnotes canvas add-edge ...
1526
1527
  // npx cnotes canvas agent-run end --canvas <id>
1527
1528
  //
1528
- // `begin` prints `export CNOTES_AGENT_RUN_ID=...` for shell eval. Subsequent
1529
- // commands pick up the env var and the api-client adds it as a header,
1530
- // so all mutations between begin and end share one operation row + a
1531
- // before/after snapshot pair, recoverable later via "Revert this run".
1529
+ // `begin` PERSISTS the run as the active run for this server (same store
1530
+ // `cnotes operations begin` uses), so every subsequent cnotes write attaches
1531
+ // automatically — across separate shells and stateless tool calls, with no
1532
+ // env var to thread through. It also still prints `export CNOTES_AGENT_RUN_ID=...`
1533
+ // so the older `eval "$(...)"` form keeps working; the env var wins over the
1534
+ // persisted pointer in ApiClient's precedence order.
1535
+ //
1536
+ // Persisting is load-bearing, not a convenience. Before it, an agent that ran
1537
+ // begin and then issued each write as its own process (the normal shape of
1538
+ // agent work) got NO run attached: the writes silently landed as
1539
+ // `manual.edit` / actorSource "human", and `end` failed with "No agent run ID
1540
+ // found". The bracket looked fine and recorded a lie.
1541
+ //
1542
+ // Two things persist, and BOTH are required for "Revert this run" to work:
1543
+ // the runId (which clusters the writes into one operation) and the
1544
+ // before-snapshot id (which the run's first mutation back-links as
1545
+ // operation.checkpointBeforeId — the state revert rewinds to). Persist only
1546
+ // the first and you get a run that looks healthy in the timeline and throws
1547
+ // "Operation cannot be reverted" when the user tries to undo it.
1532
1548
  const agentRun = canvas
1533
1549
  .command("agent-run")
1534
1550
  .description("Bracket canvas commands with before/after auto-checkpoints");
1535
1551
  agentRun
1536
1552
  .command("begin")
1537
- .description("Open an agent-run session — captures a before-snapshot and prints CNOTES_AGENT_RUN_ID")
1553
+ .description("Open an agent-run session — captures a before-snapshot and makes the run active for this server")
1538
1554
  .requiredOption("--canvas <id>", "Canvas ID to bracket")
1539
1555
  .requiredOption("--prompt <text>", "User prompt or summary describing the run")
1540
1556
  .option("--rationale <text>", "Optional rationale (e.g. agent's plan)")
1557
+ .option("--force", "If an active run already exists on this server, end it server-side and start a fresh one")
1558
+ .addHelpText("after", `
1559
+ Examples:
1560
+ $ cnotes canvas agent-run begin --canvas <id> --prompt "Reorganize into sections"
1561
+ $ cnotes canvas place <id> --spec ./layout.json # attaches automatically
1562
+ $ cnotes canvas agent-run end --canvas <id>
1563
+
1564
+ After 'begin', every subsequent cnotes write on this server attaches to the
1565
+ run automatically — no env var to export, and it survives across separate
1566
+ shells. End with 'cnotes canvas agent-run end --canvas <id>'.
1567
+
1568
+ If a run is already open (from 'agent-run begin' or 'operations begin'),
1569
+ begin refuses rather than silently clobbering it. Pass --force to end the
1570
+ prior run server-side and start fresh.
1571
+ `)
1541
1572
  .action(async (opts) => {
1542
1573
  const cfg = resolveConfig(program.opts());
1543
1574
  const api = new ApiClient(cfg.serverUrl, cfg.token);
1544
- // This command is designed to be used via `eval "$(...)"`, which makes
1575
+ // This command is designed to be usable via `eval "$(...)"`, which makes
1545
1576
  // stdout a non-TTY. resolveConfig's default would then flip cfg.json on
1546
1577
  // and we'd emit JSON instead of shell exports — breaking the eval form.
1547
1578
  // Honour JSON only when the user asked for it explicitly.
1548
1579
  const wantsJson = program.opts().json === true || process.env.CNOTES_JSON === "1";
1549
1580
  try {
1581
+ // Refuse to open a second run while one is still active on this server.
1582
+ // Both `operations begin` and `canvas agent-run begin` write the same
1583
+ // pointer, and a request carries exactly one X-Agent-Run-Id — so a
1584
+ // second begin would either clobber the first or silently attach writes
1585
+ // to the wrong run. Mirrors `cnotes operations begin`.
1586
+ const existing = getActiveAgentRunId(loadConfig(), cfg.serverUrl);
1587
+ if (existing) {
1588
+ if (opts.force) {
1589
+ try {
1590
+ await api.post("/api/operations", {
1591
+ action: "end",
1592
+ agentRunId: existing.agentRunId,
1593
+ });
1594
+ }
1595
+ catch {
1596
+ // Best-effort: ending an already-completed/abandoned run is
1597
+ // idempotent server-side. Either way clear the local pointer so
1598
+ // the caller isn't wedged.
1599
+ }
1600
+ clearActiveAgentRunId(loadConfig(), cfg.serverUrl);
1601
+ }
1602
+ else {
1603
+ throw new CliError(`An active agent-run already exists on this server (agentRunId ${existing.agentRunId}, started ${formatAgo(existing.startedAt)}).`, EXIT_CODES.CONFLICT, "End it first (`cnotes canvas agent-run end --canvas <id>` or `cnotes operations end`), or pass --force to end it server-side and start fresh.");
1604
+ }
1605
+ }
1550
1606
  const body = {
1551
1607
  action: "agentRunBegin",
1552
1608
  prompt: opts.prompt,
@@ -1557,6 +1613,29 @@ Examples:
1557
1613
  if (agentSession)
1558
1614
  body.agentSession = agentSession;
1559
1615
  const data = await api.post(`/api/canvas/${encodeURIComponent(opts.canvas)}`, body);
1616
+ // Persist the active run so subsequent cnotes writes attach to it even
1617
+ // when each one is a separate process. The server response carries no
1618
+ // startedAt for canvas runs, so stamp it locally — it only feeds the
1619
+ // "started Xs ago" text on a conflicting begin.
1620
+ //
1621
+ // beforeCheckpointId rides along on every write as
1622
+ // X-Agent-Before-Snapshot-Id: the server can only back-link
1623
+ // operation.checkpointBeforeId at the run's FIRST mutation (at begin
1624
+ // time the operation row doesn't exist yet), and without that link
1625
+ // getRevertContext bails — "Revert this run" is dead for the whole
1626
+ // batch. Persisting the run id without it produces a run that looks
1627
+ // perfectly healthy and cannot be undone.
1628
+ const persisted = setActiveAgentRunId(loadConfig(), cfg.serverUrl, data.agentRunId, Date.now(), data.beforeCheckpointId);
1629
+ // No stored config entry for this server (token came from --token /
1630
+ // CNOTES_TOKEN against a server never logged into), so there is nowhere
1631
+ // to record the run. Say so rather than printing "every write now
1632
+ // attaches" — silently attaching nothing is the bug this flow exists to
1633
+ // kill. stderr, so it can't corrupt --json or the eval form.
1634
+ if (!persisted) {
1635
+ console.error(`# WARNING: could not persist the active run for ${cfg.serverUrl} — no stored login for that server.`);
1636
+ console.error(`# Subsequent cnotes writes will NOT attach to it. Either run \`cnotes auth login\`,`);
1637
+ console.error(`# or use the env form: eval "$(cnotes canvas agent-run begin --canvas ${opts.canvas} --prompt "...")"`);
1638
+ }
1560
1639
  if (cfg.quiet) {
1561
1640
  outputQuiet(data.agentRunId);
1562
1641
  return;
@@ -1565,14 +1644,16 @@ Examples:
1565
1644
  outputJson(data);
1566
1645
  return;
1567
1646
  }
1568
- // Print eval-friendly export so users can:
1647
+ // Still print the eval-friendly exports so the older
1569
1648
  // eval "$(npx cnotes canvas agent-run begin ...)"
1570
- // Only the runId + before-snapshot id need to ride along on
1571
- // each request — prompt + rationale were already persisted on
1572
- // the operation row at agentRunBegin time.
1649
+ // form keeps working. The env var takes precedence over the persisted
1650
+ // pointer, so both paths agree on the same run.
1573
1651
  console.log(`export CNOTES_AGENT_RUN_ID=${data.agentRunId}`);
1574
1652
  console.log(`export CNOTES_AGENT_BEFORE_SNAPSHOT_ID=${data.beforeCheckpointId}`);
1575
1653
  console.error(`# agent run started: v${data.beforeSnapshotNumber} (${data.agentRunId})`);
1654
+ if (persisted) {
1655
+ console.error(`# every cnotes write on this server now attaches to this run`);
1656
+ }
1576
1657
  console.error(`# end with: npx cnotes canvas agent-run end --canvas ${opts.canvas}`);
1577
1658
  }
1578
1659
  catch (error) {
@@ -1612,6 +1693,14 @@ Examples:
1612
1693
  // need to propagate — prompt + rationale are persisted on the
1613
1694
  // operation row at agentRunBegin time, so resending them per-request
1614
1695
  // would be redundant (and would add a spoofing surface).
1696
+ //
1697
+ // Unlike `begin`, wrap deliberately does NOT persist the run to the
1698
+ // server config: the env var scopes it to this subprocess tree, and wrap
1699
+ // always closes the run in its own finally-path. Persisting would risk
1700
+ // leaving a stale pointer if wrap were killed, and would make wrap
1701
+ // unusable inside an open `operations` run (a common nesting: open an
1702
+ // operation for `notes create`, then wrap a canvas layout inside it).
1703
+ // Env var beats the persisted pointer in ApiClient, so the nesting works.
1615
1704
  const { spawn } = await import("node:child_process");
1616
1705
  const child = spawn(command[0], command.slice(1), {
1617
1706
  stdio: "inherit",
@@ -1663,24 +1752,37 @@ Examples:
1663
1752
  });
1664
1753
  agentRun
1665
1754
  .command("end")
1666
- .description("Close an agent-run session — captures an after-snapshot and prints unset commands")
1755
+ .description("Close an agent-run session — captures an after-snapshot and clears the active run")
1667
1756
  .requiredOption("--canvas <id>", "Canvas ID to close")
1668
- .option("--run-id <id>", "Agent run ID (defaults to CNOTES_AGENT_RUN_ID env var)")
1757
+ .option("--run-id <id>", "Agent run ID (defaults to the active run on this server, then CNOTES_AGENT_RUN_ID)")
1669
1758
  .action(async (opts) => {
1670
1759
  const cfg = resolveConfig(program.opts());
1671
1760
  const api = new ApiClient(cfg.serverUrl, cfg.token);
1672
- const runId = opts.runId ?? process.env.CNOTES_AGENT_RUN_ID;
1673
- if (!runId) {
1674
- throw new ValidationError("No agent run ID found. Pass --run-id or set CNOTES_AGENT_RUN_ID.");
1675
- }
1676
1761
  // Same reasoning as `begin`: eval form makes stdout non-TTY, so don't
1677
1762
  // let the auto-detect flip us into JSON mode.
1678
1763
  const wantsJson = program.opts().json === true || process.env.CNOTES_JSON === "1";
1679
1764
  try {
1765
+ // Resolve the run to close through the SAME resolver ApiClient uses to
1766
+ // attach it (explicit flag first, then env, then the persisted run).
1767
+ // Sharing the function, rather than re-deriving the precedence here, is
1768
+ // the point: `end` closing a run the client would not have attached to
1769
+ // is exactly the desync that let agent edits record as human ones.
1770
+ const runId = opts.runId ?? resolveActiveAgentRun(cfg.serverUrl)?.agentRunId;
1771
+ if (!runId) {
1772
+ throw new ValidationError("No active run on this server, and no --run-id passed", "Open one with `cnotes canvas agent-run begin --canvas <id> --prompt \"...\"`, or pass --run-id <id> to close a specific run.");
1773
+ }
1680
1774
  const data = await api.post(`/api/canvas/${encodeURIComponent(opts.canvas)}`, {
1681
1775
  action: "agentRunEnd",
1682
1776
  agentRunId: runId,
1683
1777
  });
1778
+ // Clear the local pointer so the next write detaches. Only clear when
1779
+ // it matches the run we just closed, so a concurrent begin from another
1780
+ // shell isn't stomped. Done after a successful response so a transient
1781
+ // server error can't desync local state from the server.
1782
+ const active = getActiveAgentRunId(loadConfig(), cfg.serverUrl);
1783
+ if (active?.agentRunId === data.agentRunId) {
1784
+ clearActiveAgentRunId(loadConfig(), cfg.serverUrl);
1785
+ }
1684
1786
  if (cfg.quiet) {
1685
1787
  outputQuiet(String(data.afterSnapshotNumber));
1686
1788
  return;