@layers/amba 4.1.6 → 4.1.8
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.
- package/dist/index.js +52 -19
- package/dist/sandbox.d.ts +2 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -597,12 +597,36 @@ const RESERVED_BINDING_PREFIXES = ["AMBA_", "EDGE_"];
|
|
|
597
597
|
const RESERVED_BINDING_EXACT_NAMES = ["STORAGE", "EDGE_DB_PROXY"];
|
|
598
598
|
const VALID_BINDING_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
599
599
|
const MAX_BINDING_NAME_LENGTH = 64;
|
|
600
|
+
const INJECTED_BINDINGS_BY_PREFIX = {
|
|
601
|
+
AMBA_: [
|
|
602
|
+
"AMBA_API_URL",
|
|
603
|
+
"AMBA_PROJECT_ID",
|
|
604
|
+
"AMBA_INTERNAL_TOKEN",
|
|
605
|
+
"AMBA_AI_GATEWAY_URL"
|
|
606
|
+
],
|
|
607
|
+
EDGE_: ["EDGE_HEADER_SIGNING_SECRET"]
|
|
608
|
+
};
|
|
609
|
+
const RESERVED_EXACT_PURPOSE = {
|
|
610
|
+
STORAGE: "the platform's built-in object-storage binding (env.STORAGE)",
|
|
611
|
+
EDGE_DB_PROXY: "a platform database binding"
|
|
612
|
+
};
|
|
613
|
+
const SUGGESTED_BINDING_PREFIX = "APP_";
|
|
614
|
+
function suggestedBindingName(name) {
|
|
615
|
+
const reservedPrefix = RESERVED_BINDING_PREFIXES.find((prefix) => name.startsWith(prefix)) ?? "";
|
|
616
|
+
const rest = name.slice(reservedPrefix.length);
|
|
617
|
+
const candidate = `${SUGGESTED_BINDING_PREFIX}${rest}`;
|
|
618
|
+
return rest.length > 0 && VALID_BINDING_NAME_RE.test(candidate) ? candidate : `${SUGGESTED_BINDING_PREFIX}SECRET`;
|
|
619
|
+
}
|
|
600
620
|
/** Return why a binding name is reserved/invalid, or `null` if acceptable. */
|
|
601
621
|
function getBindingReservationReason(name) {
|
|
602
622
|
if (typeof name !== "string" || name.length === 0) return "Binding name must be a non-empty string";
|
|
603
623
|
if (name.length > MAX_BINDING_NAME_LENGTH) return `Binding name must be at most ${MAX_BINDING_NAME_LENGTH} characters`;
|
|
604
|
-
for (const
|
|
605
|
-
for (const
|
|
624
|
+
for (const exact of RESERVED_BINDING_EXACT_NAMES) if (name === exact) return `"${exact}" is reserved for ${RESERVED_EXACT_PURPOSE[exact]}. Use your own name instead, for example "${suggestedBindingName(name)}"`;
|
|
625
|
+
for (const prefix of RESERVED_BINDING_PREFIXES) if (name.startsWith(prefix)) {
|
|
626
|
+
const injected = INJECTED_BINDINGS_BY_PREFIX[prefix];
|
|
627
|
+
if (injected.includes(name)) return `"${name}" is injected into every function by the platform; read env.${name} instead of setting it`;
|
|
628
|
+
return `names starting with "${prefix}" are reserved for values the platform injects into every function (${injected.join(", ")}). Use your own prefix instead, for example "${suggestedBindingName(name)}"`;
|
|
629
|
+
}
|
|
606
630
|
if (!VALID_BINDING_NAME_RE.test(name)) return "Binding name must match /^[A-Z][A-Z0-9_]*$/ (uppercase ASCII, digits, underscore; must start with a letter)";
|
|
607
631
|
return null;
|
|
608
632
|
}
|
|
@@ -660,6 +684,13 @@ function isPatShape(token) {
|
|
|
660
684
|
*/
|
|
661
685
|
async function browserAuthFlow() {
|
|
662
686
|
return new Promise((resolve, reject) => {
|
|
687
|
+
let timeout;
|
|
688
|
+
const settle = (outcome) => {
|
|
689
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
690
|
+
server.close();
|
|
691
|
+
server.closeAllConnections();
|
|
692
|
+
outcome();
|
|
693
|
+
};
|
|
663
694
|
const server = createServer((req, res) => {
|
|
664
695
|
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
665
696
|
if (url.pathname !== "/callback") {
|
|
@@ -672,20 +703,16 @@ async function browserAuthFlow() {
|
|
|
672
703
|
const expiresAt = url.searchParams.get("expires_at");
|
|
673
704
|
if (!accessToken || !refreshToken || !expiresAt) {
|
|
674
705
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
675
|
-
res.end("<html><body><h2>Authentication failed</h2><p>Missing token parameters. Please try again.</p></body></html>");
|
|
676
|
-
server.close();
|
|
677
|
-
reject(/* @__PURE__ */ new Error("Missing token parameters in callback"));
|
|
706
|
+
res.end("<html><body><h2>Authentication failed</h2><p>Missing token parameters. Please try again.</p></body></html>", () => settle(() => reject(/* @__PURE__ */ new Error("Missing token parameters in callback"))));
|
|
678
707
|
return;
|
|
679
708
|
}
|
|
680
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
681
|
-
res.end("<html><body style=\"font-family:system-ui;text-align:center;padding:3rem\"><h2>Authenticated!</h2><p>You can close this window and return to the terminal.</p></body></html>");
|
|
682
709
|
const creds = {
|
|
683
710
|
access_token: accessToken,
|
|
684
711
|
refresh_token: refreshToken,
|
|
685
712
|
expires_at: expiresAt
|
|
686
713
|
};
|
|
687
|
-
|
|
688
|
-
resolve(creds);
|
|
714
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
715
|
+
res.end("<html><body style=\"font-family:system-ui;text-align:center;padding:3rem\"><h2>Authenticated!</h2><p>You can close this window and return to the terminal.</p></body></html>", () => settle(() => resolve(creds)));
|
|
689
716
|
});
|
|
690
717
|
server.listen(0, "127.0.0.1", () => {
|
|
691
718
|
const addr = server.address();
|
|
@@ -703,9 +730,8 @@ async function browserAuthFlow() {
|
|
|
703
730
|
console.log(pc.yellow(` Please open this URL manually:`));
|
|
704
731
|
console.log(` ${pc.underline(authUrl)}`);
|
|
705
732
|
});
|
|
706
|
-
setTimeout(() => {
|
|
707
|
-
|
|
708
|
-
reject(/* @__PURE__ */ new Error("Authentication timed out. Please try again."));
|
|
733
|
+
timeout = setTimeout(() => {
|
|
734
|
+
settle(() => reject(/* @__PURE__ */ new Error("Authentication timed out. Please try again.")));
|
|
709
735
|
}, 12e4);
|
|
710
736
|
});
|
|
711
737
|
});
|
|
@@ -1298,6 +1324,8 @@ function generateSandboxEmail() {
|
|
|
1298
1324
|
function generateSandboxPassword() {
|
|
1299
1325
|
return randomBytes(24).toString("base64url");
|
|
1300
1326
|
}
|
|
1327
|
+
/** Request header carrying AMBA_SIGNUP_TOKEN on the signup call. */
|
|
1328
|
+
const SIGNUP_TOKEN_HEADER = "X-Amba-Signup-Token";
|
|
1301
1329
|
/**
|
|
1302
1330
|
* POST the synthesized credentials at the public signup endpoint.
|
|
1303
1331
|
*
|
|
@@ -1313,12 +1341,17 @@ function generateSandboxPassword() {
|
|
|
1313
1341
|
*/
|
|
1314
1342
|
async function performSandboxSignup(req, options = {}) {
|
|
1315
1343
|
const apiUrl = options.apiUrl ?? process.env["AMBA_API_URL"] ?? "https://api.amba.dev";
|
|
1316
|
-
const
|
|
1344
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1345
|
+
const url = `${apiUrl}/v1/auth/developer/signup`;
|
|
1346
|
+
const headers = {
|
|
1347
|
+
"Content-Type": "application/json",
|
|
1348
|
+
"User-Agent": "amba-cli/sandbox"
|
|
1349
|
+
};
|
|
1350
|
+
const signupToken = process.env["AMBA_SIGNUP_TOKEN"]?.trim();
|
|
1351
|
+
if (signupToken) headers[SIGNUP_TOKEN_HEADER] = signupToken;
|
|
1352
|
+
const res = await fetchImpl(url, {
|
|
1317
1353
|
method: "POST",
|
|
1318
|
-
headers
|
|
1319
|
-
"Content-Type": "application/json",
|
|
1320
|
-
"User-Agent": "amba-cli/sandbox"
|
|
1321
|
-
},
|
|
1354
|
+
headers,
|
|
1322
1355
|
body: JSON.stringify({
|
|
1323
1356
|
email: req.email,
|
|
1324
1357
|
password: req.password,
|
|
@@ -9778,7 +9811,7 @@ async function diffConfigCommand(file = DEFAULT_BUNDLE_FILE, options = {}) {
|
|
|
9778
9811
|
//#endregion
|
|
9779
9812
|
//#region src/index.ts
|
|
9780
9813
|
const program = new Command();
|
|
9781
|
-
program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.
|
|
9814
|
+
program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.8");
|
|
9782
9815
|
program.option("--token <pat>", "Use a Personal Access Token for headless / CI / agent use (overrides ~/.amba/credentials.json + AMBA_PAT env)");
|
|
9783
9816
|
program.hook("preAction", (thisCommand) => {
|
|
9784
9817
|
setBearerOverride(resolveTokenSource({
|
|
@@ -9983,7 +10016,7 @@ functionDomains.command("remove <name> <hostname>").description("Detach an exact
|
|
|
9983
10016
|
await runAction(() => functionDomainsRemoveCommand(name, hostname));
|
|
9984
10017
|
});
|
|
9985
10018
|
const secrets = program.command("secrets").description("Function secrets (GCP canonical, Workers-Secret synced)");
|
|
9986
|
-
secrets.command("set <name> [value]").description("Store a secret and queue its sync to your deployed function(s). Omit --function for a project-wide secret. Use --from-stdin to keep the value out of shell history.").option("--function <name>", "Function the secret binds to. OMIT this flag to set a PROJECT-WIDE secret — one value visible to every function in the project (and to any function deployed later). With it, the secret is scoped to just that function and overrides a same-name project-wide value. Use the same name as the entry passed to 'amba functions deploy'. Secrets may be set before the function is deployed; the sync drains once you deploy.").option("--env <env>", "'dev' | 'prod' — INFORMATIONAL ONLY. Both share one secret namespace per project. Use distinct secret names (e.g. STRIPE_KEY_DEV / STRIPE_KEY_PROD) or two amba projects for real env isolation.").option("--from-stdin", "Read the secret value from stdin instead of the positional arg. Mutually exclusive with <value>. Pipe in: `echo $KEY | amba secrets set NAME --from-stdin`.").action(async (name, value, opts) => {
|
|
10019
|
+
secrets.command("set <name> [value]").description("Store a secret and queue its sync to your deployed function(s). Omit --function for a project-wide secret. Use --from-stdin to keep the value out of shell history. Names starting with AMBA_ or EDGE_ are reserved for values every function already receives (env.AMBA_PROJECT_ID, env.AMBA_API_URL, env.AMBA_INTERNAL_TOKEN); use your own prefix, e.g. APP_CLIENT_KEY.").option("--function <name>", "Function the secret binds to. OMIT this flag to set a PROJECT-WIDE secret — one value visible to every function in the project (and to any function deployed later). With it, the secret is scoped to just that function and overrides a same-name project-wide value. Use the same name as the entry passed to 'amba functions deploy'. Secrets may be set before the function is deployed; the sync drains once you deploy.").option("--env <env>", "'dev' | 'prod' — INFORMATIONAL ONLY. Both share one secret namespace per project. Use distinct secret names (e.g. STRIPE_KEY_DEV / STRIPE_KEY_PROD) or two amba projects for real env isolation.").option("--from-stdin", "Read the secret value from stdin instead of the positional arg. Mutually exclusive with <value>. Pipe in: `echo $KEY | amba secrets set NAME --from-stdin`.").action(async (name, value, opts) => {
|
|
9987
10020
|
await runAction(() => secretsSetCommand(name, value, {
|
|
9988
10021
|
function: opts.function,
|
|
9989
10022
|
env: opts.env ?? "dev",
|
package/dist/sandbox.d.ts
CHANGED
|
@@ -146,6 +146,8 @@ export declare function generateSandboxEmail(): string;
|
|
|
146
146
|
* accept "agent signup" with no password and we'd drop this entirely.
|
|
147
147
|
*/
|
|
148
148
|
export declare function generateSandboxPassword(): string;
|
|
149
|
+
/** Request header carrying AMBA_SIGNUP_TOKEN on the signup call. */
|
|
150
|
+
export declare const SIGNUP_TOKEN_HEADER = "X-Amba-Signup-Token";
|
|
149
151
|
/**
|
|
150
152
|
* POST the synthesized credentials at the public signup endpoint.
|
|
151
153
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@layers/amba",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.8",
|
|
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-
|
|
53
|
-
"@layers/amba-
|
|
52
|
+
"@layers/amba-shared": "4.0.6",
|
|
53
|
+
"@layers/amba-mcp": "4.0.11"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "tsdown && tsc --emitDeclarationOnly",
|