@layers/amba 4.1.5 → 4.1.6

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.
@@ -21,6 +21,8 @@ export interface ProjectSummary {
21
21
  environment?: string;
22
22
  bundle_id?: string | null;
23
23
  google_oauth_client_id?: string | null;
24
+ magic_link_redirect_url?: string | null;
25
+ auth_email_brand_name?: string | null;
24
26
  status?: string;
25
27
  created_at?: string;
26
28
  /** Persisted subscription/accounting tier. */
@@ -71,6 +73,8 @@ export declare function updateProject(projectId: string, patch: {
71
73
  name?: string;
72
74
  bundle_id?: string | null;
73
75
  google_oauth_client_id?: string | null;
76
+ magic_link_redirect_url?: string | null;
77
+ auth_email_brand_name?: string | null;
74
78
  platform?: string;
75
79
  environment?: string;
76
80
  }): Promise<ApiResponse<{
@@ -78,6 +82,8 @@ export declare function updateProject(projectId: string, patch: {
78
82
  name: string;
79
83
  bundle_id: string | null;
80
84
  google_oauth_client_id: string | null;
85
+ magic_link_redirect_url: string | null;
86
+ auth_email_brand_name: string | null;
81
87
  platform: string;
82
88
  environment: string;
83
89
  }>>;
@@ -87,6 +93,8 @@ export declare function getProject(projectId: string): Promise<ApiResponse<{
87
93
  platform: string;
88
94
  environment: string;
89
95
  bundle_id: string | null;
96
+ magic_link_redirect_url: string | null;
97
+ auth_email_brand_name: string | null;
90
98
  created_at: string;
91
99
  }>>;
92
100
  export declare function deleteProject(projectId: string): Promise<ApiResponse<{
@@ -674,7 +682,7 @@ export declare function dropCollection(projectId: string, name: string, confirm:
674
682
  deleted: boolean;
675
683
  version: number;
676
684
  }>>;
677
- export type AiProviderName = 'anthropic' | 'openai';
685
+ export type AiProviderName = 'anthropic' | 'openai' | 'mistral' | 'gemini';
678
686
  export interface AiProviderRow {
679
687
  name: AiProviderName;
680
688
  /** Whether an API key is currently configured for this provider. */
@@ -1,3 +1,4 @@
1
+ import { updateProject } from '../api-client.js';
1
2
  export declare function projectsListCommand(): Promise<void>;
2
3
  export declare function projectsCreateCommand(input: {
3
4
  name: string;
@@ -6,13 +7,19 @@ export declare function projectsCreateCommand(input: {
6
7
  googleOauthClientId?: string;
7
8
  platform?: string;
8
9
  }): Promise<void>;
9
- export declare function projectsUpdateCommand(projectId: string, input: {
10
+ export interface ProjectUpdateCommandInput {
10
11
  name?: string;
11
12
  bundleId?: string;
12
13
  googleOauthClientId?: string;
14
+ magicLinkRedirectUrl?: string;
15
+ authEmailBrandName?: string;
16
+ clearMagicLinkRedirectUrl?: boolean;
17
+ clearAuthEmailBrandName?: boolean;
13
18
  platform?: string;
14
19
  environment?: string;
15
- }): Promise<void>;
20
+ }
21
+ export declare function buildProjectUpdatePatch(input: ProjectUpdateCommandInput): Parameters<typeof updateProject>[1];
22
+ export declare function projectsUpdateCommand(projectId: string, input: ProjectUpdateCommandInput): Promise<void>;
16
23
  export declare function projectsShowCommand(projectId: string): Promise<void>;
17
24
  export declare function projectsDeleteCommand(projectId: string, opts?: {
18
25
  yes?: boolean;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from "commander";
2
+ import { Command, Option } from "commander";
3
3
  import pc from "picocolors";
4
4
  import { access, chmod, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
5
5
  import { homedir, tmpdir } from "node:os";
@@ -4738,22 +4738,34 @@ async function projectsCreateCommand(input) {
4738
4738
  handleError$2(err);
4739
4739
  }
4740
4740
  }
4741
- async function projectsUpdateCommand(projectId, input) {
4742
- console.log();
4743
- console.log(pc.bold(` amba projects update ${projectId}`));
4744
- console.log(pc.dim(" ─────────────────────────────────"));
4745
- console.log();
4741
+ function buildProjectUpdatePatch(input) {
4742
+ if (input.magicLinkRedirectUrl !== void 0 && input.clearMagicLinkRedirectUrl) throw new Error("--magic-link-redirect-url conflicts with --clear-magic-link-redirect-url");
4743
+ if (input.authEmailBrandName !== void 0 && input.clearAuthEmailBrandName) throw new Error("--auth-email-brand-name conflicts with --clear-auth-email-brand-name");
4746
4744
  const patch = {};
4747
4745
  if (input.name !== void 0) patch.name = input.name;
4748
4746
  if (input.bundleId !== void 0) patch.bundle_id = input.bundleId;
4749
4747
  if (input.googleOauthClientId !== void 0) patch.google_oauth_client_id = input.googleOauthClientId;
4750
4748
  if (input.platform !== void 0) patch.platform = input.platform;
4751
4749
  if (input.environment !== void 0) patch.environment = input.environment;
4750
+ if (input.magicLinkRedirectUrl !== void 0) patch.magic_link_redirect_url = input.magicLinkRedirectUrl;
4751
+ if (input.clearMagicLinkRedirectUrl) patch.magic_link_redirect_url = null;
4752
+ if (input.authEmailBrandName !== void 0) patch.auth_email_brand_name = input.authEmailBrandName;
4753
+ if (input.clearAuthEmailBrandName) patch.auth_email_brand_name = null;
4754
+ return patch;
4755
+ }
4756
+ async function projectsUpdateCommand(projectId, input) {
4757
+ console.log();
4758
+ console.log(pc.bold(` amba projects update ${projectId}`));
4759
+ console.log(pc.dim(" ─────────────────────────────────"));
4760
+ console.log();
4761
+ const patch = buildProjectUpdatePatch(input);
4752
4762
  try {
4753
4763
  const res = await updateProject(projectId, patch);
4754
4764
  console.log(pc.green(" ✓") + ` Updated ${pc.bold(res.data.name)} ${pc.dim(`(${res.data.id})`)}`);
4755
4765
  if (res.data.bundle_id) console.log(pc.dim(` Bundle ID: ${res.data.bundle_id}`));
4756
4766
  if (res.data.google_oauth_client_id) console.log(pc.dim(` Google OAuth client id: ${res.data.google_oauth_client_id}`));
4767
+ if (res.data.magic_link_redirect_url) console.log(pc.dim(` Magic-link callback: ${res.data.magic_link_redirect_url}`));
4768
+ if (res.data.auth_email_brand_name) console.log(pc.dim(` Auth email brand: ${res.data.auth_email_brand_name}`));
4757
4769
  console.log();
4758
4770
  } catch (err) {
4759
4771
  if (err instanceof ApiClientError && err.statusCode === 404) {
@@ -6602,7 +6614,12 @@ async function functionDomainsRemoveCommand(functionName, hostnameInput) {
6602
6614
  * server-side; the CLI never echoes plaintext back. The API returns a
6603
6615
  * `api_key_preview` (first-6 + last-4) for confirmation.
6604
6616
  */
6605
- const VALID_PROVIDERS = new Set(["anthropic", "openai"]);
6617
+ const VALID_PROVIDERS = new Set([
6618
+ "anthropic",
6619
+ "openai",
6620
+ "mistral",
6621
+ "gemini"
6622
+ ]);
6606
6623
  function assertValidProvider(name) {
6607
6624
  if (!VALID_PROVIDERS.has(name)) throw new Error(`Unknown AI provider '${name}'. Supported: ${[...VALID_PROVIDERS].join(", ")}.`);
6608
6625
  }
@@ -9761,7 +9778,7 @@ async function diffConfigCommand(file = DEFAULT_BUNDLE_FILE, options = {}) {
9761
9778
  //#endregion
9762
9779
  //#region src/index.ts
9763
9780
  const program = new Command();
9764
- program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.5");
9781
+ program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.6");
9765
9782
  program.option("--token <pat>", "Use a Personal Access Token for headless / CI / agent use (overrides ~/.amba/credentials.json + AMBA_PAT env)");
9766
9783
  program.hook("preAction", (thisCommand) => {
9767
9784
  setBearerOverride(resolveTokenSource({
@@ -9850,13 +9867,17 @@ projects.command("create").description("Create a new project").requiredOption("-
9850
9867
  platform: opts.platform
9851
9868
  }));
9852
9869
  });
9853
- projects.command("update <projectId>").description("Update mutable fields on a project").option("--name <name>", "Display name").option("--bundle-id <id>", "Bundle identifier (Apple Sign In audience)").option("--google-oauth-client-id <id>", "Google OAuth 2.0 client id (Google Sign In audience). Public identifier, not a secret.").option("--platform <platform>", "Platform: 'ios' | 'android' | 'all'").option("--environment <env>", "Environment label: 'development' | 'production'").action(async (projectId, opts) => {
9870
+ projects.command("update <projectId>").description("Update mutable fields on a project").option("--name <name>", "Display name").option("--bundle-id <id>", "Bundle identifier (Apple Sign In audience)").option("--google-oauth-client-id <id>", "Google OAuth 2.0 client id (Google Sign In audience). Public identifier, not a secret.").option("--platform <platform>", "Platform: 'ios' | 'android' | 'all'").option("--environment <env>", "Environment label: 'development' | 'production'").addOption(new Option("--magic-link-redirect-url <url>", "Exact HTTPS callback URL for magic-link sign-in (Amba appends the token query parameter)").conflicts("clearMagicLinkRedirectUrl")).addOption(new Option("--clear-magic-link-redirect-url", "Clear the project callback and restore the platform fallback").conflicts("magicLinkRedirectUrl")).addOption(new Option("--auth-email-brand-name <name>", "Brand shown in auth emails and sender attribution").conflicts("clearAuthEmailBrandName")).addOption(new Option("--clear-auth-email-brand-name", "Clear the auth-email brand and fall back to the project name").conflicts("authEmailBrandName")).action(async (projectId, opts) => {
9854
9871
  await runAction(() => projectsUpdateCommand(projectId, {
9855
9872
  name: opts.name,
9856
9873
  bundleId: opts.bundleId,
9857
9874
  googleOauthClientId: opts.googleOauthClientId,
9858
9875
  platform: opts.platform,
9859
- environment: opts.environment
9876
+ environment: opts.environment,
9877
+ magicLinkRedirectUrl: opts.magicLinkRedirectUrl,
9878
+ authEmailBrandName: opts.authEmailBrandName,
9879
+ clearMagicLinkRedirectUrl: opts.clearMagicLinkRedirectUrl,
9880
+ clearAuthEmailBrandName: opts.clearAuthEmailBrandName
9860
9881
  }));
9861
9882
  });
9862
9883
  projects.command("show <projectId>").description("Show full project details as JSON").action(async (projectId) => {
@@ -10156,7 +10177,7 @@ domains.command("buy <domain>").description("Buy a domain + connect it to a site
10156
10177
  domains.command("list").description("List domains this project has purchased through Amba").action(async () => {
10157
10178
  await runAction(domainsListCommand);
10158
10179
  });
10159
- const aiProviders = program.command("ai").description("AI gateway — manage provider keys + prompts").command("providers").description("Per-project provider key registration (Anthropic, OpenAI)");
10180
+ const aiProviders = program.command("ai").description("AI gateway — manage provider keys + prompts").command("providers").description("Per-project provider key registration (Anthropic, OpenAI, Mistral, Gemini)");
10160
10181
  aiProviders.command("add <provider>").description("Register an AI provider key. Plaintext stored securely server-side; a preview (first-6+last-4) is printed back. Use --from-stdin to keep the key out of shell history.").option("--key <value>", "Provider API key plaintext (alternative: --from-stdin)").option("--from-stdin", "Read the key from stdin instead of --key").action(async (provider, opts) => {
10161
10182
  await runAction(() => aiProvidersAddCommand(provider, opts));
10162
10183
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layers/amba",
3
- "version": "4.1.5",
3
+ "version": "4.1.6",
4
4
  "description": "amba — agent-native backend-as-a-service. Functions, collections, storage, AI, email, queues, sites, and `amba ship` to take an Expo app live on the App Store + Google Play. `npx @layers/amba init` to start.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,8 +49,8 @@
49
49
  "tsdown": "^0.12.5",
50
50
  "typescript": "^5.8.3",
51
51
  "vitest": "^3.2.4",
52
- "@layers/amba-mcp": "4.0.9",
53
- "@layers/amba-shared": "4.0.4"
52
+ "@layers/amba-mcp": "4.0.10",
53
+ "@layers/amba-shared": "4.0.5"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsdown && tsc --emitDeclarationOnly",
@@ -9,8 +9,8 @@ There is no separate "identity provisioning" step — `auth` is the default surf
9
9
  | Tool | Purpose | Example args |
10
10
  | --- | --- | --- |
11
11
  | `amba_developer_me` | Verify the developer PAT and read the developer's profile. Pre-flight check before any provisioning. | `{}` |
12
- | `amba_projects_get` | Read a project's config (bundle id, OAuth client id, platform). | `{ project_id: "p_…" }` |
13
- | `amba_projects_update` | Set `bundle_id` (Apple audience) and `google_oauth_client_id` (Google audience). Required before Sign in with Apple / Google works. | `{ project_id, bundle_id: "com.example.fitness", google_oauth_client_id: "1234.apps.googleusercontent.com" }` |
12
+ | `amba_projects_get` | Read a project's config (social audiences, magic-link callback, auth-email brand, platform). | `{ project_id: "p_…" }` |
13
+ | `amba_projects_update` | Set social audiences plus the exact magic-link callback and auth-email brand. Pass null for an auth field to restore its fallback. | `{ project_id, magic_link_redirect_url: "https://app.example.com/auth/callback", auth_email_brand_name: "My App" }` |
14
14
  | `amba_users_list` | Browse end-users (app_users) of the project — useful as a smoke check after the first sign-in. | `{ project_id, limit: 20 }` |
15
15
  | `amba_users_get` | Fetch a single app_user by id. | `{ project_id, user_id }` |
16
16
  | `amba_users_bulk_update` | Set custom properties on many users at once (e.g. backfilling a `tier` property used for segmentation). | `{ project_id, user_ids: [...], properties: { tier: "trial" } }` |
@@ -18,7 +18,7 @@ There is no separate "identity provisioning" step — `auth` is the default surf
18
18
  | `amba_delete_api_key` / `amba_api_keys_delete` | Revoke a leaked key. | `{ project_id, api_key_id }` |
19
19
  | `amba_assign_role` / `amba_roles_assign` | Grant an RBAC role to an app_user (admin / moderator / etc.). | `{ project_id, user_id, role_id }` |
20
20
 
21
- There's no `amba_auth_*` namespace — auth is owned by the SDK on the client side, and there are no provisioning calls for it beyond setting the project's audience identifiers. If the user wants Apple/Google sign-in, the **mandatory** preflight is:
21
+ There's no `amba_auth_*` namespace — auth is owned by the SDK on the client side. Project-level routing, branding, and social audience configuration use `amba_projects_update`. If the user wants Apple/Google sign-in, the **mandatory** preflight is:
22
22
 
23
23
  ```
24
24
  mcp__amba__amba_projects_update({
@@ -30,6 +30,18 @@ mcp__amba__amba_projects_update({
30
30
 
31
31
  Without this, the server rejects identity tokens with `AUDIENCE_NOT_CONFIGURED` and the user thinks Amba is broken. If they don't know their bundle id, ask; if they don't have a Google OAuth client yet, tell them to create one at console.cloud.google.com and link it later.
32
32
 
33
+ For magic-link sign-in, configure the exact HTTPS callback before sending a production email:
34
+
35
+ ```
36
+ mcp__amba__amba_projects_update({
37
+ project_id: process.env.AMBA_PROJECT_ID,
38
+ magic_link_redirect_url: "https://app.example.com/auth/callback",
39
+ auth_email_brand_name: "Example App"
40
+ })
41
+ ```
42
+
43
+ Amba appends the one-time `token` query parameter to that exact URL. Auth-email copy uses the project brand; sends from the shared platform mailbox retain visible `via Amba` attribution. A pure customer-branded From address requires a separately verified sender domain.
44
+
33
45
  ## SDK init per stack
34
46
 
35
47
  ### Expo