@mnemahq/cli 0.1.0 → 0.1.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 +9 -2
- package/package.json +1 -1
- package/src/cli.mjs +44 -4
- package/src/util.mjs +10 -0
package/README.md
CHANGED
|
@@ -15,8 +15,15 @@ The package is `@mnemahq/cli`; the command it installs is **`mnema`**. (The unsc
|
|
|
15
15
|
name `mnema` is blocked on npm — its typosquat filter rejects it as too close to the
|
|
16
16
|
existing package `mem`.)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
Run `mnema login` first and the workspace is filled in automatically. Otherwise you'll be asked
|
|
19
|
+
for two values, which live on **two different settings pages**:
|
|
20
|
+
|
|
21
|
+
| value | where |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| Workspace id | **Settings → Workspace** |
|
|
24
|
+
| Hook token | **Settings → Access** → *Session capture* → **Generate token** |
|
|
25
|
+
|
|
26
|
+
Optionally an **API key** for search. `init` installs the capture hook, stores your secrets in the OS
|
|
20
27
|
keychain, and writes a `.mnema/config.json` (safe to commit — it holds no secrets).
|
|
21
28
|
|
|
22
29
|
Start a Claude Code session and it appears under **Sessions** with its cost.
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { existsSync } from 'node:fs';
|
|
14
|
-
import { cmdLogin, cmdLogout } from './login.mjs';
|
|
14
|
+
import { cmdLogin, cmdLogout, accessToken } from './login.mjs';
|
|
15
15
|
import {
|
|
16
|
-
DEFAULT_ORIGIN, c, gitInfo, canonicalRepo, readConfig, writeConfig, removeConfigDir,
|
|
16
|
+
DEFAULT_ORIGIN, DEFAULT_APP_URL, c, gitInfo, canonicalRepo, readConfig, writeConfig, removeConfigDir,
|
|
17
17
|
apiFetch, prompt, promptHidden, localSessionsForRepo,
|
|
18
18
|
} from './util.mjs';
|
|
19
19
|
import { getSecret, setSecret, deleteSecrets, backendName, usingFallback } from './secrets.mjs';
|
|
@@ -57,6 +57,33 @@ function fmtAge(mtimeMs) {
|
|
|
57
57
|
|
|
58
58
|
// ── init ───────────────────────────────────────────────────────────────────────
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The workspace this login already belongs to.
|
|
62
|
+
*
|
|
63
|
+
* ⭐ THE CLI WAS ASKING FOR SOMETHING IT COULD LOOK UP. `init` prompted "Workspace
|
|
64
|
+
* id:" with no hint, and the id lives on a different settings page from the hook
|
|
65
|
+
* token — so the first thing a new user did was go hunting for a UUID the tool
|
|
66
|
+
* could have read from their own session. If they have run `mnema login`, /api/me
|
|
67
|
+
* answers it.
|
|
68
|
+
*
|
|
69
|
+
* Returns null on any failure. This is a convenience, never a requirement: not
|
|
70
|
+
* logged in, offline, or an unexpected body all fall through to the prompt.
|
|
71
|
+
*/
|
|
72
|
+
async function workspaceFromSession(origin) {
|
|
73
|
+
try {
|
|
74
|
+
const token = await accessToken();
|
|
75
|
+
if (!token) return null;
|
|
76
|
+
const res = await fetch(`${origin}/api/me`, { headers: { Authorization: `Bearer ${token}` } });
|
|
77
|
+
if (!res.ok) return null;
|
|
78
|
+
const body = await res.json();
|
|
79
|
+
if (!body?.workspace_id) return null;
|
|
80
|
+
console.log(c.green(`\u2713 Workspace: ${body.workspace_name || body.workspace_id} (from your login)`));
|
|
81
|
+
return body.workspace_id;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
60
87
|
async function cmdInit(flags) {
|
|
61
88
|
const git = gitInfo();
|
|
62
89
|
const root = git.root || process.cwd();
|
|
@@ -66,11 +93,24 @@ async function cmdInit(flags) {
|
|
|
66
93
|
const existing = readConfig(root);
|
|
67
94
|
|
|
68
95
|
let workspaceId = flags.workspace || process.env.MNEMA_WORKSPACE_ID || existing?.workspaceId;
|
|
69
|
-
if (!workspaceId) workspaceId = await
|
|
96
|
+
if (!workspaceId) workspaceId = await workspaceFromSession(origin);
|
|
97
|
+
if (!workspaceId) {
|
|
98
|
+
console.log(` Workspace id — ${c.dim('Settings \u2192 Workspace')} ${c.dim(`${DEFAULT_APP_URL}/app/settings/workspace`)}`);
|
|
99
|
+
console.log(` ${c.dim('(or run `mnema login` first and this is filled in for you)')}`);
|
|
100
|
+
workspaceId = await prompt('Workspace id: ');
|
|
101
|
+
}
|
|
70
102
|
if (!workspaceId) { console.error(c.red('A workspace id is required.')); process.exit(1); }
|
|
71
103
|
|
|
72
104
|
let hookToken = process.env.MNEMA_HOOK_TOKEN || getSecret(workspaceId, 'hook-token');
|
|
73
|
-
if (!hookToken)
|
|
105
|
+
if (!hookToken) {
|
|
106
|
+
// ⚠️ NOT "Settings → Developer". That page does not exist and never did in
|
|
107
|
+
// this shape — the hook token moved to the Access page when tokens were
|
|
108
|
+
// consolidated there, and the id lives on Workspace. The old prompt sent
|
|
109
|
+
// every new user looking for a page that was not in the app.
|
|
110
|
+
console.log(` Hook token — ${c.dim('Settings \u2192 Access \u2192 "Session capture" \u2192 Generate token')}`);
|
|
111
|
+
console.log(` ${c.dim(`${DEFAULT_APP_URL}/app/settings/access`)}`);
|
|
112
|
+
hookToken = await promptHidden('Hook token: ');
|
|
113
|
+
}
|
|
74
114
|
if (!hookToken) { console.error(c.red('A hook token is required.')); process.exit(1); }
|
|
75
115
|
|
|
76
116
|
let apiKey = process.env.MNEMA_API_KEY || getSecret(workspaceId, 'api-key') || '';
|
package/src/util.mjs
CHANGED
|
@@ -18,6 +18,16 @@ import { createInterface } from 'node:readline';
|
|
|
18
18
|
// capture hook uses.
|
|
19
19
|
export const DEFAULT_ORIGIN = process.env.MNEMA_API_ORIGIN || 'https://api.theboringpeople.in';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* ⭐ THE APP, NOT THE API — a SEPARATE host, and the reason this constant exists.
|
|
23
|
+
* Everything the CLI asks a human to fetch lives in the web app at
|
|
24
|
+
* mnema.theboringpeople.in, while every request it makes goes to
|
|
25
|
+
* api.theboringpeople.in. Deriving one from the other is not possible, and
|
|
26
|
+
* printing the API origin in a "go here and copy this" message sends people to a
|
|
27
|
+
* host with no UI on it.
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_APP_URL = process.env.MNEMA_APP_URL || 'https://mnema.theboringpeople.in';
|
|
30
|
+
|
|
21
31
|
export const c = {
|
|
22
32
|
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
23
33
|
green: (s) => `\x1b[32m${s}\x1b[0m`,
|