microgit-cli 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -2
- package/package.json +26 -4
- package/src/api/index.js +111 -0
- package/src/api/index.js.map +1 -0
- package/src/cli/index.js +58 -0
- package/src/cli/index.js.map +1 -0
- package/src/cli/repl.js +312 -0
- package/src/cli/repl.js.map +1 -0
- package/src/commands/auth.js +305 -0
- package/src/commands/auth.js.map +1 -0
- package/src/commands/config.js +106 -0
- package/src/commands/config.js.map +1 -0
- package/src/commands/git.js +159 -0
- package/src/commands/git.js.map +1 -0
- package/src/commands/registry.js +139 -0
- package/src/commands/registry.js.map +1 -0
- package/src/commands/repos.js +256 -0
- package/src/commands/repos.js.map +1 -0
- package/src/config/index.js +124 -0
- package/src/config/index.js.map +1 -0
- package/src/config/settings.js +34 -0
- package/src/config/settings.js.map +1 -0
- package/src/error/index.js +14 -0
- package/src/error/index.js.map +1 -0
- package/src/git/index.js +163 -0
- package/src/git/index.js.map +1 -0
- package/src/help/index.js +52 -0
- package/src/help/index.js.map +1 -0
- package/src/index.js +9 -0
- package/src/index.js.map +1 -0
- package/src/theme/banner.js +44 -0
- package/src/theme/banner.js.map +1 -0
- package/src/theme/index.js +5 -0
- package/src/theme/index.js.map +1 -0
- package/src/theme/logo.js +36 -0
- package/src/theme/logo.js.map +1 -0
- package/src/theme/palette.js +32 -0
- package/src/theme/palette.js.map +1 -0
- package/src/ui/index.js +104 -0
- package/src/ui/index.js.map +1 -0
- package/src/util/index.js +30 -0
- package/src/util/index.js.map +1 -0
package/README.md
CHANGED
|
@@ -1,3 +1,258 @@
|
|
|
1
|
-
#
|
|
1
|
+
# microgit-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`microgit` — **your own git server — clone, push, host**.
|
|
4
|
+
|
|
5
|
+
A small command line client for [git-server](../readme.md). It keeps the API
|
|
6
|
+
session in a local config file, creates and deletes repositories, and wraps the
|
|
7
|
+
usual `git` commands so push/pull work with the account credentials.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd cli
|
|
14
|
+
npm install
|
|
15
|
+
npm run build
|
|
16
|
+
npm link # puts `microgit` on PATH; or: npm install -g .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Without `npm link`, run the built entry point directly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node dist/index.js --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Interactive shell
|
|
26
|
+
|
|
27
|
+
Run `microgit` with no arguments (or `microgit shell`) to open the shell:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
● ━━━ ●
|
|
31
|
+
┣━━━ ● microgit-cli • gitserver v0.1.0
|
|
32
|
+
┗━━━ ● your own git server — clone, push, host
|
|
33
|
+
|
|
34
|
+
# server http://{HOST_NAME}:{PORT}
|
|
35
|
+
# account alice · 7d left
|
|
36
|
+
|
|
37
|
+
✓ ready · type help to list the commands
|
|
38
|
+
# one-shot microgit --help
|
|
39
|
+
|
|
40
|
+
alice@microgit:~/repos$ new app
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Starting the shell clears the screen and the scrollback first, so the banner is
|
|
44
|
+
the only thing on the page. The prompt shows the signed-in account and the
|
|
45
|
+
current folder. Commands are the same words as the one-shot CLI, and the shell
|
|
46
|
+
adds:
|
|
47
|
+
|
|
48
|
+
| Keys / command | What it does |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `↑` / `↓` | History, kept in `~/.microgit/history` |
|
|
51
|
+
| `Tab` | Complete command names, `config` actions and `--options` |
|
|
52
|
+
| `Ctrl+C` | Clear the line; on an empty line it leaves the shell |
|
|
53
|
+
| `Ctrl+D` | Leave the shell |
|
|
54
|
+
| `help` | The command list |
|
|
55
|
+
| `clear` | Clear the screen and redraw the banner |
|
|
56
|
+
| `exit` / `quit` | Leave the shell |
|
|
57
|
+
|
|
58
|
+
Errors never close the shell. A command that asks something (`login`,
|
|
59
|
+
`commit`, `delete`, `new`) asks inside the same prompt.
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# 1. Point the CLI at the server and sign in. Use the host and port the
|
|
65
|
+
# server actually listens on, or its domain behind a reverse proxy.
|
|
66
|
+
microgit login --url http://{HOST_NAME}:{PORT}
|
|
67
|
+
|
|
68
|
+
# 2. Create a repository and a local checkout with a first commit,
|
|
69
|
+
# then push it.
|
|
70
|
+
microgit new app --init
|
|
71
|
+
|
|
72
|
+
# 3. Work as usual; commit also pushes.
|
|
73
|
+
cd app
|
|
74
|
+
echo "hello" > hello.txt
|
|
75
|
+
microgit commit -m "Add hello.txt"
|
|
76
|
+
|
|
77
|
+
# 4. Check the session or list repositories.
|
|
78
|
+
microgit whoami
|
|
79
|
+
microgit list
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Signing in again reuses a valid stored session, so the username and password are
|
|
83
|
+
only asked for once. Add `--force` to sign in as another account.
|
|
84
|
+
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
| Command | What it does |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `microgit login` | Sign in, store the API token and optionally the git password |
|
|
90
|
+
| `microgit register` | Create an account (when the server allows registration) and sign in |
|
|
91
|
+
| `microgit logout` | Revoke the server session, remove the local token and password |
|
|
92
|
+
| `microgit whoami` | Show the signed-in account, server and expiry |
|
|
93
|
+
| `microgit new <name>` | Create a repository; asks public/private, plus `--private`, `--public`, `--clone`, `--init` |
|
|
94
|
+
| `microgit list` | List repositories; `--owner`, `--name`, `--public`, `--private` |
|
|
95
|
+
| `microgit delete <name\|owner/name>` | Delete a repository; asks before doing it |
|
|
96
|
+
| `microgit clone <name\|owner/name> [dir]` | Clone using the clone URL from the server |
|
|
97
|
+
| `microgit init [dir]` | Local only: project folder, `git init` and an empty `README.md` |
|
|
98
|
+
| `microgit status` | `git status --short --branch` |
|
|
99
|
+
| `microgit commit -m "message"` | `git add -A`, commit, then push |
|
|
100
|
+
| `microgit push [git args]` | Push with stored credentials |
|
|
101
|
+
| `microgit pull [git args]` | Pull with stored credentials |
|
|
102
|
+
| `microgit config` | Show the config file, server and session |
|
|
103
|
+
|
|
104
|
+
Every command accepts `-h` / `--help`. Commands that print API data also accept
|
|
105
|
+
`--json` where it makes sense (`whoami`, `list`, `new`, `delete`, `clone`,
|
|
106
|
+
`login`, `register`, `logout`, `config`).
|
|
107
|
+
|
|
108
|
+
### Creating repositories
|
|
109
|
+
|
|
110
|
+
Without `--private` or `--public` the CLI asks for the visibility, and Enter
|
|
111
|
+
creates a public repository:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
microgit new app # asks: public or private
|
|
115
|
+
microgit new secret --private
|
|
116
|
+
microgit new app --public
|
|
117
|
+
microgit new app --clone # create it and clone the empty repository
|
|
118
|
+
microgit new app --init # create it, README, first commit and push
|
|
119
|
+
microgit new app --init --dir ./projects/app
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The request sent to the server is `{"name":"app","private":false}` (or `true`).
|
|
123
|
+
|
|
124
|
+
`new` prints the server's clone URL. Existing repositories are not an error:
|
|
125
|
+
the API returns `created: false` and the CLI says it already exists.
|
|
126
|
+
|
|
127
|
+
### Starting a local repository
|
|
128
|
+
|
|
129
|
+
`microgit init` is local only — it does not talk to the server:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
microgit init app # creates ./app, .git on main, empty README.md
|
|
133
|
+
microgit init # initialise the current folder
|
|
134
|
+
microgit init --dir ./projects/app
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
It refuses to run inside an existing repository, and reuses the saved account
|
|
138
|
+
name for `user.name` / `user.email` when git has none configured.
|
|
139
|
+
|
|
140
|
+
### Deleting repositories
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
microgit delete app # asks for confirmation
|
|
144
|
+
microgit delete app --yes # non-interactive
|
|
145
|
+
microgit delete bob/app --yes # administrators can delete for another owner
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Listing repositories
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
microgit list
|
|
152
|
+
microgit list --owner alice
|
|
153
|
+
microgit list --private --limit 20
|
|
154
|
+
microgit list --public --page 2 --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Authentication and git credentials
|
|
158
|
+
|
|
159
|
+
The JSON API uses the session token returned by `login`. The CLI sends it as
|
|
160
|
+
`Authorization: Bearer <token>` and stores it in the config file. Git uses HTTP
|
|
161
|
+
Basic with the **account username and password**, not the API token.
|
|
162
|
+
|
|
163
|
+
For that reason `login` only stores the token by default. Add
|
|
164
|
+
`--save-password` to also store the password, which makes `push`, `pull`,
|
|
165
|
+
`clone` of a private repository and `new --init` work without another prompt:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
microgit login --save-password
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The password is stored in plain text in the config file. The file is created
|
|
172
|
+
with mode `600` on Unix; on Windows the user profile permissions apply. If you
|
|
173
|
+
do not want to store it, leave `--save-password` off and let `git` ask when it
|
|
174
|
+
needs credentials, or set `MICROGIT_PASSWORD` for one command:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
MICROGIT_PASSWORD='alicepass1' microgit push
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Logging in again keeps a saved password when the username is unchanged; pass
|
|
181
|
+
`--forget-password` to remove it.
|
|
182
|
+
|
|
183
|
+
The CLI passes credentials to git as an `http.extraHeader`, so they are **not**
|
|
184
|
+
written into `.git/config` and are not part of the clone URL.
|
|
185
|
+
|
|
186
|
+
The server's message of the day, when it publishes one, is printed after
|
|
187
|
+
`login`, `register` and `whoami`.
|
|
188
|
+
|
|
189
|
+
## Config file
|
|
190
|
+
|
|
191
|
+
Default location: `~/.microgit/config.json`. Use `MICROGIT_CONFIG` to point at a
|
|
192
|
+
different file, for example one per server:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
MICROGIT_CONFIG=~/.microgit/work.json microgit login --url https://git.example.com
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The shell keeps its history next to it, in `~/.microgit/history`.
|
|
199
|
+
|
|
200
|
+
Show it with:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
microgit config
|
|
204
|
+
microgit config set url https://git.example.com
|
|
205
|
+
microgit config set username alice
|
|
206
|
+
microgit config unset password
|
|
207
|
+
microgit config clear
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Environment variables override the file for one command:
|
|
211
|
+
|
|
212
|
+
| Variable | Meaning |
|
|
213
|
+
| --- | --- |
|
|
214
|
+
| `MICROGIT_URL` | Server URL |
|
|
215
|
+
| `MICROGIT_TOKEN` | API token, instead of the stored one |
|
|
216
|
+
| `MICROGIT_USERNAME` | Account name, also used for git |
|
|
217
|
+
| `MICROGIT_PASSWORD` | Git password, also used for git |
|
|
218
|
+
| `MICROGIT_CONFIG` | Config file path |
|
|
219
|
+
| `NO_COLOR` | Disable colours |
|
|
220
|
+
|
|
221
|
+
## Renaming the CLI
|
|
222
|
+
|
|
223
|
+
Package name, command name, banner, tagline, label, version, environment prefix
|
|
224
|
+
and config folder all live in [`src/settings.ts`](src/settings.ts). Change them
|
|
225
|
+
there and update `name` / `bin` in `package.json` to match.
|
|
226
|
+
|
|
227
|
+
## Domains
|
|
228
|
+
|
|
229
|
+
The server's `domain` / `publicUrl` setting changes the clone URL it returns.
|
|
230
|
+
`microgit clone` and `microgit new --init` use that URL, so the name must resolve
|
|
231
|
+
from the machine running the CLI. If the public domain is only reachable from
|
|
232
|
+
outside, log in with the internal URL instead; the repository still works, but a
|
|
233
|
+
`domain` configured on the server wins when it builds the clone URL.
|
|
234
|
+
|
|
235
|
+
## Exit codes
|
|
236
|
+
|
|
237
|
+
- `0` success
|
|
238
|
+
- `1` expected failure (bad input, server error, git failure)
|
|
239
|
+
|
|
240
|
+
Errors are printed once without a stack trace. `--json` output is still the
|
|
241
|
+
server's JSON (or, for `whoami`/`config`, a small object with the same fields).
|
|
242
|
+
|
|
243
|
+
## Troubleshooting
|
|
244
|
+
|
|
245
|
+
- **`You are not signed in`** — run `microgit login`; the token expires after the
|
|
246
|
+
server's `sessionDays`.
|
|
247
|
+
- **`login` keeps signing in as the old account** — the stored session is still
|
|
248
|
+
valid; pass `--force` or run `microgit logout` first.
|
|
249
|
+
- **`Authentication failed` on push** — `login --save-password`, or set
|
|
250
|
+
`MICROGIT_PASSWORD`; the username/password is your account, not the API token.
|
|
251
|
+
- **`No 'origin' remote`** — use `microgit clone <owner/name>` or add a remote
|
|
252
|
+
with git.
|
|
253
|
+
- **`Directory is not empty`** — `--clone`/`--init` refuse to overwrite files;
|
|
254
|
+
choose another `--dir`.
|
|
255
|
+
- **`Already a git repository`** — `microgit init` skips folders that already
|
|
256
|
+
have `.git`.
|
|
257
|
+
- **Server unreachable** — pass `--url` or set `MICROGIT_URL`; check the server's
|
|
258
|
+
`host`, `port` and reverse proxy.
|
package/package.json
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "microgit-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Command line client for a self-hosted git server (microgit)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"microgit": "src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node src/index.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"git",
|
|
22
|
+
"git-cli",
|
|
23
|
+
"microgit",
|
|
24
|
+
"self-hosted"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|
package/src/api/index.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { CliError } from '../error/index.js';
|
|
2
|
+
import { BIN } from '../config/settings.js';
|
|
3
|
+
/** A non-2xx response from the server. */
|
|
4
|
+
export class ApiError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
constructor(status, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'ApiError';
|
|
9
|
+
this.status = status;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Minimal typed client for the JSON API; the token goes in a Bearer header. */
|
|
13
|
+
export class ApiClient {
|
|
14
|
+
baseUrl;
|
|
15
|
+
token;
|
|
16
|
+
constructor(baseUrl, token) {
|
|
17
|
+
this.baseUrl = baseUrl;
|
|
18
|
+
this.token = token;
|
|
19
|
+
}
|
|
20
|
+
endpoint(pathname) {
|
|
21
|
+
return `${this.baseUrl}${pathname}`;
|
|
22
|
+
}
|
|
23
|
+
repoPath(owner, name) {
|
|
24
|
+
return `/api/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`;
|
|
25
|
+
}
|
|
26
|
+
async request(method, pathname, body) {
|
|
27
|
+
const headers = { Accept: 'application/json' };
|
|
28
|
+
if (this.token)
|
|
29
|
+
headers.Authorization = `Bearer ${this.token}`;
|
|
30
|
+
if (body !== undefined)
|
|
31
|
+
headers['Content-Type'] = 'application/json';
|
|
32
|
+
let response;
|
|
33
|
+
try {
|
|
34
|
+
response = await fetch(this.endpoint(pathname), {
|
|
35
|
+
method,
|
|
36
|
+
headers,
|
|
37
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
38
|
+
signal: AbortSignal.timeout(20_000),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43
|
+
throw new CliError(`Cannot reach ${this.baseUrl}: ${message}`);
|
|
44
|
+
}
|
|
45
|
+
const text = await response.text();
|
|
46
|
+
let data = null;
|
|
47
|
+
if (text.trim() !== '') {
|
|
48
|
+
try {
|
|
49
|
+
data = JSON.parse(text);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
data = text;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
let message = `HTTP ${response.status}`;
|
|
57
|
+
if (data !== null &&
|
|
58
|
+
typeof data === 'object' &&
|
|
59
|
+
'error' in data &&
|
|
60
|
+
typeof data.error === 'string') {
|
|
61
|
+
message = data.error;
|
|
62
|
+
}
|
|
63
|
+
if (response.status === 401 && this.token) {
|
|
64
|
+
message += `\nRun \`${BIN} login\` to start a new session.`;
|
|
65
|
+
}
|
|
66
|
+
throw new ApiError(response.status, message);
|
|
67
|
+
}
|
|
68
|
+
if (data === null || typeof data !== 'object') {
|
|
69
|
+
throw new CliError(`${this.endpoint(pathname)} did not return JSON. Is ${this.baseUrl} a git-server?`);
|
|
70
|
+
}
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
73
|
+
login(username, password) {
|
|
74
|
+
return this.request('POST', '/api/login', { username, password });
|
|
75
|
+
}
|
|
76
|
+
register(username, password) {
|
|
77
|
+
return this.request('POST', '/api/register', { username, password });
|
|
78
|
+
}
|
|
79
|
+
logout() {
|
|
80
|
+
return this.request('POST', '/api/logout');
|
|
81
|
+
}
|
|
82
|
+
me() {
|
|
83
|
+
return this.request('GET', '/api/me');
|
|
84
|
+
}
|
|
85
|
+
listRepos(options = {}) {
|
|
86
|
+
const query = new URLSearchParams();
|
|
87
|
+
if (options.owner)
|
|
88
|
+
query.set('owner', options.owner);
|
|
89
|
+
if (options.name)
|
|
90
|
+
query.set('name', options.name);
|
|
91
|
+
if (options.private !== undefined && options.private !== null) {
|
|
92
|
+
query.set('private', options.private ? 'true' : 'false');
|
|
93
|
+
}
|
|
94
|
+
if (options.page !== undefined)
|
|
95
|
+
query.set('page', String(options.page));
|
|
96
|
+
if (options.limit !== undefined)
|
|
97
|
+
query.set('limit', String(options.limit));
|
|
98
|
+
const suffix = query.toString() === '' ? '' : `?${query.toString()}`;
|
|
99
|
+
return this.request('GET', `/api/repos${suffix}`);
|
|
100
|
+
}
|
|
101
|
+
repo(owner, name) {
|
|
102
|
+
return this.request('GET', this.repoPath(owner, name));
|
|
103
|
+
}
|
|
104
|
+
newRepo(name, isPrivate = false) {
|
|
105
|
+
return this.request('POST', '/api/new', { name, private: isPrivate });
|
|
106
|
+
}
|
|
107
|
+
deleteRepo(owner, name) {
|
|
108
|
+
return this.request('DELETE', this.repoPath(owner, name));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CliError } from '../error/index.js';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { printHelp, printVersion } from '../help/index.js';
|
|
4
|
+
import { dispatch } from '../commands/registry.js';
|
|
5
|
+
import { startShell } from './repl.js';
|
|
6
|
+
import { configFilePath, normalizeUrl, readStoredConfig, writeStoredConfig } from '../config/index.js';
|
|
7
|
+
/** Only start the shell when a person is actually looking at the terminal. */
|
|
8
|
+
export function isInteractive() {
|
|
9
|
+
return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
10
|
+
}
|
|
11
|
+
export async function main(argv) {
|
|
12
|
+
const [command, ...rest] = argv;
|
|
13
|
+
if (command === undefined) {
|
|
14
|
+
// Bare `microgit` opens the shell; piped runs still get the help output.
|
|
15
|
+
if (isInteractive()) {
|
|
16
|
+
// First run: no config file yet → ask for the server URL.
|
|
17
|
+
const stored = readStoredConfig();
|
|
18
|
+
const fileExists = (() => {
|
|
19
|
+
try {
|
|
20
|
+
fs.accessSync(configFilePath());
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
if (!fileExists) {
|
|
28
|
+
const readline = await import('node:readline');
|
|
29
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
30
|
+
const answer = await new Promise((resolve) => {
|
|
31
|
+
rl.question('Server URL (e.g. http://127.0.0.1:80): ', (a) => resolve(a.trim()));
|
|
32
|
+
});
|
|
33
|
+
rl.close();
|
|
34
|
+
const url = normalizeUrl(answer || 'http://127.0.0.1:3000');
|
|
35
|
+
writeStoredConfig({ ...stored, url });
|
|
36
|
+
console.log(`Saved server URL: ${url}`);
|
|
37
|
+
console.log('');
|
|
38
|
+
}
|
|
39
|
+
return startShell();
|
|
40
|
+
}
|
|
41
|
+
printHelp();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (command === 'help' || command === '--help' || command === '-h' || command === '?') {
|
|
45
|
+
return printHelp();
|
|
46
|
+
}
|
|
47
|
+
if (command === '--version' || command === '-v' || command === 'version') {
|
|
48
|
+
return printVersion();
|
|
49
|
+
}
|
|
50
|
+
if (command === 'shell' || command === 'repl') {
|
|
51
|
+
return startShell();
|
|
52
|
+
}
|
|
53
|
+
if (command === '--') {
|
|
54
|
+
throw new CliError('Expected a command after `--`. Type `help` to list the commands.');
|
|
55
|
+
}
|
|
56
|
+
await dispatch([command, ...rest]);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|