fimo 0.2.5 → 0.3.0-staging.17

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.
@@ -3,9 +3,10 @@ name: fimo-cli
3
3
  description: >-
4
4
  The `fimo` CLI scaffolds and manages Fimo projects from the command line.
5
5
  Load when the user wants to create a new Fimo project, build content
6
- (schemas, entries, forms, media, labels/locales), deploy a site, invite
7
- collaborators, inspect AI credits, log in or switch organizations, or set
8
- up Fimo for their AI tool. Most "do X with Fimo" tasks go through this skill.
6
+ (schemas, entries, forms, media, labels/locales), deploy a site, manage
7
+ custom domains, check site traffic/analytics, invite collaborators, inspect
8
+ AI credits, log in or switch organizations, or set up Fimo for their AI
9
+ tool. Most "do X with Fimo" tasks go through this skill.
9
10
  ---
10
11
 
11
12
  # Fimo CLI
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-06-30T13:10:02.190Z",
5
- "cliVersion": "0.2.5",
4
+ "bundledAt": "2026-07-02T12:32:25.763Z",
5
+ "cliVersion": "0.3.0-staging.17",
6
6
  "external": [
7
7
  "oxc-parser",
8
8
  "fsevents"
package/dist/cli/index.js CHANGED
@@ -19612,6 +19612,15 @@ function throwUnauthorized(cfg, status) {
19612
19612
  }
19613
19613
  throw new UnauthorizedError(cfg.apiUrl, status);
19614
19614
  }
19615
+ async function throwPermissionDenied(cfg, response) {
19616
+ let required2 = [];
19617
+ try {
19618
+ const body = await response.json();
19619
+ required2 = Array.isArray(body.required) ? body.required : body.required ? [body.required] : [];
19620
+ } catch {
19621
+ }
19622
+ throw new PermissionDeniedError(cfg.apiUrl, required2);
19623
+ }
19615
19624
  async function fimoFetch(url2, init) {
19616
19625
  const headers = new Headers(init.headers);
19617
19626
  if (!headers.has(CLI_VERSION_HEADER)) {
@@ -19626,7 +19635,7 @@ async function fimoFetch(url2, init) {
19626
19635
  }
19627
19636
  return response;
19628
19637
  }
19629
- var ApiRequestError, UnauthorizedError, CLI_VERSION_HEADER, CLI_LATEST_HEADER, FIMO_ENV_HEADER, FimoApiClient;
19638
+ var ApiRequestError, UnauthorizedError, PermissionDeniedError, CLI_VERSION_HEADER, CLI_LATEST_HEADER, FIMO_ENV_HEADER, FimoApiClient;
19630
19639
  var init_api = __esm({
19631
19640
  "src/cli/api.ts"() {
19632
19641
  init_auth();
@@ -19667,7 +19676,27 @@ var init_api = __esm({
19667
19676
  this.status = status;
19668
19677
  }
19669
19678
  };
19679
+ PermissionDeniedError = class extends Error {
19680
+ static {
19681
+ __name(this, "PermissionDeniedError");
19682
+ }
19683
+ // 403: the session is VALID but the principal's role lacks a permission.
19684
+ // Distinct from UnauthorizedError (401, stale/invalid token) so the CLI
19685
+ // never tells a correctly-authenticated caller to `fimo login` — that
19686
+ // false diagnosis gets repeated verbatim by agent callers.
19687
+ apiUrl;
19688
+ /** Permission(s) the server reported missing — the 403 body's `required`. */
19689
+ required;
19690
+ constructor(apiUrl, required2 = [], message) {
19691
+ const detail = required2.length ? ` Missing permission: ${required2.join(", ")}.` : "";
19692
+ super(message ?? `You don't have permission to do this on ${apiUrl}.${detail}`);
19693
+ this.name = "PermissionDeniedError";
19694
+ this.apiUrl = apiUrl;
19695
+ this.required = required2;
19696
+ }
19697
+ };
19670
19698
  __name(throwUnauthorized, "throwUnauthorized");
19699
+ __name(throwPermissionDenied, "throwPermissionDenied");
19671
19700
  CLI_VERSION_HEADER = "X-Fimo-CLI-Version";
19672
19701
  CLI_LATEST_HEADER = "X-Fimo-CLI-Latest";
19673
19702
  FIMO_ENV_HEADER = "X-Fimo-Env";
@@ -19732,9 +19761,12 @@ var init_api = __esm({
19732
19761
  headers.set(FIMO_ENV_HEADER, this.env);
19733
19762
  }
19734
19763
  const response = await fimoFetch(`${this.cfg.apiUrl}${path46}`, { ...init, headers });
19735
- if (response.status === 401 || response.status === 403 && !opts.allow403) {
19764
+ if (response.status === 401) {
19736
19765
  throwUnauthorized(this.cfg, response.status);
19737
19766
  }
19767
+ if (response.status === 403 && !opts.allow403) {
19768
+ await throwPermissionDenied(this.cfg, response);
19769
+ }
19738
19770
  if (!response.ok) {
19739
19771
  const error45 = await this.formatError(response);
19740
19772
  throw new ApiRequestError(init.method ?? "GET", path46, response.status, error45);
@@ -19752,9 +19784,12 @@ var init_api = __esm({
19752
19784
  headers.set(FIMO_ENV_HEADER, this.env);
19753
19785
  }
19754
19786
  const response = await fimoFetch(`${this.cfg.apiUrl}${path46}`, { ...init, headers });
19755
- if (response.status === 401 || response.status === 403) {
19787
+ if (response.status === 401) {
19756
19788
  throwUnauthorized(this.cfg, response.status);
19757
19789
  }
19790
+ if (response.status === 403) {
19791
+ await throwPermissionDenied(this.cfg, response);
19792
+ }
19758
19793
  if (!response.ok) {
19759
19794
  const error45 = await this.formatError(response);
19760
19795
  throw new Error(`API ${init.method ?? "GET"} ${path46} failed: ${response.status} ${error45}`);
@@ -41596,7 +41631,7 @@ var init_color = __esm({
41596
41631
 
41597
41632
  // src/cli/utils/auth-errors.ts
41598
41633
  function isCliAuthError(err) {
41599
- return err instanceof NotAuthenticatedError || err instanceof UnauthorizedError;
41634
+ return err instanceof NotAuthenticatedError || err instanceof UnauthorizedError || err instanceof PermissionDeniedError;
41600
41635
  }
41601
41636
  function rethrowCliAuthError(err, beforeThrow) {
41602
41637
  if (isCliAuthError(err)) {
@@ -41610,6 +41645,16 @@ function retryCommand(args = process.argv.slice(2)) {
41610
41645
  function formatCliAuthError(err, opts = {}) {
41611
41646
  const p2 = opts.palette ?? palette;
41612
41647
  const command = retryCommand(opts.args);
41648
+ if (err instanceof PermissionDeniedError) {
41649
+ return [
41650
+ `${p2.red("\u2717")} You don't have permission to run this command.`,
41651
+ "",
41652
+ err.required.length ? ` Missing permission: ${p2.bold(err.required.join(", "))}` : " Your role on this project does not allow this action.",
41653
+ " Ask a project admin for access, or use an account with a higher role.",
41654
+ "",
41655
+ p2.dim(` Backend: ${err.apiUrl}`)
41656
+ ].join("\n");
41657
+ }
41613
41658
  const expired = err instanceof UnauthorizedError;
41614
41659
  const intro = expired ? "Your Fimo session is expired or invalid." : "You're not signed in to Fimo.";
41615
41660
  const loginVerb = expired ? "refresh your session" : "sign in";
@@ -42736,7 +42781,9 @@ var init_integration_dto = __esm({
42736
42781
  BindProjectChannelRequestSchema = external_exports.object({
42737
42782
  ref: external_exports.string().min(1),
42738
42783
  connectionAlias: external_exports.string().min(1).optional(),
42739
- agentName: external_exports.string().min(1).max(160).nullable().optional()
42784
+ agentName: external_exports.string().min(1).max(160).nullable().optional(),
42785
+ /** Take over a channel already bound to another project in the same org (org owner/admin only). */
42786
+ force: external_exports.boolean().optional()
42740
42787
  });
42741
42788
  ProjectChannelBindingDTOSchema = external_exports.object({
42742
42789
  id: external_exports.uuid(),
@@ -45322,6 +45369,16 @@ var init_git = __esm({
45322
45369
  });
45323
45370
 
45324
45371
  // src/cli/utils/project-context.ts
45372
+ function projectSettingsFromEnv() {
45373
+ const projectId = process.env.FIMO_PROJECT_ID?.trim();
45374
+ if (!projectId) return null;
45375
+ return {
45376
+ projectId,
45377
+ organizationId: process.env.FIMO_ORGANIZATION_ID?.trim() ?? "",
45378
+ name: process.env.FIMO_PROJECT_NAME?.trim() ?? projectId,
45379
+ apiUrl: process.env.FIMO_API_URL?.trim() ?? loadConfig().apiUrl
45380
+ };
45381
+ }
45325
45382
  async function resolveEnvFromGit(cwd) {
45326
45383
  if (!await isGitRepo(cwd)) return null;
45327
45384
  try {
@@ -45343,7 +45400,11 @@ async function getProjectContext(opts = {}) {
45343
45400
  try {
45344
45401
  settings = loadProjectSettings(cwd);
45345
45402
  } catch {
45346
- throw new NotInProjectError();
45403
+ const fromEnv = projectSettingsFromEnv();
45404
+ if (!fromEnv) {
45405
+ throw new NotInProjectError();
45406
+ }
45407
+ settings = fromEnv;
45347
45408
  }
45348
45409
  let env2;
45349
45410
  let source;
@@ -45447,6 +45508,7 @@ var init_project_context = __esm({
45447
45508
  this.name = "NotInProjectError";
45448
45509
  }
45449
45510
  };
45511
+ __name(projectSettingsFromEnv, "projectSettingsFromEnv");
45450
45512
  InvalidEnvOverrideError = class extends Error {
45451
45513
  static {
45452
45514
  __name(this, "InvalidEnvOverrideError");
@@ -100149,7 +100211,7 @@ Examples:
100149
100211
  Run \`fimo channels <command> --help\` for command-specific help.
100150
100212
  `
100151
100213
  );
100152
- channels.command("bind <ref>").description("Bind a provider channel to this project").option("--project <id>", "Project id (defaults to .fimo.settings.json projectId)").option("-C, --cwd <dir>", "Project directory").option("--connection <alias>", "Connection alias when a project has more than one provider connection").option("--agent <name>", "Agent name reserved for this channel binding").action((ref, opts) => bindCommand(ref, opts));
100214
+ channels.command("bind <ref>").description("Bind a provider channel to this project").option("--project <id>", "Project id (defaults to .fimo.settings.json projectId)").option("-C, --cwd <dir>", "Project directory").option("--connection <alias>", "Connection alias when a project has more than one provider connection").option("--agent <name>", "Agent name reserved for this channel binding").option("--force", "Take over a channel already bound to another project in your org (org owner/admin only)").action((ref, opts) => bindCommand(ref, opts));
100153
100215
  }
100154
100216
  __name(registerChannelsCommands, "registerChannelsCommands");
100155
100217
  async function bindCommand(ref, opts) {
@@ -100158,7 +100220,8 @@ async function bindCommand(ref, opts) {
100158
100220
  const binding = await api.bindProjectChannel(projectId, {
100159
100221
  ref,
100160
100222
  connectionAlias: opts.connection,
100161
- agentName: opts.agent
100223
+ agentName: opts.agent,
100224
+ force: opts.force
100162
100225
  });
100163
100226
  ui.ok({
100164
100227
  command: "channels bind",
@@ -109194,13 +109257,22 @@ program.parseAsync(process.argv).catch((err) => {
109194
109257
  const command = process.argv[2] ?? "fimo";
109195
109258
  if (isCliAuthError(err)) {
109196
109259
  if (currentMode().format === "json") {
109197
- const isExpired = err instanceof UnauthorizedError;
109198
- ui.error({
109199
- command,
109200
- code: isExpired ? "SESSION_EXPIRED" : "NOT_AUTHENTICATED",
109201
- message: err.message,
109202
- hint: isExpired ? "Run `fimo login` to refresh." : "Run `fimo login` to sign in."
109203
- });
109260
+ if (err instanceof PermissionDeniedError) {
109261
+ ui.error({
109262
+ command,
109263
+ code: "PERMISSION_DENIED",
109264
+ message: err.message,
109265
+ hint: err.required.length ? `Your role lacks \`${err.required.join(", ")}\` on this project \u2014 ask a project admin for access.` : "Your role does not allow this action on this project \u2014 ask a project admin for access."
109266
+ });
109267
+ } else {
109268
+ const isExpired = err instanceof UnauthorizedError;
109269
+ ui.error({
109270
+ command,
109271
+ code: isExpired ? "SESSION_EXPIRED" : "NOT_AUTHENTICATED",
109272
+ message: err.message,
109273
+ hint: isExpired ? "Run `fimo login` to refresh." : "Run `fimo login` to sign in."
109274
+ });
109275
+ }
109204
109276
  } else {
109205
109277
  renderCliAuthError(err);
109206
109278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.2.5",
3
+ "version": "0.3.0-staging.17",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
package/release.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "source": "npm",
3
- "apiUrl": "https://api.fimo.ai",
4
- "webUrl": "https://fimo.ai"
3
+ "apiUrl": "https://api.staging.fimo.team",
4
+ "webUrl": "https://staging.fimo.team"
5
5
  }
@@ -21,7 +21,7 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "0.2.5",
24
+ "fimo": "0.3.0-staging.17",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",