@layers/amba 4.1.1 → 4.1.2
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/api-client.d.ts
CHANGED
|
@@ -23,6 +23,26 @@ export interface ProjectSummary {
|
|
|
23
23
|
google_oauth_client_id?: string | null;
|
|
24
24
|
status?: string;
|
|
25
25
|
created_at?: string;
|
|
26
|
+
/** Persisted subscription/accounting tier. */
|
|
27
|
+
tier?: 'free' | 'pro' | 'scale' | 'enterprise';
|
|
28
|
+
/** Runtime entitlement tier; differs from `tier` for comped projects. */
|
|
29
|
+
effective_tier?: 'free' | 'pro' | 'scale' | 'enterprise';
|
|
30
|
+
comped?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface CreatedProject extends ProjectSummary {
|
|
33
|
+
api_keys?: {
|
|
34
|
+
client: {
|
|
35
|
+
key: string;
|
|
36
|
+
prefix?: string;
|
|
37
|
+
type?: 'client';
|
|
38
|
+
};
|
|
39
|
+
server: {
|
|
40
|
+
key: string;
|
|
41
|
+
prefix?: string;
|
|
42
|
+
type?: 'server';
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
provisioning_status?: string;
|
|
26
46
|
}
|
|
27
47
|
export declare function listProjects(): Promise<ApiListResponse<ProjectSummary>>;
|
|
28
48
|
export declare function createProject(input: {
|
|
@@ -41,12 +61,7 @@ export declare function createProject(input: {
|
|
|
41
61
|
* bootstrap. Defaults to `'production'` when omitted.
|
|
42
62
|
*/
|
|
43
63
|
environment?: 'development' | 'production';
|
|
44
|
-
}): Promise<ApiResponse<
|
|
45
|
-
id: string;
|
|
46
|
-
name: string;
|
|
47
|
-
platform: string;
|
|
48
|
-
environment?: string;
|
|
49
|
-
}>>;
|
|
64
|
+
}): Promise<ApiResponse<CreatedProject>>;
|
|
50
65
|
/**
|
|
51
66
|
* PATCH /admin/projects/:projectId — update mutable fields. Server
|
|
52
67
|
* silently ignores unknown keys; we filter to the documented allow-list
|
|
@@ -281,6 +296,48 @@ export interface FunctionDeploymentRow {
|
|
|
281
296
|
* function so external webhook senders can reach it. Default false. */
|
|
282
297
|
is_public?: boolean;
|
|
283
298
|
}
|
|
299
|
+
/** Provider-neutral lifecycle for an exact hostname bound to one function. */
|
|
300
|
+
export interface FunctionDomain {
|
|
301
|
+
id: string;
|
|
302
|
+
project_id: string;
|
|
303
|
+
function_name: string;
|
|
304
|
+
hostname: string;
|
|
305
|
+
cert_status: 'pending_validation' | 'pending_issuance' | 'pending_deployment' | 'active' | 'error';
|
|
306
|
+
ownership_status: 'pending' | 'active' | 'error';
|
|
307
|
+
/** True only when certificate/ownership are active and routing reconciliation succeeded. */
|
|
308
|
+
live: boolean;
|
|
309
|
+
created_at: string;
|
|
310
|
+
updated_at: string;
|
|
311
|
+
}
|
|
312
|
+
export interface FunctionDomainValidationRecord {
|
|
313
|
+
txt_name?: string;
|
|
314
|
+
txt_value?: string;
|
|
315
|
+
http_url?: string;
|
|
316
|
+
http_body?: string;
|
|
317
|
+
}
|
|
318
|
+
export interface FunctionDomainOwnershipVerification {
|
|
319
|
+
type?: string;
|
|
320
|
+
name?: string;
|
|
321
|
+
value?: string;
|
|
322
|
+
}
|
|
323
|
+
/** Attach always returns DNS instructions; refresh may add them when available. */
|
|
324
|
+
export interface AttachedFunctionDomain extends FunctionDomain {
|
|
325
|
+
dns_target: string;
|
|
326
|
+
/** Additive status when Amba controls the purchased domain's DNS zone. */
|
|
327
|
+
dns_autoconfigured?: boolean;
|
|
328
|
+
dns_records_published?: number;
|
|
329
|
+
/** True when automatic DNS publication failed and refresh should be retried. */
|
|
330
|
+
dns_error?: boolean;
|
|
331
|
+
ssl_validation: FunctionDomainValidationRecord[];
|
|
332
|
+
ownership_verification: FunctionDomainOwnershipVerification | null;
|
|
333
|
+
ownership_verification_http: {
|
|
334
|
+
http_url?: string;
|
|
335
|
+
http_body?: string;
|
|
336
|
+
} | null;
|
|
337
|
+
}
|
|
338
|
+
export type RefreshedFunctionDomain = FunctionDomain & Partial<Pick<AttachedFunctionDomain, 'dns_target' | 'dns_autoconfigured' | 'dns_records_published' | 'dns_error' | 'ssl_validation' | 'ownership_verification' | 'ownership_verification_http'>> & {
|
|
339
|
+
cleanup_pending?: boolean;
|
|
340
|
+
};
|
|
284
341
|
export declare function recordFunctionDeployment(projectId: string, input: {
|
|
285
342
|
name: string;
|
|
286
343
|
runtime_script_name: string;
|
|
@@ -324,6 +381,17 @@ export declare function deployFunctionViaApi(projectId: string, input: {
|
|
|
324
381
|
export declare function listFunctionDeployments(projectId: string, options?: {
|
|
325
382
|
activeOnly?: boolean;
|
|
326
383
|
}): Promise<ApiListResponse<FunctionDeploymentRow>>;
|
|
384
|
+
/** Attach one exact hostname to one deployed function. */
|
|
385
|
+
export declare function attachFunctionDomainViaApi(projectId: string, functionName: string, hostname: string): Promise<ApiResponse<AttachedFunctionDomain>>;
|
|
386
|
+
/** List provider-neutral ownership/certificate status for a function's hostnames. */
|
|
387
|
+
export declare function listFunctionDomainsViaApi(projectId: string, functionName: string): Promise<ApiListResponse<FunctionDomain>>;
|
|
388
|
+
/** Re-poll DNS ownership and certificate state for one exact hostname. */
|
|
389
|
+
export declare function refreshFunctionDomainViaApi(projectId: string, functionName: string, hostname: string): Promise<ApiResponse<RefreshedFunctionDomain>>;
|
|
390
|
+
/** Detach one exact hostname from a function. */
|
|
391
|
+
export declare function removeFunctionDomainViaApi(projectId: string, functionName: string, hostname: string): Promise<ApiResponse<{
|
|
392
|
+
hostname: string;
|
|
393
|
+
deleted: true;
|
|
394
|
+
}>>;
|
|
327
395
|
export interface FunctionDeployContext {
|
|
328
396
|
/** Dispatch namespace for the script PUT (e.g. `amba_tenant_functions`). */
|
|
329
397
|
dispatch_namespace: string;
|
|
@@ -382,6 +450,11 @@ export interface FunctionLogEvent {
|
|
|
382
450
|
export interface FunctionLogsResponse {
|
|
383
451
|
events: FunctionLogEvent[];
|
|
384
452
|
truncated: boolean;
|
|
453
|
+
/** Provider-neutral freshness for the underlying log-ingestion stream. */
|
|
454
|
+
ingestion?: {
|
|
455
|
+
status: 'current' | 'delayed' | 'no_batches' | 'unknown' | 'historical';
|
|
456
|
+
latest_batch_at: string | null;
|
|
457
|
+
};
|
|
385
458
|
since: string;
|
|
386
459
|
until: string;
|
|
387
460
|
/** Present when `since_request_id` anchored the range — the anchor event's timestamp. */
|
|
@@ -419,7 +492,7 @@ export interface SecretSyncPendingRow {
|
|
|
419
492
|
function_name: string | null;
|
|
420
493
|
secret_name: string;
|
|
421
494
|
expected_version: number;
|
|
422
|
-
status: 'pending' | 'syncing' | 'sync_failed_retrying' | 'synced';
|
|
495
|
+
status: 'pending' | 'syncing' | 'sync_failed_retrying' | 'synced' | 'revocation_pending' | 'revoking' | 'revoke_failed_retrying';
|
|
423
496
|
last_error: string | null;
|
|
424
497
|
attempts: number;
|
|
425
498
|
created_at: string;
|
|
@@ -441,7 +514,7 @@ export interface SetSecretResultRow {
|
|
|
441
514
|
/** Backend version resource path — exposed for ops debugging. */
|
|
442
515
|
version_path: string;
|
|
443
516
|
/** `'pending'` until the secret sync completes. */
|
|
444
|
-
sync_status: 'pending'
|
|
517
|
+
sync_status: 'pending';
|
|
445
518
|
created_at: string;
|
|
446
519
|
}
|
|
447
520
|
export declare function setSecretViaApi(projectId: string, input: {
|
|
@@ -463,7 +536,7 @@ export interface ListSecretsRow {
|
|
|
463
536
|
name: string;
|
|
464
537
|
/** Latest secret-store version number. */
|
|
465
538
|
version: number;
|
|
466
|
-
sync_status: 'pending' | 'syncing' | 'sync_failed_retrying' | 'synced';
|
|
539
|
+
sync_status: 'pending' | 'syncing' | 'sync_failed_retrying' | 'synced' | 'revocation_pending' | 'revoking' | 'revoke_failed_retrying';
|
|
467
540
|
attempts: number;
|
|
468
541
|
last_error: string | null;
|
|
469
542
|
updated_at: string;
|
|
@@ -473,11 +546,7 @@ export declare function listSecretsViaApi(projectId: string, options?: {
|
|
|
473
546
|
}): Promise<ApiListResponse<ListSecretsRow>>;
|
|
474
547
|
export declare function deleteSecretViaApi(projectId: string, name: string, options?: {
|
|
475
548
|
function?: string;
|
|
476
|
-
}): Promise<
|
|
477
|
-
name: string;
|
|
478
|
-
function: string | null;
|
|
479
|
-
deleted: true;
|
|
480
|
-
}>>;
|
|
549
|
+
}): Promise<void>;
|
|
481
550
|
/** Closed set of column types the server accepts. */
|
|
482
551
|
export type CollectionColumnType = 'uuid' | 'text' | 'integer' | 'bigint' | 'numeric' | 'boolean' | 'timestamptz' | 'date' | 'jsonb' | 'vector' | 'text[]' | 'integer[]' | 'bigint[]' | 'numeric[]' | 'boolean[]' | 'uuid[]';
|
|
483
552
|
export interface CollectionColumn {
|
package/dist/bundle.d.ts
CHANGED
|
@@ -76,8 +76,8 @@ export declare class BundleSizeError extends Error {
|
|
|
76
76
|
export declare const HANDLER_SHAPE_FIX_MESSAGE: string;
|
|
77
77
|
/** Message used when the entry file has no `export default` at all. */
|
|
78
78
|
export declare const HANDLER_SHAPE_MISSING_DEFAULT_MESSAGE: string;
|
|
79
|
-
/**
|
|
80
|
-
export declare const HANDLER_SHAPE_NO_FETCH_MESSAGE
|
|
79
|
+
/** Back-compatible export name for the no-supported-handler message. */
|
|
80
|
+
export declare const HANDLER_SHAPE_NO_FETCH_MESSAGE = "Function entrypoint must have a callable `fetch` or `scheduled` method on its default export.";
|
|
81
81
|
/**
|
|
82
82
|
* Thrown when the bundled entry's `export default` is not the Module Worker
|
|
83
83
|
* shape (`{ fetch(req, env, ctx) }`). Raised BEFORE any upload so a wrong
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** `amba functions domains ...` exact-hostname lifecycle commands. */
|
|
2
|
+
export declare function functionDomainsAttachCommand(functionName: string, hostnameInput: string): Promise<void>;
|
|
3
|
+
export declare function functionDomainsListCommand(functionName: string): Promise<void>;
|
|
4
|
+
export declare function functionDomainsRefreshCommand(functionName: string, hostnameInput: string): Promise<void>;
|
|
5
|
+
export declare function functionDomainsRemoveCommand(functionName: string, hostnameInput: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -864,6 +864,7 @@ async function request(method, path, body) {
|
|
|
864
864
|
} catch {}
|
|
865
865
|
throw new ApiClientError(errorMessage, res.status, errorCode);
|
|
866
866
|
}
|
|
867
|
+
if (res.status === 204) return void 0;
|
|
867
868
|
return await res.json();
|
|
868
869
|
}
|
|
869
870
|
/**
|
|
@@ -880,7 +881,13 @@ async function listProjects() {
|
|
|
880
881
|
return request("GET", "/projects");
|
|
881
882
|
}
|
|
882
883
|
async function createProject(input) {
|
|
883
|
-
|
|
884
|
+
const response = await request("POST", "/projects", input);
|
|
885
|
+
if ("project" in response.data) return { data: {
|
|
886
|
+
...response.data.project,
|
|
887
|
+
api_keys: response.data.api_keys,
|
|
888
|
+
provisioning_status: response.data.provisioning_status
|
|
889
|
+
} };
|
|
890
|
+
return response;
|
|
884
891
|
}
|
|
885
892
|
/**
|
|
886
893
|
* PATCH /admin/projects/:projectId — update mutable fields. Server
|
|
@@ -1019,6 +1026,25 @@ async function deployFunctionViaApi(projectId, input) {
|
|
|
1019
1026
|
async function listFunctionDeployments(projectId, options = {}) {
|
|
1020
1027
|
return request("GET", `/projects/${projectId}/functions/deployments${options.activeOnly ? "?active=1" : ""}`);
|
|
1021
1028
|
}
|
|
1029
|
+
function functionDomainsPath(projectId, functionName) {
|
|
1030
|
+
return `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionName)}/domains`;
|
|
1031
|
+
}
|
|
1032
|
+
/** Attach one exact hostname to one deployed function. */
|
|
1033
|
+
async function attachFunctionDomainViaApi(projectId, functionName, hostname) {
|
|
1034
|
+
return request("POST", functionDomainsPath(projectId, functionName), { hostname });
|
|
1035
|
+
}
|
|
1036
|
+
/** List provider-neutral ownership/certificate status for a function's hostnames. */
|
|
1037
|
+
async function listFunctionDomainsViaApi(projectId, functionName) {
|
|
1038
|
+
return request("GET", functionDomainsPath(projectId, functionName));
|
|
1039
|
+
}
|
|
1040
|
+
/** Re-poll DNS ownership and certificate state for one exact hostname. */
|
|
1041
|
+
async function refreshFunctionDomainViaApi(projectId, functionName, hostname) {
|
|
1042
|
+
return request("POST", `${functionDomainsPath(projectId, functionName)}/${encodeURIComponent(hostname)}/refresh`);
|
|
1043
|
+
}
|
|
1044
|
+
/** Detach one exact hostname from a function. */
|
|
1045
|
+
async function removeFunctionDomainViaApi(projectId, functionName, hostname) {
|
|
1046
|
+
return request("DELETE", `${functionDomainsPath(projectId, functionName)}/${encodeURIComponent(hostname)}`);
|
|
1047
|
+
}
|
|
1022
1048
|
async function scheduleFunction(projectId, input) {
|
|
1023
1049
|
return request("POST", `/projects/${projectId}/functions/schedules`, input);
|
|
1024
1050
|
}
|
|
@@ -2002,7 +2028,11 @@ async function ensureProjectForCwd(cwd, options) {
|
|
|
2002
2028
|
name: uniqueName,
|
|
2003
2029
|
environment
|
|
2004
2030
|
});
|
|
2005
|
-
const
|
|
2031
|
+
const initialKeys = project.data.api_keys;
|
|
2032
|
+
const { clientKey, serverKey } = initialKeys ? {
|
|
2033
|
+
clientKey: initialKeys.client.key,
|
|
2034
|
+
serverKey: initialKeys.server.key
|
|
2035
|
+
} : await mintProjectKeyPair(project.data.id, environment);
|
|
2006
2036
|
const creds = {
|
|
2007
2037
|
version: 1,
|
|
2008
2038
|
project_id: project.data.id,
|
|
@@ -2924,7 +2954,7 @@ That's the entire setup. The CLI:
|
|
|
2924
2954
|
detects on disk — Claude Code, Cursor, Windsurf.
|
|
2925
2955
|
6. Verifies the PAT against the API and confirms it's good.
|
|
2926
2956
|
|
|
2927
|
-
The Amba MCP toolset (\`amba_*\` tools —
|
|
2957
|
+
The Amba MCP toolset (\`amba_*\` tools — 500+ of them) is available to
|
|
2928
2958
|
the agent immediately: pass the freshly-minted \`pat\` as an inline
|
|
2929
2959
|
argument on every \`amba_*\` call in the current session. The next time
|
|
2930
2960
|
your MCP client starts it picks the PAT up from the config as the
|
|
@@ -3874,6 +3904,7 @@ async function initCommand(options = {}) {
|
|
|
3874
3904
|
const defaultProjectName = basename(cwd) || "amba-project";
|
|
3875
3905
|
let projectId;
|
|
3876
3906
|
let projectName;
|
|
3907
|
+
let createdKeys = null;
|
|
3877
3908
|
if (linkedProject) {
|
|
3878
3909
|
projectId = linkedProject.project_id;
|
|
3879
3910
|
projectName = linkedProject.project_name;
|
|
@@ -3910,20 +3941,30 @@ async function initCommand(options = {}) {
|
|
|
3910
3941
|
} else {
|
|
3911
3942
|
const name = await prompt(` Project name (default: ${defaultProjectName}): `);
|
|
3912
3943
|
const finalName = name.length > 0 ? name : defaultProjectName;
|
|
3913
|
-
|
|
3944
|
+
const res = await createProject({
|
|
3914
3945
|
name: finalName,
|
|
3915
3946
|
environment
|
|
3916
|
-
})
|
|
3947
|
+
});
|
|
3948
|
+
projectId = res.data.id;
|
|
3917
3949
|
projectName = finalName;
|
|
3950
|
+
if (res.data.api_keys) createdKeys = {
|
|
3951
|
+
clientKey: res.data.api_keys.client.key,
|
|
3952
|
+
serverKey: res.data.api_keys.server.key
|
|
3953
|
+
};
|
|
3918
3954
|
}
|
|
3919
3955
|
} else {
|
|
3920
3956
|
const name = await prompt(` Project name (default: ${defaultProjectName}): `);
|
|
3921
3957
|
const finalName = name.length > 0 ? name : defaultProjectName;
|
|
3922
|
-
|
|
3958
|
+
const res = await createProject({
|
|
3923
3959
|
name: finalName,
|
|
3924
3960
|
environment
|
|
3925
|
-
})
|
|
3961
|
+
});
|
|
3962
|
+
projectId = res.data.id;
|
|
3926
3963
|
projectName = finalName;
|
|
3964
|
+
if (res.data.api_keys) createdKeys = {
|
|
3965
|
+
clientKey: res.data.api_keys.client.key,
|
|
3966
|
+
serverKey: res.data.api_keys.server.key
|
|
3967
|
+
};
|
|
3927
3968
|
}
|
|
3928
3969
|
}
|
|
3929
3970
|
if (spinner.stopped) spinner = createSpinner("minting keys");
|
|
@@ -3935,6 +3976,9 @@ async function initCommand(options = {}) {
|
|
|
3935
3976
|
clientKey = linkedProject.client_key;
|
|
3936
3977
|
if (linkedProject.server_key) serverKey = linkedProject.server_key;
|
|
3937
3978
|
else serverKey = (await createApiKey(projectId, "server", environment)).data.key;
|
|
3979
|
+
} else if (createdKeys) {
|
|
3980
|
+
clientKey = createdKeys.clientKey;
|
|
3981
|
+
serverKey = createdKeys.serverKey;
|
|
3938
3982
|
} else {
|
|
3939
3983
|
const clientRes = await createApiKey(projectId, "client", environment);
|
|
3940
3984
|
const serverRes = await createApiKey(projectId, "server", environment);
|
|
@@ -5562,11 +5606,11 @@ var BundleSizeError = class extends Error {
|
|
|
5562
5606
|
* service-worker shape (`export default async function (req) { … }`) is
|
|
5563
5607
|
* rejected by the runtime at upload, so we reject it before the round-trip.
|
|
5564
5608
|
*/
|
|
5565
|
-
const HANDLER_SHAPE_FIX_MESSAGE = "Function entrypoint must `export default { async fetch(req, env, ctx) { … } }` (the Module Worker shape). Found a default-exported function instead.";
|
|
5609
|
+
const HANDLER_SHAPE_FIX_MESSAGE = "Function entrypoint must `export default { async fetch(req, env, ctx) { … } }` or `export default { async scheduled(controller, env, ctx) { … } }` (the Module Worker shape). Found a default-exported function instead.";
|
|
5566
5610
|
/** Message used when the entry file has no `export default` at all. */
|
|
5567
|
-
const HANDLER_SHAPE_MISSING_DEFAULT_MESSAGE = "Function entrypoint must `export default { async fetch(req, env, ctx) { … } }` (the Module Worker shape). No default export was found.";
|
|
5568
|
-
/**
|
|
5569
|
-
const HANDLER_SHAPE_NO_FETCH_MESSAGE = "Function entrypoint must
|
|
5611
|
+
const HANDLER_SHAPE_MISSING_DEFAULT_MESSAGE = "Function entrypoint must `export default { async fetch(req, env, ctx) { … } }` or `export default { async scheduled(controller, env, ctx) { … } }` (the Module Worker shape). No default export was found.";
|
|
5612
|
+
/** Back-compatible export name for the no-supported-handler message. */
|
|
5613
|
+
const HANDLER_SHAPE_NO_FETCH_MESSAGE = "Function entrypoint must have a callable `fetch` or `scheduled` method on its default export.";
|
|
5570
5614
|
/**
|
|
5571
5615
|
* Thrown when the bundled entry's `export default` is not the Module Worker
|
|
5572
5616
|
* shape (`{ fetch(req, env, ctx) }`). Raised BEFORE any upload so a wrong
|
|
@@ -5600,11 +5644,14 @@ function assertHandlerShape(mod) {
|
|
|
5600
5644
|
const def = mod.default;
|
|
5601
5645
|
if (def === void 0 || def === null) throw new HandlerShapeError(HANDLER_SHAPE_MISSING_DEFAULT_MESSAGE, "missing-default");
|
|
5602
5646
|
if (typeof def === "function") {
|
|
5603
|
-
|
|
5647
|
+
const fetchOnFn = def.fetch;
|
|
5648
|
+
const scheduledOnFn = def.scheduled;
|
|
5649
|
+
if (typeof fetchOnFn !== "function" && typeof scheduledOnFn !== "function") throw new HandlerShapeError(HANDLER_SHAPE_FIX_MESSAGE, "plain-function");
|
|
5604
5650
|
return;
|
|
5605
5651
|
}
|
|
5606
5652
|
if (typeof def !== "object") throw new HandlerShapeError(HANDLER_SHAPE_NO_FETCH_MESSAGE, "no-fetch");
|
|
5607
|
-
|
|
5653
|
+
const handler = def;
|
|
5654
|
+
if (typeof handler.fetch !== "function" && typeof handler.scheduled !== "function") throw new HandlerShapeError(HANDLER_SHAPE_NO_FETCH_MESSAGE, "no-fetch");
|
|
5608
5655
|
}
|
|
5609
5656
|
/**
|
|
5610
5657
|
* Validate that the bundled customer code exports the Module Worker handler
|
|
@@ -5727,7 +5774,7 @@ async function functionsDeployCommand(target, options = {}) {
|
|
|
5727
5774
|
const entryPoint = await resolveFunctionEntry(target, options.entry);
|
|
5728
5775
|
const defaultName = targetIsDir ? basename(target.replace(/[/\\]+$/, "")) : basename(entryPoint).replace(/\.[^.]+$/, "");
|
|
5729
5776
|
const functionName = options.name ?? defaultName;
|
|
5730
|
-
validateFunctionName(functionName);
|
|
5777
|
+
validateFunctionName$1(functionName);
|
|
5731
5778
|
const rateLimit = parseRateLimitFlags(options);
|
|
5732
5779
|
console.log();
|
|
5733
5780
|
console.log(pc.bold(` amba functions deploy ${pc.cyan(functionName)}`));
|
|
@@ -5775,7 +5822,7 @@ async function functionsListCommand() {
|
|
|
5775
5822
|
console.log();
|
|
5776
5823
|
}
|
|
5777
5824
|
async function functionsDeleteCommand(name, options = {}) {
|
|
5778
|
-
validateFunctionName(name);
|
|
5825
|
+
validateFunctionName$1(name);
|
|
5779
5826
|
if (!options.confirm || options.confirm !== name) throw new Error(`Delete is destructive. Pass --confirm ${name} to proceed. Clients calling this function will start 404'ing immediately.`);
|
|
5780
5827
|
const cascade = (await deleteFunctionViaApi((await loadProjectConfig()).projectId, name, { confirm: name })).data.cascade;
|
|
5781
5828
|
console.log(pc.green(" ✓") + ` Deleted ${pc.bold(name)}.`);
|
|
@@ -5783,7 +5830,7 @@ async function functionsDeleteCommand(name, options = {}) {
|
|
|
5783
5830
|
}
|
|
5784
5831
|
async function functionsScheduleCommand(name, cron, options = {}) {
|
|
5785
5832
|
const projectConfig = await loadProjectConfig();
|
|
5786
|
-
validateFunctionName(name);
|
|
5833
|
+
validateFunctionName$1(name);
|
|
5787
5834
|
const timezone = options.tz ?? "UTC";
|
|
5788
5835
|
const res = await scheduleFunction(projectConfig.projectId, {
|
|
5789
5836
|
name,
|
|
@@ -6115,6 +6162,14 @@ try {
|
|
|
6115
6162
|
};
|
|
6116
6163
|
return fetchFn(req, env, ctx);
|
|
6117
6164
|
};
|
|
6165
|
+
} else if (mod.default && typeof mod.default.scheduled === 'function') {
|
|
6166
|
+
// Scheduled-only modules are valid in production. This command exposes
|
|
6167
|
+
// an HTTP server, so return a clear 405 instead of rejecting a deployable
|
|
6168
|
+
// handler shape during local startup.
|
|
6169
|
+
handler = () => new Response(
|
|
6170
|
+
'This function only exports scheduled(); use amba functions schedule/run-now to invoke it.',
|
|
6171
|
+
{ status: 405, headers: { 'content-type': 'text/plain; charset=utf-8' } },
|
|
6172
|
+
);
|
|
6118
6173
|
} else if (typeof mod.default === 'function') {
|
|
6119
6174
|
// Legacy service-worker shape (\`export default async function (req) { ... }\`).
|
|
6120
6175
|
// REJECTED — production runs functions as Module Workers and the runtime
|
|
@@ -6124,7 +6179,7 @@ try {
|
|
|
6124
6179
|
process.stderr.write(' Error: Function entrypoint must \`export default { async fetch(req, env, ctx) { … } }\` (the Module Worker shape). Found a default-exported function instead.\\n');
|
|
6125
6180
|
process.exit(2);
|
|
6126
6181
|
} else {
|
|
6127
|
-
process.stderr.write(' Error: Function entrypoint
|
|
6182
|
+
process.stderr.write(' Error: Function entrypoint default export must have a callable \`fetch\` or \`scheduled\` method.\\n');
|
|
6128
6183
|
process.exit(2);
|
|
6129
6184
|
}
|
|
6130
6185
|
} catch (err) {
|
|
@@ -6194,8 +6249,8 @@ process.on('SIGINT', () => shutdown('SIGINT'));
|
|
|
6194
6249
|
* function-name overwrites — same upsert semantics as `amba secrets set`.
|
|
6195
6250
|
*/
|
|
6196
6251
|
async function functionsConsumeCommand(queueName, functionName, options = {}) {
|
|
6197
|
-
validateFunctionName(functionName);
|
|
6198
|
-
validateFunctionName(queueName);
|
|
6252
|
+
validateFunctionName$1(functionName);
|
|
6253
|
+
validateFunctionName$1(queueName);
|
|
6199
6254
|
const res = await upsertQueueBinding((await loadProjectConfig()).projectId, {
|
|
6200
6255
|
queue_name: queueName,
|
|
6201
6256
|
function_name: functionName,
|
|
@@ -6225,11 +6280,11 @@ async function functionsConsumersListCommand() {
|
|
|
6225
6280
|
console.log();
|
|
6226
6281
|
}
|
|
6227
6282
|
async function functionsConsumersUnbindCommand(queueName) {
|
|
6228
|
-
validateFunctionName(queueName);
|
|
6283
|
+
validateFunctionName$1(queueName);
|
|
6229
6284
|
await deleteQueueBinding((await loadProjectConfig()).projectId, queueName);
|
|
6230
6285
|
console.log(pc.green(" ✓") + ` Unbound queue ${pc.cyan(queueName)}.`);
|
|
6231
6286
|
}
|
|
6232
|
-
function validateFunctionName(name) {
|
|
6287
|
+
function validateFunctionName$1(name) {
|
|
6233
6288
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Invalid function name '${name}'. Must match /^[a-z][a-z0-9_-]*$/ (lowercase ASCII, digits, underscore or hyphen; must start with a letter).`);
|
|
6234
6289
|
if (name.length > 58) throw new Error("Function name must be 58 characters or fewer.");
|
|
6235
6290
|
}
|
|
@@ -6302,6 +6357,10 @@ async function functionsLogsCommand(functionName, options = {}) {
|
|
|
6302
6357
|
});
|
|
6303
6358
|
if (res.data.anchor && !options.json) console.error(pc.dim(` Anchored at ${res.data.anchor} (--since-request-id).`));
|
|
6304
6359
|
printEvents(res.data.events, options.json);
|
|
6360
|
+
if (res.data.events.length === 0 && !options.json) {
|
|
6361
|
+
if (res.data.ingestion?.status === "delayed") console.error(pc.yellow(` ! Log ingestion is delayed${res.data.ingestion.latest_batch_at ? `; latest batch arrived ${res.data.ingestion.latest_batch_at}` : ""}. Retry shortly.`));
|
|
6362
|
+
else if (res.data.ingestion?.status === "no_batches") console.error(pc.yellow(" ! No log batches reached this window yet. Retry shortly."));
|
|
6363
|
+
}
|
|
6305
6364
|
if (res.data.truncated) if (options.json) process.stderr.write(`{"truncated":true}\n`);
|
|
6306
6365
|
else console.error(pc.yellow(` ! Result truncated at limit; narrow --since / --until or raise --limit.`));
|
|
6307
6366
|
}
|
|
@@ -6385,6 +6444,85 @@ function sleep$1(ms) {
|
|
|
6385
6444
|
return new Promise((r) => setTimeout(r, ms));
|
|
6386
6445
|
}
|
|
6387
6446
|
//#endregion
|
|
6447
|
+
//#region src/commands/function-domains.ts
|
|
6448
|
+
/** `amba functions domains ...` exact-hostname lifecycle commands. */
|
|
6449
|
+
const FUNCTION_NAME_RE = /^[a-z][a-z0-9_-]{0,57}$/;
|
|
6450
|
+
function validateFunctionName(name) {
|
|
6451
|
+
if (!FUNCTION_NAME_RE.test(name)) throw new Error(`Invalid function name "${name}". Use lowercase letters, numbers, hyphens, or underscores (max 58 chars; start with a letter).`);
|
|
6452
|
+
}
|
|
6453
|
+
/**
|
|
6454
|
+
* Normalize case/trailing-dot spelling without trying to replace the API's
|
|
6455
|
+
* authoritative DNS validation. This command accepts one exact hostname only:
|
|
6456
|
+
* schemes, paths, ports, and wildcard labels are never meaningful here.
|
|
6457
|
+
*/
|
|
6458
|
+
function normalizeExactHostname(input) {
|
|
6459
|
+
const hostname = input.trim().toLowerCase().replace(/\.$/, "");
|
|
6460
|
+
if (hostname.length === 0 || hostname.length > 253 || !hostname.includes(".") || hostname.includes("*") || hostname.includes("/") || hostname.includes(":") || /\s/.test(hostname)) throw new Error(`Invalid hostname "${input}". Pass one exact DNS hostname such as feeds.example.com (no scheme, path, port, or wildcard).`);
|
|
6461
|
+
return hostname;
|
|
6462
|
+
}
|
|
6463
|
+
function lifecycle(domain) {
|
|
6464
|
+
if (domain.live) return pc.green("active");
|
|
6465
|
+
if (domain.cert_status === "active" && domain.ownership_status === "active") return "certificate=active, ownership=active, reconciliation=pending";
|
|
6466
|
+
return `certificate=${domain.cert_status}, ownership=${domain.ownership_status}`;
|
|
6467
|
+
}
|
|
6468
|
+
function printDomain(domain) {
|
|
6469
|
+
console.log(` ${pc.bold(domain.hostname)} ${lifecycle(domain)}`);
|
|
6470
|
+
}
|
|
6471
|
+
function printDnsInstructions(domain) {
|
|
6472
|
+
if (domain.dns_error === true) console.log(pc.yellow(" Automatic DNS publication failed; retry with domains refresh."));
|
|
6473
|
+
if ("cleanup_pending" in domain && domain.cleanup_pending === true) console.log(pc.yellow(" Routing cleanup is incomplete; retry with domains refresh."));
|
|
6474
|
+
if ("dns_autoconfigured" in domain && domain.dns_autoconfigured === true) console.log(pc.dim(` DNS records published automatically${domain.dns_records_published === void 0 ? "" : ` (${domain.dns_records_published})`}.`));
|
|
6475
|
+
if (domain.dns_target) console.log(pc.dim(` DNS target: CNAME ${domain.hostname} → ${domain.dns_target}`));
|
|
6476
|
+
for (const record of domain.ssl_validation ?? []) if (record.txt_name && record.txt_value) console.log(pc.dim(` Certificate TXT: ${record.txt_name} = ${record.txt_value}`));
|
|
6477
|
+
else if (record.http_url && record.http_body) console.log(pc.dim(` Certificate HTTP: ${record.http_url} → ${record.http_body}`));
|
|
6478
|
+
const ownership = domain.ownership_verification;
|
|
6479
|
+
if (ownership?.name && ownership.value) console.log(pc.dim(` Ownership ${ownership.type ?? "TXT"}: ${ownership.name} = ${ownership.value}`));
|
|
6480
|
+
const ownershipHttp = domain.ownership_verification_http;
|
|
6481
|
+
if (ownershipHttp?.http_url && ownershipHttp.http_body) console.log(pc.dim(` Ownership HTTP: ${ownershipHttp.http_url} → ${ownershipHttp.http_body}`));
|
|
6482
|
+
}
|
|
6483
|
+
async function functionDomainsAttachCommand(functionName, hostnameInput) {
|
|
6484
|
+
validateFunctionName(functionName);
|
|
6485
|
+
const hostname = normalizeExactHostname(hostnameInput);
|
|
6486
|
+
const { projectId } = await loadProjectConfig();
|
|
6487
|
+
const result = await attachFunctionDomainViaApi(projectId, functionName, hostname);
|
|
6488
|
+
console.log();
|
|
6489
|
+
console.log(pc.green(" ✓") + ` Attached ${pc.bold(result.data.hostname)} to ${functionName}.`);
|
|
6490
|
+
console.log(pc.dim(` Status: ${lifecycle(result.data)}`));
|
|
6491
|
+
printDnsInstructions(result.data);
|
|
6492
|
+
console.log(pc.dim(" Exact hostname only; no automatic www hostname was created."));
|
|
6493
|
+
if (!result.data.live && result.data.ownership_status !== "active") console.log(pc.dim(" Unverified claims become eligible for reclaim after 24 hours."));
|
|
6494
|
+
console.log();
|
|
6495
|
+
}
|
|
6496
|
+
async function functionDomainsListCommand(functionName) {
|
|
6497
|
+
validateFunctionName(functionName);
|
|
6498
|
+
const { projectId } = await loadProjectConfig();
|
|
6499
|
+
const result = await listFunctionDomainsViaApi(projectId, functionName);
|
|
6500
|
+
console.log();
|
|
6501
|
+
if (result.data.length === 0) console.log(pc.dim(` No custom domains attached to ${functionName}.`));
|
|
6502
|
+
else for (const domain of result.data) printDomain(domain);
|
|
6503
|
+
console.log();
|
|
6504
|
+
}
|
|
6505
|
+
async function functionDomainsRefreshCommand(functionName, hostnameInput) {
|
|
6506
|
+
validateFunctionName(functionName);
|
|
6507
|
+
const hostname = normalizeExactHostname(hostnameInput);
|
|
6508
|
+
const { projectId } = await loadProjectConfig();
|
|
6509
|
+
const result = await refreshFunctionDomainViaApi(projectId, functionName, hostname);
|
|
6510
|
+
console.log();
|
|
6511
|
+
console.log(pc.green(" ✓") + ` Refreshed ${pc.bold(result.data.hostname)}.`);
|
|
6512
|
+
console.log(pc.dim(` Status: ${lifecycle(result.data)}`));
|
|
6513
|
+
printDnsInstructions(result.data);
|
|
6514
|
+
console.log();
|
|
6515
|
+
}
|
|
6516
|
+
async function functionDomainsRemoveCommand(functionName, hostnameInput) {
|
|
6517
|
+
validateFunctionName(functionName);
|
|
6518
|
+
const hostname = normalizeExactHostname(hostnameInput);
|
|
6519
|
+
const { projectId } = await loadProjectConfig();
|
|
6520
|
+
const result = await removeFunctionDomainViaApi(projectId, functionName, hostname);
|
|
6521
|
+
console.log();
|
|
6522
|
+
console.log(pc.green(" ✓") + ` Removed ${pc.bold(result.data.hostname)} from ${functionName}.`);
|
|
6523
|
+
console.log();
|
|
6524
|
+
}
|
|
6525
|
+
//#endregion
|
|
6388
6526
|
//#region src/commands/ai.ts
|
|
6389
6527
|
/**
|
|
6390
6528
|
* `amba ai providers ...` — register / list / remove AI provider API keys.
|
|
@@ -6517,8 +6655,8 @@ async function secretsListCommand(options = {}) {
|
|
|
6517
6655
|
async function secretsUnsetCommand(name, options) {
|
|
6518
6656
|
validateSecretName(name);
|
|
6519
6657
|
await deleteSecretViaApi((await loadProjectConfig()).projectId, name, { function: options.function });
|
|
6520
|
-
console.log(pc.green(" ✓") + `
|
|
6521
|
-
console.log(pc.dim(" Workers
|
|
6658
|
+
console.log(pc.green(" ✓") + ` Secret removed from the backing store.`);
|
|
6659
|
+
console.log(pc.dim(" Workers binding revocation queued; provider failures retry automatically."));
|
|
6522
6660
|
}
|
|
6523
6661
|
function validateSecretName(name) {
|
|
6524
6662
|
const reason = getBindingReservationReason(name);
|
|
@@ -6529,6 +6667,9 @@ function renderStatus(status) {
|
|
|
6529
6667
|
case "synced": return pc.green("synced");
|
|
6530
6668
|
case "syncing": return pc.cyan("syncing");
|
|
6531
6669
|
case "sync_failed_retrying": return pc.yellow("retrying");
|
|
6670
|
+
case "revocation_pending": return pc.yellow("revocation pending");
|
|
6671
|
+
case "revoking": return pc.cyan("revoking");
|
|
6672
|
+
case "revoke_failed_retrying": return pc.yellow("revocation retrying");
|
|
6532
6673
|
default: return pc.dim(status);
|
|
6533
6674
|
}
|
|
6534
6675
|
}
|
|
@@ -6618,7 +6759,10 @@ async function billingStatusCommand() {
|
|
|
6618
6759
|
try {
|
|
6619
6760
|
const { projectId } = await loadProjectConfig();
|
|
6620
6761
|
const s = (await adminGet(`/projects/${projectId}/billing/status`)).data;
|
|
6621
|
-
|
|
6762
|
+
const effectiveTier = s.effective_tier ?? s.tier;
|
|
6763
|
+
console.log(` Effective tier: ${pc.bold(effectiveTier)}`);
|
|
6764
|
+
if (effectiveTier !== s.tier) console.log(` Stored tier: ${s.tier}`);
|
|
6765
|
+
if (s.comped === true) console.log(` Billing: ${pc.green("comped")} ${pc.dim("(no customer charge)")}`);
|
|
6622
6766
|
console.log(` Subscription: ${s.subscription_status ?? pc.dim("—")}`);
|
|
6623
6767
|
console.log(` Next bill anchor: ${s.current_period_end ? new Date(s.current_period_end).toLocaleDateString() : pc.dim("—")}`);
|
|
6624
6768
|
const pctCeiling = s.ceiling_pct_consumed === null || s.ceiling_pct_consumed === void 0 ? null : Math.round(s.ceiling_pct_consumed * 100);
|
|
@@ -9530,7 +9674,7 @@ async function diffConfigCommand(file = DEFAULT_BUNDLE_FILE, options = {}) {
|
|
|
9530
9674
|
//#endregion
|
|
9531
9675
|
//#region src/index.ts
|
|
9532
9676
|
const program = new Command();
|
|
9533
|
-
program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.
|
|
9677
|
+
program.name("amba").description("amba — agent-native backend-as-a-service for mobile apps.").version("4.1.2");
|
|
9534
9678
|
program.option("--token <pat>", "Use a Personal Access Token for headless / CI / agent use (overrides ~/.amba/credentials.json + AMBA_PAT env)");
|
|
9535
9679
|
program.hook("preAction", (thisCommand) => {
|
|
9536
9680
|
setBearerOverride(resolveTokenSource({
|
|
@@ -9678,8 +9822,8 @@ functions.command("deploy <target>").description("Bundle a function (a single fi
|
|
|
9678
9822
|
functions.command("list").description("List active functions for the current project").action(async () => {
|
|
9679
9823
|
await runAction(functionsListCommand);
|
|
9680
9824
|
});
|
|
9681
|
-
functions.command("delete <name>").description("Disable + remove a function from the dispatch namespace").action(async (name) => {
|
|
9682
|
-
await runAction(() => functionsDeleteCommand(name));
|
|
9825
|
+
functions.command("delete <name>").description("Disable + remove a function from the dispatch namespace (destructive)").option("--confirm <name>", "Pass the function name to confirm deletion").action(async (name, opts) => {
|
|
9826
|
+
await runAction(() => functionsDeleteCommand(name, { confirm: opts.confirm }));
|
|
9683
9827
|
});
|
|
9684
9828
|
functions.command("schedule <name> <cron>").description("Register a cron schedule that invokes a deployed function").option("--tz <iana>", "IANA timezone for the schedule (default: UTC)").action(async (name, cron, opts) => {
|
|
9685
9829
|
await runAction(() => functionsScheduleCommand(name, cron, opts));
|
|
@@ -9711,8 +9855,21 @@ consumers.command("list").description("List queue bindings for the current proje
|
|
|
9711
9855
|
consumers.command("unbind <queue-name>").description("Remove the binding for a queue (DROP)").action(async (queueName) => {
|
|
9712
9856
|
await runAction(() => functionsConsumersUnbindCommand(queueName));
|
|
9713
9857
|
});
|
|
9858
|
+
const functionDomains = functions.command("domains").description("Manage exact custom hostnames for functions");
|
|
9859
|
+
functionDomains.command("attach <name> <hostname>").description("Attach one exact hostname to a deployed function").action(async (name, hostname) => {
|
|
9860
|
+
await runAction(() => functionDomainsAttachCommand(name, hostname));
|
|
9861
|
+
});
|
|
9862
|
+
functionDomains.command("list <name>").description("List custom hostname ownership and certificate status").action(async (name) => {
|
|
9863
|
+
await runAction(() => functionDomainsListCommand(name));
|
|
9864
|
+
});
|
|
9865
|
+
functionDomains.command("refresh <name> <hostname>").description("Refresh DNS ownership and certificate status").action(async (name, hostname) => {
|
|
9866
|
+
await runAction(() => functionDomainsRefreshCommand(name, hostname));
|
|
9867
|
+
});
|
|
9868
|
+
functionDomains.command("remove <name> <hostname>").description("Detach an exact hostname from a function").action(async (name, hostname) => {
|
|
9869
|
+
await runAction(() => functionDomainsRemoveCommand(name, hostname));
|
|
9870
|
+
});
|
|
9714
9871
|
const secrets = program.command("secrets").description("Function secrets (GCP canonical, Workers-Secret synced)");
|
|
9715
|
-
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. 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) => {
|
|
9872
|
+
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) => {
|
|
9716
9873
|
await runAction(() => secretsSetCommand(name, value, {
|
|
9717
9874
|
function: opts.function,
|
|
9718
9875
|
env: opts.env ?? "dev",
|
|
@@ -9722,7 +9879,7 @@ secrets.command("set <name> [value]").description("Store a secret and queue its
|
|
|
9722
9879
|
secrets.command("list").description("List secret sync status for the current project").option("--function <name>", "Filter to secrets for a single function (avoids listing all functions at once)").action(async (opts) => {
|
|
9723
9880
|
await runAction(() => secretsListCommand({ function: opts.function }));
|
|
9724
9881
|
});
|
|
9725
|
-
secrets.command("unset <name>").description("Remove a secret
|
|
9882
|
+
secrets.command("unset <name>").description("Remove a secret and revoke its Worker binding").option("--function <name>", "Function the secret was bound to. Omit to remove the project-wide secret of this name. A surviving same-name value at the other scope is restored automatically.").action(async (name, opts) => {
|
|
9726
9883
|
await runAction(() => secretsUnsetCommand(name, opts));
|
|
9727
9884
|
});
|
|
9728
9885
|
const billing = program.command("billing").description("Per-project subscription, headroom, and spend controls");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@layers/amba",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.2",
|
|
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.4",
|
|
53
|
+
"@layers/amba-mcp": "4.0.6"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "tsdown && tsc --emitDeclarationOnly",
|
package/skill-bundle/SKILL.md
CHANGED
|
@@ -89,7 +89,7 @@ Check whether a project exists in this directory:
|
|
|
89
89
|
> }'
|
|
90
90
|
> ```
|
|
91
91
|
> The response is the standard MCP envelope `{"jsonrpc":"2.0","id":"1","result":{"content":[{"type":"text","text":"<JSON body>"}]}}`. You construct calls by hand the same way you would with typed tools — every `amba_*` tool name from the catalog works. This is the same wire the typed-tool surface uses; you just skip the client-side configuration loop.
|
|
92
|
-
> 2. **The `amba` CLI via bash.** Already authenticated via `~/.amba/credentials.json`. Coverage: `amba projects {list,create,update,show,delete}`, `amba collections {create,alter,list,drop}`, `amba functions {deploy,list,delete,schedule,dev,logs,consume}`, `amba push test`, `amba secrets {set,list,unset}`, `amba config {list,set}`, `amba sites <subcommands>`, `amba seed`, `amba status --detailed`, `amba logs tail`, `amba analytics export`, `amba schema export`, `amba ai <subcommands>`, `amba db migrate`, `amba billing {status,upgrade,portal,set-ceiling}`, `amba claim <email>` (sandbox → verified_free magic link). Run `amba --help` once if you need to see the full subcommand surface. Use this when you want a typed wrapper for a common op; the JSON-RPC path is strictly more flexible.
|
|
92
|
+
> 2. **The `amba` CLI via bash.** Already authenticated via `~/.amba/credentials.json`. Coverage: `amba projects {list,create,update,show,delete}`, `amba collections {create,alter,list,drop}`, `amba functions {deploy,list,delete,schedule,dev,logs,consume,domains}`, `amba push test`, `amba secrets {set,list,unset}`, `amba config {list,set}`, `amba sites <subcommands>`, `amba seed`, `amba status --detailed`, `amba logs tail`, `amba analytics export`, `amba schema export`, `amba ai <subcommands>`, `amba db migrate`, `amba billing {status,upgrade,portal,set-ceiling}`, `amba claim <email>` (sandbox → verified_free magic link). Run `amba --help` once if you need to see the full subcommand surface. Use this when you want a typed wrapper for a common op; the JSON-RPC path is strictly more flexible.
|
|
93
93
|
> 3. **Wait for next agent launch.** The MCP entry is already persisted; on the next launch, `mcp__amba__*` tools appear automatically.
|
|
94
94
|
>
|
|
95
95
|
> A SEPARATE case: if Amba MCP IS wired in this session but the inbound Bearer is stale (e.g., the user just ran `amba_developer_signup` and got a fresh PAT), pass `pat: "<the new PAT>"` as an inline argument on every subsequent `amba_*` tool call to override the inbound Bearer for that one call. This only helps when the `mcp__amba__*` tools already exist in your tool list — it does not summon them into existence.
|
|
@@ -37,6 +37,9 @@ Run user code in a sandbox triggered by HTTP, cron, or webhook. The function get
|
|
|
37
37
|
| Tool | Purpose | Example args |
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| `amba_functions_deploy` / `amba_deploy_function` | Deploy a function from source (TypeScript / JavaScript / Python). | `{ project_id, name: "send_welcome_email", runtime: "node22", source: "export default async (req) => { ... }", trigger: { type: "http" } }` |
|
|
40
|
+
| `amba_function_domains_attach` | Attach an exact hostname to a function and return DNS validation records. | `{ project_id, name: "feed", hostname: "feeds.example.com" }` |
|
|
41
|
+
| `amba_function_domains_list` / `amba_function_domains_refresh` | Read or refresh ownership/certificate status. | `{ project_id, name: "feed", hostname: "feeds.example.com" }` |
|
|
42
|
+
| `amba_function_domains_remove` | Detach an exact function hostname. | `{ project_id, name: "feed", hostname: "feeds.example.com" }` |
|
|
40
43
|
| `amba_functions_list` / `amba_list_functions` | List functions. | `{ project_id }` |
|
|
41
44
|
| `amba_functions_get` / `amba_get_function` | Read function metadata. | `{ project_id, function_id }` |
|
|
42
45
|
| `amba_functions_get_logs` / `amba_get_function_logs` | Recent invocation logs. | `{ project_id, function_id, limit: 100 }` |
|
|
@@ -46,6 +49,11 @@ Run user code in a sandbox triggered by HTTP, cron, or webhook. The function get
|
|
|
46
49
|
| `amba_functions_resume_schedule` / `amba_resume_function_schedule` | Resume. | `{ project_id, function_id }` |
|
|
47
50
|
| `amba_functions_trigger_schedule` / `amba_trigger_function_schedule` | Fire a scheduled function ad-hoc (testing). | `{ project_id, function_id }` |
|
|
48
51
|
|
|
52
|
+
Function-domain MVP limits use effective tier: free 1, pro 5, scale 20,
|
|
53
|
+
enterprise/comped 50. Attach is limited to five attempts per project per hour.
|
|
54
|
+
Unverified claims become eligible for exact-host reclaim after 24 hours; Worker
|
|
55
|
+
route/KV allocation waits until provider ownership is active.
|
|
56
|
+
|
|
49
57
|
### AI prompts
|
|
50
58
|
|
|
51
59
|
Managed LLM templates: a stored prompt with provider + model + system message, invoked by name from the SDK. The actual LLM call is rewritten server-side per-tenant — the customer's provider API key (Anthropic / OpenAI / Mistral / Gemini) stays server-side, never on the device.
|
|
@@ -80,7 +88,7 @@ Managed LLM templates: a stored prompt with provider + model + system message, i
|
|
|
80
88
|
|
|
81
89
|
| Tool | Purpose | Example args |
|
|
82
90
|
| --- | --- | --- |
|
|
83
|
-
| `amba_secrets_set` / `amba_set_secret` | Set a
|
|
91
|
+
| `amba_secrets_set` / `amba_set_secret` | Set or rotate a function secret. Omit `function` for a project-wide value or pass it for one function. Already-deployed functions receive the new binding asynchronously; a future deploy also reconciles it. For third-party API keys called from your functions — NOT AI provider keys (use `amba_ai_providers_set` for those). | `{ project_id, name: "STRIPE_WEBHOOK_SECRET", value: "whsec_..." }` |
|
|
84
92
|
| `amba_secrets_get` / `amba_get_secret` | Read a secret (returns `"<redacted>"` unless explicitly requested). | `{ project_id, name }` |
|
|
85
93
|
| `amba_secrets_list` / `amba_list_secrets` | List secret names. | `{ project_id }` |
|
|
86
94
|
| `amba_secrets_delete` / `amba_delete_secret` | Delete. | `{ project_id, name }` |
|