@uetuluk/create-cli 0.0.1 → 0.0.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.
Files changed (2) hide show
  1. package/dist/cli.js +59 -17
  2. package/package.json +52 -47
package/dist/cli.js CHANGED
@@ -101,6 +101,33 @@ function requireAuth() {
101
101
  }
102
102
  return c;
103
103
  }
104
+ async function apiAuth(path, init = {}) {
105
+ const creds = requireAuth();
106
+ const url = init.server ?? creds.serverUrl;
107
+ const { server, ...rest } = init;
108
+ try {
109
+ return await api(url, path, { ...rest, token: creds.token });
110
+ } catch (e) {
111
+ if (!(e instanceof ApiError) || e.status !== 401 || !creds.refreshToken) throw e;
112
+ let refreshed;
113
+ try {
114
+ refreshed = await api(url, "/auth/refresh-token", {
115
+ method: "POST",
116
+ body: JSON.stringify({ refresh_token: creds.refreshToken })
117
+ });
118
+ } catch {
119
+ throw e;
120
+ }
121
+ if (url === creds.serverUrl) {
122
+ saveCredentials({
123
+ ...creds,
124
+ token: refreshed.token,
125
+ refreshToken: refreshed.refresh_token
126
+ });
127
+ }
128
+ return await api(url, path, { ...rest, token: refreshed.token });
129
+ }
130
+ }
104
131
  function wrap(fn) {
105
132
  return async (...args) => {
106
133
  try {
@@ -109,7 +136,15 @@ function wrap(fn) {
109
136
  if (e instanceof ApiError) {
110
137
  console.error(colors.red(`${e.status} ${e.message}`));
111
138
  } else {
112
- console.error(colors.red(e.message));
139
+ const err = e;
140
+ let msg = err.message || String(e);
141
+ if (msg === "fetch failed" && err.cause) {
142
+ const cause = err.cause;
143
+ const code = cause.code ? ` [${cause.code}]` : "";
144
+ msg = `fetch failed: ${cause.message ?? cause}${code}`;
145
+ }
146
+ console.error(colors.red(msg));
147
+ if (process.env.TEENY_DEBUG) console.error(err.stack ?? err);
113
148
  }
114
149
  process.exit(1);
115
150
  }
@@ -168,19 +203,15 @@ cli.command("whoami", "show current user").action(wrap(async () => {
168
203
  cli.command("create", "provision a new instance").action(wrap(async (opts) => {
169
204
  const c = requireAuth();
170
205
  const url = opts.server ? serverUrl(opts) : c.serverUrl;
171
- const r = await api(url, "/platform/instances", {
172
- method: "POST",
173
- token: c.token
174
- });
206
+ const r = await apiAuth("/platform/instances", { method: "POST" });
175
207
  saveInstance({ name: r.name, url: r.url, adminToken: r.admin_token, serverUrl: url });
176
208
  console.log(colors.green(`created ${colors.bold(r.name)}`));
177
209
  console.log(` url: ${r.url}`);
178
210
  console.log(` admin token saved to local store (only shown once)`);
179
211
  }));
180
212
  cli.command("list", "list your instances").action(wrap(async (opts) => {
181
- const c = requireAuth();
182
- const url = opts.server ? serverUrl(opts) : c.serverUrl;
183
- const r = await api(url, "/platform/instances", { token: c.token });
213
+ const url = opts.server ? serverUrl(opts) : void 0;
214
+ const r = await apiAuth("/platform/instances", { server: url });
184
215
  if (!r.result.length) {
185
216
  console.log("no instances. run `teeny create`");
186
217
  return;
@@ -191,16 +222,14 @@ cli.command("list", "list your instances").action(wrap(async (opts) => {
191
222
  }
192
223
  }));
193
224
  cli.command("status <name>", "show instance details").action(wrap(async (name, opts) => {
194
- const c = requireAuth();
195
- const url = opts.server ? serverUrl(opts) : c.serverUrl;
196
- const r = await api(url, `/platform/instances/${encodeURIComponent(name)}`, { token: c.token });
225
+ const url = opts.server ? serverUrl(opts) : void 0;
226
+ const r = await apiAuth(`/platform/instances/${encodeURIComponent(name)}`, { server: url });
197
227
  console.log(colors.bold(r.result.name));
198
228
  console.log(` url: ${r.result.url}`);
199
229
  console.log(` created: ${r.result.created_at}`);
200
230
  }));
201
231
  cli.command("delete <name>", "tear down an instance").option("-y, --yes", "[boolean] skip confirmation").action(wrap(async (name, opts) => {
202
- const c = requireAuth();
203
- const url = opts.server ? serverUrl(opts) : c.serverUrl;
232
+ const url = opts.server ? serverUrl(opts) : void 0;
204
233
  if (!opts.yes) {
205
234
  const r = await prompts({ type: "confirm", name: "v", message: `delete ${name}? this is permanent`, initial: false });
206
235
  if (!r.v) {
@@ -208,7 +237,7 @@ cli.command("delete <name>", "tear down an instance").option("-y, --yes", "[bool
208
237
  return;
209
238
  }
210
239
  }
211
- await api(url, `/platform/instances/${encodeURIComponent(name)}`, { method: "DELETE", token: c.token });
240
+ await apiAuth(`/platform/instances/${encodeURIComponent(name)}`, { method: "DELETE", server: url });
212
241
  deleteInstance(name);
213
242
  console.log(colors.green(`deleted ${name}`));
214
243
  }));
@@ -245,9 +274,22 @@ cli.command("logs <name>", "stream live logs from an instance").option("--tail <
245
274
  const c = requireAuth();
246
275
  const url = opts.server ? serverUrl(opts) : c.serverUrl;
247
276
  const tail = opts.tail || "200";
248
- const res = await fetch(`${url}/platform/instances/${encodeURIComponent(name)}/logs?tail=${tail}`, {
249
- headers: { authorization: `Bearer ${c.token}` }
250
- });
277
+ const streamUrl = `${url}/platform/instances/${encodeURIComponent(name)}/logs?tail=${tail}`;
278
+ const open = (token) => fetch(streamUrl, { headers: { authorization: `Bearer ${token}` } });
279
+ let res = await open(c.token);
280
+ if (res.status === 401 && c.refreshToken) {
281
+ try {
282
+ const refreshed = await api(url, "/auth/refresh-token", {
283
+ method: "POST",
284
+ body: JSON.stringify({ refresh_token: c.refreshToken })
285
+ });
286
+ if (url === c.serverUrl) {
287
+ saveCredentials({ ...c, token: refreshed.token, refreshToken: refreshed.refresh_token });
288
+ }
289
+ res = await open(refreshed.token);
290
+ } catch {
291
+ }
292
+ }
251
293
  if (!res.ok || !res.body) {
252
294
  console.error(colors.red(`failed to stream: ${res.status}`));
253
295
  process.exit(1);
package/package.json CHANGED
@@ -1,49 +1,54 @@
1
1
  {
2
- "name": "@uetuluk/create-cli",
3
- "version": "0.0.1",
4
- "description": "CLI for the create.ritsdev.top platform — register, provision, and deploy teenybase instances.",
5
- "type": "module",
6
- "bin": {
7
- "teeny": "./dist/cli.js"
8
- },
9
- "files": [
10
- "dist",
11
- "README.md"
12
- ],
13
- "engines": {
14
- "node": ">=20"
15
- },
16
- "publishConfig": {
17
- "access": "public"
18
- },
19
- "keywords": ["teenybase", "platform", "cli", "ritsdev"],
20
- "author": "uetuluk",
21
- "license": "MIT",
22
- "repository": {
23
- "type": "git",
24
- "url": "git+https://github.com/uetuluk/create-ritsdev-platform.git",
25
- "directory": "cli"
26
- },
27
- "homepage": "https://github.com/uetuluk/create-ritsdev-platform/tree/main/cli#readme",
28
- "bugs": {
29
- "url": "https://github.com/uetuluk/create-ritsdev-platform/issues"
30
- },
31
- "scripts": {
32
- "build": "esbuild src/cli.ts --bundle --outfile=dist/cli.js --platform=node --format=esm --packages=external --banner:js=\"#!/usr/bin/env node\" && chmod +x dist/cli.js",
33
- "check": "tsc -p tsconfig.json --noEmit",
34
- "dev": "tsx src/cli.ts",
35
- "prepublishOnly": "npm run check && npm run build"
36
- },
37
- "dependencies": {
38
- "cac": "^6.7.14",
39
- "picocolors": "^1.1.1",
40
- "prompts": "^2.4.2"
41
- },
42
- "devDependencies": {
43
- "@types/node": "^22.0.0",
44
- "@types/prompts": "^2.4.9",
45
- "esbuild": "^0.24.0",
46
- "tsx": "^4.19.0",
47
- "typescript": "^5.6.0"
48
- }
2
+ "name": "@uetuluk/create-cli",
3
+ "version": "0.0.2",
4
+ "description": "CLI for the create.ritsdev.top platform — register, provision, and deploy teenybase instances.",
5
+ "type": "module",
6
+ "bin": {
7
+ "teeny": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "keywords": [
20
+ "teenybase",
21
+ "platform",
22
+ "cli",
23
+ "ritsdev"
24
+ ],
25
+ "author": "uetuluk",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/uetuluk/create-ritsdev-platform.git",
30
+ "directory": "cli"
31
+ },
32
+ "homepage": "https://github.com/uetuluk/create-ritsdev-platform/tree/main/cli#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/uetuluk/create-ritsdev-platform/issues"
35
+ },
36
+ "scripts": {
37
+ "build": "esbuild src/cli.ts --bundle --outfile=dist/cli.js --platform=node --format=esm --packages=external --banner:js=\"#!/usr/bin/env node\" && chmod +x dist/cli.js",
38
+ "check": "tsc -p tsconfig.json --noEmit",
39
+ "dev": "tsx src/cli.ts",
40
+ "prepublishOnly": "npm run check && npm run build"
41
+ },
42
+ "dependencies": {
43
+ "cac": "^6.7.14",
44
+ "picocolors": "^1.1.1",
45
+ "prompts": "^2.4.2"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.0.0",
49
+ "@types/prompts": "^2.4.9",
50
+ "esbuild": "^0.24.0",
51
+ "tsx": "^4.19.0",
52
+ "typescript": "^5.6.0"
53
+ }
49
54
  }