muse-cli 0.2.0__tar.gz → 0.2.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: muse-cli
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Command-line client for your personal muse.ai AI agent: chat, automate, and manage side chats, feed, goals, ideas, and sessions from the terminal. No browser needed.
5
5
  Project-URL: Homepage, https://github.com/nikships/muse-cli
6
6
  Project-URL: Documentation, https://github.com/nikships/muse-cli#readme
@@ -66,27 +66,124 @@ A command-line client for your personal muse.ai AI agent: chat from the terminal
66
66
  uv tool install muse-cli # or: pipx install muse-cli / pip install muse-cli
67
67
  ```
68
68
 
69
- Then log in and check the connection:
69
+ The command is `muse-cli`. The name `muse` belongs to Muse Code on many machines.
70
+
71
+ Want the agent skill too? The installer sets up the CLI and copies the skill
72
+ to `~/.agents/skills/muse-cli`:
73
+
74
+ ```bash
75
+ curl -fsSL https://raw.githubusercontent.com/nikships/muse-cli/main/install.sh | bash
76
+ ```
77
+
78
+ Upgrade with `uv tool upgrade muse-cli`, remove with `uv tool uninstall muse-cli`.
79
+
80
+ Logging in is a separate one-time step. Chrome shares its cookies after you
81
+ turn on remote debugging. Follow [Log in once](#log-in-once) before running
82
+ any other command.
83
+
84
+ ## Log in once
85
+
86
+ muse-cli borrows the login you already have in Chrome, saves those cookies
87
+ to `~/.config/muse-cli/cookies.txt` (mode `600`, only your user can read it),
88
+ and after that talks to muse.ai directly. Access tokens are fetched fresh
89
+ on every command. When a command later says the cookies expired, repeat this
90
+ section.
91
+
92
+ Do the steps in order. Each one checks itself before you continue.
93
+
94
+ ### 1. Install the cookie reader
95
+
96
+ `auth export` uses [agent-browser](https://github.com/vercel-labs/agent-browser)
97
+ to read Chrome's cookies. That package comes from npm, so you need Node.js first.
98
+
99
+ ```bash
100
+ node --version # if this fails, install Node.js LTS from https://nodejs.org
101
+ # and open a new terminal
102
+ npm i -g agent-browser
103
+ agent-browser --version
104
+ ```
105
+
106
+ `npm i -g` needs permission to write to npm's global bin directory. If it
107
+ errors with `EACCES`, pick one and use it from then on: configure an
108
+ [npm prefix you own](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally),
109
+ or run the install with the same rights you use for other global npm tools.
110
+ Then check `command -v agent-browser` prints a path.
111
+
112
+ ### 2. Let Chrome share cookies
113
+
114
+ Open **Google Chrome**, the same profile you use for muse.ai. Paste this in
115
+ the address bar:
116
+
117
+ ```
118
+ chrome://inspect/#remote-debugging
119
+ ```
120
+
121
+ Turn on remote debugging. The checkbox reads **Allow remote debugging for
122
+ this browser instance**. Leave Chrome open.
123
+
124
+ Turn this on while Chrome is already open and muse.ai is loaded. The export
125
+ can see that window only after the switch is on. The page is in Chrome 144
126
+ and newer.
127
+
128
+ Stay in this window. Starting a second Chrome with
129
+ `--remote-debugging-port` opens a different profile, and that profile does
130
+ not have your muse.ai login.
131
+
132
+ ### 3. Be logged in, on a muse.ai tab
133
+
134
+ In that same Chrome window, open https://muse.ai/ and log in. Leave the tab
135
+ open. The exporter reads whichever tab is active, so the muse.ai tab has to
136
+ be the one in front when you run the next command.
137
+
138
+ ### 4. Save the login
70
139
 
71
140
  ```bash
72
- # 1. Log in to https://muse.ai/ in Chrome
73
- # 2. Export your session (one time; re-run when it expires):
74
141
  muse-cli auth export
142
+ ```
75
143
 
76
- muse-cli status
144
+ Chrome may ask to allow the debugging connection. Click **Allow**. If the
145
+ command already failed, run it again after you click Allow.
146
+
147
+ Success looks like this (the count varies):
148
+
149
+ ```
150
+ saved 12 muse.ai cookies to /home/you/.config/muse-cli/cookies.txt
77
151
  ```
78
152
 
79
- Want the agent skill too? The installer sets up the CLI and copies the skill
80
- to `~/.agents/skills/muse-cli`:
153
+ The command prints the fix on the failure itself. These are the ones it
154
+ recognizes:
155
+
156
+ | What it says | What to do |
157
+ | --- | --- |
158
+ | No running Chrome / remote debugging | The switch in step 2 is off. Turn it on in the Chrome you already have open, then run the command again. |
159
+ | No muse.ai tab | Step 3. Open https://muse.ai/ in that same window and leave the tab open. |
160
+ | No `hatch_sess` | The tab is open and you are logged out. Log in, then export again. The cookies file already on disk is left as it was. |
161
+ | `agent-browser` is not installed | Step 1. `node --version`, then `npm i -g agent-browser`, then a new terminal. |
162
+ | Daemon already running | A previous attempt is stuck. `agent-browser close`, then `muse-cli auth export`. |
163
+
164
+ ### 5. Check the connection
81
165
 
82
166
  ```bash
83
- curl -fsSL https://raw.githubusercontent.com/nikships/muse-cli/main/install.sh | bash
167
+ muse-cli status
84
168
  ```
85
169
 
86
- Upgrade with `uv tool upgrade muse-cli`, remove with `uv tool uninstall muse-cli`.
170
+ You get JSON with your VM id, how many chats you have, the unread count, and
171
+ your identity. That means install and login both worked.
87
172
 
88
- The command is `muse-cli`, not `muse`, because `muse` clashes with Muse Code
89
- on many machines.
173
+ ### Copy cookies by hand
174
+
175
+ Use this when you don't have Node, or you don't want remote debugging on.
176
+
177
+ 1. In Chrome, open https://muse.ai/ and log in.
178
+ 2. Open DevTools (F12, or Ctrl+Shift+I) → **Application** → **Cookies** → `https://muse.ai`.
179
+ 3. Copy the cookies onto one line. `hatch_sess` has to be there. Separate cookies with `; ` (semicolon, space). A Netscape cookie jar (what curl writes) and a JSON object such as `{"hatch_sess": "..."}` also work.
180
+
181
+ ```bash
182
+ mkdir -p ~/.config/muse-cli
183
+ printf '%s\n' 'hatch_sess=the-value-from-devtools; other_cookie=other_value' > ~/.config/muse-cli/cookies.txt
184
+ chmod 600 ~/.config/muse-cli/cookies.txt
185
+ muse-cli status
186
+ ```
90
187
 
91
188
  ## Usage
92
189
 
@@ -164,12 +261,13 @@ pyproject.toml
164
261
 
165
262
  ## Setup notes
166
263
 
167
- - `auth export` reads cookies from a running Chrome via
168
- [agent-browser](https://github.com/nikships/foundry) (`npm i -g agent-browser`).
169
- No Chrome? Copy your `muse.ai` cookies into `~/.config/muse-cli/cookies.txt`
170
- by hand (Netscape jar or `name=value; ...` format, needs `hatch_sess`).
171
- - Cookies live at `~/.config/muse-cli/cookies.txt` (mode 600). Access and
172
- gateway tokens are fetched fresh on every run, nothing long-lived is stored.
264
+ - Login is the [Log in once](#log-in-once) section above. Cookies live at
265
+ `~/.config/muse-cli/cookies.txt` (mode 600). Access and gateway tokens are
266
+ fetched fresh on every run.
267
+ - `auth export` reads those cookies from Google Chrome through
268
+ [agent-browser](https://github.com/vercel-labs/agent-browser). Chrome has
269
+ to be open, with remote debugging on
270
+ (`chrome://inspect/#remote-debugging`) and a logged-in muse.ai tab in front.
173
271
  - Respect muse.ai's terms and rate limits. Internal APIs are unversioned and
174
272
  can change; if calls fail, re-derive from a fresh app bundle.
175
273
 
@@ -29,27 +29,124 @@ A command-line client for your personal muse.ai AI agent: chat from the terminal
29
29
  uv tool install muse-cli # or: pipx install muse-cli / pip install muse-cli
30
30
  ```
31
31
 
32
- Then log in and check the connection:
32
+ The command is `muse-cli`. The name `muse` belongs to Muse Code on many machines.
33
+
34
+ Want the agent skill too? The installer sets up the CLI and copies the skill
35
+ to `~/.agents/skills/muse-cli`:
36
+
37
+ ```bash
38
+ curl -fsSL https://raw.githubusercontent.com/nikships/muse-cli/main/install.sh | bash
39
+ ```
40
+
41
+ Upgrade with `uv tool upgrade muse-cli`, remove with `uv tool uninstall muse-cli`.
42
+
43
+ Logging in is a separate one-time step. Chrome shares its cookies after you
44
+ turn on remote debugging. Follow [Log in once](#log-in-once) before running
45
+ any other command.
46
+
47
+ ## Log in once
48
+
49
+ muse-cli borrows the login you already have in Chrome, saves those cookies
50
+ to `~/.config/muse-cli/cookies.txt` (mode `600`, only your user can read it),
51
+ and after that talks to muse.ai directly. Access tokens are fetched fresh
52
+ on every command. When a command later says the cookies expired, repeat this
53
+ section.
54
+
55
+ Do the steps in order. Each one checks itself before you continue.
56
+
57
+ ### 1. Install the cookie reader
58
+
59
+ `auth export` uses [agent-browser](https://github.com/vercel-labs/agent-browser)
60
+ to read Chrome's cookies. That package comes from npm, so you need Node.js first.
61
+
62
+ ```bash
63
+ node --version # if this fails, install Node.js LTS from https://nodejs.org
64
+ # and open a new terminal
65
+ npm i -g agent-browser
66
+ agent-browser --version
67
+ ```
68
+
69
+ `npm i -g` needs permission to write to npm's global bin directory. If it
70
+ errors with `EACCES`, pick one and use it from then on: configure an
71
+ [npm prefix you own](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally),
72
+ or run the install with the same rights you use for other global npm tools.
73
+ Then check `command -v agent-browser` prints a path.
74
+
75
+ ### 2. Let Chrome share cookies
76
+
77
+ Open **Google Chrome**, the same profile you use for muse.ai. Paste this in
78
+ the address bar:
79
+
80
+ ```
81
+ chrome://inspect/#remote-debugging
82
+ ```
83
+
84
+ Turn on remote debugging. The checkbox reads **Allow remote debugging for
85
+ this browser instance**. Leave Chrome open.
86
+
87
+ Turn this on while Chrome is already open and muse.ai is loaded. The export
88
+ can see that window only after the switch is on. The page is in Chrome 144
89
+ and newer.
90
+
91
+ Stay in this window. Starting a second Chrome with
92
+ `--remote-debugging-port` opens a different profile, and that profile does
93
+ not have your muse.ai login.
94
+
95
+ ### 3. Be logged in, on a muse.ai tab
96
+
97
+ In that same Chrome window, open https://muse.ai/ and log in. Leave the tab
98
+ open. The exporter reads whichever tab is active, so the muse.ai tab has to
99
+ be the one in front when you run the next command.
100
+
101
+ ### 4. Save the login
33
102
 
34
103
  ```bash
35
- # 1. Log in to https://muse.ai/ in Chrome
36
- # 2. Export your session (one time; re-run when it expires):
37
104
  muse-cli auth export
105
+ ```
38
106
 
39
- muse-cli status
107
+ Chrome may ask to allow the debugging connection. Click **Allow**. If the
108
+ command already failed, run it again after you click Allow.
109
+
110
+ Success looks like this (the count varies):
111
+
112
+ ```
113
+ saved 12 muse.ai cookies to /home/you/.config/muse-cli/cookies.txt
40
114
  ```
41
115
 
42
- Want the agent skill too? The installer sets up the CLI and copies the skill
43
- to `~/.agents/skills/muse-cli`:
116
+ The command prints the fix on the failure itself. These are the ones it
117
+ recognizes:
118
+
119
+ | What it says | What to do |
120
+ | --- | --- |
121
+ | No running Chrome / remote debugging | The switch in step 2 is off. Turn it on in the Chrome you already have open, then run the command again. |
122
+ | No muse.ai tab | Step 3. Open https://muse.ai/ in that same window and leave the tab open. |
123
+ | No `hatch_sess` | The tab is open and you are logged out. Log in, then export again. The cookies file already on disk is left as it was. |
124
+ | `agent-browser` is not installed | Step 1. `node --version`, then `npm i -g agent-browser`, then a new terminal. |
125
+ | Daemon already running | A previous attempt is stuck. `agent-browser close`, then `muse-cli auth export`. |
126
+
127
+ ### 5. Check the connection
44
128
 
45
129
  ```bash
46
- curl -fsSL https://raw.githubusercontent.com/nikships/muse-cli/main/install.sh | bash
130
+ muse-cli status
47
131
  ```
48
132
 
49
- Upgrade with `uv tool upgrade muse-cli`, remove with `uv tool uninstall muse-cli`.
133
+ You get JSON with your VM id, how many chats you have, the unread count, and
134
+ your identity. That means install and login both worked.
50
135
 
51
- The command is `muse-cli`, not `muse`, because `muse` clashes with Muse Code
52
- on many machines.
136
+ ### Copy cookies by hand
137
+
138
+ Use this when you don't have Node, or you don't want remote debugging on.
139
+
140
+ 1. In Chrome, open https://muse.ai/ and log in.
141
+ 2. Open DevTools (F12, or Ctrl+Shift+I) → **Application** → **Cookies** → `https://muse.ai`.
142
+ 3. Copy the cookies onto one line. `hatch_sess` has to be there. Separate cookies with `; ` (semicolon, space). A Netscape cookie jar (what curl writes) and a JSON object such as `{"hatch_sess": "..."}` also work.
143
+
144
+ ```bash
145
+ mkdir -p ~/.config/muse-cli
146
+ printf '%s\n' 'hatch_sess=the-value-from-devtools; other_cookie=other_value' > ~/.config/muse-cli/cookies.txt
147
+ chmod 600 ~/.config/muse-cli/cookies.txt
148
+ muse-cli status
149
+ ```
53
150
 
54
151
  ## Usage
55
152
 
@@ -127,12 +224,13 @@ pyproject.toml
127
224
 
128
225
  ## Setup notes
129
226
 
130
- - `auth export` reads cookies from a running Chrome via
131
- [agent-browser](https://github.com/nikships/foundry) (`npm i -g agent-browser`).
132
- No Chrome? Copy your `muse.ai` cookies into `~/.config/muse-cli/cookies.txt`
133
- by hand (Netscape jar or `name=value; ...` format, needs `hatch_sess`).
134
- - Cookies live at `~/.config/muse-cli/cookies.txt` (mode 600). Access and
135
- gateway tokens are fetched fresh on every run, nothing long-lived is stored.
227
+ - Login is the [Log in once](#log-in-once) section above. Cookies live at
228
+ `~/.config/muse-cli/cookies.txt` (mode 600). Access and gateway tokens are
229
+ fetched fresh on every run.
230
+ - `auth export` reads those cookies from Google Chrome through
231
+ [agent-browser](https://github.com/vercel-labs/agent-browser). Chrome has
232
+ to be open, with remote debugging on
233
+ (`chrome://inspect/#remote-debugging`) and a logged-in muse.ai tab in front.
136
234
  - Respect muse.ai's terms and rate limits. Internal APIs are unversioned and
137
235
  can change; if calls fail, re-derive from a fresh app bundle.
138
236
 
@@ -74,6 +74,9 @@ chat / feed / goals / ideas / sessions / ...
74
74
  delete: `{method: "/api/session/<op>", session_id}`.
75
75
  - `api.idea-cards.execute`: `{ideaCardId, mode: "full"}` plus path param.
76
76
  - POSTs to muse.ai need browser `Sec-Fetch-*` headers or they return 403.
77
- - `auth export` must focus a muse.ai tab before reading cookies (the export
78
- follows the active tab) and must never overwrite a working jar without a
79
- `hatch_sess` in the new one.
77
+ - `auth export` reads cookies from the user's own Google Chrome through
78
+ agent-browser `--auto-connect`. Chrome 144+ shares that profile only after
79
+ remote debugging is enabled at `chrome://inspect/#remote-debugging`. The
80
+ export follows the active tab, so a muse.ai tab has to be focused first,
81
+ and it must never overwrite a working jar without a `hatch_sess` in the
82
+ new one. A freshly launched browser has no login; do not fall back to one.
@@ -32,22 +32,96 @@ muse-cli --help >/dev/null && echo cli-ok # proves the install + deps resolve
32
32
 
33
33
  ## 2. Auth
34
34
 
35
- The user must be logged in to https://muse.ai/ in Chrome first. Then:
35
+ The CLI stores the user's muse.ai browser login at
36
+ `~/.config/muse-cli/cookies.txt` (mode 0600). Chrome hides those cookies
37
+ until remote debugging is on. Walk the user through the steps below and wait
38
+ for them. Do not run `muse-cli auth export` before they say the Chrome
39
+ switch is on and a logged-in muse.ai tab is open.
40
+
41
+ Access and gateway tokens are fetched fresh on every run; only cookies
42
+ persist. When a later command fails with `auth error`, the cookies expired:
43
+ repeat 2b (or 2c).
44
+
45
+ ### 2a. Cookie reader
46
+
47
+ ```bash
48
+ command -v node >/dev/null && node --version
49
+ command -v npm >/dev/null && npm --version
50
+ command -v agent-browser >/dev/null && agent-browser --version
51
+ ```
52
+
53
+ If `agent-browser` is missing and `npm` works:
36
54
 
37
55
  ```bash
38
- muse-cli auth export # saves muse.ai cookies to ~/.config/muse-cli/cookies.txt (0600)
56
+ npm i -g agent-browser
57
+ agent-browser --version # must print a version
58
+ command -v agent-browser # must print a path
59
+ ```
60
+
61
+ `EACCES` from `npm i -g` means npm cannot write its global bin directory.
62
+ Stop and tell the user. They fix it with an npm prefix they own
63
+ (https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally)
64
+ or by installing with the same rights they use for other global npm tools.
65
+ Do not sudo unless they tell you to.
66
+
67
+ If `node` or `npm` is missing, stop. Tell them to install Node.js LTS from
68
+ https://nodejs.org, open a new terminal, and come back. Offer 2c if they
69
+ would rather not install Node.
70
+
71
+ ### 2b. Chrome, then export
72
+
73
+ Tell the user to do all of this in the Google Chrome window they already
74
+ use for muse.ai. Needs Chrome 144 or newer. Wait until they confirm:
75
+
76
+ 1. Address bar: `chrome://inspect/#remote-debugging`
77
+ 2. Turn on remote debugging ("Allow remote debugging for this browser instance").
78
+ 3. Open https://muse.ai/ and log in. Leave that tab in front. The export
79
+ reads the active tab.
80
+
81
+ Then:
82
+
83
+ ```bash
84
+ muse-cli auth export
39
85
  test -s ~/.config/muse-cli/cookies.txt && echo auth-ok
40
86
  ```
41
87
 
42
- `auth export` pulls cookies from a running Chrome via
43
- [agent-browser](https://github.com/nikships/foundry) (`npm i -g agent-browser`
44
- if it is missing). No Chrome? Copy the `muse.ai` cookies by hand (DevTools →
45
- Application → Cookies; needs `hatch_sess`) into
46
- `~/.config/muse-cli/cookies.txt` as Netscape-jar or `name=value; ...` text.
88
+ If Chrome shows an Allow prompt, they click Allow. If the command failed
89
+ before that click, run it once more.
47
90
 
48
- Access and gateway tokens are fetched fresh on every run; only cookies
49
- persist. When commands later fail with `auth error`, cookies expired:
50
- re-run `auth export`.
91
+ Success is a line like `saved N muse.ai cookies to ~/.config/muse-cli/cookies.txt`.
92
+
93
+ If it fails, the CLI prints the next step. Map it like this, and do not retry
94
+ in a loop:
95
+
96
+ - "No running Chrome" or "remote-debugging-port": the switch in step 2 is
97
+ off, or they are in a different Chrome. Send them back to the steps above.
98
+ Do not tell them to launch Chrome with `--remote-debugging-port`. That
99
+ opens another profile, and it does not have their muse.ai login.
100
+ - "no muse.ai tab": Chrome connected. They still need https://muse.ai/ open
101
+ and in front in that same window.
102
+ - "no hatch_sess" / "do not include hatch_sess": the tab is open and they
103
+ are logged out. The cookies file on disk was kept.
104
+ - "daemon already running": `agent-browser close`, then `muse-cli auth export` once.
105
+ - `agent-browser` not found: back to 2a.
106
+
107
+ ### 2c. Copy cookies by hand
108
+
109
+ Use this when Node is unavailable, or the user does not want remote debugging on.
110
+
111
+ 1. They are logged in at https://muse.ai/ in Chrome.
112
+ 2. DevTools (F12) → Application → Cookies → `https://muse.ai`.
113
+ 3. Write one line to `~/.config/muse-cli/cookies.txt`. `hatch_sess` is required.
114
+ Separate cookies with `; `:
115
+
116
+ ```
117
+ hatch_sess=VALUE; other_name=other_value
118
+ ```
119
+
120
+ A Netscape jar or `{"hatch_sess": "..."}` JSON also works.
121
+
122
+ 4. `chmod 600 ~/.config/muse-cli/cookies.txt`
123
+ 5. `test -s ~/.config/muse-cli/cookies.txt && echo auth-ok`, then `muse-cli status`.
124
+ Stop if status fails and report the error text.
51
125
 
52
126
  ## 3. Verify end to end
53
127
 
@@ -1,3 +1,3 @@
1
1
  """muse-cli: talk to your personal muse.ai AI agent from the terminal."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -1,14 +1,17 @@
1
- """muse-cli: CLI for your personal muse.ai agent. No browser needed (after cookie export).
1
+ """muse-cli: CLI for your personal muse.ai agent. No browser needed after cookie export.
2
2
 
3
- Setup:
4
- 1. Log in to https://muse.ai/ in Chrome (Auth profile).
5
- 2. muse-cli auth export # saves session cookies locally (chmod 600)
3
+ One-time setup (also printed by `muse-cli auth export` when a step is missing):
4
+ 1. npm i -g agent-browser
5
+ 2. In Chrome, open chrome://inspect/#remote-debugging and turn remote debugging on.
6
+ 3. Open https://muse.ai/ in that same window and log in. Leave the tab open.
7
+ 4. muse-cli auth export # saves session cookies locally (chmod 600)
6
8
 
7
9
  Then: muse-cli status | muse-cli threads | muse-cli history | muse-cli send "hello" | ...
8
10
  """
9
11
  import argparse
10
12
  import json
11
13
  import os
14
+ import shutil
12
15
  import sys
13
16
  import time
14
17
 
@@ -43,8 +46,11 @@ def load_config():
43
46
 
44
47
  def connect(cfg):
45
48
  if not os.path.exists(cfg["cookies_file"]):
46
- raise AuthError(f"no cookies at {cfg['cookies_file']}; log in to https://muse.ai/ "
47
- "in Chrome, then run `muse-cli auth export`")
49
+ raise AuthError(
50
+ f"no cookies at {cfg['cookies_file']}. Run `muse-cli auth export` "
51
+ "and follow the steps it prints: turn on Chrome remote debugging at "
52
+ "chrome://inspect/#remote-debugging, open https://muse.ai/ in that "
53
+ "window, then export again.")
48
54
  cookies = load_cookies(cfg["cookies_file"])
49
55
  if not cookies.strip():
50
56
  raise AuthError(f"cookies file {cfg['cookies_file']} is empty; run `muse-cli auth export`")
@@ -55,66 +61,175 @@ def out(obj):
55
61
  print(json.dumps(obj, indent=2, ensure_ascii=False))
56
62
 
57
63
 
64
+ def _parse_browser_doc(stdout):
65
+ """agent-browser prints a JSON envelope, sometimes after a warning line."""
66
+ text = (stdout or "").strip()
67
+ if not text:
68
+ return None
69
+ blobs = [text]
70
+ for line in reversed(text.splitlines()):
71
+ line = line.strip()
72
+ if line.startswith("{") and line not in blobs:
73
+ blobs.append(line)
74
+ for blob in blobs:
75
+ try:
76
+ doc = json.loads(blob)
77
+ except json.JSONDecodeError:
78
+ continue
79
+ if isinstance(doc, dict):
80
+ return doc
81
+ return None
82
+
83
+
58
84
  def _browser_run(argv):
59
- """Run agent-browser, parsing its JSON envelope. NOTE: it exits 0 even
60
- on failure, reporting {"success": false, "error": ...} on stdout."""
85
+ """Run agent-browser and return the JSON envelope's data.
86
+
87
+ On failure the tool prints {"success": false, "error": ...}. Some versions
88
+ exit 0 anyway, some exit non-zero; trust the envelope when it is there.
89
+ """
61
90
  import subprocess
62
91
  r = subprocess.run(argv, capture_output=True, text=True)
63
- if r.returncode != 0:
64
- raise RuntimeError((r.stderr or r.stdout)[:200] or f"exit {r.returncode}")
65
- try:
66
- doc = json.loads(r.stdout)
67
- except json.JSONDecodeError:
68
- raise RuntimeError((r.stdout or r.stderr)[:200] or "empty output")
92
+ doc = _parse_browser_doc(r.stdout) or _parse_browser_doc(r.stderr)
69
93
  if isinstance(doc, dict) and doc.get("success") is False:
70
- raise RuntimeError(str(doc.get("error") or "unknown error")[:200])
94
+ raise RuntimeError(str(doc.get("error") or "unknown error")[:400])
95
+ if r.returncode != 0 or doc is None:
96
+ msg = (r.stderr or r.stdout or "").strip()
97
+ raise RuntimeError(msg[:400] or f"exit {r.returncode}")
71
98
  return doc.get("data", {}) if isinstance(doc, dict) else doc
72
99
 
73
100
 
74
- def _browser_cookies(headed):
75
- """Read the cookie jar via agent-browser. Headed auto-connect attaches
76
- to the user's real Chrome; plain mode uses a fresh browser (no login)."""
77
- base = ["agent-browser"] + (["--headed", "--auto-connect"] if headed else [])
101
+ def _export_error_kind(err):
102
+ e = (err or "").lower()
103
+ if "daemon already running" in e:
104
+ return "daemon"
105
+ if ("no running chrome" in e or "remote-debugging" in e
106
+ or "auto-launch failed" in e or "auto-connect" in e):
107
+ return "debug"
108
+ if err == "no muse.ai tab open in Chrome":
109
+ return "tab"
110
+ return "other"
111
+
112
+
113
+ def _print_hand_copy():
114
+ print("Copy the login by hand instead:", file=sys.stderr)
115
+ print(" 1. In Chrome, open https://muse.ai/ and log in.", file=sys.stderr)
116
+ print(" 2. DevTools (F12) → Application → Cookies → https://muse.ai", file=sys.stderr)
117
+ print(" 3. Write one line to ~/.config/muse-cli/cookies.txt", file=sys.stderr)
118
+ print(" hatch_sess=VALUE; other_name=other_value", file=sys.stderr)
119
+ print(" hatch_sess is required. Separate cookies with '; '.", file=sys.stderr)
120
+ print(" 4. chmod 600 ~/.config/muse-cli/cookies.txt", file=sys.stderr)
121
+ print(" 5. muse-cli status", file=sys.stderr)
122
+
123
+
124
+ def _print_chrome_debug_steps():
125
+ print("Turn on remote debugging so muse-cli can read the muse.ai cookies.", file=sys.stderr)
126
+ print("Do this in the Chrome window where you already use muse.ai:", file=sys.stderr)
127
+ print(file=sys.stderr)
128
+ print(" 1. Open chrome://inspect/#remote-debugging", file=sys.stderr)
129
+ print(" 2. Turn on remote debugging", file=sys.stderr)
130
+ print(' ("Allow remote debugging for this browser instance").', file=sys.stderr)
131
+ print(" 3. Open https://muse.ai/ and log in. Leave that tab open.", file=sys.stderr)
132
+ print(" 4. Run `muse-cli auth export` again.", file=sys.stderr)
133
+ print(" If Chrome asks to allow the connection, click Allow.", file=sys.stderr)
134
+ print(" If this command already failed, run it again after Allow.", file=sys.stderr)
135
+ print(file=sys.stderr)
136
+ print("Stay in that same window. A second Chrome started with", file=sys.stderr)
137
+ print("--remote-debugging-port is another profile and has no muse.ai login.", file=sys.stderr)
138
+ print(file=sys.stderr)
139
+ print("Success looks like:", file=sys.stderr)
140
+ print(f" saved N muse.ai cookies to {COOKIES_FILE}", file=sys.stderr)
141
+ print(file=sys.stderr)
142
+ _print_hand_copy()
143
+
144
+
145
+ def _print_need_agent_browser():
146
+ print("agent-browser is not installed, so muse-cli cannot read Chrome's cookies.", file=sys.stderr)
147
+ print(file=sys.stderr)
148
+ print(" 1. Install Node.js LTS if `node --version` fails: https://nodejs.org", file=sys.stderr)
149
+ print(" 2. Open a new terminal, then:", file=sys.stderr)
150
+ print(" npm i -g agent-browser", file=sys.stderr)
151
+ print(" agent-browser --version", file=sys.stderr)
152
+ print(" 3. Run `muse-cli auth export` again and follow the Chrome steps it prints.", file=sys.stderr)
153
+ print(file=sys.stderr)
154
+ _print_hand_copy()
155
+
156
+
157
+ def _print_export_failure(err):
158
+ kind = _export_error_kind(err)
159
+ print("cookie export failed.", file=sys.stderr)
160
+ if kind == "daemon":
161
+ print("agent-browser is still running from an earlier attempt. Close it, then retry:", file=sys.stderr)
162
+ print(" agent-browser close", file=sys.stderr)
163
+ print(" muse-cli auth export", file=sys.stderr)
164
+ print(file=sys.stderr)
165
+ _print_chrome_debug_steps()
166
+ return
167
+ if kind == "debug":
168
+ _print_chrome_debug_steps()
169
+ return
170
+ if kind == "tab":
171
+ print("Chrome is connected, but no muse.ai tab is open.", file=sys.stderr)
172
+ print("Open https://muse.ai/, log in, leave that tab open, and run", file=sys.stderr)
173
+ print("`muse-cli auth export` again.", file=sys.stderr)
174
+ return
175
+ print(err, file=sys.stderr)
176
+ print(file=sys.stderr)
177
+ print("Check both of these, then run `muse-cli auth export` again:", file=sys.stderr)
178
+ print(" - chrome://inspect/#remote-debugging has remote debugging on", file=sys.stderr)
179
+ print(" - https://muse.ai/ is open in that same Chrome and you are logged in", file=sys.stderr)
180
+ print(file=sys.stderr)
181
+ _print_hand_copy()
182
+
183
+
184
+ def _browser_cookies():
185
+ """Read muse.ai cookies from the user's Chrome via agent-browser.
186
+
187
+ Auto-connect attaches to the real profile. A fresh browser has no login,
188
+ so this never falls back to launching one.
189
+ """
190
+ base = ["agent-browser", "--headed", "--auto-connect"]
78
191
  last_err = "unknown error"
79
- for _ in range(3):
192
+ for attempt in range(3):
80
193
  try:
81
- if headed:
82
- # Cookies follow the active tab: focus a muse.ai tab first,
83
- # else the export comes back empty even when logged in.
84
- data = _browser_run(base + ["tab", "list", "--json"])
85
- tabs = data.get("tabs", []) if isinstance(data, dict) else []
86
- muse_tabs = [t for t in tabs
87
- if isinstance(t, dict) and "muse.ai" in (t.get("url") or "")
88
- and (t.get("id") or t.get("tabId"))]
89
- if not muse_tabs:
90
- return None, "no muse.ai tab open in Chrome"
91
- _browser_run(base + ["tab", muse_tabs[0].get("id") or muse_tabs[0]["tabId"]])
194
+ # Cookies follow the active tab: focus a muse.ai tab first,
195
+ # else the export comes back empty even when logged in.
196
+ data = _browser_run(base + ["tab", "list", "--json"])
197
+ tabs = data.get("tabs", []) if isinstance(data, dict) else []
198
+ muse_tabs = [t for t in tabs
199
+ if isinstance(t, dict) and "muse.ai" in (t.get("url") or "")
200
+ and (t.get("id") or t.get("tabId"))]
201
+ if not muse_tabs:
202
+ return None, "no muse.ai tab open in Chrome"
203
+ _browser_run(base + ["tab", muse_tabs[0].get("id") or muse_tabs[0]["tabId"]])
92
204
  data = _browser_run(base + ["cookies", "get", "--json"])
93
205
  jar = data.get("cookies", []) if isinstance(data, dict) else []
94
206
  return [c for c in jar if "muse.ai" in c.get("domain", "")], None
95
207
  except RuntimeError as e:
96
208
  last_err = str(e)
209
+ # Missing debug port and a stuck daemon will not change on retry.
210
+ if _export_error_kind(last_err) in ("debug", "daemon") or attempt == 2:
211
+ return None, last_err
97
212
  time.sleep(2)
98
213
  return None, last_err
99
214
 
100
215
 
101
216
  def cmd_auth_export(_args):
102
217
  os.makedirs(CONFIG_DIR, exist_ok=True)
103
- jar, err = _browser_cookies(headed=True)
104
- if jar is None and err != "no muse.ai tab open in Chrome":
105
- # Headed attach failed (no Chrome, old agent-browser, ...): a plain
106
- # browser shares no login, so this is a last resort at best.
107
- jar, err = _browser_cookies(headed=False)
218
+ if shutil.which("agent-browser") is None:
219
+ _print_need_agent_browser()
220
+ sys.exit(1)
221
+ print("Reading muse.ai cookies from Chrome...", file=sys.stderr)
222
+ jar, err = _browser_cookies()
108
223
  if jar is None:
109
- print(f"cookie export failed: {err}", file=sys.stderr)
110
- print("is Chrome running with muse.ai open?", file=sys.stderr)
111
- print("alternative: export cookies by hand, see README Setup.", file=sys.stderr)
224
+ _print_export_failure(err)
112
225
  sys.exit(1)
113
226
  # Never clobber a working login with an empty or logged-out jar.
114
227
  if not any(c["name"] == "hatch_sess" for c in jar):
115
- print("refusing to overwrite cookies: no hatch_sess in export "
116
- "(are you logged in to muse.ai?). Existing file left intact.",
228
+ print("Chrome answered, but the cookies do not include hatch_sess.", file=sys.stderr)
229
+ print("That cookie is set only after you log in at https://muse.ai/.", file=sys.stderr)
230
+ print("Log in, leave the muse.ai tab open, and run `muse-cli auth export` again.",
117
231
  file=sys.stderr)
232
+ print(f"Left {COOKIES_FILE} unchanged.", file=sys.stderr)
118
233
  sys.exit(1)
119
234
  lines = ["# Netscape HTTP Cookie File"]
120
235
  for c in jar:
@@ -471,7 +586,32 @@ def main():
471
586
  sub = ap.add_subparsers(dest="cmd", required=True)
472
587
 
473
588
  p = sub.add_parser("auth", help="auth helpers"); a = p.add_subparsers(dest="op", required=True)
474
- a.add_parser("export", help="export Chrome session cookies for the CLI").set_defaults(fn=cmd_auth_export)
589
+ export_help = (
590
+ "Copy your muse.ai login out of Google Chrome into\n"
591
+ "~/.config/muse-cli/cookies.txt (mode 600).\n"
592
+ "\n"
593
+ "Chrome shares those cookies only after remote debugging is on.\n"
594
+ "In the Chrome window you already use for muse.ai:\n"
595
+ "\n"
596
+ " 1. Open chrome://inspect/#remote-debugging\n"
597
+ " 2. Turn on remote debugging\n"
598
+ ' ("Allow remote debugging for this browser instance")\n'
599
+ " 3. Open https://muse.ai/ and log in. Leave that tab open.\n"
600
+ " 4. Run this command. If Chrome asks to allow the connection,\n"
601
+ " click Allow, then run it again if it failed before the click.\n"
602
+ "\n"
603
+ "Requires agent-browser: npm i -g agent-browser\n"
604
+ "(install Node.js LTS from https://nodejs.org first if npm is missing).\n"
605
+ "\n"
606
+ "Stay in that same Chrome. Starting another Chrome with\n"
607
+ "--remote-debugging-port uses a different profile and has no muse.ai login."
608
+ )
609
+ a.add_parser(
610
+ "export",
611
+ help="copy the muse.ai login out of Chrome (remote debugging must be on)",
612
+ description=export_help,
613
+ formatter_class=argparse.RawDescriptionHelpFormatter,
614
+ ).set_defaults(fn=cmd_auth_export)
475
615
 
476
616
  sub.add_parser("status", help="VM, session count, unread, identity").set_defaults(fn=cmd_status)
477
617
  p = sub.add_parser("threads", help="list chats and side chats")
File without changes
File without changes
File without changes