@usefillo/cli 0.14.0 → 0.14.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 +95 -42
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -91,7 +91,9 @@ function die(msg) {
91
91
  }
92
92
 
93
93
  // src/lib/api.ts
94
- var API = (process.env.FILLO_API ?? "https://fillo.so").replace(/\/$/, "");
94
+ var DEFAULT_API = "https://fillo.so";
95
+ var apiOverride = process.env.FILLO_API?.trim();
96
+ var API = (apiOverride || DEFAULT_API).replace(/\/$/, "");
95
97
  var REQUEST_TIMEOUT_MS = 3e4;
96
98
  var SYNC_TOKEN_ENV = "FILLO_SYNC_TOKEN";
97
99
  function requireTokenFor(apiBase) {
@@ -113,24 +115,51 @@ function requireTokenFor(apiBase) {
113
115
  function requireToken() {
114
116
  return requireTokenFor(API);
115
117
  }
118
+ function networkFailure(apiBase, error) {
119
+ const timedOut = error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError");
120
+ const reason = causeCode(error) ?? (timedOut ? `no response within ${REQUEST_TIMEOUT_MS / 1e3}s` : void 0);
121
+ const hint = apiBase === DEFAULT_API ? "Check your network connection and try again." : `The CLI is pointed there by FILLO_API or --api \u2014 unset the override to use ${DEFAULT_API}, or start that deployment.`;
122
+ return new Error(
123
+ `Couldn't reach ${terminalText(apiBase)}${reason ? ` (${reason})` : ""}. ${hint}`,
124
+ {
125
+ cause: error
126
+ }
127
+ );
128
+ }
129
+ function causeCode(error, depth = 0) {
130
+ if (!error || typeof error !== "object" || depth > 8) return void 0;
131
+ const { code, errors, cause } = error;
132
+ if (typeof code === "string" && code) return code;
133
+ if (Array.isArray(errors)) {
134
+ for (const nested of errors) {
135
+ const found = causeCode(nested, depth + 1);
136
+ if (found) return found;
137
+ }
138
+ }
139
+ return causeCode(cause, depth + 1);
140
+ }
116
141
  async function api(path, init2 = {}, apiBase = API) {
117
142
  const { token, ...rest } = init2;
118
- return fetch(`${apiBase}/api/v1${path}`, {
119
- ...rest,
120
- signal: rest.signal ?? AbortSignal.timeout(REQUEST_TIMEOUT_MS),
121
- headers: {
122
- "Content-Type": "application/json",
123
- ...token ? { Authorization: `Bearer ${token}` } : {},
124
- ...rest.headers
125
- }
126
- });
143
+ try {
144
+ return await fetch(`${apiBase}/api/v1${path}`, {
145
+ ...rest,
146
+ signal: rest.signal ?? AbortSignal.timeout(REQUEST_TIMEOUT_MS),
147
+ headers: {
148
+ "Content-Type": "application/json",
149
+ ...token ? { Authorization: `Bearer ${token}` } : {},
150
+ ...rest.headers
151
+ }
152
+ });
153
+ } catch (error) {
154
+ throw networkFailure(apiBase, error);
155
+ }
127
156
  }
128
- async function readJson(res) {
157
+ async function readJson(res, apiBase = API) {
129
158
  const text = await res.text();
130
159
  try {
131
160
  return text ? JSON.parse(text) : {};
132
161
  } catch {
133
- die(`Unexpected non-JSON response from ${API} (${res.status}).`);
162
+ die(`Unexpected non-JSON response from ${apiBase} (${res.status}).`);
134
163
  }
135
164
  }
136
165
 
@@ -722,7 +751,7 @@ function readSecret(promptText) {
722
751
  // src/commands/init.ts
723
752
  var EMAIL_REQUIRED = 'Usage: fillo init --email you@company.com [--name "Your Name"] (or run `fillo login` for an existing account).';
724
753
  var execFileAsync = promisify(execFile);
725
- async function provisionWorkspace(flags, usageHint) {
754
+ async function provisionWorkspace(flags, usageHint, apiBase = API) {
726
755
  const json = jsonMode(flags);
727
756
  let email3 = flagString(flags, "email")?.trim() ?? "";
728
757
  let name = flagString(flags, "name")?.trim() ?? "";
@@ -752,11 +781,15 @@ async function provisionWorkspace(flags, usageHint) {
752
781
  }
753
782
  }
754
783
  }
755
- const res = await api("/workspaces/provision", {
756
- method: "POST",
757
- body: JSON.stringify({ email: email3, source: "cli", ...name ? { name } : {} })
758
- });
759
- const body = await readJson(res);
784
+ const res = await api(
785
+ "/workspaces/provision",
786
+ {
787
+ method: "POST",
788
+ body: JSON.stringify({ email: email3, source: "cli", ...name ? { name } : {} })
789
+ },
790
+ apiBase
791
+ );
792
+ const body = await readJson(res, apiBase);
760
793
  if (!res.ok || !body.key) die(body.error ?? `Provisioning failed (${res.status}).`);
761
794
  const claimToken = claimCookieValue(res);
762
795
  const { claimUrl: _legacyWorkspaceLink, ...current } = readConfig();
@@ -797,7 +830,12 @@ async function init(flags) {
797
830
  const json = jsonMode(flags);
798
831
  const ws = await provisionWorkspace(flags, EMAIL_REQUIRED);
799
832
  if (json) {
800
- return emitResult({ pk: ws.pk, email: ws.email, name: ws.name || null, limits: ws.limits ?? null });
833
+ return emitResult({
834
+ pk: ws.pk,
835
+ email: ws.email,
836
+ name: ws.name || null,
837
+ limits: ws.limits ?? null
838
+ });
801
839
  }
802
840
  console.log(
803
841
  `
@@ -1059,12 +1097,14 @@ var skillCommand = {
1059
1097
 
1060
1098
  // src/commands/agent.ts
1061
1099
  var BOOTSTRAP_USAGE = 'To set up a workspace I need an email for your recovery link. Re-run: npx @usefillo/cli@latest agent bootstrap --email you@company.com [--name "Your Name"]. (Already have an account? `fillo login`.)';
1062
- async function bootstrapStandalone(flags) {
1100
+ async function bootstrapStandalone(flags, apiBase) {
1063
1101
  if (flags.account === true) {
1064
- die("--account is for the browser handoff: `fillo agent bootstrap --run <id> --token <token> --account`.");
1102
+ die(
1103
+ "--account is for the browser handoff: `fillo agent bootstrap --run <id> --token <token> --account`."
1104
+ );
1065
1105
  }
1066
1106
  const json = jsonMode(flags);
1067
- const ws = await provisionWorkspace(flags, BOOTSTRAP_USAGE);
1107
+ const ws = await provisionWorkspace(flags, BOOTSTRAP_USAGE, apiBase);
1068
1108
  const targets = installSkill(flags, { quiet: json });
1069
1109
  if (json) {
1070
1110
  return emitResult({
@@ -1079,7 +1119,9 @@ async function bootstrapStandalone(flags) {
1079
1119
  `
1080
1120
  ${bold("Ready.")} Workspace provisioned and the Build with Fillo skill installed.`
1081
1121
  );
1082
- console.log(` ${dim(`A workspace link was sent to ${terminalText(ws.email)} \u2014 claim it anytime with \`fillo claim\`.`)}`);
1122
+ console.log(
1123
+ ` ${dim(`A workspace link was sent to ${terminalText(ws.email)} \u2014 claim it anytime with \`fillo claim\`.`)}`
1124
+ );
1083
1125
  const preview = previewLimitLine(ws.limits);
1084
1126
  if (preview) console.log(` ${dim(preview)}`);
1085
1127
  console.log(`
@@ -1112,7 +1154,7 @@ async function agent(subcommand, flags) {
1112
1154
  die("--account does not take a value.");
1113
1155
  }
1114
1156
  if (subcommand === "bootstrap") {
1115
- if (!run || !token) return bootstrapStandalone(flags);
1157
+ if (!run || !token) return bootstrapStandalone(flags, apiBase);
1116
1158
  installSkill(flags);
1117
1159
  if (flags.account === true) {
1118
1160
  await login({ ...flags, json: false }, { continueToAgentRun: true });
@@ -1176,29 +1218,39 @@ async function connectAgentRun(apiBase, run, token, attachAccount) {
1176
1218
  );
1177
1219
  }
1178
1220
  async function postAgentEvent(apiBase, run, token, body) {
1179
- const res = await fetch(`${apiBase}/api/v1/agent-runs/${encodeURIComponent(run)}/events`, {
1180
- method: "POST",
1181
- headers: {
1182
- Authorization: `Bearer ${token}`,
1183
- "Content-Type": "application/json"
1184
- },
1185
- body: JSON.stringify(body),
1186
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
1187
- });
1221
+ let res;
1222
+ try {
1223
+ res = await fetch(`${apiBase}/api/v1/agent-runs/${encodeURIComponent(run)}/events`, {
1224
+ method: "POST",
1225
+ headers: {
1226
+ Authorization: `Bearer ${token}`,
1227
+ "Content-Type": "application/json"
1228
+ },
1229
+ body: JSON.stringify(body),
1230
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
1231
+ });
1232
+ } catch (error) {
1233
+ throw networkFailure(apiBase, error);
1234
+ }
1188
1235
  const payload = await res.json().catch(() => ({}));
1189
1236
  if (!res.ok) die(payload.error ?? `agent progress failed (${res.status}).`);
1190
1237
  }
1191
1238
  async function attachAgentAccount(apiBase, run, progressToken) {
1192
1239
  const accountToken = requireTokenFor(apiBase);
1193
- const res = await fetch(`${apiBase}/api/v1/agent-runs/${encodeURIComponent(run)}/account`, {
1194
- method: "POST",
1195
- redirect: "error",
1196
- headers: {
1197
- Authorization: `Bearer ${accountToken}`,
1198
- "X-Fillo-Agent-Run-Token": progressToken
1199
- },
1200
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
1201
- });
1240
+ let res;
1241
+ try {
1242
+ res = await fetch(`${apiBase}/api/v1/agent-runs/${encodeURIComponent(run)}/account`, {
1243
+ method: "POST",
1244
+ redirect: "error",
1245
+ headers: {
1246
+ Authorization: `Bearer ${accountToken}`,
1247
+ "X-Fillo-Agent-Run-Token": progressToken
1248
+ },
1249
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
1250
+ });
1251
+ } catch (error) {
1252
+ throw networkFailure(apiBase, error);
1253
+ }
1202
1254
  const payload = await res.json().catch(() => ({}));
1203
1255
  if (!res.ok) {
1204
1256
  const hint = res.status === 401 ? " Run `fillo login` and try again." : "";
@@ -1217,6 +1269,7 @@ function agentHelp() {
1217
1269
  agent bootstrap Set up a workspace and install the Build with Fillo skill
1218
1270
  ${dimRaw("--email you@company.com provision a fresh workspace (a TTY confirms your git identity)")}
1219
1271
  ${dimRaw('--name "Your Name" optional display name')}
1272
+ ${dimRaw("--api <url> target a different Fillo deployment (default: FILLO_API or https://fillo.so)")}
1220
1273
  ${dimRaw("--run <id> --token <token> instead: attach to an open browser modal")}
1221
1274
  ${dimRaw("--account with --run/--token: approve an existing workspace in the browser")}
1222
1275
  agent connect Connect a coding-agent run to the open browser modal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefillo/cli",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Create and publish Fillo forms, and install the Fillo Agent Skill.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup && node scripts/copy-skill.mjs",
33
- "test": "node scripts/validate-skill-bundle.mjs && node scripts/test-skill-bundle.mjs && node scripts/test-skill-install.mjs && node scripts/test-agent-account.mjs && node scripts/test-stage-push.mjs && node scripts/test-local-validation.mjs && node scripts/test-loopback.mjs && node scripts/test-init.mjs && node scripts/test-init-tty.mjs && node scripts/test-keys-claim.mjs && node scripts/test-responses.mjs && node scripts/test-workspace-commands.mjs",
33
+ "test": "node scripts/validate-skill-bundle.mjs && node scripts/test-skill-bundle.mjs && node scripts/test-skill-install.mjs && node scripts/test-agent-account.mjs && node scripts/test-stage-push.mjs && node scripts/test-local-validation.mjs && node scripts/test-loopback.mjs && node scripts/test-init.mjs && node scripts/test-api-target.mjs && node scripts/test-init-tty.mjs && node scripts/test-keys-claim.mjs && node scripts/test-responses.mjs && node scripts/test-workspace-commands.mjs",
34
34
  "typecheck": "tsc --noEmit"
35
35
  }
36
36
  }