muse-cli 0.2.1__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.1
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,29 +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.
70
98
 
71
99
  ```bash
72
- # 1. Install agent-browser (reads cookies from Chrome):
100
+ node --version # if this fails, install Node.js LTS from https://nodejs.org
101
+ # and open a new terminal
73
102
  npm i -g agent-browser
74
- # 2. Log in to https://muse.ai/ in Chrome
75
- # 3. Export your session (one time; re-run when it expires):
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
139
+
140
+ ```bash
76
141
  muse-cli auth export
142
+ ```
77
143
 
78
- 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
79
151
  ```
80
152
 
81
- Want the agent skill too? The installer sets up the CLI and copies the skill
82
- 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
83
165
 
84
166
  ```bash
85
- curl -fsSL https://raw.githubusercontent.com/nikships/muse-cli/main/install.sh | bash
167
+ muse-cli status
86
168
  ```
87
169
 
88
- 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.
89
172
 
90
- The command is `muse-cli`, not `muse`, because `muse` clashes with Muse Code
91
- 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
+ ```
92
187
 
93
188
  ## Usage
94
189
 
@@ -166,12 +261,13 @@ pyproject.toml
166
261
 
167
262
  ## Setup notes
168
263
 
169
- - `auth export` reads cookies from a running Chrome via
170
- [agent-browser](https://github.com/nikships/foundry) (`npm i -g agent-browser`).
171
- No Chrome? Copy your `muse.ai` cookies into `~/.config/muse-cli/cookies.txt`
172
- by hand (Netscape jar or `name=value; ...` format, needs `hatch_sess`).
173
- - Cookies live at `~/.config/muse-cli/cookies.txt` (mode 600). Access and
174
- 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.
175
271
  - Respect muse.ai's terms and rate limits. Internal APIs are unversioned and
176
272
  can change; if calls fail, re-derive from a fresh app bundle.
177
273
 
@@ -29,29 +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.
33
61
 
34
62
  ```bash
35
- # 1. Install agent-browser (reads cookies from Chrome):
63
+ node --version # if this fails, install Node.js LTS from https://nodejs.org
64
+ # and open a new terminal
36
65
  npm i -g agent-browser
37
- # 2. Log in to https://muse.ai/ in Chrome
38
- # 3. Export your session (one time; re-run when it expires):
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
102
+
103
+ ```bash
39
104
  muse-cli auth export
105
+ ```
40
106
 
41
- 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
42
114
  ```
43
115
 
44
- Want the agent skill too? The installer sets up the CLI and copies the skill
45
- 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
46
128
 
47
129
  ```bash
48
- curl -fsSL https://raw.githubusercontent.com/nikships/muse-cli/main/install.sh | bash
130
+ muse-cli status
49
131
  ```
50
132
 
51
- 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.
52
135
 
53
- The command is `muse-cli`, not `muse`, because `muse` clashes with Muse Code
54
- 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
+ ```
55
150
 
56
151
  ## Usage
57
152
 
@@ -129,12 +224,13 @@ pyproject.toml
129
224
 
130
225
  ## Setup notes
131
226
 
132
- - `auth export` reads cookies from a running Chrome via
133
- [agent-browser](https://github.com/nikships/foundry) (`npm i -g agent-browser`).
134
- No Chrome? Copy your `muse.ai` cookies into `~/.config/muse-cli/cookies.txt`
135
- by hand (Netscape jar or `name=value; ...` format, needs `hatch_sess`).
136
- - Cookies live at `~/.config/muse-cli/cookies.txt` (mode 600). Access and
137
- 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.
138
234
  - Respect muse.ai's terms and rate limits. Internal APIs are unversioned and
139
235
  can change; if calls fail, re-derive from a fresh app bundle.
140
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,24 +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. `auth export`
36
- reads Chrome's cookies through
37
- [agent-browser](https://github.com/nikships/foundry); install it if missing,
38
- 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:
39
54
 
40
55
  ```bash
41
- npm i -g agent-browser # one-time; skip if already installed
42
- 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
43
85
  test -s ~/.config/muse-cli/cookies.txt && echo auth-ok
44
86
  ```
45
87
 
46
- No Chrome? Copy the `muse.ai` cookies by hand (DevTools Application
47
- Cookies; needs `hatch_sess`) into `~/.config/muse-cli/cookies.txt` as
48
- 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.
49
90
 
50
- Access and gateway tokens are fetched fresh on every run; only cookies
51
- persist. When commands later fail with `auth error`, cookies expired:
52
- 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.
53
125
 
54
126
  ## 3. Verify end to end
55
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.1"
3
+ __version__ = "0.2.2"
@@ -1,8 +1,10 @@
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
  """
@@ -44,8 +46,11 @@ def load_config():
44
46
 
45
47
  def connect(cfg):
46
48
  if not os.path.exists(cfg["cookies_file"]):
47
- raise AuthError(f"no cookies at {cfg['cookies_file']}; log in to https://muse.ai/ "
48
- "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.")
49
54
  cookies = load_cookies(cfg["cookies_file"])
50
55
  if not cookies.strip():
51
56
  raise AuthError(f"cookies file {cfg['cookies_file']} is empty; run `muse-cli auth export`")
@@ -56,70 +61,175 @@ def out(obj):
56
61
  print(json.dumps(obj, indent=2, ensure_ascii=False))
57
62
 
58
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
+
59
84
  def _browser_run(argv):
60
- """Run agent-browser, parsing its JSON envelope. NOTE: it exits 0 even
61
- 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
+ """
62
90
  import subprocess
63
91
  r = subprocess.run(argv, capture_output=True, text=True)
64
- if r.returncode != 0:
65
- raise RuntimeError((r.stderr or r.stdout)[:200] or f"exit {r.returncode}")
66
- try:
67
- doc = json.loads(r.stdout)
68
- except json.JSONDecodeError:
69
- raise RuntimeError((r.stdout or r.stderr)[:200] or "empty output")
92
+ doc = _parse_browser_doc(r.stdout) or _parse_browser_doc(r.stderr)
70
93
  if isinstance(doc, dict) and doc.get("success") is False:
71
- 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}")
72
98
  return doc.get("data", {}) if isinstance(doc, dict) else doc
73
99
 
74
100
 
75
- def _browser_cookies(headed):
76
- """Read the cookie jar via agent-browser. Headed auto-connect attaches
77
- to the user's real Chrome; plain mode uses a fresh browser (no login)."""
78
- base = ["agent-browser"] + (["--headed", "--auto-connect"] if headed else [])
79
- if shutil.which("agent-browser") is None:
80
- print("agent-browser not found; install it with: npm i -g agent-browser", file=sys.stderr)
81
- print("alternative: export cookies by hand, see README Setup notes.", file=sys.stderr)
82
- sys.exit(1)
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"]
83
191
  last_err = "unknown error"
84
- for _ in range(3):
192
+ for attempt in range(3):
85
193
  try:
86
- if headed:
87
- # Cookies follow the active tab: focus a muse.ai tab first,
88
- # else the export comes back empty even when logged in.
89
- data = _browser_run(base + ["tab", "list", "--json"])
90
- tabs = data.get("tabs", []) if isinstance(data, dict) else []
91
- muse_tabs = [t for t in tabs
92
- if isinstance(t, dict) and "muse.ai" in (t.get("url") or "")
93
- and (t.get("id") or t.get("tabId"))]
94
- if not muse_tabs:
95
- return None, "no muse.ai tab open in Chrome"
96
- _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"]])
97
204
  data = _browser_run(base + ["cookies", "get", "--json"])
98
205
  jar = data.get("cookies", []) if isinstance(data, dict) else []
99
206
  return [c for c in jar if "muse.ai" in c.get("domain", "")], None
100
207
  except RuntimeError as e:
101
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
102
212
  time.sleep(2)
103
213
  return None, last_err
104
214
 
105
215
 
106
216
  def cmd_auth_export(_args):
107
217
  os.makedirs(CONFIG_DIR, exist_ok=True)
108
- jar, err = _browser_cookies(headed=True)
109
- if jar is None and err != "no muse.ai tab open in Chrome":
110
- # Headed attach failed (no Chrome, old agent-browser, ...): a plain
111
- # browser shares no login, so this is a last resort at best.
112
- 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()
113
223
  if jar is None:
114
- print(f"cookie export failed: {err}", file=sys.stderr)
115
- print("is Chrome running with muse.ai open?", file=sys.stderr)
116
- print("alternative: export cookies by hand, see README Setup.", file=sys.stderr)
224
+ _print_export_failure(err)
117
225
  sys.exit(1)
118
226
  # Never clobber a working login with an empty or logged-out jar.
119
227
  if not any(c["name"] == "hatch_sess" for c in jar):
120
- print("refusing to overwrite cookies: no hatch_sess in export "
121
- "(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.",
122
231
  file=sys.stderr)
232
+ print(f"Left {COOKIES_FILE} unchanged.", file=sys.stderr)
123
233
  sys.exit(1)
124
234
  lines = ["# Netscape HTTP Cookie File"]
125
235
  for c in jar:
@@ -476,7 +586,32 @@ def main():
476
586
  sub = ap.add_subparsers(dest="cmd", required=True)
477
587
 
478
588
  p = sub.add_parser("auth", help="auth helpers"); a = p.add_subparsers(dest="op", required=True)
479
- 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)
480
615
 
481
616
  sub.add_parser("status", help="VM, session count, unread, identity").set_defaults(fn=cmd_status)
482
617
  p = sub.add_parser("threads", help="list chats and side chats")
File without changes
File without changes
File without changes