@usefillo/cli 0.5.0 → 0.5.1

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/index.js +51 -13
  2. package/package.json +4 -1
package/dist/index.js CHANGED
@@ -40,10 +40,16 @@ function openBrowser(url) {
40
40
  if (!/^https?:\/\//i.test(url)) return;
41
41
  try {
42
42
  if (process.platform === "win32") {
43
- spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
43
+ const child = spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true });
44
+ child.on("error", () => {
45
+ });
46
+ child.unref();
44
47
  } else {
45
48
  const cmd = process.platform === "darwin" ? "open" : "xdg-open";
46
- spawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
49
+ const child = spawn(cmd, [url], { stdio: "ignore", detached: true });
50
+ child.on("error", () => {
51
+ });
52
+ child.unref();
47
53
  }
48
54
  } catch {
49
55
  }
@@ -59,6 +65,14 @@ async function api(path, init2 = {}) {
59
65
  }
60
66
  });
61
67
  }
68
+ async function readJson(res) {
69
+ const text = await res.text();
70
+ try {
71
+ return text ? JSON.parse(text) : {};
72
+ } catch {
73
+ die(`Unexpected non-JSON response from ${API} (${res.status}).`);
74
+ }
75
+ }
62
76
  async function login() {
63
77
  const res = await api("/device/code", { method: "POST" });
64
78
  if (!res.ok) die(`Couldn't start login (${res.status}).`);
@@ -80,7 +94,7 @@ async function login() {
80
94
  });
81
95
  const body = await r.json();
82
96
  if (body.access_token) {
83
- writeConfig({ token: body.access_token });
97
+ writeConfig({ ...readConfig(), token: body.access_token });
84
98
  console.log("\n");
85
99
  await whoami();
86
100
  console.log(`
@@ -98,13 +112,13 @@ async function whoami() {
98
112
  const res = await api("/cli/whoami", { token: requireToken() });
99
113
  if (res.status === 401) die("Token invalid \u2014 run `fillo login` again.");
100
114
  if (!res.ok) die(`whoami failed (${res.status}).`);
101
- const me = await res.json();
115
+ const me = await readJson(res);
102
116
  console.log(` \x1B[32m\u2713\x1B[0m ${bold(me.email)} ${dim("\xB7")} ${me.workspace}`);
103
117
  }
104
118
  async function list() {
105
119
  const res = await api("/cli/forms", { token: requireToken() });
106
120
  if (!res.ok) die(`list failed (${res.status}).`);
107
- const { forms } = await res.json();
121
+ const { forms } = await readJson(res);
108
122
  if (!forms.length) return console.log(" No forms yet.");
109
123
  for (const f of forms) {
110
124
  console.log(` ${f.status === "published" ? "\x1B[32m\u25CF\x1B[0m" : "\u25CB"} ${f.name} ${dim(f.id)}`);
@@ -118,9 +132,19 @@ async function loadSchema(file, allowCode) {
118
132
  );
119
133
  }
120
134
  try {
121
- if (abs.endsWith(".json")) return JSON.parse(readFileSync(abs, "utf8"));
135
+ if (abs.endsWith(".json")) {
136
+ const parsed = JSON.parse(readFileSync(abs, "utf8"));
137
+ if (parsed == null) die(`${file} is empty \u2014 it must contain a form schema.`);
138
+ return parsed;
139
+ }
122
140
  const mod = await import(pathToFileURL(abs).href);
123
- return mod.default ?? mod.schema ?? mod.form ?? mod.forms ?? mod.MARKETING_FORMS;
141
+ const schema = mod.default ?? mod.schema ?? mod.form ?? mod.forms ?? mod.MARKETING_FORMS;
142
+ if (schema == null) {
143
+ die(
144
+ `${file} doesn't export a form schema \u2014 default-export it (or export \`schema\`/\`form\`/\`forms\`) and push again.`
145
+ );
146
+ }
147
+ return schema;
124
148
  } catch (e) {
125
149
  die(`Couldn't read ${file}: ${e.message}`);
126
150
  }
@@ -167,7 +191,7 @@ async function push(file, flags) {
167
191
  publish: !flags.draft
168
192
  })
169
193
  });
170
- const body = await res.json();
194
+ const body = await readJson(res);
171
195
  if (!res.ok || !body.formId) die(body.error ?? `push failed (${res.status}).`);
172
196
  printPushed(body.formId, body.url, !!body.updated, !!flags.draft);
173
197
  }
@@ -185,7 +209,7 @@ async function push(file, flags) {
185
209
  theme: item.theme ?? null
186
210
  })
187
211
  });
188
- const body = await res.json();
212
+ const body = await readJson(res);
189
213
  if (!res.ok || !body.formId) die(body.error ?? `push failed (${res.status}).`);
190
214
  printPushed(body.formId, body.slug ? `${API}/f/${body.slug}` : void 0, false, !!body.staged);
191
215
  }
@@ -199,7 +223,7 @@ async function init(flags) {
199
223
  method: "POST",
200
224
  body: JSON.stringify({ email, source: "cli" })
201
225
  });
202
- const body = await res.json();
226
+ const body = await readJson(res);
203
227
  if (!res.ok || !body.key) die(body.error ?? `init failed (${res.status}).`);
204
228
  writeConfig({ ...readConfig(), pk: body.key, claimUrl: body.claim?.url ?? void 0 });
205
229
  console.log(`
@@ -282,6 +306,14 @@ function agentHelp() {
282
306
  ${dim("--run <id> --token <token> --api <url>")}
283
307
  `);
284
308
  }
309
+ function printVersion() {
310
+ try {
311
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
312
+ console.log(pkg.version);
313
+ } catch {
314
+ console.log("unknown");
315
+ }
316
+ }
285
317
  function help() {
286
318
  console.log(`
287
319
  ${bold("fillo")} \u2014 create Fillo forms from your terminal
@@ -332,6 +364,9 @@ function parseFlags(args) {
332
364
  async function main() {
333
365
  const [cmd, ...rest] = process.argv.slice(2);
334
366
  const { positional, flags } = parseFlags(rest);
367
+ if (cmd === "version" || cmd === "--version" || cmd === "-v" || flags.version === true || flags.v === true) {
368
+ return printVersion();
369
+ }
335
370
  if (flags.help === true || flags.h === true) {
336
371
  return cmd === "agent" ? agentHelp() : help();
337
372
  }
@@ -340,9 +375,11 @@ async function main() {
340
375
  return init(flags);
341
376
  case "login":
342
377
  return login();
343
- case "logout":
344
- writeConfig({});
378
+ case "logout": {
379
+ const { token, ...rest2 } = readConfig();
380
+ writeConfig(rest2);
345
381
  return console.log(" Logged out.");
382
+ }
346
383
  case "whoami":
347
384
  return whoami();
348
385
  case "push":
@@ -359,7 +396,8 @@ async function main() {
359
396
  return help();
360
397
  default:
361
398
  console.error(`Unknown command: ${cmd}`);
362
- return help();
399
+ help();
400
+ return process.exit(2);
363
401
  }
364
402
  }
365
403
  main().catch((e) => die(e instanceof Error ? e.message : String(e)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefillo/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Create and publish Fillo forms from your terminal — auth once, then your coding agent does the rest.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -10,6 +10,9 @@
10
10
  ],
11
11
  "homepage": "https://fillo.so",
12
12
  "type": "module",
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
13
16
  "bin": {
14
17
  "fillo": "./dist/index.js"
15
18
  },