@tiens.nguyen/gu-cli 1.0.881 → 1.0.883
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/LICENSE +21 -0
- package/README.md +173 -108
- package/cli-token.mjs +29 -0
- package/gu-repl.mjs +53 -0
- package/package.json +21 -10
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tien Nguyen Song
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,157 +1,222 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
<h1 align="center">gu — the coding agent in your terminal</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>It reads your repo, makes the change, and proves it works.</strong><br>
|
|
5
|
+
Your models — Ollama, MLX or any OpenAI-compatible provider. Your files stay in your folder.<br>
|
|
6
|
+
<strong>Free for 30 days. No account, no sign-in, no card.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/@tiens.nguyen/gu-cli"><img alt="npm" src="https://img.shields.io/npm/v/@tiens.nguyen/gu-cli?color=6366f1&label=npm"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@tiens.nguyen/gu-cli"><img alt="downloads" src="https://img.shields.io/npm/dm/@tiens.nguyen/gu-cli?color=6366f1"></a>
|
|
12
|
+
<img alt="node" src="https://img.shields.io/node/v/@tiens.nguyen/gu-cli?color=6366f1">
|
|
13
|
+
<img alt="platforms" src="https://img.shields.io/badge/macOS%20%C2%B7%20Linux%20%C2%B7%20Windows-supported-6366f1">
|
|
14
|
+
<a href="./LICENSE"><img alt="license" src="https://img.shields.io/npm/l/@tiens.nguyen/gu-cli?color=6366f1"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://gucli.com">Website</a> ·
|
|
19
|
+
<a href="https://gucli.com/docs">Docs</a> ·
|
|
20
|
+
<a href="https://gucli.com/pricing">Pricing</a> ·
|
|
21
|
+
<a href="https://gucli.com/support">Support</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="https://gucli.com/videos/gu-design-web.mp4">
|
|
26
|
+
<img alt="gu building a web page from one request (click to watch)" src="https://gucli.com/videos/gu-design-web.jpg" width="760">
|
|
27
|
+
</a>
|
|
28
|
+
<br><sub>One request: gu plans, writes the files and checks the result. Click to watch.</sub>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
## Install
|
|
5
32
|
|
|
6
33
|
```sh
|
|
7
34
|
npm install -g @tiens.nguyen/gu-cli
|
|
8
|
-
|
|
35
|
+
cd your-project
|
|
36
|
+
gu
|
|
9
37
|
```
|
|
10
38
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`npx @tiens.nguyen/gu-cli fix-path
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
39
|
+
**Free to start, no account needed.** The first run creates a free 30-day token for this
|
|
40
|
+
computer automatically. Press Enter to use it. It also asks how this machine runs and which model
|
|
41
|
+
provider to use. Then type what you want done.
|
|
42
|
+
|
|
43
|
+
> **`gu: command not found`?** Run `npx @tiens.nguyen/gu-cli fix-path`. See [Troubleshooting](#troubleshooting).
|
|
44
|
+
|
|
45
|
+
## What it does
|
|
46
|
+
|
|
47
|
+
- **Writes the code, in your terminal.** Ask in plain words; gu reads the repo, edits the files,
|
|
48
|
+
runs the build and the tests, and tells you what it changed.
|
|
49
|
+
- **Your models.** Run [Ollama](https://ollama.com) or MLX on your own machine, or bring any
|
|
50
|
+
OpenAI-compatible provider. Switch at any time with `/model`.
|
|
51
|
+
- **It stays in the repo.** Every edit is a file you can read, diff and revert, and `/undo` puts
|
|
52
|
+
back what a stopped turn changed. No hidden workspace.
|
|
53
|
+
- **Knows your codebase.** Local RAG indexes the folder so the model finds the right files.
|
|
54
|
+
- **Extensible.** [MCP](https://modelcontextprotocol.io) servers, skills (`SKILL.md`), and hooks.
|
|
55
|
+
Already set up for Claude Code? `/switch-skill claude` reuses its `.claude` folder.
|
|
56
|
+
- **Tests in a real browser.** The `browser` tool drives Chrome to check the web pages it builds.
|
|
57
|
+
- **Private by default.** Logs hold no prompts, code or keys unless you run `gu --log`.
|
|
21
58
|
|
|
22
59
|
## Quick start
|
|
23
60
|
|
|
24
|
-
1.
|
|
25
|
-
|
|
26
|
-
2. `gu doctor`
|
|
27
|
-
(`gu doctor --client` installs only the agent dependencies when the models
|
|
28
|
-
3.
|
|
29
|
-
|
|
61
|
+
1. **Start gu.** Run `gu` in a project folder. Your free 30-day token is created on the spot:
|
|
62
|
+
no sign-up, no browser, no card.
|
|
63
|
+
2. **Check your models.** `gu doctor` sets up and tests the models on this machine.
|
|
64
|
+
(`gu doctor --client` installs only the agent's dependencies, when the models run elsewhere.)
|
|
65
|
+
3. **Ask.** Type what you want done. Conversations are saved per folder and resumed next time;
|
|
66
|
+
`/new` starts a fresh one.
|
|
30
67
|
|
|
31
|
-
|
|
32
|
-
`--license <pro token>` to choose a pro license up front; without it a first install is private,
|
|
33
|
-
meaning the whole stack runs on this machine.
|
|
68
|
+
## Models
|
|
34
69
|
|
|
35
|
-
|
|
70
|
+
gu uses up to three models, each with its own command:
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
| Command | Model | Saved |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `/model` | **coding**: writes the code | this session (`/quick-models` keeps 2 to flip with ← / →) |
|
|
75
|
+
| `/agent-model` | **agent**: talks to you and plans | this machine |
|
|
76
|
+
| `/embed-model` | **embedding**: RAG (`nomic-embed-text`, `qwen3-embedding`), local or remote | this machine; re-indexes what was indexed |
|
|
40
77
|
|
|
41
|
-
|
|
42
|
-
- `--name <text>` draws `<text>` instead of the gu logo in the startup banner (this run only). Letters, digits, space, `-` and `_` become block art; anything else, or a name over 12 characters, is shown as plain text.
|
|
43
|
-
- `--log` logs this run in detail to `~/.gonext/worker.log`. Without it only starts, stops and errors are logged.
|
|
44
|
-
- Ctrl-C stops following a turn.
|
|
78
|
+
Run `/doctor` any time to check that each one answers.
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
## Using gu
|
|
47
81
|
|
|
48
|
-
|
|
82
|
+
```
|
|
83
|
+
gu [-v|--verbose] [--log] [--color] [--name <text>] [--license <pro token>]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
| Flag | Effect |
|
|
49
87
|
| --- | --- |
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
88
|
+
| `-v`, `--verbose` | show RAG index activity in the bottom bar |
|
|
89
|
+
| `--log` | log this run in detail to `~/.gonext/worker.log` (default: starts, stops and errors only) |
|
|
90
|
+
| `--color` | colour the live thought and the thinking spinner |
|
|
91
|
+
| `--name <text>` | show `<text>` instead of the gu logo in the banner (this run only) |
|
|
92
|
+
| `--license <token>` | choose a Pro licence on first install |
|
|
53
93
|
|
|
54
|
-
|
|
55
|
-
model from another provider (for example OpenAI) can no longer leave chat pointed at the old
|
|
56
|
-
server. After picking, `gu doctor` shows the URL and model that are actually in use.
|
|
94
|
+
Ctrl-C stops the running turn. Type `/` at the prompt to see every command; Tab completes.
|
|
57
95
|
|
|
58
|
-
|
|
96
|
+
<details>
|
|
97
|
+
<summary><strong>All slash commands</strong></summary>
|
|
59
98
|
|
|
60
99
|
| Command | What it does |
|
|
61
100
|
| --- | --- |
|
|
62
|
-
| `/model`, `/agent-model`, `/embed-model` | switch models (see
|
|
63
|
-
| `/context-window [tokens\|auto]` | tell gu how big the coding model's context is (hosted models
|
|
64
|
-
| `/doctor` | check every configured model answers, especially the embedder |
|
|
65
|
-
| `/
|
|
66
|
-
| `/tokens` | break down the last prompt sent to the coding model — how much of it was RAG |
|
|
67
|
-
| `/prices [update]` | price and cache tables, and what the last turn and this session cost |
|
|
68
|
-
| `/rag` | where this folder's RAG knowledge base lives: local, cloud, or both |
|
|
69
|
-
| `/s3 [test]` | set up your own S3 bucket (RAG index, generated PDFs/downloads) and check the AWS keys |
|
|
70
|
-
| `/proxy [show\|set\|on\|off\|clear\|test]` | route model calls through an HTTP proxy (local calls always go direct) |
|
|
71
|
-
| `/test-auto` | toggle auto-test (verify and fix until it passes) for this folder |
|
|
101
|
+
| `/model`, `/quick-models`, `/agent-model`, `/embed-model` | switch models (see [Models](#models)) |
|
|
102
|
+
| `/context-window [tokens\|auto]` | tell gu how big the coding model's context is (hosted models can't be probed) |
|
|
103
|
+
| `/doctor` | check that every configured model answers, especially the embedder |
|
|
104
|
+
| `/new` | start a new conversation in this folder (the old one is archived) |
|
|
72
105
|
| `/undo [runId]` | put back the files the last stopped turn changed, from its backups (asks first) |
|
|
106
|
+
| `/test-auto` | toggle auto-test for this folder: verify and fix until it passes |
|
|
107
|
+
| `/browser [allow\|forget\|install\|cleanup]` | the agent's web browser, through the gu Chrome extension or gu's own Chrome window ([guide](browser-extension/README.md)) |
|
|
108
|
+
| `/shells` | long commands the agent left running in the background: status, output, stop one |
|
|
109
|
+
| `/rag` | where this folder's RAG index lives: local, cloud or both |
|
|
110
|
+
| `/tokens` | break down the last prompt sent to the coding model, including how much was RAG |
|
|
111
|
+
| `/prices [update]` | price tables, and what the last turn and this session cost |
|
|
112
|
+
| `/budget` | your API provider budgets and balances (Pro) |
|
|
113
|
+
| `/mcp [list\|tools\|add\|remove\|enable\|disable\|approve\|allow\|disallow\|reconnect]` | MCP servers, and which of their tools run without asking |
|
|
114
|
+
| `/hooks [list\|approve\|unapprove]` | commands gu runs on `onStart`, `onAsk`, `onToolDone`, `onTurnEnd` |
|
|
115
|
+
| `/switch-skill [claude\|gu]` | use Claude Code's (`.claude`) or gu's (`.gonext`) skills, MCP servers and hooks in this folder |
|
|
116
|
+
| `/skills-copy-to-claude [--dry-run] [name…]` | copy gu's skills into Claude Code's folders |
|
|
117
|
+
| `/proxy [show\|set\|on\|off\|clear\|test]` | route model calls through an HTTP proxy (local calls go direct) |
|
|
118
|
+
| `/s3 [test]` | use your own S3 bucket for the RAG index and generated files |
|
|
73
119
|
| `/server` | pick a deployment server (host/user) for this session |
|
|
74
|
-
| `/host-mode` | undo client mode
|
|
75
|
-
| `/save-settings
|
|
76
|
-
| `/save-preset
|
|
77
|
-
| `/hooks [list\|approve\|unapprove]` | commands gu runs on onStart, onAsk, onToolDone, onTurnEnd |
|
|
78
|
-
| `/mcp [list\|tools\|add\|remove\|enable\|disable\|approve\|allow\|disallow\|reconnect]` | manage MCP servers and which of their tools run without asking |
|
|
79
|
-
| `/switch-skill [claude\|gu]` | use Claude Code's (`.claude`) or gu's own (`.gonext`) skills, MCP servers and hooks for this folder |
|
|
120
|
+
| `/host-mode` | undo client mode: run agent turns on this machine again |
|
|
121
|
+
| `/save-settings`, `/load-settings` | move providers, models and key *names* (no secrets) to another machine |
|
|
122
|
+
| `/save-preset`, `/open-presets`, `/use-presets` | your whole setup, keys included, to copy, edit and apply (backs up first) |
|
|
80
123
|
| `/clear-logs` | show what logs, RAG indexes and old API versions take on disk, then ask what to clear |
|
|
81
|
-
| `/
|
|
82
|
-
| `/
|
|
124
|
+
| `/voucher CODE` | use a voucher code: adds free days to this computer's trial or your free account |
|
|
125
|
+
| `/support` | send the support team a request with gu's logs attached (secrets masked; shows what goes first) |
|
|
126
|
+
| `/help`, `/exit` | list commands / quit |
|
|
83
127
|
|
|
84
|
-
Each skill in `~/.gonext/skills/<name>/SKILL.md` (
|
|
85
|
-
slash command of its own. Run `/help` in the REPL for the list your version offers.
|
|
128
|
+
Each skill in `~/.gonext/skills/<name>/SKILL.md` (or the folder's `.gonext/skills`) is a slash command too.
|
|
86
129
|
|
|
87
|
-
|
|
130
|
+
</details>
|
|
131
|
+
|
|
132
|
+
<details>
|
|
133
|
+
<summary><strong>Shell commands</strong></summary>
|
|
88
134
|
|
|
89
135
|
```
|
|
90
|
-
gu status worker
|
|
91
|
-
gu start | stop | restart
|
|
92
|
-
gu logs [--lines N] [-f] tail the
|
|
93
|
-
gu doctor [--client] set up models
|
|
94
|
-
gu rag
|
|
95
|
-
gu
|
|
96
|
-
gu rag --test can this machine embed and retrieve?
|
|
97
|
-
gu api <start|stop|restart|status|upgrade> the local API (Local / Client Expert only)
|
|
136
|
+
gu status worker status and the running model servers
|
|
137
|
+
gu start | stop | restart the background worker (stop --models: model servers too)
|
|
138
|
+
gu logs [--lines N] [-f] tail the worker log; gu logs <port|pid|chat|embed> for a model server
|
|
139
|
+
gu doctor [--client] set up and check models on this machine
|
|
140
|
+
gu rag [plan [path] | --test] test the embedder, list knowledge bases, estimate index time
|
|
141
|
+
gu api <start|stop|restart|status|upgrade> the local API (private machines)
|
|
98
142
|
gu workspace revert [runId] undo an agent run's file edits from its backups
|
|
99
|
-
gu
|
|
100
|
-
gu
|
|
143
|
+
gu support send a support request with masked logs
|
|
144
|
+
gu test-proxy check model calls through a proxy
|
|
145
|
+
gu mongo migrate-from-docker macOS: move a Docker MongoDB to Homebrew
|
|
101
146
|
gu clear [--scratch|--keep-history] [--yes] back to a fresh machine (asks first)
|
|
102
|
-
gu fix-path | fix-powershell repair PATH /
|
|
147
|
+
gu fix-path | fix-powershell repair PATH / remove PowerShell's `gu` alias
|
|
103
148
|
```
|
|
104
149
|
|
|
105
|
-
`gu-cli`
|
|
106
|
-
`token [<token>]`, `ws-ping-test` and `simulate-chat [text
|
|
107
|
-
|
|
150
|
+
`gu-cli` takes the same commands, plus `login [--api-base <url>]`, `set <workerKey>`,
|
|
151
|
+
`token [<token>]`, `ws-ping-test` and `simulate-chat [text…]`.
|
|
152
|
+
|
|
153
|
+
</details>
|
|
108
154
|
|
|
109
|
-
##
|
|
155
|
+
## Where gu keeps things
|
|
110
156
|
|
|
111
157
|
| Path | Contents |
|
|
112
158
|
| --- | --- |
|
|
113
|
-
| `~/.gonext/worker.env` | sign-in: worker key and API
|
|
114
|
-
| `~/.gonext/sessions/` |
|
|
115
|
-
| `~/.gonext/worker.log` |
|
|
116
|
-
| `~/.gonext/local-api.log` | the local API's log |
|
|
159
|
+
| `~/.gonext/worker.env` | sign-in: worker key and API address |
|
|
160
|
+
| `~/.gonext/sessions/` | conversation history per folder (`archive/` holds old ones) |
|
|
161
|
+
| `~/.gonext/worker.log`, `~/.gonext/local-api.log` | logs |
|
|
117
162
|
| `~/.gonext/mcp.json` | MCP servers and their allow rules |
|
|
118
|
-
| `~/.gonext/skills/`,
|
|
163
|
+
| `~/.gonext/skills/`, `<folder>/.gonext/skills/` | skills: everywhere / this folder only |
|
|
164
|
+
|
|
165
|
+
## Plans
|
|
166
|
+
|
|
167
|
+
| | How long | What you need |
|
|
168
|
+
| --- | --- | --- |
|
|
169
|
+
| **Free trial** | 30 days, starts on the first run | nothing: the token is created for you |
|
|
170
|
+
| **Free plan** | 3 more months on that computer | a free account at [gucli.com](https://gucli.com) |
|
|
171
|
+
| **Pro** | while subscribed | more computers, plus provider budgets |
|
|
172
|
+
|
|
173
|
+
Got a **voucher code**? Type `/voucher CODE` in gu (or enter it on your
|
|
174
|
+
[account page](https://gucli.com/account)) for extra free days on top.
|
|
119
175
|
|
|
120
|
-
|
|
176
|
+
Free time belongs to the computer: reinstalling doesn't restart it. Already have a token? Paste
|
|
177
|
+
it at the first-run question instead, or later with `gu-cli token <token>`. Current prices: [gucli.com/pricing](https://gucli.com/pricing).
|
|
121
178
|
|
|
122
|
-
|
|
179
|
+
## Troubleshooting
|
|
123
180
|
|
|
124
|
-
|
|
181
|
+
**`gu: command not found` after install.** npm 11 skips install scripts it wasn't told to allow,
|
|
182
|
+
so the step that adds npm's bin folder to your PATH may not have run. Run
|
|
183
|
+
`npx @tiens.nguyen/gu-cli fix-path`. It also finds what an old install left behind (broken links,
|
|
184
|
+
a second gu from another npm, a PATH line pinned to an old Homebrew folder) and asks before
|
|
185
|
+
fixing each one. `--check` only reports.
|
|
125
186
|
|
|
126
|
-
|
|
127
|
-
[smolagents](https://github.com/huggingface/smolagents) agent running on your local model. It
|
|
128
|
-
needs `pip install smolagents certifi` in the worker's Python environment (`certifi` gives
|
|
129
|
-
HTTPS a trusted CA bundle on macOS). Agent mode is blocked for cloud models; pick a local
|
|
130
|
-
MLX or Ollama model first. The script is `gonext_agent_chat.py`.
|
|
187
|
+
**Windows: `gu` runs `Get-Unique`.** PowerShell has a built-in `gu` alias. Run `gu-cli fix-powershell`.
|
|
131
188
|
|
|
132
|
-
|
|
189
|
+
**Node version.** Node.js 18+ runs the CLI. A private machine (API and database on this
|
|
190
|
+
computer) needs **Node.js 20.19+**.
|
|
133
191
|
|
|
134
|
-
|
|
135
|
-
body, classifies it (2xx/3xx/4xx/5xx/timeout/network_error), and the local model writes a one-line summary.
|
|
136
|
-
- **Agents (`agentic`)**: a smolagents agent writes the summary; the measurement stays the source of truth.
|
|
192
|
+
**A change in a model or setting seems ignored.** `gu doctor` shows the model and URL actually in use.
|
|
137
193
|
|
|
138
|
-
|
|
194
|
+
**Still stuck?** Run `gu support` (or `/support` in gu, or click **? support** in the footer). It
|
|
195
|
+
sends the team your request with gu's logs attached, secrets masked, and shows you what goes
|
|
196
|
+
before sending.
|
|
197
|
+
|
|
198
|
+
<details>
|
|
199
|
+
<summary><strong>Advanced: worker jobs and environment variables</strong></summary>
|
|
200
|
+
|
|
201
|
+
The background worker also runs jobs queued from the [web app](https://gucli.com) on this machine:
|
|
202
|
+
|
|
203
|
+
- **Agent chat**: choosing **Agent** in the web app runs a
|
|
204
|
+
[smolagents](https://github.com/huggingface/smolagents) agent on your local model
|
|
205
|
+
(`pip install smolagents certifi`). Cloud models are not allowed here; pick a local MLX or Ollama model.
|
|
206
|
+
- **API checks**: the worker makes the HTTP request itself, measures status, latency and body,
|
|
207
|
+
and a local model writes a one-line summary.
|
|
139
208
|
|
|
140
209
|
| Variable | Meaning |
|
|
141
210
|
| --- | --- |
|
|
142
|
-
| `GONEXT_API_BASE`, `GONEXT_WORKER_KEY` | API
|
|
143
|
-
| `GONEXT_PROBE_PYTHON` | Python for
|
|
144
|
-
| `GONEXT_MLX_LM_PYTHON` | Python for the MLX
|
|
145
|
-
| `GONEXT_OCR_MODEL_PATH` | local GLM-OCR
|
|
146
|
-
| `GONEXT_OCR_DEBUG=1` | print raw OCR
|
|
147
|
-
| `GONEXT_SIMULATE_TEXT` | default reply
|
|
211
|
+
| `GONEXT_API_BASE`, `GONEXT_WORKER_KEY` | API address and worker key (normally from `~/.gonext/worker.env`) |
|
|
212
|
+
| `GONEXT_PROBE_PYTHON` | Python for agent scripts (default: `GONEXT_MLX_LM_PYTHON` or `python3`) |
|
|
213
|
+
| `GONEXT_MLX_LM_PYTHON` | Python for the MLX probe (default `python3`) |
|
|
214
|
+
| `GONEXT_OCR_MODEL_PATH` | local GLM-OCR directory (default `~/mlx-models/GLM-OCR-bf16`) |
|
|
215
|
+
| `GONEXT_OCR_DEBUG=1` | print raw OCR output |
|
|
216
|
+
| `GONEXT_SIMULATE_TEXT` | default reply for `simulate-chat` |
|
|
148
217
|
|
|
149
|
-
|
|
218
|
+
</details>
|
|
150
219
|
|
|
151
|
-
|
|
152
|
-
npm test # python unittest + node --test
|
|
153
|
-
npm run preflight # checks before a release
|
|
154
|
-
npm run publish:org # npm publish --access public
|
|
155
|
-
```
|
|
220
|
+
## License
|
|
156
221
|
|
|
157
|
-
|
|
222
|
+
[MIT](./LICENSE) © Tien Nguyen Song
|
package/cli-token.mjs
CHANGED
|
@@ -533,6 +533,35 @@ export async function renewToken({ mode, apiBase, hostedBase, token, workerKey,
|
|
|
533
533
|
}
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
+
/**
|
|
537
|
+
* /voucher CODE (#425): redeem a marketing code with this computer's token as the credential — a
|
|
538
|
+
* trial (no account) or an account. Returns { ok: true, days, token } — the token already carries the
|
|
539
|
+
* extra days — or { ok: false, why, error } where error is the service's own sentence. Never throws.
|
|
540
|
+
*/
|
|
541
|
+
export async function redeemVoucher({ mode, apiBase, hostedBase, token, device, code, fetchImpl = fetch, timeoutMs = 8000 }) {
|
|
542
|
+
const base = String(mode === "expert" ? hostedBase : apiBase || hostedBase).replace(/\/+$/, "");
|
|
543
|
+
if (!token) return { ok: false, why: "no-token", error: "This computer has no gu token yet — restart gu to get the free trial, then try again." };
|
|
544
|
+
if (!base) return { ok: false, why: "no-api", error: "No gu service address is set on this computer." };
|
|
545
|
+
try {
|
|
546
|
+
const res = await fetchImpl(`${base}/api/billing/cli-token/voucher`, {
|
|
547
|
+
method: "POST",
|
|
548
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
549
|
+
body: JSON.stringify({ code: String(code ?? ""), ...(device ? { device } : {}) }),
|
|
550
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
551
|
+
});
|
|
552
|
+
const data = await res.json().catch(() => null);
|
|
553
|
+
if (!res.ok) {
|
|
554
|
+
const error = typeof data?.error === "string" && data.error
|
|
555
|
+
? data.error
|
|
556
|
+
: res.status === 404 ? "This gu service does not take vouchers yet." : `The gu service answered HTTP ${res.status}.`;
|
|
557
|
+
return { ok: false, why: typeof data?.why === "string" ? data.why : `http-${res.status}`, error };
|
|
558
|
+
}
|
|
559
|
+
return { ok: true, days: Number(data?.days ?? 0), token: typeof data?.token === "string" ? data.token : "" };
|
|
560
|
+
} catch (e) {
|
|
561
|
+
return { ok: false, why: "network", error: e?.name === "TimeoutError" ? "The gu service did not answer in time. Try again." : "Can't reach the gu service right now." };
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
536
565
|
// ---------------------------------------------------------------------------
|
|
537
566
|
// WHAT THE BAR SAYS
|
|
538
567
|
// ---------------------------------------------------------------------------
|
package/gu-repl.mjs
CHANGED
|
@@ -1041,6 +1041,7 @@ const COMMANDS = [
|
|
|
1041
1041
|
{ name: "/shells", desc: "long commands the agent left running in the background — status, runtime, command, output; stop one" },
|
|
1042
1042
|
{ name: "/budget", desc: "short view of your API provider budgets and balances (Pro) — the same items as the web /providers page" },
|
|
1043
1043
|
{ name: "/tokens", desc: "break down the last prompt sent to the coding model — how much of it was RAG" },
|
|
1044
|
+
{ name: "/voucher", usage: "/voucher CODE", desc: "use a voucher code — it adds free days to this computer's trial or your free account" },
|
|
1044
1045
|
{ name: "/support", desc: "send the gu support team a request with gu's logs attached (secrets masked) — shows what goes first, then asks" },
|
|
1045
1046
|
{ name: "/clear-logs", desc: "show what gu's logs, RAG indexes and old API versions take on disk, then ask what to clear — the logs first" },
|
|
1046
1047
|
{ name: "/prices", usage: "/prices [update]", desc: "the price table and the cache table, and what the last turn and this session cost — \"update\" applies gu's newest published tables" },
|
|
@@ -8397,6 +8398,54 @@ async function noteTurnCost(inputTokens, breakdown, coder = null) {
|
|
|
8397
8398
|
* use marked; what the last turn and this session cost; and, when gu has published newer tables,
|
|
8398
8399
|
* that they are waiting. `/prices update` applies them — the same as clicking the notice.
|
|
8399
8400
|
*/
|
|
8401
|
+
/**
|
|
8402
|
+
* /voucher CODE (#425) — a marketing code that adds free days. Works on the free trial (no account)
|
|
8403
|
+
* and on a free account; the service answers with a fresh token that already carries the days, so
|
|
8404
|
+
* the bar and the licence see them at once — no restart.
|
|
8405
|
+
*/
|
|
8406
|
+
async function runVoucherCommand(line = "/voucher") {
|
|
8407
|
+
const code = line.trim().slice("/voucher".length).trim();
|
|
8408
|
+
console.log("");
|
|
8409
|
+
if (!code) {
|
|
8410
|
+
console.log(GUTTER + "Type the code after the command: " + accent("/voucher CODE") + "\n");
|
|
8411
|
+
return;
|
|
8412
|
+
}
|
|
8413
|
+
const ct = await import("./cli-token.mjs");
|
|
8414
|
+
const pk = ct.publicKey();
|
|
8415
|
+
const dir = join(homedir(), ".gonext");
|
|
8416
|
+
const state = await ct.readState(dir);
|
|
8417
|
+
const { DEFAULT_API_BASE } = await import("./device-login.mjs");
|
|
8418
|
+
const device = await ct.thisDevice({ instanceId: myInstanceId || "" }).catch(() => null);
|
|
8419
|
+
const r = await withPendingStatus("checking the voucher", () =>
|
|
8420
|
+
ct.redeemVoucher({
|
|
8421
|
+
mode: isClientExpert() ? "expert" : "client",
|
|
8422
|
+
apiBase,
|
|
8423
|
+
hostedBase: String(process.env.GU_TOKEN_API_BASE ?? "").trim() || DEFAULT_API_BASE,
|
|
8424
|
+
token: state.token,
|
|
8425
|
+
device,
|
|
8426
|
+
code,
|
|
8427
|
+
})
|
|
8428
|
+
);
|
|
8429
|
+
if (!r.ok) {
|
|
8430
|
+
console.log(GUTTER + yellow(r.error) + "\n");
|
|
8431
|
+
return;
|
|
8432
|
+
}
|
|
8433
|
+
// Kept only if it checks out here, like every renewal.
|
|
8434
|
+
const check = r.token && pk ? ct.verifyCliToken(r.token, pk) : { ok: false };
|
|
8435
|
+
if (check.ok) {
|
|
8436
|
+
await ct.writeState(dir, { token: r.token, lastAttemptAt: new Date().toISOString() }).catch(() => {});
|
|
8437
|
+
const d = ct.enforcementDecision({ token: r.token, publicKeyB64: pk, site: ct.siteUrl(), deviceId: await thisDeviceId(ct) });
|
|
8438
|
+
licenceBlock = d.enforced && !d.allowed ? d.why : null;
|
|
8439
|
+
tokenNotice = ct.barNotice({ token: r.token, publicKeyB64: pk, mode: isClientExpert() ? "expert" : "client", now: new Date(), attempted: true, site: ct.siteUrl() });
|
|
8440
|
+
}
|
|
8441
|
+
const p = check.ok ? check.payload : null;
|
|
8442
|
+
const until = p?.plan === "trial" ? p.paidUntil : p?.plan === "free" && typeof p.freeUntil === "string" ? p.freeUntil : null;
|
|
8443
|
+
console.log(GUTTER + accent(`✓ +${r.days} free days added.`) + (until ? ` Free on this computer until ${ct.shortDate(until)}.` : ""));
|
|
8444
|
+
if (!check.ok) console.log(GUTTER + dim("gu picks the days up at its next token refresh (within the hour)."));
|
|
8445
|
+
console.log("");
|
|
8446
|
+
if (barPinned()) paintBar();
|
|
8447
|
+
}
|
|
8448
|
+
|
|
8400
8449
|
async function runPricesCommand(line = "/prices") {
|
|
8401
8450
|
const mp = await import("./model-prices.mjs");
|
|
8402
8451
|
console.log("");
|
|
@@ -15080,6 +15129,10 @@ async function main() {
|
|
|
15080
15129
|
await runPricesCommand(line);
|
|
15081
15130
|
continue;
|
|
15082
15131
|
}
|
|
15132
|
+
if (line === "/voucher" || line.startsWith("/voucher ")) {
|
|
15133
|
+
await runVoucherCommand(line);
|
|
15134
|
+
continue;
|
|
15135
|
+
}
|
|
15083
15136
|
if (line === "/clear-logs") {
|
|
15084
15137
|
await runClearLogs();
|
|
15085
15138
|
continue;
|
package/package.json
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gu-cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
"version": "1.0.883",
|
|
4
|
+
"description": "gu: the coding agent in your terminal. It reads your repo, makes the change and runs the tests, with your own models (Ollama, MLX or any OpenAI-compatible provider). macOS, Linux and Windows.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"coding-agent",
|
|
8
|
+
"cli",
|
|
9
|
+
"terminal",
|
|
10
|
+
"llm",
|
|
11
|
+
"ollama",
|
|
12
|
+
"mlx",
|
|
13
|
+
"openai",
|
|
14
|
+
"agent",
|
|
15
|
+
"mcp",
|
|
16
|
+
"rag",
|
|
17
|
+
"code-assistant"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://gucli.com",
|
|
12
20
|
"bugs": {
|
|
13
|
-
"url": "https://
|
|
21
|
+
"url": "https://gucli.com/support"
|
|
14
22
|
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Tien Nguyen Song",
|
|
25
|
+
"type": "module",
|
|
15
26
|
"bin": {
|
|
16
27
|
"gu-cli": "gu-cli.mjs",
|
|
17
28
|
"gu-local-worker": "gu-cli.mjs",
|