promigence 0.1.0-beta.12
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/NOTICE +14 -0
- package/README.md +88 -0
- package/dist/account.js +850 -0
- package/dist/agent.js +374 -0
- package/dist/argv.js +159 -0
- package/dist/bin.js +332 -0
- package/dist/bundle.js +133 -0
- package/dist/capacity.js +69 -0
- package/dist/commands.d.ts +110 -0
- package/dist/commands.js +2675 -0
- package/dist/connectreq.js +125 -0
- package/dist/credfile.js +188 -0
- package/dist/credits.js +197 -0
- package/dist/devcontainer.d.ts +99 -0
- package/dist/devcontainer.js +744 -0
- package/dist/device.js +129 -0
- package/dist/dirsnap.js +378 -0
- package/dist/errfix.js +262 -0
- package/dist/generated.d.ts +35 -0
- package/dist/generated.js +307 -0
- package/dist/github.js +307 -0
- package/dist/gitlab.js +210 -0
- package/dist/guide.d.ts +19 -0
- package/dist/guide.js +366 -0
- package/dist/handlers.js +511 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +7 -0
- package/dist/init.d.ts +40 -0
- package/dist/init.js +352 -0
- package/dist/install.d.ts +23 -0
- package/dist/install.js +127 -0
- package/dist/io.d.ts +37 -0
- package/dist/io.js +318 -0
- package/dist/live.js +469 -0
- package/dist/login.js +1063 -0
- package/dist/mcp.js +889 -0
- package/dist/money.js +5 -0
- package/dist/orgs.js +475 -0
- package/dist/pricing.d.ts +52 -0
- package/dist/pricing.js +181 -0
- package/dist/report.js +132 -0
- package/dist/routes.js +135 -0
- package/dist/runners-github.js +257 -0
- package/dist/runners-gitlab.js +125 -0
- package/dist/runners-spec.js +137 -0
- package/dist/runners.js +1458 -0
- package/dist/sdk/blake3.d.ts +10 -0
- package/dist/sdk/blake3.js +111 -0
- package/dist/sdk/bundle.d.ts +96 -0
- package/dist/sdk/bundle.js +227 -0
- package/dist/sdk/ca-guard.js +166 -0
- package/dist/sdk/caller.d.ts +73 -0
- package/dist/sdk/caller.js +47 -0
- package/dist/sdk/client.d.ts +3015 -0
- package/dist/sdk/client.js +2583 -0
- package/dist/sdk/command-errors.d.ts +41 -0
- package/dist/sdk/command-errors.js +36 -0
- package/dist/sdk/context.d.ts +119 -0
- package/dist/sdk/context.js +588 -0
- package/dist/sdk/core.d.ts +285 -0
- package/dist/sdk/core.js +266 -0
- package/dist/sdk/credstore.d.ts +90 -0
- package/dist/sdk/credstore.js +206 -0
- package/dist/sdk/dispatcher.js +86 -0
- package/dist/sdk/github.d.ts +158 -0
- package/dist/sdk/github.js +78 -0
- package/dist/sdk/gitlab.d.ts +104 -0
- package/dist/sdk/gitlab.js +20 -0
- package/dist/sdk/handles.d.ts +82 -0
- package/dist/sdk/handles.js +135 -0
- package/dist/sdk/hint.js +7 -0
- package/dist/sdk/http2.js +1921 -0
- package/dist/sdk/index.d.ts +34 -0
- package/dist/sdk/index.js +15 -0
- package/dist/sdk/keyed.gen.js +7 -0
- package/dist/sdk/models.gen.d.ts +6162 -0
- package/dist/sdk/models.gen.js +1 -0
- package/dist/sdk/proxy.js +249 -0
- package/dist/sdk/pty.d.ts +171 -0
- package/dist/sdk/pty.js +496 -0
- package/dist/sdk/sandbox.d.ts +537 -0
- package/dist/sdk/sandbox.js +725 -0
- package/dist/sdk/ssh.d.ts +25 -0
- package/dist/sdk/ssh.js +114 -0
- package/dist/sdk/trace.js +80 -0
- package/dist/sdk/transport-worker.js +466 -0
- package/dist/sdk/warm.js +40 -0
- package/dist/ssh.js +267 -0
- package/dist/surface.d.ts +95 -0
- package/dist/surface.js +1452 -0
- package/dist/templates.js +53 -0
- package/dist/term.js +50 -0
- package/dist/timing.js +71 -0
- package/dist/untar.js +324 -0
- package/dist/uploadguard.js +75 -0
- package/llms.txt +209 -0
- package/package.json +52 -0
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { PromigenceClient } from "./sdk/index.js";
|
|
2
|
+
import { bool, clientOpts, nexts } from "./commands.js";
|
|
3
|
+
import { BUILD_MAX_S, PRICING, TIERS } from "./generated.js";
|
|
4
|
+
import { emit, okEnvelope, recordNotice } from "./io.js";
|
|
5
|
+
import { usd } from "./money.js";
|
|
6
|
+
export const COMPILED_NOTE = "list prices at build time; could not reach the API";
|
|
7
|
+
export const PUBLISHED_NOTE = "published list prices; your organization's own need a key (promigence login)";
|
|
8
|
+
const nano = (n) => Math.round(n * 1e9) / 1e9;
|
|
9
|
+
export function publishedPrices() {
|
|
10
|
+
return { ...compiledPrices(PUBLISHED_NOTE), source: "published" };
|
|
11
|
+
}
|
|
12
|
+
export const DEFAULT_BUILD_TIER = "small";
|
|
13
|
+
export function compiledPrices(note = COMPILED_NOTE, reason) {
|
|
14
|
+
const perSecond = (v, g) => (t) => nano(t.vcpus * v + t.mem_gib * g);
|
|
15
|
+
const od = perSecond(PRICING.vcpu_second_usd, PRICING.gib_second_usd);
|
|
16
|
+
const pe = perSecond(PRICING.preemptible_vcpu_second_usd, PRICING.preemptible_gib_second_usd);
|
|
17
|
+
const buildTier = TIERS.find((t) => t.name === DEFAULT_BUILD_TIER);
|
|
18
|
+
return {
|
|
19
|
+
source: "compiled",
|
|
20
|
+
note,
|
|
21
|
+
...(reason === undefined ? {} : { reason }),
|
|
22
|
+
vcpu_second_usd: PRICING.vcpu_second_usd,
|
|
23
|
+
gib_second_usd: PRICING.gib_second_usd,
|
|
24
|
+
preemptible_vcpu_second_usd: PRICING.preemptible_vcpu_second_usd,
|
|
25
|
+
preemptible_gib_second_usd: PRICING.preemptible_gib_second_usd,
|
|
26
|
+
build_usd: buildTier ? nano(od(buildTier) * BUILD_MAX_S) : 0,
|
|
27
|
+
build_max_s: BUILD_MAX_S,
|
|
28
|
+
granularity_seconds: PRICING.granularity_seconds,
|
|
29
|
+
tiers: TIERS.filter((t) => t.sellable)
|
|
30
|
+
.slice()
|
|
31
|
+
.sort((a, b) => a.vcpus - b.vcpus || a.mem_gib - b.mem_gib)
|
|
32
|
+
.map((t) => ({
|
|
33
|
+
name: t.name, vcpus: t.vcpus, mem_gib: t.mem_gib, disk_gib: t.disk_gib,
|
|
34
|
+
per_second_usd: od(t), per_hour_usd: nano(od(t) * 3600), preemptible_per_second_usd: pe(t),
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function pricesFrom(view) {
|
|
39
|
+
return {
|
|
40
|
+
source: "api",
|
|
41
|
+
view,
|
|
42
|
+
vcpu_second_usd: view.rates.vcpu_second_usd,
|
|
43
|
+
gib_second_usd: view.rates.gib_second_usd,
|
|
44
|
+
preemptible_vcpu_second_usd: view.rates.preemptible_vcpu_second_usd,
|
|
45
|
+
preemptible_gib_second_usd: view.rates.preemptible_gib_second_usd,
|
|
46
|
+
build_usd: view.rates.build_usd,
|
|
47
|
+
build_max_s: view.limits?.build_max_s ?? BUILD_MAX_S,
|
|
48
|
+
granularity_seconds: view.granularity_seconds,
|
|
49
|
+
tiers: view.tiers.map((t) => ({ ...t })),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const CALLERS_OWN = new Set(["auth", "key_expired", "forbidden", "invalid_argument", "usage"]);
|
|
53
|
+
const CACHE_MS = 60_000;
|
|
54
|
+
const cache = new Map();
|
|
55
|
+
export async function livePricing(c, opts = {}) {
|
|
56
|
+
if (opts.newAccount === true || !c.authenticated)
|
|
57
|
+
return publishedPrices();
|
|
58
|
+
const key = `${c.apiUrl}|${c.apiKey ?? ""}`;
|
|
59
|
+
const hit = cache.get(key);
|
|
60
|
+
let p;
|
|
61
|
+
if (hit && Date.now() - hit.at < CACHE_MS) {
|
|
62
|
+
p = hit.p;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
p = c.pricing().then(pricesFrom);
|
|
66
|
+
cache.set(key, { at: Date.now(), p });
|
|
67
|
+
p.catch(() => cache.delete(key));
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return await p;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
const err = e;
|
|
74
|
+
if (opts.strict && typeof err?.code === "string" && CALLERS_OWN.has(err.code))
|
|
75
|
+
throw e;
|
|
76
|
+
return compiledPrices(COMPILED_NOTE, err?.message ?? String(e));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function buildPriceIn(p, tier) {
|
|
80
|
+
const t = tier === undefined ? undefined : p.tiers.find((x) => x.name === tier);
|
|
81
|
+
const max = p.build_max_s > 0 ? p.build_max_s : BUILD_MAX_S;
|
|
82
|
+
if (t !== undefined) {
|
|
83
|
+
return { most_usd: nano(t.per_second_usd * max), per_second_usd: t.per_second_usd, max_s: max };
|
|
84
|
+
}
|
|
85
|
+
return { most_usd: p.build_usd, per_second_usd: nano(p.build_usd / max), max_s: max };
|
|
86
|
+
}
|
|
87
|
+
export async function buildPrice(c, tier) {
|
|
88
|
+
return buildPriceIn(await livePricing(c), tier);
|
|
89
|
+
}
|
|
90
|
+
export function buildLimit(s) {
|
|
91
|
+
return hours(s);
|
|
92
|
+
}
|
|
93
|
+
export function buildPriceWords(p, tier) {
|
|
94
|
+
return `up to ${usd(p.most_usd)} (${tier === undefined ? "" : `tier ${tier}, `}${rate(p.per_second_usd)}/s for at most ${buildLimit(p.max_s)})`;
|
|
95
|
+
}
|
|
96
|
+
export function quickClient(ctx) {
|
|
97
|
+
const opts = clientOpts(ctx);
|
|
98
|
+
return new PromigenceClient({
|
|
99
|
+
...opts,
|
|
100
|
+
requestTimeoutMs: Math.min(opts.requestTimeoutMs ?? 5_000, 5_000),
|
|
101
|
+
maxRetries: 0,
|
|
102
|
+
onNotice: recordNotice,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export function rate(n) {
|
|
106
|
+
return `$${Number(n.toPrecision(12))}`;
|
|
107
|
+
}
|
|
108
|
+
const hours = (s) => s % 3600 === 0 ? `${s / 3600} h` : s % 60 === 0 ? `${s / 60} min` : `${s} s`;
|
|
109
|
+
export function pricingLines(p, opts = {}) {
|
|
110
|
+
const v = p.view;
|
|
111
|
+
const head = p.source === "api" && v
|
|
112
|
+
? `promigence pricing — live, ${v.as_of} · your organization (${v.org_id ?? "?"}), plan ${v.plan.display_name}${v.plan.version === null ? "" : ` v${v.plan.version}`}`
|
|
113
|
+
: `promigence pricing — ${p.note ?? COMPILED_NOTE}`;
|
|
114
|
+
const lines = [
|
|
115
|
+
head,
|
|
116
|
+
` rates ${rate(p.vcpu_second_usd)} per vCPU-second + ${rate(p.gib_second_usd)} per GiB-second, billed per second (rounded up to ${p.granularity_seconds} s)`,
|
|
117
|
+
` preempt. ${rate(p.preemptible_vcpu_second_usd)} per vCPU-second + ${rate(p.preemptible_gib_second_usd)} per GiB-second (preemptible runs)`,
|
|
118
|
+
` build a new snapshot build is billed per second at its tier's rate while it runs, at most ${hours(p.build_max_s)} (up to ${usd(p.build_usd)} at ${DEFAULT_BUILD_TIER}); not billed when it fails on our side`,
|
|
119
|
+
];
|
|
120
|
+
if (v) {
|
|
121
|
+
const eg = v.rates.egress_gb_usd;
|
|
122
|
+
const inc = v.limits.egress_gb_month;
|
|
123
|
+
const cut = v.limits.max_egress_gb_month;
|
|
124
|
+
lines.push(` data out ${[
|
|
125
|
+
inc === null ? "none included" : `${inc} GiB a month included`,
|
|
126
|
+
eg === null ? "not priced beyond it" : `then ${rate(eg)} per GiB`,
|
|
127
|
+
cut === null ? "no cut" : `stops at ${cut} GiB a month`,
|
|
128
|
+
].join(", ")}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push("", " tier vCPU RAM disk $/second $/hour");
|
|
131
|
+
for (const t of p.tiers) {
|
|
132
|
+
const over = t.available === false ? ` (over your ${v?.limits.max_sandbox_vcpus ?? "?"} vCPU limit)` : "";
|
|
133
|
+
lines.push(` ${t.name.padEnd(9)} ${String(t.vcpus).padEnd(5)} ${`${t.mem_gib} GiB`.padEnd(8)} ${`${t.disk_gib} GiB`.padEnd(8)} ${rate(t.per_second_usd).padEnd(12)} ${usd(t.per_hour_usd)}${over}`);
|
|
134
|
+
}
|
|
135
|
+
if (v) {
|
|
136
|
+
const l = v.limits;
|
|
137
|
+
lines.push("", ` limits ${l.max_concurrency === null ? "no concurrency limit" : `${l.max_concurrency} sandboxes at once`} · ${l.max_sandbox_vcpus === null ? "any shape" : `up to ${l.max_sandbox_vcpus} vCPU`} · ${hours(l.max_session_s)} per sandbox · ${l.max_count_per_run} per run · default timeout ${hours(l.default_timeout_s)}`);
|
|
138
|
+
const f = v.free_credit;
|
|
139
|
+
lines.push(` credit new accounts: ${usd(f.signup_credit_usd)} claimed in the browser + ${usd(f.card_bonus_usd)} with a card${opts.cardsOff ? " (card payments are not open yet)" : ""}${f.expiry_days_after_claim === null ? "" : `; expires ${f.expiry_days_after_claim} days after it is granted`}${f.expires_by === null ? "" : `, ${f.expiry_days_after_claim === null ? "expires" : "and"} by ${f.expires_by}`}${f.invite_required ? "; an invite code is needed to sign up" : ""}`, ` plan ${v.plan.display_name}: ${usd(v.monthly_fee_usd)} a month${v.included_credits_usd > 0 ? ` (includes ${usd(v.included_credits_usd)} of usage)` : ""}${v.monthly_minimum_usd > 0 ? `, ${usd(v.monthly_minimum_usd)} monthly minimum` : ""}${v.plan.on_trial ? " · on the free trial" : ""}`);
|
|
140
|
+
}
|
|
141
|
+
return lines;
|
|
142
|
+
}
|
|
143
|
+
export function pricingJson(p) {
|
|
144
|
+
if (p.source === "api" && p.view)
|
|
145
|
+
return { source: "api", note: null, ...p.view };
|
|
146
|
+
return {
|
|
147
|
+
source: p.source === "published" ? "published" : "compiled",
|
|
148
|
+
note: p.note ?? COMPILED_NOTE,
|
|
149
|
+
...(p.reason === undefined ? {} : { reason: p.reason }),
|
|
150
|
+
schema: "promigence.pricing/v1",
|
|
151
|
+
currency: "usd",
|
|
152
|
+
scope: null,
|
|
153
|
+
org_id: null,
|
|
154
|
+
plan: null,
|
|
155
|
+
billing: "per_second",
|
|
156
|
+
granularity_seconds: p.granularity_seconds,
|
|
157
|
+
min_billable_seconds: PRICING.min_billable_seconds,
|
|
158
|
+
rates: {
|
|
159
|
+
vcpu_second_usd: p.vcpu_second_usd,
|
|
160
|
+
gib_second_usd: p.gib_second_usd,
|
|
161
|
+
preemptible_vcpu_second_usd: p.preemptible_vcpu_second_usd,
|
|
162
|
+
preemptible_gib_second_usd: p.preemptible_gib_second_usd,
|
|
163
|
+
build_usd: p.build_usd,
|
|
164
|
+
egress_gb_usd: null,
|
|
165
|
+
storage_gb_month_usd: null,
|
|
166
|
+
},
|
|
167
|
+
tiers: p.tiers,
|
|
168
|
+
limits: null,
|
|
169
|
+
free_credit: null,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const pricing = async (ctx) => {
|
|
173
|
+
const c = new PromigenceClient({ ...clientOpts(ctx), onNotice: recordNotice });
|
|
174
|
+
const p = await livePricing(c, { newAccount: bool(ctx, "--new-account"), strict: true });
|
|
175
|
+
const cardsOff = c.authenticated && p.view?.free_credit && p.view.free_credit.card_bonus_usd > 0
|
|
176
|
+
? await c.billing.usage().then((b) => b.billing_enabled === false).catch(() => false)
|
|
177
|
+
: false;
|
|
178
|
+
const next = nexts(["promigence run quote --snapshot promigence/base --count 1", "the most one run can cost, at these prices"], ["promigence me", "your limits, concurrency in use and spend"]);
|
|
179
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, pricingJson(p), next), pricingLines(p, { cardsOff }), 0, ctx.pointer);
|
|
180
|
+
};
|
|
181
|
+
export const PRICING_HANDLERS = { pricing };
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { forkLine, gradeLine, sandboxGrade, totalsLine } from "./io.js";
|
|
2
|
+
import { usd } from "./money.js";
|
|
3
|
+
import { sh, shellArg } from "./term.js";
|
|
4
|
+
const hasVerdict = (s) => s.graded === true && (s.passed !== undefined || s.holdout_passed !== undefined);
|
|
5
|
+
const live = (s) => s.state !== undefined && s.state !== "ended";
|
|
6
|
+
const needsAttention = (s) => Boolean(s.failure) || (!live(s) && s.outcome !== undefined && s.outcome !== "completed") || hasVerdict(s);
|
|
7
|
+
export function duration(msIn) {
|
|
8
|
+
if (msIn === undefined)
|
|
9
|
+
return "—";
|
|
10
|
+
const total = Math.round(msIn / 1000);
|
|
11
|
+
if (total < 1)
|
|
12
|
+
return `${Math.round(msIn)}ms`;
|
|
13
|
+
const h = Math.floor(total / 3600);
|
|
14
|
+
const m = Math.floor((total % 3600) / 60);
|
|
15
|
+
const sec = total % 60;
|
|
16
|
+
if (h === 0 && m === 0)
|
|
17
|
+
return `${sec}s`;
|
|
18
|
+
return `${h ? `${h}h` : ""}${m ? `${m}m` : ""}${sec ? `${sec}s` : ""}`;
|
|
19
|
+
}
|
|
20
|
+
export function selectRows(report, view) {
|
|
21
|
+
const all = (report.sandboxes ?? []);
|
|
22
|
+
const picked = view.sandboxes === "all" ? all : all.filter(needsAttention);
|
|
23
|
+
const rows = view.limit > 0 ? picked.slice(0, view.limit) : picked;
|
|
24
|
+
return { rows, hidden: picked.length - rows.length, total: all.length };
|
|
25
|
+
}
|
|
26
|
+
function gradingLines(report) {
|
|
27
|
+
const g = gradeLine(report.grade);
|
|
28
|
+
return g ? [` ${g}`] : [];
|
|
29
|
+
}
|
|
30
|
+
function costLine(report) {
|
|
31
|
+
const c = report.cost;
|
|
32
|
+
if (!c)
|
|
33
|
+
return [];
|
|
34
|
+
const committed = (report.sandboxes ?? [])
|
|
35
|
+
.reduce((n, s) => n + (s.committed_seconds ?? 0), 0);
|
|
36
|
+
const billed = c.billable_seconds;
|
|
37
|
+
const seconds = billed === undefined
|
|
38
|
+
? ""
|
|
39
|
+
: ` · billed ${billed} s${committed ? ` of ${committed} committed` : ""}`;
|
|
40
|
+
const cap = report.spend_cap === undefined ? "" : ` · cap ${usd(report.spend_cap)}`;
|
|
41
|
+
return [` cost: quoted ${usd(c.quoted)} · charged ${usd(c.charged)} · unbilled ${usd(c.unbilled)}${cap}${seconds}`];
|
|
42
|
+
}
|
|
43
|
+
export const GRADER_OUTPUT_LINES = 5;
|
|
44
|
+
export function graderOutputLines(s, at) {
|
|
45
|
+
const out = s.grade?.output;
|
|
46
|
+
if (out === undefined)
|
|
47
|
+
return [];
|
|
48
|
+
const code = s.failure?.code;
|
|
49
|
+
const failed = s.passed === false || s.outcome === "grader_failed" ||
|
|
50
|
+
code === "grade_not_passed" || code === "grader_failed";
|
|
51
|
+
if (!failed)
|
|
52
|
+
return [];
|
|
53
|
+
if (out === "")
|
|
54
|
+
return [`${at}grader output: (nothing printed)`];
|
|
55
|
+
const all = out.replace(/\n$/, "").split("\n").map((l) => (l.endsWith("\r") ? l.slice(0, -1) : l));
|
|
56
|
+
const shown = all.slice(-GRADER_OUTPUT_LINES);
|
|
57
|
+
const head = shown.length < all.length || s.grade?.output_truncated
|
|
58
|
+
? `grader output, last ${shown.length} line${shown.length === 1 ? "" : "s"} (--json for all):`
|
|
59
|
+
: "grader output:";
|
|
60
|
+
return [`${at}${head}`, ...shown.map((l) => `${at} ${l}`)];
|
|
61
|
+
}
|
|
62
|
+
export function sandboxLines(s, indent = " ") {
|
|
63
|
+
const f = s.failure;
|
|
64
|
+
const tag = f ? ` (${f.category ?? "?"}/${f.code ?? "?"})` : "";
|
|
65
|
+
const isLive = live(s);
|
|
66
|
+
const billed = s.billable_seconds === undefined || isLive
|
|
67
|
+
? ""
|
|
68
|
+
: ` · ${s.billable_seconds}s${s.committed_seconds ? `/${s.committed_seconds}s` : ""} ${usd(s.cost)}`;
|
|
69
|
+
const waiting = s.state === "queued";
|
|
70
|
+
const lines = [`${indent}${s.sandbox_id}: ${isLive ? s.state : (s.outcome ?? s.state ?? "?")}${tag}${billed}`];
|
|
71
|
+
const at = `${indent} `;
|
|
72
|
+
if (waiting && s.queued_message)
|
|
73
|
+
lines.push(`${at}queued: ${s.queued_message}`);
|
|
74
|
+
if (f?.message)
|
|
75
|
+
lines.push(`${at}${f.message}`);
|
|
76
|
+
if (f?.fix)
|
|
77
|
+
lines.push(`${at}fix: ${f.fix}`);
|
|
78
|
+
if (f?.docs)
|
|
79
|
+
lines.push(`${at}docs: ${f.docs}`);
|
|
80
|
+
if (hasVerdict(s))
|
|
81
|
+
lines.push(`${at}grade: ${sandboxGrade(s)}`);
|
|
82
|
+
lines.push(...graderOutputLines(s, at));
|
|
83
|
+
if (s.capture?.snapshot_id) {
|
|
84
|
+
lines.push(`${at}captured: ${s.capture.snapshot_id}${s.capture.expires_at ? ` (expires ${s.capture.expires_at})` : ""}`);
|
|
85
|
+
}
|
|
86
|
+
for (const w of s.warnings ?? [])
|
|
87
|
+
lines.push(`${at}warning: ${w}`);
|
|
88
|
+
return lines;
|
|
89
|
+
}
|
|
90
|
+
export function reproCommand(report, s) {
|
|
91
|
+
const snap = report.snapshot_id || report.alias || undefined;
|
|
92
|
+
const timeout = report.requested?.timeout_ms;
|
|
93
|
+
const cap = report.spend_cap ?? 1;
|
|
94
|
+
const argv = s.execs?.[s.execs.length - 1]?.argv ?? [];
|
|
95
|
+
const cmd = argv.length ? ` -- ${argv.map(shellArg).join(" ")}` : " -- CMD";
|
|
96
|
+
const env = [
|
|
97
|
+
...(report.requested?.network === "none" ? [" --network none"] : []),
|
|
98
|
+
...(report.requested?.secrets ?? []).map((n) => sh ` --secret ${n}`),
|
|
99
|
+
...Object.entries(report.labels ?? {}).map(([k, v]) => sh ` --label ${`${k}=${v}`}`),
|
|
100
|
+
].join("");
|
|
101
|
+
return (snap === undefined ? sh `promigence run --count 1 --cap ${cap}` : sh `promigence run --snapshot ${snap} --count 1 --cap ${cap}`) +
|
|
102
|
+
(timeout ? ` --timeout ${Math.round(timeout / 1000)}s` : "") + env + cmd;
|
|
103
|
+
}
|
|
104
|
+
export function reportLines(report, view) {
|
|
105
|
+
const { rows, hidden, total } = selectRows(report, view);
|
|
106
|
+
const lines = [
|
|
107
|
+
`run ${report.run_id}: ${report.state}${report.snapshot_id ? ` · snapshot ${report.snapshot_id}` : ""}`,
|
|
108
|
+
` ${forkLine(report.fork)}`,
|
|
109
|
+
` ${totalsLine(report.totals)}`,
|
|
110
|
+
...costLine(report),
|
|
111
|
+
...gradingLines(report),
|
|
112
|
+
];
|
|
113
|
+
if (report.requested) {
|
|
114
|
+
lines.push(` requested: ${report.requested.count} × ${duration(report.requested.timeout_ms)}${report.requested.verify_each ? " · verify-each" : ""}`);
|
|
115
|
+
}
|
|
116
|
+
for (const s of rows)
|
|
117
|
+
lines.push(...sandboxLines(s));
|
|
118
|
+
if (hidden > 0) {
|
|
119
|
+
lines.push(` … ${hidden} more (--limit 0 for all of them, --sandbox SID for one in full)`);
|
|
120
|
+
}
|
|
121
|
+
if (!rows.length) {
|
|
122
|
+
const all = (report.sandboxes ?? []);
|
|
123
|
+
const liveN = all.filter(live).length;
|
|
124
|
+
const plural = (n) => `${n} sandbox${n === 1 ? "" : "es"}`;
|
|
125
|
+
lines.push(view.sandboxes === "all"
|
|
126
|
+
? ` (no sandboxes)`
|
|
127
|
+
: liveN > 0
|
|
128
|
+
? ` ${plural(liveN)} still live${total - liveN ? `, ${total - liveN} ended` : ""}; none needs attention yet — --sandboxes all to list them`
|
|
129
|
+
: ` ${total === 1 ? "the 1 sandbox" : `all ${total} sandboxes`} completed — --sandboxes all to list them`);
|
|
130
|
+
}
|
|
131
|
+
return lines;
|
|
132
|
+
}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { COMMANDS } from "./surface.js";
|
|
2
|
+
export const ROUTE_COMMANDS = {
|
|
3
|
+
"GET /health": "doctor",
|
|
4
|
+
"GET /status": "status",
|
|
5
|
+
"GET /me": "me",
|
|
6
|
+
"GET /llms.txt": "agent-guide",
|
|
7
|
+
"GET /auth/config": "login",
|
|
8
|
+
"GET /auth/cli-callback": "login",
|
|
9
|
+
"GET /auth/claim": "credits claim",
|
|
10
|
+
"GET /credits/offer": "credits",
|
|
11
|
+
"POST /credits/claim-link": "credits claim",
|
|
12
|
+
"POST /credits/claim": "credits claim",
|
|
13
|
+
"POST /auth/session": "login",
|
|
14
|
+
"POST /auth/cli/requests": "login",
|
|
15
|
+
"POST /auth/cli/token": "login",
|
|
16
|
+
"POST /auth/invite": "login",
|
|
17
|
+
"POST /auth/invite/release": "login",
|
|
18
|
+
"GET /keys": "keys list",
|
|
19
|
+
"POST /keys": "keys create",
|
|
20
|
+
"DELETE /keys/:id": "keys revoke",
|
|
21
|
+
"GET /orgs": "org list",
|
|
22
|
+
"POST /orgs": "org create",
|
|
23
|
+
"POST /orgs/:id/default": "org switch",
|
|
24
|
+
"POST /orgs/:id/keys": "keys create",
|
|
25
|
+
"GET /orgs/:id/members": "org members",
|
|
26
|
+
"PATCH /orgs/:id/members/:id": "org role",
|
|
27
|
+
"DELETE /orgs/:id/members/:id": "org remove",
|
|
28
|
+
"GET /orgs/:id/invites": "org invites",
|
|
29
|
+
"POST /orgs/:id/invites": "org invite",
|
|
30
|
+
"DELETE /orgs/:id/invites/:id": "org uninvite",
|
|
31
|
+
"POST /orgs/:id/invites/:id/resend": "org resend",
|
|
32
|
+
"GET /invites": "org invites",
|
|
33
|
+
"POST /invites/:id/accept": "org join",
|
|
34
|
+
"POST /invites/:id/decline": "org join",
|
|
35
|
+
"GET /orgs/:id/projects": "project list",
|
|
36
|
+
"POST /orgs/:id/projects": "project create",
|
|
37
|
+
"PATCH /orgs/:id/projects/:id": "project rename",
|
|
38
|
+
"DELETE /orgs/:id/projects/:id": "project delete",
|
|
39
|
+
"POST /orgs/:id/projects/:id/move": "project move",
|
|
40
|
+
"PATCH /orgs/:id": "org rename",
|
|
41
|
+
"DELETE /orgs/:id": "org delete",
|
|
42
|
+
"GET /orgs/:id/audit": "org audit",
|
|
43
|
+
"GET /pricing": "pricing",
|
|
44
|
+
"GET /billing": "billing usage",
|
|
45
|
+
"GET /billing/history": "billing history",
|
|
46
|
+
"GET /me/ledger": "billing usage",
|
|
47
|
+
"GET /me/outcomes": "me",
|
|
48
|
+
"POST /billing/checkout": "billing card",
|
|
49
|
+
"POST /billing/portal": "billing portal",
|
|
50
|
+
"GET /referrals": "referrals",
|
|
51
|
+
"POST /referrals/apply": "referrals apply",
|
|
52
|
+
"POST /snapshots": "snapshot create",
|
|
53
|
+
"GET /snapshots": "snapshot list",
|
|
54
|
+
"GET /templates": "templates",
|
|
55
|
+
"GET /templates/:id": "templates show",
|
|
56
|
+
"GET /snapshots/builds": "snapshot builds",
|
|
57
|
+
"GET /snapshots/builds/:id": "snapshot status",
|
|
58
|
+
"GET /snapshots/builds/:id/build-log": "snapshot status",
|
|
59
|
+
"GET /snapshots/:id": "snapshot inspect",
|
|
60
|
+
"GET /snapshots/:id/manifest": "snapshot export",
|
|
61
|
+
"POST /snapshots/:id/verify": "snapshot verify",
|
|
62
|
+
"PUT /snapshots/:id/alias": "snapshot alias",
|
|
63
|
+
"DELETE /snapshots/:id": "snapshot delete",
|
|
64
|
+
"POST /integrations/github/connect": "github connect",
|
|
65
|
+
"GET /integrations/github": "github status",
|
|
66
|
+
"DELETE /integrations/github/installations/:id": "github disconnect",
|
|
67
|
+
"DELETE /integrations/github/tracks/:id": "github untrack",
|
|
68
|
+
"GET /integrations/github/repos": "repos",
|
|
69
|
+
"POST /integrations/github/requests": "github connect",
|
|
70
|
+
"GET /integrations/github/requests/:id": "github connect",
|
|
71
|
+
"POST /integrations/gitlab/connect": "gitlab connect",
|
|
72
|
+
"GET /integrations/gitlab": "gitlab status",
|
|
73
|
+
"DELETE /integrations/gitlab/connections/:id": "gitlab disconnect",
|
|
74
|
+
"DELETE /integrations/gitlab/tracks/:id": "gitlab untrack",
|
|
75
|
+
"GET /integrations/gitlab/repos": "repos",
|
|
76
|
+
"POST /integrations/gitlab/requests": "gitlab connect",
|
|
77
|
+
"GET /integrations/gitlab/requests/:id": "gitlab connect",
|
|
78
|
+
"POST /images/resolve": "image resolve",
|
|
79
|
+
"POST /runs": "run",
|
|
80
|
+
"POST /runs/quote": "run quote",
|
|
81
|
+
"GET /runs": "run list",
|
|
82
|
+
"GET /runs/:id": "run status",
|
|
83
|
+
"GET /runs/:id/sandboxes": "run status",
|
|
84
|
+
"GET /runs/:id/report": "run report",
|
|
85
|
+
"POST /runs/:id/cancel": "run cancel",
|
|
86
|
+
"POST /runs/:id/replay": "run replay",
|
|
87
|
+
"POST /runs/:id/open": "run open",
|
|
88
|
+
"GET /runs/:id/captures": "run captures",
|
|
89
|
+
"DELETE /captures/:id": "run captures",
|
|
90
|
+
"DELETE /runs/:id": "run kill",
|
|
91
|
+
"GET /sandboxes": "sandbox list",
|
|
92
|
+
"GET /sandboxes/:id": "sandbox info",
|
|
93
|
+
"PATCH /sandboxes/:id": "sandbox extend",
|
|
94
|
+
"DELETE /sandboxes/:id": "sandbox kill",
|
|
95
|
+
"GET /sandboxes/:id/network": "sandbox network",
|
|
96
|
+
"POST /sandboxes/:id/network": "sandbox network",
|
|
97
|
+
"POST /sandboxes/:id/pause": "sandbox pause",
|
|
98
|
+
"POST /sandboxes/:id/resume": "sandbox resume",
|
|
99
|
+
"POST /sandboxes/:id/snapshot": "sandbox snapshot",
|
|
100
|
+
"POST /sandboxes/:id/probe": "sandbox probe",
|
|
101
|
+
"POST /sandboxes/:id/execs": "exec",
|
|
102
|
+
"GET /execs/:id": "exec output",
|
|
103
|
+
"GET /sandboxes/:id/files": "files get",
|
|
104
|
+
"PUT /sandboxes/:id/files": "files put",
|
|
105
|
+
"DELETE /sandboxes/:id/files": "files rm",
|
|
106
|
+
"GET /sandboxes/:id/files/stat": "files get",
|
|
107
|
+
"GET /sandboxes/:id/files/list": "files ls",
|
|
108
|
+
"GET /sandboxes/:id/files/search": "files search",
|
|
109
|
+
"POST /sandboxes/:id/files/mkdir": "files mkdir",
|
|
110
|
+
"POST /sandboxes/:id/files/move": "files mv",
|
|
111
|
+
"POST /sandboxes/:id/files/copy": "files cp",
|
|
112
|
+
"POST /sandboxes/:id/files/chmod": "files chmod",
|
|
113
|
+
"POST /sandboxes/:id/files/archive": "files get",
|
|
114
|
+
"POST /sandboxes/:id/files/extract": "files put",
|
|
115
|
+
"GET /sandboxes/:id/pty": "sandbox shell",
|
|
116
|
+
"GET /sandboxes/:id/execs": "exec ps",
|
|
117
|
+
"GET /sandboxes/:id/execs/:id": "exec output",
|
|
118
|
+
"POST /sandboxes/:id/execs/:id/signal": "exec kill",
|
|
119
|
+
"POST /sandboxes/:id/execs/:id/stdin": "exec stdin",
|
|
120
|
+
"DELETE /sandboxes/:id/execs/:id": "exec kill",
|
|
121
|
+
"POST /sandboxes/:id/ssh": "sandbox ssh",
|
|
122
|
+
"GET /sandboxes/:id/ssh": "sandbox ssh",
|
|
123
|
+
"DELETE /sandboxes/:id/ssh/:id": "sandbox ssh",
|
|
124
|
+
"GET /sandboxes/:id/ssh/connect": "sandbox ssh",
|
|
125
|
+
"POST /sandboxes/:id/ports": "port expose",
|
|
126
|
+
"GET /sandboxes/:id/ports": "port list",
|
|
127
|
+
"DELETE /sandboxes/:id/ports/:id": "port revoke",
|
|
128
|
+
"POST /secrets": "secrets set",
|
|
129
|
+
"GET /secrets": "secrets list",
|
|
130
|
+
"DELETE /secrets/:id": "secrets delete",
|
|
131
|
+
};
|
|
132
|
+
export function unknownCommandsInMap() {
|
|
133
|
+
const names = new Set(COMMANDS.map((c) => c.name));
|
|
134
|
+
return [...new Set(Object.values(ROUTE_COMMANDS))].filter((n) => !names.has(n)).sort();
|
|
135
|
+
}
|