@usefillo/cli 0.1.1 → 0.2.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 +126 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -120,11 +120,27 @@ async function loadSchema(file, allowCode) {
120
120
  try {
121
121
  if (abs.endsWith(".json")) return JSON.parse(readFileSync(abs, "utf8"));
122
122
  const mod = await import(pathToFileURL(abs).href);
123
- return mod.default ?? mod.schema ?? mod.form;
123
+ return mod.default ?? mod.schema ?? mod.form ?? mod.forms ?? mod.MARKETING_FORMS;
124
124
  } catch (e) {
125
125
  die(`Couldn't read ${file}: ${e.message}`);
126
126
  }
127
127
  }
128
+ function isRecord(value) {
129
+ return typeof value === "object" && value !== null && !Array.isArray(value);
130
+ }
131
+ function toPushItems(value, handle) {
132
+ const values = Array.isArray(value) ? value : [value];
133
+ return values.map((item, index) => {
134
+ if (isRecord(item) && "schema" in item) {
135
+ const ownHandle = typeof item.id === "string" ? item.id : void 0;
136
+ return { handle: handle ?? ownHandle, schema: item.schema, theme: item.theme };
137
+ }
138
+ if (values.length > 1 && !handle) {
139
+ die(`Item ${index + 1} needs an id or --handle.`);
140
+ }
141
+ return { handle, schema: item };
142
+ });
143
+ }
128
144
  function printPushed(formId, url, updated, staged) {
129
145
  console.log(`
130
146
  \x1B[32m\u2713\x1B[0m ${updated ? "Updated" : "Created"} ${bold(formId)}`);
@@ -135,28 +151,45 @@ function printPushed(formId, url, updated, staged) {
135
151
  }
136
152
  async function push(file, flags) {
137
153
  if (!file) die("Usage: fillo push <form.json> [--handle name] [--draft] [--allow-code]");
138
- const schema = await loadSchema(file, flags["allow-code"] === true);
139
154
  const handle = typeof flags.handle === "string" ? flags.handle : void 0;
155
+ const items = toPushItems(await loadSchema(file, flags["allow-code"] === true), handle);
156
+ if (items.length > 1 && handle) die("--handle can only be used when pushing one form.");
140
157
  const cfg = readConfig();
141
158
  if (cfg.token) {
142
- const res = await api("/cli/forms", {
143
- method: "POST",
144
- token: cfg.token,
145
- body: JSON.stringify({ schema, handle, publish: !flags.draft })
146
- });
147
- const body = await res.json();
148
- if (!res.ok || !body.formId) die(body.error ?? `push failed (${res.status}).`);
149
- return printPushed(body.formId, body.url, !!body.updated, !!flags.draft);
159
+ for (const item of items) {
160
+ const res = await api("/cli/forms", {
161
+ method: "POST",
162
+ token: cfg.token,
163
+ body: JSON.stringify({
164
+ schema: item.schema,
165
+ theme: item.theme ?? null,
166
+ handle: item.handle,
167
+ publish: !flags.draft
168
+ })
169
+ });
170
+ const body = await res.json();
171
+ if (!res.ok || !body.formId) die(body.error ?? `push failed (${res.status}).`);
172
+ printPushed(body.formId, body.url, !!body.updated, !!flags.draft);
173
+ }
174
+ return;
150
175
  }
151
176
  if (cfg.pk) {
152
- if (!handle) die("Pushing with a provisioned key needs --handle <name>.");
153
- const res = await api("/forms/sync", {
154
- method: "POST",
155
- body: JSON.stringify({ key: cfg.pk, id: handle, schema })
156
- });
157
- const body = await res.json();
158
- if (!res.ok || !body.formId) die(body.error ?? `push failed (${res.status}).`);
159
- return printPushed(body.formId, body.slug ? `${API}/f/${body.slug}` : void 0, false, !!body.staged);
177
+ for (const item of items) {
178
+ if (!item.handle) die("Pushing with a provisioned key needs --handle <name> or form.id.");
179
+ const res = await api("/forms/sync", {
180
+ method: "POST",
181
+ body: JSON.stringify({
182
+ key: cfg.pk,
183
+ id: item.handle,
184
+ schema: item.schema,
185
+ theme: item.theme ?? null
186
+ })
187
+ });
188
+ const body = await res.json();
189
+ if (!res.ok || !body.formId) die(body.error ?? `push failed (${res.status}).`);
190
+ printPushed(body.formId, body.slug ? `${API}/f/${body.slug}` : void 0, false, !!body.staged);
191
+ }
192
+ return;
160
193
  }
161
194
  die("Not set up yet. Run `fillo init` (no account needed) or `fillo login`.");
162
195
  }
@@ -184,6 +217,71 @@ async function init(flags) {
184
217
  `);
185
218
  }
186
219
  }
220
+ async function agent(subcommand, flags) {
221
+ const apiBase = flagString(flags, "api")?.replace(/\/$/, "") ?? API;
222
+ const run = flagString(flags, "run");
223
+ const token = flagString(flags, "token");
224
+ if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
225
+ return agentHelp();
226
+ }
227
+ if (!run || !token) {
228
+ die("Usage: fillo agent <connect|event> --run <id> --token <token> [--api <url>]");
229
+ }
230
+ if (subcommand === "connect") {
231
+ await postAgentEvent(apiBase, run, token, {
232
+ status: "connected",
233
+ message: "Agent connected. Reading the app now."
234
+ });
235
+ console.log(` \x1B[32m\u2713\x1B[0m Live progress connected.`);
236
+ console.log(` ${dim("Report next steps with:")}`);
237
+ console.log(
238
+ ` fillo agent event --api ${apiBase} --run ${run} --token <same-token> --status editing --message "Editing the form screen"`
239
+ );
240
+ return;
241
+ }
242
+ if (subcommand === "event") {
243
+ const status = flagString(flags, "status");
244
+ const message = flagString(flags, "message");
245
+ if (!status) die("Usage: fillo agent event --status <status> --message <short update>");
246
+ await postAgentEvent(apiBase, run, token, {
247
+ status,
248
+ message,
249
+ claimUrl: flagString(flags, "claim-url"),
250
+ dashboardUrl: flagString(flags, "dashboard-url"),
251
+ appUrl: flagString(flags, "app-url"),
252
+ formId: flagString(flags, "form-id"),
253
+ formName: flagString(flags, "form-name")
254
+ });
255
+ console.log(` \x1B[32m\u2713\x1B[0m Progress sent.`);
256
+ return;
257
+ }
258
+ die(`Unknown agent command: ${subcommand}`);
259
+ }
260
+ async function postAgentEvent(apiBase, run, token, body) {
261
+ const res = await fetch(`${apiBase}/api/v1/agent-runs/${encodeURIComponent(run)}/events`, {
262
+ method: "POST",
263
+ headers: {
264
+ Authorization: `Bearer ${token}`,
265
+ "Content-Type": "application/json"
266
+ },
267
+ body: JSON.stringify(body)
268
+ });
269
+ const payload = await res.json().catch(() => ({}));
270
+ if (!res.ok) die(payload.error ?? `agent progress failed (${res.status}).`);
271
+ }
272
+ function agentHelp() {
273
+ console.log(`
274
+ ${bold("fillo agent")} \u2014 report live progress back to a Fillo prompt modal
275
+
276
+ ${bold("Commands")}
277
+ agent connect Connect a coding-agent run to the open browser modal
278
+ agent event Send a short progress update
279
+ ${dim("--status <created|connected|asking|planning|installing|editing|checking|done|error>")}
280
+ ${dim('--message "Short update"')}
281
+ ${dim("--claim-url <url> --app-url <url> --form-name <name>")}
282
+ ${dim("--run <id> --token <token> --api <url>")}
283
+ `);
284
+ }
187
285
  function help() {
188
286
  console.log(`
189
287
  ${bold("fillo")} \u2014 create Fillo forms from your terminal
@@ -199,10 +297,15 @@ function help() {
199
297
  ${dim("--draft stage as a draft instead of publishing")}
200
298
  ${dim("--allow-code allow a .mjs/.js schema (executes the file)")}
201
299
  list List the workspace's forms
300
+ agent <cmd> Report live progress to a Fillo prompt modal
202
301
 
203
302
  ${dim(`API: ${API} \xB7 set FILLO_API to override`)}
204
303
  `);
205
304
  }
305
+ function flagString(flags, key) {
306
+ const value = flags[key];
307
+ return typeof value === "string" && value ? value : void 0;
308
+ }
206
309
  function parseFlags(args) {
207
310
  const flags = {};
208
311
  const positional = [];
@@ -229,6 +332,9 @@ function parseFlags(args) {
229
332
  async function main() {
230
333
  const [cmd, ...rest] = process.argv.slice(2);
231
334
  const { positional, flags } = parseFlags(rest);
335
+ if (flags.help === true || flags.h === true) {
336
+ return cmd === "agent" ? agentHelp() : help();
337
+ }
232
338
  switch (cmd) {
233
339
  case "init":
234
340
  return init(flags);
@@ -244,6 +350,8 @@ async function main() {
244
350
  case "list":
245
351
  case "ls":
246
352
  return list();
353
+ case "agent":
354
+ return agent(positional[0], flags);
247
355
  case void 0:
248
356
  case "help":
249
357
  case "--help":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefillo/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.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": [