uplink-cli 0.1.38 → 0.1.39

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.
Files changed (54) hide show
  1. package/AGENTS.md +161 -0
  2. package/LICENSE +21 -0
  3. package/README.md +46 -47
  4. package/cli/src/index.ts +5 -3
  5. package/cli/src/registrars/cloudflare.ts +148 -0
  6. package/cli/src/registrars/godaddy.ts +99 -0
  7. package/cli/src/registrars/hostinger.ts +106 -0
  8. package/cli/src/registrars/http.ts +18 -0
  9. package/cli/src/registrars/index.ts +30 -0
  10. package/cli/src/registrars/namecheap.ts +163 -0
  11. package/cli/src/registrars/secret.ts +66 -0
  12. package/cli/src/registrars/store.ts +55 -0
  13. package/cli/src/registrars/types.ts +40 -0
  14. package/cli/src/subcommands/admin.ts +17 -30
  15. package/cli/src/subcommands/db.ts +63 -57
  16. package/cli/src/subcommands/dev.ts +23 -25
  17. package/cli/src/subcommands/domains.ts +268 -0
  18. package/cli/src/subcommands/host-domains.ts +148 -0
  19. package/cli/src/subcommands/host.ts +3 -0
  20. package/cli/src/subcommands/menu/colors.ts +1 -1
  21. package/cli/src/subcommands/menu/effects/tunnel-clients.ts +87 -14
  22. package/cli/src/subcommands/menu/inline-tree-select.ts +6 -5
  23. package/cli/src/subcommands/menu/io.ts +27 -5
  24. package/cli/src/subcommands/menu/menus/domains.ts +199 -0
  25. package/cli/src/subcommands/menu/menus/hosting.ts +14 -46
  26. package/cli/src/subcommands/menu/menus/index.ts +1 -0
  27. package/cli/src/subcommands/menu/menus/tunnels.ts +25 -67
  28. package/cli/src/subcommands/menu/render.ts +2 -2
  29. package/cli/src/subcommands/menu/tests.ts +1 -1
  30. package/cli/src/subcommands/menu/tunnels.ts +10 -99
  31. package/cli/src/subcommands/menu/types.ts +8 -0
  32. package/cli/src/subcommands/menu.ts +32 -524
  33. package/cli/src/subcommands/system.ts +58 -36
  34. package/cli/src/subcommands/tunnel.ts +124 -33
  35. package/cli/src/templates/index.ts +3 -3
  36. package/cli/src/tui/App.tsx +197 -0
  37. package/cli/src/tui/AppInspector.tsx +114 -0
  38. package/cli/src/tui/HomeStatus.tsx +59 -0
  39. package/cli/src/tui/brand.tsx +20 -0
  40. package/cli/src/tui/format.ts +22 -0
  41. package/cli/src/tui/index.mts +6 -0
  42. package/cli/src/tui/liveTree.ts +40 -0
  43. package/cli/src/tui/package.json +3 -0
  44. package/cli/src/tui/runMenu.tsx +57 -0
  45. package/cli/src/tui/session.mts +382 -0
  46. package/cli/src/tui/snapshot.ts +146 -0
  47. package/cli/src/utils/launchDomainking.ts +64 -0
  48. package/docs/AGENTS.md +113 -147
  49. package/docs/MENU_STRUCTURE.md +56 -288
  50. package/docs/README.md +6 -6
  51. package/package.json +18 -35
  52. package/scripts/tunnel/client-improved.js +127 -38
  53. package/scripts/tunnel/client.js +118 -0
  54. package/assets/cli-screenshot.png +0 -0
@@ -0,0 +1,64 @@
1
+ import { spawnSync } from "child_process";
2
+ import { existsSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+
6
+ function projectRoot(): string {
7
+ return join(__dirname, "../..");
8
+ }
9
+
10
+ function resolveTsx(): string {
11
+ const root = projectRoot();
12
+ try {
13
+ return require.resolve("tsx/dist/cli.cjs", { paths: [root] });
14
+ } catch {
15
+ try {
16
+ return require.resolve("tsx/cli", { paths: [root] });
17
+ } catch {
18
+ return "tsx";
19
+ }
20
+ }
21
+ }
22
+
23
+ /** Domainking stays its own package; we spawn it, we do not import it. */
24
+ export function resolveDomainkingEntry(): string | null {
25
+ if (process.env.DOMAINKING_ENTRY && existsSync(process.env.DOMAINKING_ENTRY)) {
26
+ return process.env.DOMAINKING_ENTRY;
27
+ }
28
+ const candidates = [
29
+ join(homedir(), "domainking", "src", "index.tsx"),
30
+ join(projectRoot(), "..", "domainking", "src", "index.tsx"),
31
+ join(projectRoot(), "..", "..", "domainking", "src", "index.tsx"),
32
+ ];
33
+ return candidates.find((path) => existsSync(path)) ?? null;
34
+ }
35
+
36
+ export function launchDomainking(): string {
37
+ const entry = resolveDomainkingEntry();
38
+ if (!entry) {
39
+ return [
40
+ "Domain search TUI is not bundled with uplink-cli.",
41
+ "",
42
+ "Agent-friendly commands (no TUI needed):",
43
+ " uplink domains list --json",
44
+ " uplink domains check example.com --json",
45
+ " uplink host domains add --id app_xxx --hostname example.com --json",
46
+ " uplink host domains verify --id app_xxx --hostname example.com --json",
47
+ "",
48
+ "Optional: set DOMAINKING_ENTRY to a Domainking src/index.tsx for the search UI.",
49
+ ].join("\n");
50
+ }
51
+
52
+ const result = spawnSync(resolveTsx(), [entry], {
53
+ stdio: "inherit",
54
+ cwd: join(entry, "..", ".."),
55
+ env: process.env,
56
+ });
57
+ if (result.error) {
58
+ throw result.error;
59
+ }
60
+ if (result.status && result.status !== 0) {
61
+ return "Domain search exited.";
62
+ }
63
+ return "Back from domain search. Attach a hostname with Domains › Attach to app.";
64
+ }
package/docs/AGENTS.md CHANGED
@@ -1,195 +1,161 @@
1
1
  # Agent Integration Guide
2
2
 
3
- For agents (Cursor/Claude/GPT/Windsurf) to use Uplink non-interactively.
3
+ For agents (Cursor, Claude Code, Codex, Windsurf, and similar) to use Uplink **non-interactively**.
4
+
5
+ Install: `npm install -g uplink-cli` or `npx uplink-cli …`
6
+ Package name: `uplink-cli` · Binary: `uplink`
4
7
 
5
8
  ## Auth
6
- - Use `AGENTCLOUD_TOKEN` (bearer). Avoid argv; prefer stdin:
9
+
10
+ - Use `AGENTCLOUD_TOKEN` (bearer). Prefer stdin over argv:
7
11
  ```bash
8
- echo "$TOKEN" | uplink --token-stdin ...
12
+ echo "$TOKEN" | uplink --token-stdin
9
13
  ```
10
- - API base override: `--api-base https://api.uplink.spot` (or `AGENTCLOUD_API_BASE`).
14
+ - API base: `--api-base https://api.uplink.spot` or `AGENTCLOUD_API_BASE`.
11
15
 
12
16
  ## Signup (no auth required)
17
+
13
18
  ```bash
14
19
  uplink signup --json
15
20
  uplink signup --label "cursor-agent" --expires-days 30 --json
16
21
  ```
17
- JSON example:
18
- ```json
19
- {
20
- "id": "tok_xxx",
21
- "token": "abc123...",
22
- "tokenPrefix": "abc123",
23
- "role": "user",
24
- "userId": "user_xxx",
25
- "label": "cursor-agent",
26
- "createdAt": "2025-01-01T00:00:00.000Z",
27
- "expiresAt": "2025-01-31T00:00:00.000Z",
28
- "message": "Token created successfully. Save this token securely..."
29
- }
22
+
23
+ Save `token` from the JSON — it is shown only once. Then:
24
+
25
+ ```bash
26
+ export AGENTCLOUD_TOKEN='…'
30
27
  ```
31
- Save `token`—shown only once.
32
28
 
33
29
  ## Machine-mode contract
34
- - `--json` → stdout = JSON only; stderr = logs/errors.
35
- - Exit codes: 0 ok; 2 usage; 10 auth missing/invalid; 20 network; 30 server/unknown.
36
- - Premium alias gating: alias commands may return `ALIAS_NOT_ENABLED` / `ALIAS_LIMIT_REACHED`.
37
30
 
38
- ## Core CLI flows (non-interactive)
31
+ | Rule | Detail |
32
+ |------|--------|
33
+ | `--json` | stdout = JSON only; logs/errors go to stderr |
34
+ | Exit `0` | success |
35
+ | Exit `2` | usage / bad args |
36
+ | Exit `10` | auth missing/invalid |
37
+ | Exit `20` | network |
38
+ | Exit `30` | server / unknown |
39
+
40
+ Premium aliases may return `ALIAS_NOT_ENABLED` / `ALIAS_LIMIT_REACHED`.
41
+
42
+ ## Tunnels (share localhost)
43
+
44
+ `tunnel create` **creates the API record and starts the local client** so the public URL works. Use `--api-only` only if you will start the client yourself.
45
+
39
46
  ```bash
40
- # Create tunnel (optional alias if enabled)
41
- echo "$TOKEN" | uplink --token-stdin --api-base https://api.uplink.spot \
47
+ # Create + start client (optional alias if enabled)
48
+ echo "$TOKEN" | uplink --token-stdin \
42
49
  tunnel create --port 3000 --alias myapp --json
43
50
 
44
- # List tunnels (includes connection status)
51
+ # List (includes connected status)
45
52
  echo "$TOKEN" | uplink --token-stdin tunnel list --json
46
53
 
47
- # Set alias on tunnel
54
+ # Alias on an existing tunnel
48
55
  echo "$TOKEN" | uplink --token-stdin tunnel alias-set --id tun_xxx --alias myapp --json
49
-
50
- # Delete alias from tunnel
51
56
  echo "$TOKEN" | uplink --token-stdin tunnel alias-delete --id tun_xxx --json
52
57
 
53
- # Stats
58
+ # Stats / stop
54
59
  echo "$TOKEN" | uplink --token-stdin tunnel stats --id tun_xxx --json
55
-
56
- # Stop/delete tunnel
57
60
  echo "$TOKEN" | uplink --token-stdin tunnel stop --id tun_xxx --json
61
+ echo "$TOKEN" | uplink --token-stdin tunnel stop --all --json
62
+ ```
63
+
64
+ JSON create shape (representative):
65
+
66
+ ```json
67
+ {
68
+ "tunnel": { "id": "tun_…", "url": "https://abc.x.uplink.spot", "token": "…", "status": "…" },
69
+ "alias": "myapp",
70
+ "aliasError": null,
71
+ "url": "https://myapp.uplink.spot",
72
+ "client": { "pid": 12345, "started": true }
73
+ }
58
74
  ```
59
75
 
60
- ## Hosting flows (non-interactive)
61
- Use `--json` for machine-mode output. For interactive prompts, pass `--yes`.
76
+ `connected` on `tunnel list` means the local client is attached to the relay.
77
+
78
+ ## Hosting
79
+
80
+ Use `--json`. For prompts, pass `--yes`.
62
81
 
63
82
  ```bash
64
- # Full setup (analyze + init + create + deploy)
65
83
  echo "$TOKEN" | uplink --token-stdin host setup \
66
- --path /path/to/app \
67
- --name myapp \
68
- --env-file /path/to/.env \
69
- --wait-timeout 900 \
70
- --wait-interval 5 \
71
- --yes \
72
- --json
73
-
74
- # Deploy only (Dockerfile-based app)
84
+ --path /path/to/app --name myapp --env-file /path/to/.env \
85
+ --wait-timeout 900 --wait-interval 5 --yes --json
86
+
75
87
  echo "$TOKEN" | uplink --token-stdin host deploy \
76
- --path /path/to/app \
77
- --name myapp \
78
- --env-file /path/to/.env \
79
- --wait \
80
- --wait-timeout 900 \
81
- --wait-interval 5 \
82
- --json
83
-
84
- # Analyze project
85
- echo "$TOKEN" | uplink --token-stdin host analyze --path /path/to/app --json
88
+ --path /path/to/app --name myapp --wait --json
86
89
 
87
- # List hosted apps
90
+ echo "$TOKEN" | uplink --token-stdin host analyze --path /path/to/app --json
91
+ echo "$TOKEN" | uplink --token-stdin host preflight --path /path/to/app --json
88
92
  echo "$TOKEN" | uplink --token-stdin host list --json
89
-
90
- # Status + logs
91
93
  echo "$TOKEN" | uplink --token-stdin host status --id app_xxx --json
92
94
  echo "$TOKEN" | uplink --token-stdin host logs --id app_xxx --json
93
-
94
- # Delete app
95
95
  echo "$TOKEN" | uplink --token-stdin host delete --id app_xxx --yes --json
96
96
  ```
97
97
 
98
98
  Notes:
99
- - `host logs` returns `NOT_READY` until a deployment is running.
100
- - If builds stay `queued`, check builder/runner services and build logs on the server.
101
- - For Prisma apps, ensure the Dockerfile copies `prisma/schema.prisma` before `npm ci` and runs `npx prisma generate` before `npm run build`.
102
- - Use a `.uplinkignore` file to keep tarballs small. Typical entries include `node_modules`, `.next`, `dist`, `build`, `coverage`, `.turbo`, `.cache`, `.vercel`, `*.log`, and any local SQLite `.db` files.
103
- - `host list` (non-JSON) prints two lines per app: `- name (app_id)` and the URL on the next line.
104
- - `host delete` requires typing `DELETE` unless `--yes` is provided.
105
- - Hosting output modes: Next.js expects `output: "standalone"` for server builds. Vite and CRA are treated as static builds (dist/build).
106
-
107
- ## Interactive menu notes
108
- - All selection lists use arrow keys + Enter (no numeric entry required).
109
- - Hosted app lists display the app URL alongside name and ID.
110
-
111
- ### JSON shapes (representative)
112
- - Create: `{ "tunnel": { id, url?, token?, alias?, aliasUrl?, targetPort, status, connected?, createdAt }, "alias": "myapp"|null, "aliasError": "..."|null }`
113
- - List: `{ "tunnels": [ { id, url?, token?, alias?, aliasUrl?, targetPort, status, connected, createdAt } ], "count": n }`
114
- - Stats: alias tunnels include persisted totals + relay overlay; token-only tunnels show in-memory relay stats.
115
-
116
- **Note:** The `connected` field indicates whether the tunnel is actually connected to the relay server (verified via socket health check).
117
-
118
- ## HTTP API Reference
119
-
120
- Auth: `Authorization: Bearer <AGENTCLOUD_TOKEN>`
121
-
122
- ### Tunnels
123
- | Method | Endpoint | Description |
124
- |--------|----------|-------------|
125
- | `POST` | `/v1/tunnels` | Create tunnel (body: `{ port, alias? }`) |
126
- | `GET` | `/v1/tunnels` | List user's tunnels (includes `connected` status) |
127
- | `GET` | `/v1/tunnels/{id}` | Get tunnel details |
128
- | `GET` | `/v1/tunnels/{id}/stats` | Get tunnel statistics |
129
- | `DELETE` | `/v1/tunnels/{id}` | Delete tunnel |
130
- | `POST` | `/v1/tunnels/{id}/alias` | Set alias on tunnel (body: `{ alias }`) |
131
- | `DELETE` | `/v1/tunnels/{id}/alias` | Remove alias from tunnel |
132
-
133
- ### Port-Based Aliases (Premium)
134
- Aliases are now **port-based**: they persist across tunnel restarts and always point to the same port.
135
-
136
- | Method | Endpoint | Description |
137
- |--------|----------|-------------|
138
- | `GET` | `/v1/tunnels/aliases` | List all aliases for user |
139
- | `POST` | `/v1/tunnels/aliases` | Create alias for port (body: `{ alias, port }`) |
140
- | `PUT` | `/v1/tunnels/aliases/{alias}` | Reassign alias to different port (body: `{ port }`) |
141
- | `DELETE` | `/v1/tunnels/aliases/{alias}` | Delete alias |
142
-
143
- ### Example: Create port-based alias
144
- ```bash
145
- curl -X POST https://api.uplink.spot/v1/tunnels/aliases \
146
- -H "Authorization: Bearer $TOKEN" \
147
- -H "Content-Type: application/json" \
148
- -d '{"alias": "myapp", "port": 3000}'
149
- ```
99
+ - Next.js server hosting expects `output: "standalone"`. Vite/CRA static `dist`/`build`.
100
+ - Prefer a `.uplinkignore` (`node_modules`, `.next`, `dist`, `*.log`, local `.db`, …).
101
+ - `host logs` may return `NOT_READY` until a deployment is running.
102
+ - `host delete` requires `--yes` (or typing `DELETE` interactively).
150
103
 
151
- Response:
152
- ```json
153
- {
154
- "id": "alias_xxx",
155
- "alias": "myapp",
156
- "targetPort": 3000,
157
- "url": "https://myapp.uplink.spot",
158
- "createdAt": "2025-01-03T00:00:00.000Z"
159
- }
104
+ ## Custom domains
105
+
106
+ Registrar inventory is CLI. Attach/verify is under `host domains`.
107
+ The bare `uplink domains` search TUI is **optional** and not bundled with npm — use the JSON commands below.
108
+
109
+ ```bash
110
+ uplink domains providers connect godaddy --token-env GODADDY_PAT --json
111
+ uplink domains providers connect cloudflare --token-env CF_API_TOKEN --json
112
+ uplink domains providers connect hostinger --token-env HOSTINGER_API_TOKEN --json
113
+ uplink domains providers connect namecheap --token-env NAMECHEAP_API_KEY --user-env NAMECHEAP_API_USER --json
114
+ uplink domains providers list --json
115
+ uplink domains providers disconnect godaddy --json
116
+
117
+ uplink domains list --json
118
+ uplink domains check example.com --json
119
+
120
+ echo "$TOKEN" | uplink --token-stdin host domains add --id app_xxx --hostname example.com --json
121
+ echo "$TOKEN" | uplink --token-stdin host domains verify --id app_xxx --hostname example.com --json
122
+ echo "$TOKEN" | uplink --token-stdin host domains list --id app_xxx --json
123
+ echo "$TOKEN" | uplink --token-stdin host domains remove --id app_xxx --hostname example.com --json
160
124
  ```
161
125
 
162
- ## Domains
163
- - Public tunnels: `https://<token>.x.uplink.spot`
164
- - Permanent URLs (aliases): `https://<alias>.uplink.spot`
126
+ Do not treat RDAP “available” as buyable unless `domains check` says `buyable: true`. Purchase is not wired yet.
165
127
 
166
- ## Server Deployment (for self-hosted)
167
- Control plane + hosting runtime live in the private repo `uplink-hosting-runtime`.
128
+ ## Databases (optional)
168
129
 
169
- ### Deploy code changes
170
130
  ```bash
171
- # On server (via SSH)
172
- cd /opt/uplink-hosting-runtime
173
- git pull origin main
174
- systemctl restart backend-api
175
- systemctl restart tunnel-relay
176
- systemctl restart uplink-builder uplink-runner uplink-router # if hosting runtime changed
131
+ echo "$TOKEN" | uplink --token-stdin db create --name mydb --project myproj --json
132
+ echo "$TOKEN" | uplink --token-stdin db list --json
133
+ echo "$TOKEN" | uplink --token-stdin db info --id db_xxx --json
134
+ echo "$TOKEN" | uplink --token-stdin db delete --id db_xxx --yes --json
177
135
  ```
178
136
 
179
- ### Services
180
- | Service | Command | Description |
181
- |---------|---------|-------------|
182
- | `backend-api` | `systemctl status backend-api` | REST API server |
183
- | `tunnel-relay` | `systemctl status tunnel-relay` | WebSocket relay for tunnels |
184
- | `uplink-builder` | `systemctl status uplink-builder` | Builds Docker images |
185
- | `uplink-runner` | `systemctl status uplink-runner` | Runs containers |
186
- | `uplink-router` | `systemctl status uplink-router` | Routes host domains |
187
-
188
- ### Relay health check
189
- The relay exposes internal endpoints (protected by `RELAY_INTERNAL_SECRET`):
190
- - `/internal/connected-tokens` - List connected tunnel tokens (with socket health verification)
191
- - `/internal/traffic-stats` - Traffic statistics by token/alias
192
- - `/health` - Basic health check
193
-
194
- ## Optional JS helper
195
- Use `cli/src/agents/tunnels-client.ts` for the same retry/timeout behavior as the CLI.
137
+ ## Failure modes agents should expect
138
+
139
+ | Symptom | Likely cause |
140
+ |---------|----------------|
141
+ | URL 502 / not connected | Local process on `--port` not running, or client died — re-run `tunnel create` or check `tunnel list` |
142
+ | Auth errors | Missing/invalid `AGENTCLOUD_TOKEN`; use `--token-stdin` |
143
+ | `ALIAS_NOT_ENABLED` | Account does not have permanent aliases |
144
+ | Domain search TUI missing | Expected on npm — use `domains list` / `check` / `host domains *` |
145
+ | Hosting stuck `queued` | Edge builder/runner issue — check `host status` / `host logs` |
146
+
147
+ ## Interactive menu
148
+
149
+ Humans: `uplink` or `uplink menu` (Share · Hosting · Domains).
150
+ Agents should prefer the non-interactive commands above.
151
+
152
+ ## URLs
153
+
154
+ - Ephemeral tunnels: `https://<token>.x.uplink.spot`
155
+ - Aliases: `https://<alias>.uplink.spot`
156
+
157
+ ## More
158
+
159
+ - Menu map: `docs/MENU_STRUCTURE.md`
160
+ - Website: https://uplink.spot
161
+ - npm: https://www.npmjs.com/package/uplink-cli