@ambasada/cli 0.7.1 → 0.7.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/ambasada.mjs +37 -18
- package/package.json +2 -2
package/dist/ambasada.mjs
CHANGED
|
@@ -1700,7 +1700,7 @@ var AmbasadaAuth = class {
|
|
|
1700
1700
|
async verifyShellToken(token, options = {}) {
|
|
1701
1701
|
const c2 = this.config();
|
|
1702
1702
|
if (!c2.issuer) throw new VillageError("CONFIG", "silent sign-in needs AMBASADA_AUTH_ISSUER (injected on ambasada.dev and by `ambasada dev`)");
|
|
1703
|
-
const
|
|
1703
|
+
const trusted = [c2.issuer, c2.hubUrl].filter((v) => !!v).map((v) => v.replace(/\/+$/, ""));
|
|
1704
1704
|
const audiences = [options.audience ?? c2.audiences ?? []].flat().filter(Boolean).map((a) => a.replace(/\/+$/, ""));
|
|
1705
1705
|
if (!audiences.length) throw new VillageError("CONFIG", "silent sign-in needs to know this app: set AMBASADA_APP_ORIGIN or PUBLIC_URL, or pass { audience }");
|
|
1706
1706
|
const doFetch = c2.fetch ?? ((input, init2) => fetch(input, init2));
|
|
@@ -1715,6 +1715,9 @@ var AmbasadaAuth = class {
|
|
|
1715
1715
|
throw shellRefused("not a JWT");
|
|
1716
1716
|
}
|
|
1717
1717
|
if (header3.alg !== "RS256") throw shellRefused(`alg ${String(header3.alg)} (only RS256)`);
|
|
1718
|
+
const iss = typeof claims.iss === "string" ? claims.iss.replace(/\/+$/, "") : null;
|
|
1719
|
+
if (iss && !trusted.includes(iss)) throw shellRefused(`issuer ${claims.iss}`);
|
|
1720
|
+
const issuer = iss ?? trusted[0];
|
|
1718
1721
|
let keys3 = await jwks(issuer, doFetch);
|
|
1719
1722
|
let jwk = keys3.find((k) => k.kid === header3.kid) ?? (!header3.kid && keys3.length === 1 ? keys3[0] : void 0);
|
|
1720
1723
|
if (!jwk) {
|
|
@@ -1729,7 +1732,6 @@ var AmbasadaAuth = class {
|
|
|
1729
1732
|
if (typeof claims.exp !== "number" || claims.exp + LEEWAY_S < now) throw shellRefused("expired");
|
|
1730
1733
|
if (typeof claims.nbf === "number" && claims.nbf - LEEWAY_S > now) throw shellRefused("not valid yet");
|
|
1731
1734
|
if (typeof claims.iat === "number" && claims.iat - LEEWAY_S > now) throw shellRefused("issued in the future");
|
|
1732
|
-
if (typeof claims.iss === "string" && claims.iss.replace(/\/+$/, "") !== issuer) throw shellRefused(`issuer ${claims.iss}`);
|
|
1733
1735
|
const aud = [claims.aud].flat().filter((a) => typeof a === "string").map((a) => a.replace(/\/+$/, ""));
|
|
1734
1736
|
if (!aud.some((a) => audiences.includes(a))) throw shellRefused(`it is for ${aud.join(", ") || "no app"}, not this one`);
|
|
1735
1737
|
if (typeof claims.sub !== "string" || !claims.sub) throw shellRefused("no subject");
|
|
@@ -3128,6 +3130,7 @@ ${key}` : null;
|
|
|
3128
3130
|
const token = /^Bearer\s+(.+)$/i.exec(req.headers.get("authorization") ?? "")?.[1]?.trim();
|
|
3129
3131
|
if (!token) throw fail("UNAUTHORIZED", `a company API key is required (Authorization: Bearer ${DEV_KEY})`);
|
|
3130
3132
|
const me = token === AGENT_KEY ? FAKE_AGENT : company;
|
|
3133
|
+
if (path === "/v1/key" && method === "GET") return json2({ id: me, name: me, kind: "company" });
|
|
3131
3134
|
if (path === "/v1/wallet" && method === "GET") return json2(wallet2(me));
|
|
3132
3135
|
if (path === "/v1/ledger" && method === "GET") {
|
|
3133
3136
|
const since = Number(url.searchParams.get("since") ?? 0);
|
|
@@ -3745,6 +3748,7 @@ async function deploy2(args, flags) {
|
|
|
3745
3748
|
out(` ${deployment.tier === "listed" || deployment.tier === "verified" ? c.green("\u2713") : c.yellow("!")} in the Ambasada app: ${line}`);
|
|
3746
3749
|
for (const issue of deployment.check?.issues.filter((i) => i.level === "problem") ?? []) out(c.dim(` ${issue.message}`));
|
|
3747
3750
|
}
|
|
3751
|
+
if (deployment.kind === "project") return;
|
|
3748
3752
|
if (!await setProductUrl(project.root, deployment.url)) {
|
|
3749
3753
|
out(c.yellow(`! add \`product: { url: "${deployment.url}" }\` to ambasada.config.ts so relative offering endpoints resolve`));
|
|
3750
3754
|
}
|
|
@@ -4228,7 +4232,7 @@ async function tunnel(_args, flags) {
|
|
|
4228
4232
|
}
|
|
4229
4233
|
|
|
4230
4234
|
// src/commands.ts
|
|
4231
|
-
var SDK_VERSION = /^\d+\.\d+\.\d+$/.test("0.7.
|
|
4235
|
+
var SDK_VERSION = /^\d+\.\d+\.\d+$/.test("0.7.2") ? `^${"0.7.2"}` : "latest";
|
|
4232
4236
|
var DEV_PORT = 8788;
|
|
4233
4237
|
var out3 = (s = "") => process.stdout.write(`${s}
|
|
4234
4238
|
`);
|
|
@@ -4294,19 +4298,33 @@ async function login(_args, flags) {
|
|
|
4294
4298
|
if (device) ({ token: key, company } = device);
|
|
4295
4299
|
}
|
|
4296
4300
|
if (!key) {
|
|
4297
|
-
out3(`Paste your
|
|
4301
|
+
out3(`Paste your app's key: ${c.bold("https://developer.ambasada.dev")} \u2192 New app (or Settings \u2192 New key).`);
|
|
4298
4302
|
key = await prompt("API key: ");
|
|
4299
4303
|
}
|
|
4300
4304
|
if (!key) throw new CliError("no key given");
|
|
4301
|
-
|
|
4305
|
+
company = (await whoseKey(apiUrl, key)).id;
|
|
4306
|
+
await writeConfig({ ...await readConfig(), apiUrl, apiKey: key, company });
|
|
4307
|
+
out3(`${c.green("\u2713")} logged in as ${c.bold(company)} on ${apiUrl} ${c.dim(`(saved to ${configPath()})`)}`);
|
|
4308
|
+
}
|
|
4309
|
+
async function whoseKey(apiUrl, key) {
|
|
4310
|
+
let res;
|
|
4311
|
+
try {
|
|
4312
|
+
res = await fetch(`${apiUrl.replace(/\/$/, "")}/v1/key`, { headers: { authorization: `Bearer ${key}`, accept: "application/json" } });
|
|
4313
|
+
} catch (error) {
|
|
4314
|
+
throw new CliError(`can't reach ${apiUrl}: ${error.message}`);
|
|
4315
|
+
}
|
|
4316
|
+
if (res.status === 401) throw new CliError("that key was refused (UNAUTHORIZED)");
|
|
4317
|
+
if (res.ok) {
|
|
4318
|
+
const body = await res.json();
|
|
4319
|
+
return { id: body.id, name: body.name ?? null, kind: body.kind === "project" ? "project" : "company" };
|
|
4320
|
+
}
|
|
4321
|
+
await res.body?.cancel();
|
|
4302
4322
|
try {
|
|
4303
|
-
|
|
4323
|
+
return { id: (await createAmbasada({ apiKey: key, apiUrl, env: {} }).wallet()).account, name: null, kind: "company" };
|
|
4304
4324
|
} catch (error) {
|
|
4305
|
-
if (isVillageError(error, "UNAUTHORIZED")) throw new CliError("that key was refused
|
|
4325
|
+
if (isVillageError(error, "UNAUTHORIZED")) throw new CliError("that key was refused (UNAUTHORIZED)");
|
|
4306
4326
|
throw error;
|
|
4307
4327
|
}
|
|
4308
|
-
await writeConfig({ ...await readConfig(), apiUrl, apiKey: key, company });
|
|
4309
|
-
out3(`${c.green("\u2713")} logged in as ${c.bold(company)} on ${apiUrl} ${c.dim(`(saved to ${configPath()})`)}`);
|
|
4310
4328
|
}
|
|
4311
4329
|
async function logout() {
|
|
4312
4330
|
const { apiKey: _k, company: _c, ...rest } = await readConfig();
|
|
@@ -4315,10 +4333,10 @@ async function logout() {
|
|
|
4315
4333
|
}
|
|
4316
4334
|
async function whoami(_args, flags) {
|
|
4317
4335
|
const r = await resolve(flags);
|
|
4318
|
-
|
|
4319
|
-
const
|
|
4320
|
-
if (json(flags, { apiUrl: r.apiUrl, company:
|
|
4321
|
-
out3(`${c.bold(
|
|
4336
|
+
if (!r.apiKey) throw new CliError("not logged in: run `ambasada login` (or set AMBASADA_API_KEY)");
|
|
4337
|
+
const who = await whoseKey(r.apiUrl, r.apiKey);
|
|
4338
|
+
if (json(flags, { apiUrl: r.apiUrl, company: who.id, kind: who.kind })) return;
|
|
4339
|
+
out3(`${c.bold(who.id)} on ${r.apiUrl}`);
|
|
4322
4340
|
}
|
|
4323
4341
|
async function init(args, flags) {
|
|
4324
4342
|
const target = resolvePath4(args[0] ?? ".");
|
|
@@ -4385,7 +4403,7 @@ async function init(args, flags) {
|
|
|
4385
4403
|
out3(` npm run dev ${c.dim("# ambasada dev: mock village API on :8788 + your product on http://localhost:3000")}`);
|
|
4386
4404
|
out3(` npx ambasada dev buy translate '{"text":"Bun\u0103!","to":"en"}' ${c.dim("# in a second terminal: a fake agent pays you 1 TON")}`);
|
|
4387
4405
|
out3("");
|
|
4388
|
-
out3(`Then ${c.bold("ambasada login")}
|
|
4406
|
+
out3(`Then ${c.bold("ambasada login")} (a key from https://developer.ambasada.dev \u2192 New app) and ${c.bold("ambasada deploy")}. See README.md.`);
|
|
4389
4407
|
}
|
|
4390
4408
|
async function check(args, flags) {
|
|
4391
4409
|
const target = args[0];
|
|
@@ -4696,14 +4714,15 @@ async function links(args, flags) {
|
|
|
4696
4714
|
}
|
|
4697
4715
|
|
|
4698
4716
|
// src/main.ts
|
|
4699
|
-
var VERSION = true ? "0.7.
|
|
4700
|
-
var HELP = `${c.bold("ambasada")} ${VERSION}: build
|
|
4717
|
+
var VERSION = true ? "0.7.2" : "0.1.0-dev";
|
|
4718
|
+
var HELP = `${c.bold("ambasada")} ${VERSION}: build and host apps on ambasada.dev
|
|
4701
4719
|
|
|
4702
4720
|
ambasada init [dir] [--template next|spa|worker] [--name "Name"] [--id slug]
|
|
4703
|
-
scaffold
|
|
4721
|
+
scaffold an app (village teams also get the agent). An app is one Cloudflare
|
|
4704
4722
|
Worker: next (default, Next.js via OpenNext), spa (Vite + React,
|
|
4705
4723
|
for Lovable/Bolt exports) or worker (bare fetch handlers + web/)
|
|
4706
|
-
ambasada login [--key
|
|
4724
|
+
ambasada login [--key vk_\u2026] device-code login with Ambasada ID, or paste an app key
|
|
4725
|
+
(developer.ambasada.dev \u2192 New app, or a team's server key)
|
|
4707
4726
|
ambasada dev [--port 8788] [--product URL] mock Payments API (fake bank, fake visitor, canned LLM/tools) on :8788
|
|
4708
4727
|
+ your product's dev server (next dev | vite | wrangler dev) on
|
|
4709
4728
|
product.url (:3000), with AMBASADA_* pointed at the mock
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ambasada/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "`ambasada`: the Ambasada CLI (Hack A Ton village commands) (login, init, dev, deploy, offerings, wallet, logs, market, steer).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"vitest": "^4.1.12",
|
|
23
23
|
"wrangler": "^4.156.0",
|
|
24
24
|
"@ambasada/protocol": "0.1.0",
|
|
25
|
-
"@ambasada/sdk": "0.7.
|
|
25
|
+
"@ambasada/sdk": "0.7.2"
|
|
26
26
|
},
|
|
27
27
|
"author": "CENTYSO (https://centyso.com)",
|
|
28
28
|
"homepage": "https://ambasada.id",
|