promigence 0.1.0-beta.7 → 0.1.0-beta.9
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/README.md +10 -6
- package/dist/account.js +84 -0
- package/dist/commands.d.ts +3 -1
- package/dist/commands.js +51 -11
- package/dist/errfix.js +1 -0
- package/dist/generated.d.ts +1 -1
- package/dist/generated.js +16 -10
- package/dist/github.js +4 -4
- package/dist/gitlab.js +1 -1
- package/dist/guide.js +43 -12
- package/dist/handlers.js +2 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/init.js +1 -1
- package/dist/live.js +8 -0
- package/dist/login.js +1 -1
- package/dist/mcp.js +8 -3
- package/dist/pricing.d.ts +11 -1
- package/dist/pricing.js +23 -5
- package/dist/routes.js +1 -0
- package/dist/runners-spec.js +9 -1
- package/dist/runners.js +14 -9
- package/dist/sdk/client.d.ts +67 -6
- package/dist/sdk/client.js +39 -15
- package/dist/sdk/core.d.ts +17 -6
- package/dist/sdk/core.js +10 -3
- package/dist/sdk/credstore.d.ts +3 -2
- package/dist/sdk/credstore.js +17 -4
- package/dist/sdk/github.d.ts +1 -1
- package/dist/sdk/gitlab.d.ts +1 -1
- package/dist/sdk/models.gen.d.ts +227 -22
- package/dist/sdk/proxy.js +2 -1
- package/dist/sdk/pty.d.ts +65 -3
- package/dist/sdk/pty.js +269 -33
- package/dist/sdk/sandbox.d.ts +41 -10
- package/dist/sdk/sandbox.js +10 -3
- package/dist/sdk/ssh.js +1 -0
- package/dist/surface.d.ts +4 -3
- package/dist/surface.js +24 -11
- package/llms.txt +21 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,15 +21,13 @@ own (`npm config set prefix ~/.npm-global` and add `~/.npm-global/bin` to your `
|
|
|
21
21
|
|
|
22
22
|
## Create your account
|
|
23
23
|
|
|
24
|
-
Promigence is in private beta: you need an invite code.
|
|
25
|
-
|
|
26
24
|
```sh
|
|
27
25
|
promigence signup
|
|
28
26
|
```
|
|
29
27
|
|
|
30
|
-
It
|
|
31
|
-
|
|
32
|
-
another machine, use `promigence login`: it signs you in with a code we email you
|
|
28
|
+
It opens your browser to create the account. Your API key is saved in your system keychain, or in
|
|
29
|
+
`~/.config/promigence/credentials` (mode 0600) where there is none.
|
|
30
|
+
On another machine, use `promigence login`: it signs you in with a code we email you. In CI or on an
|
|
33
31
|
agent's machine, set `PROMIGENCE_API_KEY` instead. `promigence doctor` checks the setup.
|
|
34
32
|
|
|
35
33
|
## First commands
|
|
@@ -57,13 +55,19 @@ promigence fork --snapshot py --count 5 --cap 2
|
|
|
57
55
|
|
|
58
56
|
## For AI agents
|
|
59
57
|
|
|
60
|
-
Run `promigence agent-guide` (about 1.
|
|
58
|
+
Run `promigence agent-guide` (about 1.4k tokens) before driving the CLI: it covers the model, the
|
|
61
59
|
JSON output contract, exit codes and errors. `promigence agent-guide --install` writes the same
|
|
62
60
|
guidance into your repo for coding agents, and `promigence mcp` serves the CLI as an MCP server.
|
|
63
61
|
Every command takes `--json`.
|
|
64
62
|
|
|
65
63
|
## From TypeScript
|
|
66
64
|
|
|
65
|
+
In your project (the global install gives you the command only):
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npm i promigence
|
|
69
|
+
```
|
|
70
|
+
|
|
67
71
|
```ts
|
|
68
72
|
import { Sandbox } from "promigence";
|
|
69
73
|
|
package/dist/account.js
CHANGED
|
@@ -223,6 +223,7 @@ const billingUsage = async (ctx) => {
|
|
|
223
223
|
usage,
|
|
224
224
|
card_on_file: b.card_on_file ?? false,
|
|
225
225
|
...(b.card ? { card: b.card } : {}),
|
|
226
|
+
...(b.card_note ? { card_note: b.card_note } : {}),
|
|
226
227
|
...(b.trial ? { trial: b.trial } : {}),
|
|
227
228
|
...(b.suspended ? { suspended: true, suspend_reason: b.suspend_reason ?? null } : {}),
|
|
228
229
|
upcoming_invoice: b.upcoming_invoice ?? null,
|
|
@@ -260,6 +261,8 @@ const billingUsage = async (ctx) => {
|
|
|
260
261
|
if (trial?.credits_remaining !== undefined)
|
|
261
262
|
lines.push(` trial credits left: ${usd(trial.credits_remaining)}`);
|
|
262
263
|
lines.push(` card: ${b.card_on_file ? paymentMethodText(b.card) : `none on file${billingOff ? " (card payments are not open yet)" : ""}`}`);
|
|
264
|
+
if (b.card_note)
|
|
265
|
+
lines.push(` ${b.card_note}`);
|
|
263
266
|
if (b.upcoming_invoice?.amount_due !== undefined && b.upcoming_invoice?.amount_due !== null) {
|
|
264
267
|
lines.push(` next invoice: ${usd(b.upcoming_invoice.amount_due)}`);
|
|
265
268
|
}
|
|
@@ -282,6 +285,86 @@ const billingPortal = async (ctx) => {
|
|
|
282
285
|
const next = nexts(["promigence billing usage", "this month's spend without leaving the terminal"]);
|
|
283
286
|
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { url: res.url }, next), ["promigence billing portal: open this page to manage the card and past invoices:", ` ${res.url}`], 0, ctx.pointer);
|
|
284
287
|
};
|
|
288
|
+
const HISTORY_KINDS = ["all", "credits", "payments"];
|
|
289
|
+
const CREDIT_WHAT = {
|
|
290
|
+
free_credit: "Free credit",
|
|
291
|
+
card_bonus: "Card bonus",
|
|
292
|
+
referral: "Referral reward",
|
|
293
|
+
plan: "Included with your plan",
|
|
294
|
+
from_promigence: "Credit from Promigence",
|
|
295
|
+
service_restart: "Credit for a service restart",
|
|
296
|
+
confirmation_credit: "Credit for the $1 confirmation",
|
|
297
|
+
};
|
|
298
|
+
const CREDIT_ENDED = { used: "used", expired: "expired", taken_back: "taken back" };
|
|
299
|
+
const PAYMENT_WHAT = {
|
|
300
|
+
invoice_paid: "Invoice paid",
|
|
301
|
+
payment_failed: "Payment failed",
|
|
302
|
+
refund: "Refund",
|
|
303
|
+
};
|
|
304
|
+
const PAYMENT_STATUS = {
|
|
305
|
+
paid: "paid",
|
|
306
|
+
retrying: "will be retried",
|
|
307
|
+
failed: "not paid",
|
|
308
|
+
refunded: "refunded",
|
|
309
|
+
confirmed: "confirmed",
|
|
310
|
+
};
|
|
311
|
+
const wordFor = (m, k) => k && Object.prototype.hasOwnProperty.call(m, k) ? m[k] : undefined;
|
|
312
|
+
const unpaid = (status) => status === "retrying" || status === "failed";
|
|
313
|
+
export function historyLines(entries) {
|
|
314
|
+
return entries.map((e) => {
|
|
315
|
+
const day = (e.at ?? "").slice(0, 10);
|
|
316
|
+
const c = e.credit;
|
|
317
|
+
if (c) {
|
|
318
|
+
const state = wordFor(CREDIT_ENDED, c.status) ??
|
|
319
|
+
`${usd(c.remaining_usd)} left, ${c.expires_at ? `expires ${c.expires_at.slice(0, 10)}` : "no expiry"}`;
|
|
320
|
+
return ` ${day} ${wordFor(CREDIT_WHAT, c.kind) ?? "Credit"} ${usd(c.granted_usd)} ${state}`;
|
|
321
|
+
}
|
|
322
|
+
const p = e.payment;
|
|
323
|
+
if (!p)
|
|
324
|
+
return ` ${day} (a row this version cannot show: see --json)`;
|
|
325
|
+
const check = p.kind === "method_check";
|
|
326
|
+
const what = check
|
|
327
|
+
? `$${Number.isInteger(p.amount_usd) ? String(p.amount_usd) : p.amount_usd.toFixed(2)} to confirm ${p.method || "your payment method"}`
|
|
328
|
+
: (wordFor(PAYMENT_WHAT, p.kind) ?? "Payment");
|
|
329
|
+
const detail = [p.plan ? `${p.plan} plan` : null, p.invoice_number ? `invoice ${p.invoice_number}` : null]
|
|
330
|
+
.filter(Boolean)
|
|
331
|
+
.join(", ");
|
|
332
|
+
const status = check && p.status === "failed" ? "failed" : (wordFor(PAYMENT_STATUS, p.status) ?? p.status);
|
|
333
|
+
const url = typeof p.invoice_url === "string" && p.invoice_url.startsWith("https://") ? p.invoice_url : null;
|
|
334
|
+
const link = url && !check ? ` · ${unpaid(p.status) ? "pay at" : "receipt"} ${url}` : "";
|
|
335
|
+
return ` ${day} ${what}${detail ? ` (${detail})` : ""} ${usd(p.amount_usd)} ${status}${link}`;
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const billingHistory = async (ctx) => {
|
|
339
|
+
const kind = str(ctx, "--kind") ?? "all";
|
|
340
|
+
if (!HISTORY_KINDS.includes(kind)) {
|
|
341
|
+
throw usageError(`--kind: '${kind}' is not a kind`, `Pass one of ${HISTORY_KINDS.join(" | ")}.`);
|
|
342
|
+
}
|
|
343
|
+
const limit = limitOf(ctx, 50, 100);
|
|
344
|
+
const cursor = str(ctx, "--cursor");
|
|
345
|
+
const page = await client(ctx).billing.historyPage({
|
|
346
|
+
kind: kind,
|
|
347
|
+
limit,
|
|
348
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
349
|
+
});
|
|
350
|
+
const entries = page.items;
|
|
351
|
+
const more = page.nextCursor;
|
|
352
|
+
const owed = entries.some((e) => e.payment && e.payment.kind !== "method_check" && unpaid(e.payment.status));
|
|
353
|
+
const next = nexts(...(more
|
|
354
|
+
? [[`${sh `promigence billing history --limit ${String(limit)}`}${kind === "all" ? "" : sh ` --kind ${kind}`}${sh ` --cursor ${more}`}`, "the next page"]]
|
|
355
|
+
: []), ...(owed
|
|
356
|
+
? [["promigence billing portal", "pay the open invoice, or update the card on file"]]
|
|
357
|
+
: [["promigence billing usage", "this month's spend and credit"]]));
|
|
358
|
+
const empty = { all: "no credits or payments yet", credits: "no credit yet", payments: "no payments yet" };
|
|
359
|
+
const noun = kind === "all" ? "" : ` ${entries.length === 1 ? kind.slice(0, -1) : kind}`;
|
|
360
|
+
const lines = [
|
|
361
|
+
`promigence billing history: ${entries.length}${noun}, newest first`,
|
|
362
|
+
...historyLines(entries),
|
|
363
|
+
...(entries.length ? [] : [` (${empty[kind]})`]),
|
|
364
|
+
...(more ? [` more: --cursor ${more}`] : []),
|
|
365
|
+
];
|
|
366
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { kind, entries, count: entries.length, next_cursor: more }, next), lines, 0, ctx.pointer);
|
|
367
|
+
};
|
|
285
368
|
const referrals = async (ctx) => {
|
|
286
369
|
const r = await client(ctx).billing.referrals();
|
|
287
370
|
const next = r.code
|
|
@@ -745,6 +828,7 @@ export const ACCOUNT_HANDLERS = {
|
|
|
745
828
|
status,
|
|
746
829
|
"billing usage": billingUsage,
|
|
747
830
|
"billing portal": billingPortal,
|
|
831
|
+
"billing history": billingHistory,
|
|
748
832
|
referrals,
|
|
749
833
|
"referrals apply": referralsApply,
|
|
750
834
|
"snapshot delete": snapshotDelete,
|
package/dist/commands.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { PromigenceClient, type ExecResponse, type OptimizeFor, type RunReport, type SandboxRef, type ScoreMode, type SecretRecord } from "./sdk/index.js";
|
|
1
|
+
import { PromigenceClient, type ExecResponse, type OptimizeFor, type RunReport, type SandboxRef, type ScoreMode, type SecretRecord, type SnapshotBuild } from "./sdk/index.js";
|
|
2
2
|
import { type Mode } from "./io.js";
|
|
3
|
+
import { type BuildPrice } from "./pricing.js";
|
|
3
4
|
import { type Command, type NextHint } from "./surface.js";
|
|
4
5
|
export interface Ctx {
|
|
5
6
|
cmd: Command;
|
|
@@ -46,6 +47,7 @@ export declare function gradeOpts(ctx: Ctx): {
|
|
|
46
47
|
score?: ScoreMode;
|
|
47
48
|
} | undefined;
|
|
48
49
|
export declare function secretListLines(s: SecretRecord): string[];
|
|
50
|
+
export declare function buildPriceOf(b: SnapshotBuild): BuildPrice | undefined;
|
|
49
51
|
export declare function verifyTiming(v: Record<string, unknown> | undefined): string | undefined;
|
|
50
52
|
export declare function buildLogLines(log: string[] | undefined, state: string): string[];
|
|
51
53
|
export declare function snapshotSource(s: {
|
package/dist/commands.js
CHANGED
|
@@ -10,7 +10,7 @@ import { DevcontainerError, detectOne, load, planJson, planLines, planSetup, res
|
|
|
10
10
|
import { detect, ecosystemOfCommands, imageCreateCommand, readConfig } from "./init.js";
|
|
11
11
|
import { CREDENTIAL_REMEDY, confirmOverride, credentialRefusal, emitUploadList, localTarNoMetadataArgs } from "./uploadguard.js";
|
|
12
12
|
import { emit, errorEnvelope, errorTextLines, exitWith, forkLine, forkSummary, gradeLine, ms, noCommandFix, okEnvelope, parseMaxOutput, recordNotice, sandboxGrade, parseCommandTimeout, parseCount, parseDuration, parseUsd, pickExit, reportSummaryLines, toPromigenceError, totalsLine, usageError, write, writeBytes, } from "./io.js";
|
|
13
|
-
import { buildPrice } from "./pricing.js";
|
|
13
|
+
import { buildPrice, buildPriceWords } from "./pricing.js";
|
|
14
14
|
import { DOCS_URL, WAIT_TIMEOUT_DEFAULT } from "./surface.js";
|
|
15
15
|
import { escapeLine, fieldForTerminal, forTerminal, jsonLine, sh, shellArg } from "./term.js";
|
|
16
16
|
import { snapshotArg } from "./bundle.js";
|
|
@@ -430,6 +430,10 @@ function buildFields(b) {
|
|
|
430
430
|
out["source"] = b.source;
|
|
431
431
|
if (typeof b.verified === "boolean")
|
|
432
432
|
out["verified"] = b.verified;
|
|
433
|
+
for (const k of ["build_price", "per_second_usd", "max_seconds", "billable_seconds", "cost_usd"]) {
|
|
434
|
+
if (typeof b[k] === "number")
|
|
435
|
+
out[k] = b[k];
|
|
436
|
+
}
|
|
433
437
|
const ab = b;
|
|
434
438
|
if (ab.alias_requested !== undefined)
|
|
435
439
|
out["alias_requested"] = ab.alias_requested;
|
|
@@ -440,6 +444,11 @@ function buildFields(b) {
|
|
|
440
444
|
function isBuilding(b) {
|
|
441
445
|
return b.state === "queued" || b.state === "running";
|
|
442
446
|
}
|
|
447
|
+
export function buildPriceOf(b) {
|
|
448
|
+
return typeof b.build_price === "number" && typeof b.per_second_usd === "number" && typeof b.max_seconds === "number"
|
|
449
|
+
? { most_usd: b.build_price, per_second_usd: b.per_second_usd, max_s: b.max_seconds }
|
|
450
|
+
: undefined;
|
|
451
|
+
}
|
|
443
452
|
function buildExit(b) {
|
|
444
453
|
if (b.state === "verified" || b.state === "built")
|
|
445
454
|
return 0;
|
|
@@ -474,9 +483,15 @@ function buildLines(b) {
|
|
|
474
483
|
const timing = verifyTiming(b.verification);
|
|
475
484
|
if (timing)
|
|
476
485
|
lines.push(` verify: ${timing}`);
|
|
486
|
+
const cost = typeof b.cost_usd === "number" && !isBuilding(b) ? b.cost_usd : undefined;
|
|
487
|
+
const ran = typeof b.billable_seconds === "number" ? ` for the ${b.billable_seconds} s it ran` : "";
|
|
477
488
|
if (b.error) {
|
|
478
489
|
lines.push(` ${b.error.guard ? `guard ${b.error.guard}: ` : ""}${b.error.message ?? b.error.code ?? "rejected"}`);
|
|
479
|
-
|
|
490
|
+
const stored = `nothing was stored${b.error.stored === false ? "" : "?"}`;
|
|
491
|
+
lines.push(cost !== undefined && cost > 0 ? ` ${stored}; billed ${usd(cost)}${ran}` : ` ${stored} and nothing was charged`);
|
|
492
|
+
}
|
|
493
|
+
else if (cost !== undefined && cost > 0) {
|
|
494
|
+
lines.push(` billed ${usd(cost)}${ran}`);
|
|
480
495
|
}
|
|
481
496
|
for (const w of b.warnings ?? [])
|
|
482
497
|
lines.push(` warning: ${w}`);
|
|
@@ -875,14 +890,22 @@ const snapshotCreate = async (ctx) => {
|
|
|
875
890
|
const reused = started.deduped === true;
|
|
876
891
|
const joinedBuild = started.joined === true;
|
|
877
892
|
const replay = buildReplayNote(started, str(ctx, "--idempotency-key") !== undefined);
|
|
878
|
-
const
|
|
893
|
+
const free = reused || joinedBuild || replay !== undefined;
|
|
894
|
+
const builtAt = tier ?? (typeof started["tier"] === "string" ? started["tier"] : undefined);
|
|
895
|
+
const perSecond = buildPriceOf(started);
|
|
896
|
+
const flat = !free && perSecond === undefined && typeof started.build_price === "number" ? started.build_price : undefined;
|
|
897
|
+
const said = free || flat !== undefined ? perSecond : (perSecond ?? await buildPrice(c, builtAt));
|
|
898
|
+
const charged = free ? 0 : flat ?? said?.most_usd ?? 0;
|
|
879
899
|
submitted["build_price"] = charged;
|
|
880
|
-
if (
|
|
900
|
+
if (!free && said !== undefined)
|
|
901
|
+
Object.assign(submitted, { per_second_usd: said.per_second_usd, max_seconds: said.max_s });
|
|
902
|
+
if (free)
|
|
881
903
|
submitted["charged"] = false;
|
|
882
904
|
Object.assign(submitted, replayFields(started));
|
|
883
905
|
await write(process.stderr, ctx.mode === "json"
|
|
884
906
|
? JSON.stringify({
|
|
885
907
|
hint: "build_price", build_price: charged, unit: "USD", ...(tier === undefined ? {} : { tier }),
|
|
908
|
+
...(!free && said !== undefined ? { per_second_usd: said.per_second_usd, max_seconds: said.max_s } : {}),
|
|
886
909
|
...(reused ? { deduped: true } : {}), ...(joinedBuild ? { joined: true } : {}),
|
|
887
910
|
...(replay === undefined ? {} : { replayed: true }),
|
|
888
911
|
}) + "\n"
|
|
@@ -892,7 +915,9 @@ const snapshotCreate = async (ctx) => {
|
|
|
892
915
|
? `build_price: $0.00 - an identical build already ran (${started.build_id}); reusing it, nothing charged\n`
|
|
893
916
|
: joinedBuild
|
|
894
917
|
? `build_price: $0.00 - joining an identical build already running (${started.build_id}); it is charged once\n`
|
|
895
|
-
:
|
|
918
|
+
: flat !== undefined
|
|
919
|
+
? `build_price: $${flat.toFixed(2)} (tier ${tier ?? "default"}) - building ${label}\n`
|
|
920
|
+
: `build_price: ${buildPriceWords(said, builtAt ?? "default")} - building ${label}\n`);
|
|
896
921
|
if (sources.length && ctx.mode !== "json" && !reused) {
|
|
897
922
|
await write(process.stderr, sources
|
|
898
923
|
.map((s) => ` also ${s.repo}@${s.commit.slice(0, 7)} -> ${s.path ?? "beside the workspace, under its own name"}`)
|
|
@@ -920,7 +945,7 @@ const snapshotCreate = async (ctx) => {
|
|
|
920
945
|
const follow = sh `promigence snapshot status ${started.build_id} --wait`;
|
|
921
946
|
const err = new PromigenceError({
|
|
922
947
|
code: "aborted_by_client",
|
|
923
|
-
message: `interrupted by ${sig}; build ${started.build_id} goes on and nothing was cancelled (it is
|
|
948
|
+
message: `interrupted by ${sig}; build ${started.build_id} goes on and nothing was cancelled (it is billed per second until it ends)`,
|
|
924
949
|
fix: `Follow it: ${follow}.`,
|
|
925
950
|
exitCode: 130,
|
|
926
951
|
});
|
|
@@ -1587,7 +1612,8 @@ async function runImageSnapshot(ctx, c) {
|
|
|
1587
1612
|
checkImageRef(image);
|
|
1588
1613
|
const count = parseCount(required(ctx, "--count", "Pass --count N (number of sandboxes to fork)."), "--count");
|
|
1589
1614
|
const cap = await spendCap(ctx, c);
|
|
1590
|
-
const
|
|
1615
|
+
const most = await buildPrice(c);
|
|
1616
|
+
const price = most.most_usd;
|
|
1591
1617
|
const timeout = str(ctx, "--timeout");
|
|
1592
1618
|
const quoted = await c.runs.quote({
|
|
1593
1619
|
snapshot: DEFAULT_SNAPSHOT, count, ...(timeout === undefined ? {} : { timeoutMs: parseDuration(timeout, "--timeout") }),
|
|
@@ -1595,29 +1621,43 @@ async function runImageSnapshot(ctx, c) {
|
|
|
1595
1621
|
const needed = Math.ceil((price + (quoted ?? 0)) * 100) / 100;
|
|
1596
1622
|
if (cap < price + (quoted ?? 0)) {
|
|
1597
1623
|
const existing = (quoted ?? 0) <= cap ? await existingImageSnapshot(c, image) : undefined;
|
|
1598
|
-
throw new SpendCapError(`a new image's snapshot build
|
|
1624
|
+
throw new SpendCapError(`a new image's snapshot build can cost up to ${moneyUsd(price)} on top of the run` +
|
|
1599
1625
|
`${quoted === undefined ? "" : ` (about ${moneyUsd(quoted)})`}, over --cap ${moneyUsd(cap)}; nothing was built or started`, (existing === undefined ? "" : `This image already has snapshot ${existing}: run it under this cap with ${sh `promigence run --snapshot ${existing}`} instead of --image (any start command or settings it was made with come with it). Or `) +
|
|
1600
|
-
`${existing === undefined ? "Pass" : "pass"} --cap ${needed.toFixed(2)} or more (a build is
|
|
1626
|
+
`${existing === undefined ? "Pass" : "pass"} --cap ${needed.toFixed(2)} or more (a build is billed per second while it runs, only when this image has no snapshot yet, and reused free after), ` +
|
|
1601
1627
|
`or build it first, ${sh `promigence snapshot create --image ${image} --wait`}, and run its snapshot with --snapshot.`);
|
|
1602
1628
|
}
|
|
1603
1629
|
await write(process.stderr, ctx.mode === "json"
|
|
1604
1630
|
? jsonLine(process.stderr, { hint: "image snapshot", image })
|
|
1605
1631
|
: `image ${image}: creating or reusing its snapshot (a first build takes minutes)\n`);
|
|
1606
1632
|
let charged = 0;
|
|
1633
|
+
let perSecond = false;
|
|
1607
1634
|
let buildId;
|
|
1608
1635
|
const buildOpts = { image, allowBuild: true };
|
|
1609
1636
|
const snapshotId = await imageSnapshot(c, buildOpts, {
|
|
1610
1637
|
onBuild: async (b) => {
|
|
1611
1638
|
const free = b.deduped === true || b.joined === true || b.idempotent_replay === true || b.state === "verified" || b.state === "built";
|
|
1639
|
+
const said = buildPriceOf(b);
|
|
1640
|
+
perSecond = !free && said !== undefined;
|
|
1612
1641
|
charged = free ? 0 : (typeof b.build_price === "number" ? b.build_price : price);
|
|
1613
1642
|
buildId = b.build_id;
|
|
1614
1643
|
await write(process.stderr, ctx.mode === "json"
|
|
1615
|
-
? jsonLine(process.stderr, {
|
|
1644
|
+
? jsonLine(process.stderr, {
|
|
1645
|
+
hint: "build_price", build_price: charged, unit: "USD", image,
|
|
1646
|
+
...(perSecond && said ? { per_second_usd: said.per_second_usd, max_seconds: said.max_s } : {}),
|
|
1647
|
+
...(b.build_id ? { build_id: b.build_id } : {}), ...(free ? { charged: false } : {}),
|
|
1648
|
+
})
|
|
1616
1649
|
: free
|
|
1617
1650
|
? `build_price: $0.00 - this image's snapshot already exists; reusing it, nothing charged\n`
|
|
1618
|
-
:
|
|
1651
|
+
: perSecond && said
|
|
1652
|
+
? `build_price: ${buildPriceWords(said)} - building ${image}'s snapshot (billed per second while it builds; counted in --cap)\n`
|
|
1653
|
+
: `build_price: $${charged.toFixed(2)} - building ${image}'s snapshot (charged once it verifies; counted in --cap)\n`);
|
|
1619
1654
|
},
|
|
1620
1655
|
});
|
|
1656
|
+
if (perSecond && buildId !== undefined) {
|
|
1657
|
+
const done = await c.snapshots.status(buildId).catch(() => undefined);
|
|
1658
|
+
if (done !== undefined && typeof done.cost_usd === "number" && !isBuilding(done))
|
|
1659
|
+
charged = done.cost_usd;
|
|
1660
|
+
}
|
|
1621
1661
|
await write(process.stderr, ctx.mode === "json"
|
|
1622
1662
|
? jsonLine(process.stderr, { hint: "image snapshot ready", image, snapshot_id: snapshotId })
|
|
1623
1663
|
: fieldForTerminal(process.stderr, `image ${image}: snapshot ${snapshotId}`) + "\n");
|
package/dist/errfix.js
CHANGED
|
@@ -106,6 +106,7 @@ function notFoundFor(kind, ctx, apiFix) {
|
|
|
106
106
|
const BILLING_CMD = /`promigence billing (?:card|portal)`/;
|
|
107
107
|
export function withoutBilling(text) {
|
|
108
108
|
return text
|
|
109
|
+
.replace(/Owners: update the card with `promigence billing portal`; the hold lifts once the invoice is paid\.(?: For a disputed payment, write to support@promigence\.ai\.)?/, `To lift it, email ${SUPPORT}.`)
|
|
109
110
|
.replace(/,? or move to a plan that allows it: `promigence billing (?:card|portal)`\.?/, `, or email ${SUPPORT} for a plan that allows it.`)
|
|
110
111
|
.replace(/or add a card to lift it: `promigence billing (?:card|portal)`/, `or email ${SUPPORT} to lift it`)
|
|
111
112
|
.replace(/adding a card raises it \(owners: `promigence billing (?:card|portal)`\)\.?/, `to raise it, email ${SUPPORT}.`)
|
package/dist/generated.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface TierEntry {
|
|
|
20
20
|
per_second_usd: number;
|
|
21
21
|
}
|
|
22
22
|
export declare const TIERS: readonly TierEntry[];
|
|
23
|
-
export declare const
|
|
23
|
+
export declare const BUILD_MAX_S = 3600;
|
|
24
24
|
export declare const PRICING: {
|
|
25
25
|
readonly vcpu_second_usd: 0.000014;
|
|
26
26
|
readonly gib_second_usd: 0.0000045;
|
package/dist/generated.js
CHANGED
|
@@ -106,7 +106,7 @@ export const ERRORS = [
|
|
|
106
106
|
fix: "Set PROMIGENCE_API_KEY or run `promigence auth login --from-env VAR`. No key yet: `promigence login` (sign in) or https://promigence.ai." },
|
|
107
107
|
{ code: "forbidden", category: "other", http: 403, exit: 7, billed: "no",
|
|
108
108
|
message: "{detail}",
|
|
109
|
-
fix: "Do what the message names: sign in (`promigence login`), use a key from that org, or ask an owner." },
|
|
109
|
+
fix: "Do what the message names: sign in (`promigence login`), use a full-access key (`promigence keys create`) or one from that org, or ask an owner." },
|
|
110
110
|
{ code: "not_found", category: "other", http: 404, exit: 8, billed: "no",
|
|
111
111
|
message: "{kind} `{id}` not found",
|
|
112
112
|
fix: "Check the id; aliases resolve in your org first, then public snapshots (`promigence examples`)." },
|
|
@@ -158,12 +158,12 @@ export const ERRORS = [
|
|
|
158
158
|
{ code: "capacity_limit", category: "env", http: 409, exit: 3, billed: "no",
|
|
159
159
|
message: "{requested} {tier} sandboxes at once is more than one run can start at once; at most {max_at_once}",
|
|
160
160
|
fix: "Not billed; nothing started. Run at most {max_at_once} at once (lower `--count`), or use a smaller tier." },
|
|
161
|
-
{ code: "build_timeout", category: "other", http: 422, exit: 4, billed: "
|
|
161
|
+
{ code: "build_timeout", category: "other", http: 422, exit: 4, billed: "yes",
|
|
162
162
|
message: "the build ran past its {limit_min}-minute limit",
|
|
163
|
-
fix: "
|
|
164
|
-
{ code: "dockerfile_build_failed", category: "other", http: 400, exit: 2, billed: "
|
|
163
|
+
fix: "Nothing was stored; the {limit_min} minutes it ran are billed. Shorten `setup` (at most 64 steps, {limit_min} minutes in all) or bake the slow part into the image." },
|
|
164
|
+
{ code: "dockerfile_build_failed", category: "other", http: 400, exit: 2, billed: "yes",
|
|
165
165
|
message: "your image could not be built from the Dockerfile: {detail}",
|
|
166
|
-
fix: "Nothing was stored
|
|
166
|
+
fix: "Nothing was stored; the time the build ran is billed. Fix the Dockerfile (the lines above are the end of your own build output) and run `promigence snapshot create --dockerfile PATH ...` again." },
|
|
167
167
|
{ code: "image_unresolvable", category: "other", http: 422, exit: 2, billed: "no",
|
|
168
168
|
message: "`{image}` could not be resolved to a digest: {detail}",
|
|
169
169
|
fix: "Nothing was stored and nothing was billed. Check the name and tag; a private image needs `registry_secret` naming a stored secret that holds `user:password` (`promigence secrets set`). You can also pass the image already pinned as NAME@sha256:<digest>." },
|
|
@@ -191,7 +191,7 @@ export const ERRORS = [
|
|
|
191
191
|
{ code: "concurrency_limit", category: "other", http: 409, exit: 5, billed: "no",
|
|
192
192
|
message: "{live} live + {requested} requested sandboxes is over your limit of {limit}",
|
|
193
193
|
fix: "Wait for sandboxes to end or lower `--count` (`--allow-partial` covers the spend cap only); a retry of the same request fails the same way. Trial orgs get {trial_limit}; to raise it, email support@promigence.ai." },
|
|
194
|
-
{ code: "egress_cap_reached", category: "other", http: 403, exit:
|
|
194
|
+
{ code: "egress_cap_reached", category: "other", http: 403, exit: 5, billed: "no",
|
|
195
195
|
message: "monthly data-out cap reached: {used_gb} of {cap_gb} GB",
|
|
196
196
|
fix: "Use `--network none` (still allowed) or wait for the 1st (UTC). For more data out a month, email support@promigence.ai." },
|
|
197
197
|
{ code: "project_cap_reached", category: "cap", http: 403, exit: 5, billed: "no",
|
|
@@ -199,7 +199,10 @@ export const ERRORS = [
|
|
|
199
199
|
fix: "Ask for less (`--count`, `--cap`, a shorter extension), wait for the 1st (UTC), or have an owner raise it: `promigence project cap {project} <usd>`." },
|
|
200
200
|
{ code: "spend_limit_reached", category: "cap", http: 402, exit: 5, billed: "no",
|
|
201
201
|
message: "today's spend limit {limit} has {remaining} left; this needs {needed}",
|
|
202
|
-
fix: "Running sandboxes finish. The limit resets at 00:00 UTC and grows with each paid invoice; ask for less, or email support@promigence.ai.
|
|
202
|
+
fix: "Running sandboxes finish. The limit resets at 00:00 UTC and grows with each paid invoice; ask for less, or email support@promigence.ai." },
|
|
203
|
+
{ code: "billing_hold", category: "other", http: 402, exit: 5, billed: "no",
|
|
204
|
+
message: "new work is on hold: {reason}",
|
|
205
|
+
fix: "Running sandboxes finish. Owners: update the card with `promigence billing portal`; the hold lifts once the invoice is paid. For a disputed payment, write to support@promigence.ai." },
|
|
203
206
|
{ code: "tier_over_plan", category: "other", http: 403, exit: 2, billed: "no",
|
|
204
207
|
message: "the {tier} tier ({vcpus} vCPU) is over the {limit} vCPU per-sandbox limit of your {plan} plan",
|
|
205
208
|
fix: "Use a tier of at most {limit} vCPU (`promigence snapshot create … --tier {max_tier}` is the largest; a fork runs on its snapshot's), or move to a plan that allows it: `promigence billing portal`." },
|
|
@@ -260,12 +263,15 @@ export const CLIENT_ERRORS = [
|
|
|
260
263
|
{ code: "env_failed", category: "env", http: 0, exit: 3, billed: "no",
|
|
261
264
|
message: "the sandbox ended before hand-over",
|
|
262
265
|
fix: "Ours, not billed. The run's report names the failure's own code (`promigence run report RUN`); start the sandbox again." },
|
|
263
|
-
{ code: "snapshot_rejected", category: "other", http: 0, exit: 6, billed: "
|
|
266
|
+
{ code: "snapshot_rejected", category: "other", http: 0, exit: 6, billed: "unless_ours",
|
|
264
267
|
message: "a build guard refused it; nothing was stored",
|
|
265
|
-
fix: "
|
|
268
|
+
fix: "The time the build ran is billed, unless it ended on our side. Read the build log (`promigence snapshot status BUILD --log 80`): the guard named in the error says what to change (verify exited non-zero, a setup step failed, a time or size bound), then build again." },
|
|
266
269
|
{ code: "build_failed", category: "env", http: 0, exit: 3, billed: "no",
|
|
267
270
|
message: "the build could not finish; nothing was stored",
|
|
268
271
|
fix: "Not charged. Build again; if it repeats, email support@promigence.ai with the build id." },
|
|
272
|
+
{ code: "service_restart", category: "env", http: 0, exit: 3, billed: "no",
|
|
273
|
+
message: "a service restart stopped the command while its output was streaming",
|
|
274
|
+
fix: "Not billed: the time the command ran is credited back. The sandbox kept running; the command may have done part of its work before it stopped: run it again if that is safe." },
|
|
269
275
|
{ code: "not_connected", category: "other", http: 0, exit: 7, billed: "no",
|
|
270
276
|
message: "the connection request ended without connecting anything",
|
|
271
277
|
fix: "Nothing was connected. Do what the message names, then run the connect command again; `promigence github status` (or `gitlab status`) lists what is connected." },
|
|
@@ -285,7 +291,7 @@ export const TIERS = [
|
|
|
285
291
|
{ name: "bench-8x16", vcpus: 8, mem_gib: 16, disk_gib: 20, sellable: false, per_second_usd: 0 },
|
|
286
292
|
{ name: "bench-4x8", vcpus: 4, mem_gib: 8, disk_gib: 40, sellable: false, per_second_usd: 0 },
|
|
287
293
|
];
|
|
288
|
-
export const
|
|
294
|
+
export const BUILD_MAX_S = 3600;
|
|
289
295
|
export const PRICING = {
|
|
290
296
|
vcpu_second_usd: 0.000014,
|
|
291
297
|
gib_second_usd: 0.0000045,
|
package/dist/github.js
CHANGED
|
@@ -119,7 +119,7 @@ const githubStatus = async (ctx) => {
|
|
|
119
119
|
lines.push("pending:", ...s.pending.map((p) => ` installation ${p.installation_id}${p.account ? ` (${p.account})` : ""}: ${p.message}`));
|
|
120
120
|
}
|
|
121
121
|
if (s.tracks.length)
|
|
122
|
-
lines.push("tracked branches (each push rebuilds,
|
|
122
|
+
lines.push("tracked branches (each push rebuilds, billed per second):", ...s.tracks.map(trackLine));
|
|
123
123
|
const next = s.connections.length
|
|
124
124
|
? nexts(["promigence repos", "the repositories the connection can read"])
|
|
125
125
|
: nexts(["promigence github connect", "install the App on your GitHub account"]);
|
|
@@ -289,12 +289,12 @@ export async function githubCreate(ctx) {
|
|
|
289
289
|
};
|
|
290
290
|
if (track) {
|
|
291
291
|
const branch = ref ?? "the default branch";
|
|
292
|
-
const each = await buildPrice(client(ctx));
|
|
292
|
+
const each = (await buildPrice(client(ctx), tier)).most_usd;
|
|
293
293
|
await write(process.stderr, ctx.mode === "json"
|
|
294
294
|
? jsonLine(process.stderr, { hint: "track", repo, branch, alias, build_price_each: each, unit: "USD" })
|
|
295
295
|
: glPath
|
|
296
|
-
? `tracking: every new commit on ${branch} (checked every minute) rebuilds this snapshot and moves ${alias} to it,
|
|
297
|
-
: `tracking: every push to ${branch} rebuilds this snapshot and moves ${alias} to it,
|
|
296
|
+
? `tracking: every new commit on ${branch} (checked every minute) rebuilds this snapshot and moves ${alias} to it, each build billed per second while it runs (up to $${each.toFixed(2)}), charged to your org; stop with \`${sh `promigence gitlab untrack ${alias}`}\`\n`
|
|
297
|
+
: `tracking: every push to ${branch} rebuilds this snapshot and moves ${alias} to it, each build billed per second while it runs (up to $${each.toFixed(2)}), charged to your org; stop with \`${sh `promigence github untrack ${alias}`}\`\n`);
|
|
298
298
|
}
|
|
299
299
|
return { opts, label, submitted, tier };
|
|
300
300
|
}
|
package/dist/gitlab.js
CHANGED
|
@@ -120,7 +120,7 @@ const gitlabStatus = async (ctx) => {
|
|
|
120
120
|
lines.push("waiting for an owner's confirmation:", ...s.pending.map((p) => ` ${p.username} (${p.connection_id}): ${p.message}`));
|
|
121
121
|
}
|
|
122
122
|
if (s.tracks.length) {
|
|
123
|
-
lines.push("tracked branches (each new commit rebuilds,
|
|
123
|
+
lines.push("tracked branches (each new commit rebuilds, billed per second; checked every minute):", ...s.tracks.map(trackLine));
|
|
124
124
|
}
|
|
125
125
|
if (s.connections.length) {
|
|
126
126
|
lines.push("optional, for instant rebuilds: a project webhook (push events) with each connection's URL and token in --json (webhook_url, webhook_token)");
|
package/dist/guide.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS,
|
|
1
|
+
import { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS, BUILD_MAX_S, PRICING, } from "./generated.js";
|
|
2
2
|
export const BILLED_TEXT = {
|
|
3
3
|
no: "no: nothing is billed for this",
|
|
4
4
|
yes: "yes: the time used is billed",
|
|
5
5
|
credited: "no: ours, credited back",
|
|
6
6
|
unless_ours: "the time used is billed, unless the cause is shown to be ours: then it is credited back",
|
|
7
7
|
};
|
|
8
|
-
import { DOCS_URL, EXIT_CODES, EXIT_PRECEDENCE_TEXT, GOLDEN_PATH, GROUPS, GUIDE_COMMON_ERRORS, LISTED_COMMANDS,
|
|
8
|
+
import { DOCS_URL, EXIT_CODES, EXIT_PRECEDENCE_TEXT, GOLDEN_PATH, GROUPS, GUIDE_COMMON_ERRORS, LISTED_COMMANDS, NOT_LIVE_SUMMARY, SCHEMA, VERSION, WAIT_TIMEOUT_DEFAULT, compactNames, } from "./surface.js";
|
|
9
9
|
export const SECTIONS = ["core", "commands", "errors", "cost"];
|
|
10
10
|
export const BUDGETS = {
|
|
11
|
-
core:
|
|
12
|
-
commands:
|
|
11
|
+
core: 1500,
|
|
12
|
+
commands: 4500,
|
|
13
13
|
errors: 2325,
|
|
14
14
|
cost: 600,
|
|
15
15
|
};
|
|
@@ -20,7 +20,25 @@ export function lookupError(code) {
|
|
|
20
20
|
export const ALL_ERROR_CODES = [...ERRORS, ...CLIENT_ERRORS].map((e) => e.code);
|
|
21
21
|
export const estimateTokens = (s) => Math.ceil(s.length / 4);
|
|
22
22
|
const exitLine = EXIT_CODES.filter((e) => e.code !== 125 && e.code !== 13).map((e) => `${e.code} ${e.name}`).join(" · ");
|
|
23
|
+
const codesOn = (n) => ERRORS.filter((e) => e.exit === n).map((e) => e.code).join(", ");
|
|
24
|
+
const EXIT_NOTE = {
|
|
25
|
+
7: codesOn(7),
|
|
26
|
+
10: `${codesOn(10)} only: retry after Retry-After`,
|
|
27
|
+
12: `some sandboxes ${codesOn(12)}`,
|
|
28
|
+
13: "agent-guide --check: files would change",
|
|
29
|
+
125: "exec --sandbox: ours; else the command's code",
|
|
30
|
+
};
|
|
31
|
+
const exitLegend = EXIT_CODES.map((e) => `${e.code} ${e.name}${EXIT_NOTE[e.code] ? ` (${EXIT_NOTE[e.code]})` : ""}`).join(" · ");
|
|
23
32
|
const liveNames = new Set(LISTED_COMMANDS.map((c) => c.name));
|
|
33
|
+
const HOSTED_LIVE = compactNames(LISTED_COMMANDS.filter((c) => !c.notOnHosted).map((c) => c.name));
|
|
34
|
+
const NOT_ON_HOSTED = LISTED_COMMANDS.filter((c) => c.notOnHosted);
|
|
35
|
+
const COUNT_WORDS = ["no", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];
|
|
36
|
+
const exitOf = (code) => {
|
|
37
|
+
const e = ERROR_BY_CODE[code];
|
|
38
|
+
if (!e)
|
|
39
|
+
throw new Error(`the guide names ${code}, which is not a known error code`);
|
|
40
|
+
return e.exit;
|
|
41
|
+
};
|
|
24
42
|
function renderCore() {
|
|
25
43
|
const common = GUIDE_COMMON_ERRORS.map((code) => {
|
|
26
44
|
const e = ERROR_BY_CODE[code];
|
|
@@ -37,7 +55,7 @@ function renderCore() {
|
|
|
37
55
|
" verify → snapshot → fork(N) → exec → validity report",
|
|
38
56
|
"",
|
|
39
57
|
"- **snapshot** — an image (pinned) + ordered `setup` steps + a `verify` command that, if given, must exit 0.",
|
|
40
|
-
" If verify fails nothing is stored
|
|
58
|
+
" If verify fails nothing is stored; the build's own time is billed. A branch is not a pin.",
|
|
41
59
|
"- **fork** — a warm copy of that workspace, with its `start` service ready;",
|
|
42
60
|
" N at once, each checked before you get it.",
|
|
43
61
|
"- **validity report** — per sandbox, and this split is the product: **env** failures are ours",
|
|
@@ -63,15 +81,22 @@ function renderCore() {
|
|
|
63
81
|
"Progress, prices and diagnostics go to **stderr** — never parse them. `next[].cmd` is",
|
|
64
82
|
"id-based, runnable verbatim (fill any UPPERCASE word); use it, never invent one.",
|
|
65
83
|
"",
|
|
66
|
-
`Exit codes: ${
|
|
84
|
+
`Exit codes: ${exitLegend}.`,
|
|
67
85
|
`Highest-precedence code wins: ${EXIT_PRECEDENCE_TEXT}.`,
|
|
68
86
|
"",
|
|
69
|
-
|
|
87
|
+
`## The ${COUNT_WORDS[common.length] ?? common.length} you will actually hit`,
|
|
70
88
|
"",
|
|
71
89
|
"(`{name}` in a fix is filled from the failure's evidence.)",
|
|
72
90
|
"",
|
|
73
91
|
...common,
|
|
74
92
|
"",
|
|
93
|
+
"## Limits and stops",
|
|
94
|
+
"",
|
|
95
|
+
`- \`setup_required\` (exit ${exitOf("setup_required")}): answer the account's setup questions in the console first.`,
|
|
96
|
+
`- \`egress_cap_reached\` (exit ${exitOf("egress_cap_reached")}): the month's data out is spent; \`--network none\` still starts; it resets on the 1st (UTC).`,
|
|
97
|
+
"- A paused sandbox is kept 30 days (`pause_expires_at`); resuming keeps it, else it is deleted (`expired`).",
|
|
98
|
+
`- \`stopped_by_support\` (exit ${exitOf("stopped_by_support")}): support stopped it; the message says why.`,
|
|
99
|
+
"",
|
|
75
100
|
"## Do",
|
|
76
101
|
"",
|
|
77
102
|
"- Pass `--cap USD` on every `fork`/`run`: a run over it is refused (exit 5) before anything starts.",
|
|
@@ -99,7 +124,10 @@ function renderCore() {
|
|
|
99
124
|
`· \`promigence <command> --help\` · ${DOCS_URL}`,
|
|
100
125
|
"Same surface as MCP tools: `promigence mcp` (stdio; `claude mcp add promigence -- promigence mcp`).",
|
|
101
126
|
"",
|
|
102
|
-
`Live in v${VERSION}: ${
|
|
127
|
+
`Live in v${VERSION}: ${HOSTED_LIVE}`,
|
|
128
|
+
...(NOT_ON_HOSTED.length
|
|
129
|
+
? [`Not on the hosted service yet: ${compactNames(NOT_ON_HOSTED.map((c) => c.name))} (${[...new Set(NOT_ON_HOSTED.map((c) => c.notOnHosted))].join(", ")}).`]
|
|
130
|
+
: []),
|
|
103
131
|
...(NOT_LIVE_SUMMARY ? ["Every other command exits 1 `not_released` in the same envelope."] : []),
|
|
104
132
|
"",
|
|
105
133
|
].join("\n");
|
|
@@ -217,7 +245,8 @@ function renderCost(prices) {
|
|
|
217
245
|
const vcpu = prices?.vcpu_second_usd ?? PRICING.vcpu_second_usd;
|
|
218
246
|
const gib = prices?.gib_second_usd ?? PRICING.gib_second_usd;
|
|
219
247
|
const granularity = prices?.granularity_seconds ?? PRICING.granularity_seconds;
|
|
220
|
-
const
|
|
248
|
+
const buildMax = prices?.build_max_s ?? BUILD_MAX_S;
|
|
249
|
+
const buildHours = buildMax % 3600 === 0 ? `${buildMax / 3600} h` : `${Math.ceil(buildMax / 60)} min`;
|
|
221
250
|
const view = prices?.view;
|
|
222
251
|
const source = prices === undefined
|
|
223
252
|
? "List prices when this CLI was built. Live, and your plan's: `promigence pricing`."
|
|
@@ -241,8 +270,10 @@ function renderCost(prices) {
|
|
|
241
270
|
...tiers.map((t) => `| \`${t.name}\` | ${t.vcpus} | ${t.mem_gib} GiB | ${t.disk_gib} GiB | $${t.per_second_usd} | ${perHour(t.per_second_usd)} |`),
|
|
242
271
|
...(typeof limit === "number" ? ["", `Your plan runs sandboxes of up to ${limit} vCPU.`] : []),
|
|
243
272
|
"",
|
|
244
|
-
`Building a snapshot
|
|
245
|
-
"
|
|
273
|
+
`Building a snapshot bills per second at its tier's rate while it runs, at most ${buildHours} (the most it can`,
|
|
274
|
+
"cost is printed on stderr **before** the build starts; what it cost, when it ends). A build that",
|
|
275
|
+
"fails `verify` or a guard stores nothing and is billed the time it ran; one that fails on our",
|
|
276
|
+
"side is not billed, and reusing an identical build is free.",
|
|
246
277
|
"",
|
|
247
278
|
"`env` failures (`promigence agent-guide --section errors`): one before hand-over is not billed,",
|
|
248
279
|
"e.g. " + ERRORS.filter((e) => e.category === "env").slice(0, 3).map((e) => `\`${e.code}\``).join(", ") + ". After hand-over, a failure our",
|
|
@@ -320,7 +351,7 @@ export function renderLlmsTxt() {
|
|
|
320
351
|
"",
|
|
321
352
|
"## Index",
|
|
322
353
|
"",
|
|
323
|
-
...LISTED_COMMANDS.map((c) => `- \`promigence ${c.name}\` — ${c.summary}`),
|
|
354
|
+
...LISTED_COMMANDS.map((c) => `- \`promigence ${c.name}\` — ${c.summary}${c.notOnHosted ? ` (not on the hosted service yet)` : ""}`),
|
|
324
355
|
"",
|
|
325
356
|
"- `promigence agent-guide` — this document's first section",
|
|
326
357
|
"- `promigence agent-guide --section commands` — every command with its flags",
|
package/dist/handlers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import { BUNDLE_EXT, DEFAULT_SNAPSHOT, PromigenceError, VERSION as SDK_VERSION, configDir, credentialsPath,
|
|
3
|
+
import { BUNDLE_EXT, DEFAULT_SNAPSHOT, PromigenceError, VERSION as SDK_VERSION, configDir, credentialsPath, platformOf, readCredentialsWithStore, renderBundle, verifyBundle, } from "./sdk/index.js";
|
|
4
4
|
import { storeLabel } from "./sdk/credstore.js";
|
|
5
5
|
import { bool, client, list, nexts, positional, required, str, } from "./commands.js";
|
|
6
6
|
import { emit, exitWith, forkSummary, markGuideSeen, ms, okEnvelope, parseCount, parseDuration, parseUsd, pickExit, usageError, write, } from "./io.js";
|
|
@@ -30,7 +30,7 @@ const agentGuide = async (ctx) => {
|
|
|
30
30
|
const skipped = res.artifacts.find((a) => a.action === "skipped");
|
|
31
31
|
const next = nexts(...(skipped
|
|
32
32
|
? [["promigence agent-guide --install --create", "create AGENTS.md and write the fence into it"]]
|
|
33
|
-
: []), ["promigence agent-guide", "the guide itself (~1.
|
|
33
|
+
: []), ["promigence agent-guide", "the guide itself (~1.4k tokens)"], ["promigence examples", "what you can fork with no build (the built-in base)"]);
|
|
34
34
|
const env = okEnvelope(ctx.cmd.name, {
|
|
35
35
|
mode: checking ? "check" : "install",
|
|
36
36
|
dir: res.dir,
|
|
@@ -259,9 +259,6 @@ function keySource(ctx) {
|
|
|
259
259
|
if (creds.api_key) {
|
|
260
260
|
return { source: store === "file" || store === undefined ? credentialsPath() : `${storeLabel(store)} (item ${credentialsPath()})`, present: true };
|
|
261
261
|
}
|
|
262
|
-
if (compatApiKey(process.env["E2B_API_KEY"])) {
|
|
263
|
-
return { source: "E2B_API_KEY (compatibility)", present: true };
|
|
264
|
-
}
|
|
265
262
|
return { source: "none", present: false };
|
|
266
263
|
}
|
|
267
264
|
async function clientChecks(ctx) {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { SCHEMA, GUIDE_POINTER, GOLDEN_PATH, COMMANDS, LISTED_COMMANDS, GROUPS,
|
|
|
3
3
|
export { ALL_ERROR_CODES, BILLED_TEXT, BUDGETS, SECTIONS, estimateTokens, guideJson, lookupError, renderGuide, renderLlmsTxt,
|
|
4
4
|
renderGuide as renderAgentGuide, } from "./guide.js";
|
|
5
5
|
export type { Section } from "./guide.js";
|
|
6
|
-
export { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS,
|
|
6
|
+
export { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS, BUILD_MAX_S, PRICING } from "./generated.js";
|
|
7
7
|
export type { ErrorEntry, TierEntry } from "./generated.js";
|
|
8
8
|
export { FENCE_BEGIN, FENCE_END, FENCE_MAX_LINES, fenceBody, install, spliceFence } from "./install.js";
|
|
9
9
|
export type { Artifact, InstallResult } from "./install.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./sdk/index.js";
|
|
2
2
|
export { SCHEMA, GUIDE_POINTER, GOLDEN_PATH, COMMANDS, LISTED_COMMANDS, GROUPS, GLOBAL_FLAGS, EXIT_CODES, LIVE_COMMANDS, LIVE_SUMMARY, NOT_LIVE_SUMMARY, STATUS_LINE, DESCRIPTION, compactNames, EXIT_PRECEDENCE, EXIT_PRECEDENCE_TEXT, OUTPUT_MODE_PRECEDENCE, WAIT_TIMEOUT_DEFAULT, REQUEST_TIMEOUT_DEFAULT, GUIDE_COMMON_ERRORS, findCommand, knownFlags, nearestFlag, unknownFlags, renderHelp, renderCommandHelp, signInLine, notReleasedEnvelope, notReleasedLines, } from "./surface.js";
|
|
3
3
|
export { ALL_ERROR_CODES, BILLED_TEXT, BUDGETS, SECTIONS, estimateTokens, guideJson, lookupError, renderGuide, renderLlmsTxt, renderGuide as renderAgentGuide, } from "./guide.js";
|
|
4
|
-
export { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS,
|
|
4
|
+
export { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS, BUILD_MAX_S, PRICING } from "./generated.js";
|
|
5
5
|
export { FENCE_BEGIN, FENCE_END, FENCE_MAX_LINES, fenceBody, install, spliceFence } from "./install.js";
|
|
6
6
|
export { detect, gitFacts, normalizeRemote, readConfig, renderConfig, stripRemoteCredentials } from "./init.js";
|
|
7
7
|
export { DEVCONTAINER_NAMES, DEVCONTAINER_SPEC, DevcontainerError, detectOne, discover, load, parseJsonc, planJson, planLines, planSetup, resolveGiven, shellQuote, snapshotEnv, snapshotUsers, substituteString, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|