@appforge-ci/core 0.1.1
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/LICENSE +21 -0
- package/dist/agent-readiness.d.ts +163 -0
- package/dist/agent-readiness.d.ts.map +1 -0
- package/dist/agent-readiness.js +179 -0
- package/dist/agent-readiness.js.map +1 -0
- package/dist/agent-readiness.test.d.ts +2 -0
- package/dist/agent-readiness.test.d.ts.map +1 -0
- package/dist/agent-readiness.test.js +177 -0
- package/dist/agent-readiness.test.js.map +1 -0
- package/dist/api-client.d.ts +529 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +551 -0
- package/dist/api-client.js.map +1 -0
- package/dist/api-client.test.d.ts +2 -0
- package/dist/api-client.test.d.ts.map +1 -0
- package/dist/api-client.test.js +305 -0
- package/dist/api-client.test.js.map +1 -0
- package/dist/build-cost.test.d.ts +2 -0
- package/dist/build-cost.test.d.ts.map +1 -0
- package/dist/build-cost.test.js +21 -0
- package/dist/build-cost.test.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/ota-artifact.d.ts +41 -0
- package/dist/ota-artifact.d.ts.map +1 -0
- package/dist/ota-artifact.js +43 -0
- package/dist/ota-artifact.js.map +1 -0
- package/dist/ota-artifact.test.d.ts +2 -0
- package/dist/ota-artifact.test.d.ts.map +1 -0
- package/dist/ota-artifact.test.js +44 -0
- package/dist/ota-artifact.test.js.map +1 -0
- package/dist/ota-bundle-hash.d.ts +38 -0
- package/dist/ota-bundle-hash.d.ts.map +1 -0
- package/dist/ota-bundle-hash.js +40 -0
- package/dist/ota-bundle-hash.js.map +1 -0
- package/dist/ota-bundle-hash.test.d.ts +2 -0
- package/dist/ota-bundle-hash.test.d.ts.map +1 -0
- package/dist/ota-bundle-hash.test.js +28 -0
- package/dist/ota-bundle-hash.test.js.map +1 -0
- package/dist/ota-rollout.d.ts +38 -0
- package/dist/ota-rollout.d.ts.map +1 -0
- package/dist/ota-rollout.js +53 -0
- package/dist/ota-rollout.js.map +1 -0
- package/dist/ota-rollout.test.d.ts +2 -0
- package/dist/ota-rollout.test.d.ts.map +1 -0
- package/dist/ota-rollout.test.js +48 -0
- package/dist/ota-rollout.test.js.map +1 -0
- package/dist/schemas.d.ts +776 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +538 -0
- package/dist/schemas.js.map +1 -0
- package/dist/token-packs.test.d.ts +2 -0
- package/dist/token-packs.test.d.ts.map +1 -0
- package/dist/token-packs.test.js +34 -0
- package/dist/token-packs.test.js.map +1 -0
- package/dist/types.d.ts +445 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +80 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared domain types used by the API, dashboard, CLI, and agent.
|
|
3
|
+
* These mirror (but do not import from) the Prisma schema in packages/db,
|
|
4
|
+
* so packages that never need a DB connection don't need Prisma as a dep.
|
|
5
|
+
*/
|
|
6
|
+
export type Platform = "ios" | "android" | "flutter" | "react-native";
|
|
7
|
+
export type BuildStatus = "queued" | "leased" | "running" | "succeeded" | "failed" | "canceled";
|
|
8
|
+
export type AgentStatus = "online" | "offline" | "draining" | "building";
|
|
9
|
+
export interface User {
|
|
10
|
+
id: string;
|
|
11
|
+
email: string;
|
|
12
|
+
name: string | null;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
/** True for the platform operator, not a per-org role — fleet
|
|
15
|
+
* management (enrolling/draining/rebooting Mac minis) is an
|
|
16
|
+
* AppForge-the-service concern, the same way Codemagic customers never
|
|
17
|
+
* manage Codemagic's own build machines. Regular org
|
|
18
|
+
* owners/admins/members never see or touch fleet endpoints regardless
|
|
19
|
+
* of their org role. */
|
|
20
|
+
isPlatformAdmin: boolean;
|
|
21
|
+
/** Set once `POST /auth/verify-email` redeems the token from the
|
|
22
|
+
* signup verification email (`apps/api/src/lib/email-verification.ts`),
|
|
23
|
+
* `null` until then. Purely informational — nothing in this app gates
|
|
24
|
+
* on it (see that route's own docblock), so this is safe to ignore
|
|
25
|
+
* entirely; it exists so a dashboard page can show verification status
|
|
26
|
+
* and offer a resend. */
|
|
27
|
+
emailVerifiedAt: string | null;
|
|
28
|
+
/** TOTP 2FA status (apps/api/src/routes/two-factor.ts) — whether a
|
|
29
|
+
* confirmed authenticator is required at login, not just enrolled. */
|
|
30
|
+
twoFactorEnabled: boolean;
|
|
31
|
+
/** How many single-use recovery codes are still unconsumed — the only
|
|
32
|
+
* self-service fallback if the authenticator itself is lost, so
|
|
33
|
+
* running out silently is a real lockout risk. `null` whenever 2FA
|
|
34
|
+
* isn't enabled (there's nothing to count); always a fresh 10 right
|
|
35
|
+
* after enrollment or a regenerate, since both replace the whole set
|
|
36
|
+
* (see `packages/db`'s `replaceTotpRecoveryCodes`/
|
|
37
|
+
* `countUnusedTotpRecoveryCodes`). */
|
|
38
|
+
unusedRecoveryCodeCount: number | null;
|
|
39
|
+
}
|
|
40
|
+
export type BillingMode = "prepaid" | "postpaid_monthly";
|
|
41
|
+
export interface Org {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
slug: string;
|
|
45
|
+
/** Prepaid build tokens. Unused once billingMode is postpaid_monthly. See TOKEN_PACKS / BUILD_COST_TOKENS below. */
|
|
46
|
+
tokenBalance: number;
|
|
47
|
+
billingMode: BillingMode;
|
|
48
|
+
/** Opt-in outgoing webhook, POSTed on every terminal build report for
|
|
49
|
+
* any app in this org — see `apps/admin-api/src/lib/build-webhook.ts`.
|
|
50
|
+
* null means disabled (the default). */
|
|
51
|
+
buildWebhookUrl: string | null;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
}
|
|
54
|
+
/** LHV Paytech is the only postpaid payment provider on this instance —
|
|
55
|
+
* Stripe support was removed entirely (#516) rather than left unwired. */
|
|
56
|
+
export type PaymentProvider = "lhv";
|
|
57
|
+
export interface BillingUsage {
|
|
58
|
+
billingMode: BillingMode;
|
|
59
|
+
tokenBalance: number;
|
|
60
|
+
buildCostTokens: number;
|
|
61
|
+
/** Only meaningful when billingMode is "postpaid_monthly". */
|
|
62
|
+
accruedUnbilledTokens: number;
|
|
63
|
+
estimatedAmountCents: number;
|
|
64
|
+
monthlyInvoiceDay: number;
|
|
65
|
+
billingPastDue: boolean;
|
|
66
|
+
lastInvoicedAt: string | null;
|
|
67
|
+
/** True if LHV has a saved payment method — the only postpaid provider
|
|
68
|
+
* left after Stripe's removal (#516); currently always equal to
|
|
69
|
+
* `hasLhvCard`, kept as a separate field since it's what the
|
|
70
|
+
* dashboard/CLI actually gate "switch to monthly billing" on. */
|
|
71
|
+
hasPaymentMethod: boolean;
|
|
72
|
+
hasLhvCard: boolean;
|
|
73
|
+
lhvCardLastFour: string | null;
|
|
74
|
+
postpaidProvider: PaymentProvider | null;
|
|
75
|
+
}
|
|
76
|
+
export type MonthlyInvoiceStatus = "pending" | "paid" | "failed";
|
|
77
|
+
export interface MonthlyInvoice {
|
|
78
|
+
id: string;
|
|
79
|
+
orgId: string;
|
|
80
|
+
periodStart: string;
|
|
81
|
+
periodEnd: string;
|
|
82
|
+
tokens: number;
|
|
83
|
+
amountCents: number;
|
|
84
|
+
status: MonthlyInvoiceStatus;
|
|
85
|
+
provider: PaymentProvider;
|
|
86
|
+
createdAt: string;
|
|
87
|
+
paidAt: string | null;
|
|
88
|
+
}
|
|
89
|
+
export interface Membership {
|
|
90
|
+
userId: string;
|
|
91
|
+
orgId: string;
|
|
92
|
+
role: "owner" | "admin" | "member";
|
|
93
|
+
}
|
|
94
|
+
export interface App {
|
|
95
|
+
id: string;
|
|
96
|
+
orgId: string;
|
|
97
|
+
name: string;
|
|
98
|
+
platform: Platform;
|
|
99
|
+
repoUrl: string;
|
|
100
|
+
defaultBranch: string;
|
|
101
|
+
createdAt: string;
|
|
102
|
+
/** Whether an OTA app key has ever been generated for this app — not
|
|
103
|
+
* the key itself (that's never exposed once hashed), just enough to
|
|
104
|
+
* tell whether clicking "Generate/rotate" is a first-time mint or a
|
|
105
|
+
* destructive rotation that invalidates every build already
|
|
106
|
+
* embedding the old key. See `appforge ota app-key`. */
|
|
107
|
+
hasOtaAppKey: boolean;
|
|
108
|
+
}
|
|
109
|
+
/** Public shape of `GET /orgs/:orgId/tokens` — deliberately never
|
|
110
|
+
* includes the token value or its hash (see docs/security.md); once
|
|
111
|
+
* minted, the raw token is only ever shown once, at creation time. */
|
|
112
|
+
export interface ApiTokenInfo {
|
|
113
|
+
id: string;
|
|
114
|
+
label: string;
|
|
115
|
+
createdAt: string;
|
|
116
|
+
lastUsedAt: string | null;
|
|
117
|
+
}
|
|
118
|
+
export interface Build {
|
|
119
|
+
id: string;
|
|
120
|
+
appId: string;
|
|
121
|
+
orgId: string;
|
|
122
|
+
status: BuildStatus;
|
|
123
|
+
/** The `appforge.yaml` workflow name this build ran (or was queued to
|
|
124
|
+
* run) — `"default"` unless a caller passed `--workflow`/`workflow:`
|
|
125
|
+
* explicitly. Was silently dropped by `serializeBuild`
|
|
126
|
+
* (`apps/api/src/routes/builds.ts`) despite being a real, always-
|
|
127
|
+
* present column (`BuildRow.workflow`, `packages/db/src/types.ts`)
|
|
128
|
+
* set at trigger time — this repo's own "no client can ever see
|
|
129
|
+
* it" bug shape (see `defaultBranch`'s and `triggeredBy`'s own
|
|
130
|
+
* fix history in ROADMAP.md). */
|
|
131
|
+
workflow: string;
|
|
132
|
+
branch: string;
|
|
133
|
+
commitSha: string | null;
|
|
134
|
+
triggeredBy: "manual" | "webhook" | "cli";
|
|
135
|
+
/** Only meaningful when status is "failed" — see BuildResult.
|
|
136
|
+
* failureKind's docblock (packages/agent/src/lib/builder.ts) and
|
|
137
|
+
* docs/billing.md for what "infra" means and that it drives an
|
|
138
|
+
* automatic refund. Null for a succeeded/canceled build, or a failed
|
|
139
|
+
* one reported by an agent that predates this field. */
|
|
140
|
+
failureKind: "user" | "infra" | null;
|
|
141
|
+
agentId: string | null;
|
|
142
|
+
costTokens: number;
|
|
143
|
+
queuedAt: string;
|
|
144
|
+
startedAt: string | null;
|
|
145
|
+
finishedAt: string | null;
|
|
146
|
+
artifacts: BuildArtifact[];
|
|
147
|
+
/** One entry per smoke-test screenshot this build produced (up to 3 on
|
|
148
|
+
* Android's fixed-walk v1, at most 1 on iOS today — see
|
|
149
|
+
* packages/agent/src/lib/android-smoke-test.ts/ios-smoke-test.ts),
|
|
150
|
+
* index-ordered. Empty when `smokeTest: true` wasn't set, or the
|
|
151
|
+
* smoke test itself couldn't run (no device/Simulator attached). */
|
|
152
|
+
smokeTestResults: SmokeTestScreenshotResult[];
|
|
153
|
+
}
|
|
154
|
+
/** Per-screenshot smoke-test state — see `Build.smokeTestResults`. */
|
|
155
|
+
export interface SmokeTestScreenshotResult {
|
|
156
|
+
screenshotIndex: number;
|
|
157
|
+
fileName: string;
|
|
158
|
+
downloadUrl: string;
|
|
159
|
+
/** Set once automatic analysis (triggered by `GET /builds/:id` — see
|
|
160
|
+
* apps/api/src/routes/builds.ts) or a manual "Analyze smoke test"
|
|
161
|
+
* click succeeds for this screenshot. Null until then. */
|
|
162
|
+
verdict: SmokeTestVerdict | null;
|
|
163
|
+
/** True while automatic analysis of this screenshot is claimed but not
|
|
164
|
+
* yet resolved either way — the dashboard polls a bit longer while
|
|
165
|
+
* any screenshot is still analyzing instead of stopping the moment
|
|
166
|
+
* the build itself reaches a terminal status. */
|
|
167
|
+
analyzing: boolean;
|
|
168
|
+
/** Set if automatic analysis of this screenshot was attempted and
|
|
169
|
+
* failed (e.g. `ANTHROPIC_API_KEY` isn't configured on this
|
|
170
|
+
* deployment) — distinct from `verdict` being null because nothing's
|
|
171
|
+
* run yet. */
|
|
172
|
+
error: string | null;
|
|
173
|
+
}
|
|
174
|
+
export interface BuildArtifact {
|
|
175
|
+
id: string;
|
|
176
|
+
buildId: string;
|
|
177
|
+
fileName: string;
|
|
178
|
+
sizeBytes: number;
|
|
179
|
+
downloadUrl: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* AI triage of a failed build's log — see apps/api/src/lib/ai-triage.ts.
|
|
183
|
+
* Best-effort: `confidence: "low"` (or the whole feature 501ing when
|
|
184
|
+
* ANTHROPIC_API_KEY isn't configured) is expected, not a bug — this is a
|
|
185
|
+
* starting point for debugging, not a guaranteed diagnosis.
|
|
186
|
+
*/
|
|
187
|
+
export interface BuildFailureExplanation {
|
|
188
|
+
summary: string;
|
|
189
|
+
likelyCause: string;
|
|
190
|
+
suggestedFix: string;
|
|
191
|
+
confidence: "low" | "medium" | "high";
|
|
192
|
+
}
|
|
193
|
+
export interface AgentInfo {
|
|
194
|
+
id: string;
|
|
195
|
+
orgId: string;
|
|
196
|
+
name: string;
|
|
197
|
+
status: AgentStatus;
|
|
198
|
+
/** The Mac's hardware UUID — stable per machine. Null for agents
|
|
199
|
+
* enrolled before this field existed (until they next re-enroll) and
|
|
200
|
+
* for non-Mac hosts. See @appforge-ci/db's upsertAgent. */
|
|
201
|
+
machineId: string | null;
|
|
202
|
+
hostname: string;
|
|
203
|
+
macOsVersion: string;
|
|
204
|
+
xcodeVersion: string | null;
|
|
205
|
+
flutterVersion: string | null;
|
|
206
|
+
javaVersion: string | null;
|
|
207
|
+
androidSdkVersion: string | null;
|
|
208
|
+
cocoapodsVersion: string | null;
|
|
209
|
+
cpuModel: string;
|
|
210
|
+
/** This Mac's own packages/agent daemon version, self-reported at
|
|
211
|
+
* enroll/heartbeat. Null until it (re-)enrolls or heartbeats on a
|
|
212
|
+
* build that sends it — see isAgentVersionCurrent in
|
|
213
|
+
* ./agent-readiness.js, the fleet-wide "is this row current" check. */
|
|
214
|
+
agentVersion: string | null;
|
|
215
|
+
/** Whether macOS Screen Sharing (the built-in VNC server) is enabled on
|
|
216
|
+
* this Mac, self-reported at enroll/heartbeat — see
|
|
217
|
+
* @appforge-ci/agent's system-info.ts. Null until it (re-)enrolls or
|
|
218
|
+
* heartbeats on a build that sends it. 0/1/null, not a real boolean —
|
|
219
|
+
* this is passed straight through from the D1 row (see
|
|
220
|
+
* apps/admin-api/src/routes/fleet.ts), not re-serialized. */
|
|
221
|
+
screenSharingEnabled: number | null;
|
|
222
|
+
/** Whether a platform admin has authorized forwarding mouse/keyboard
|
|
223
|
+
* input into this Mac's VNC session, not just watching it — see
|
|
224
|
+
* @appforge/db's migrations/0024_agent_remote_control.sql. Unlike
|
|
225
|
+
* screenSharingEnabled just above, this is never self-reported by the
|
|
226
|
+
* agent; it only changes via `PATCH /orgs/:orgId/agents/:agentId/
|
|
227
|
+
* remote-control` (apps/admin-api) or `appforge-admin agents
|
|
228
|
+
* allow-remote-control`. 0/1/null, passed straight through from the
|
|
229
|
+
* D1 row same as screenSharingEnabled — but null and 0 both mean "not
|
|
230
|
+
* granted" (fail closed), the opposite of screenSharingEnabled's
|
|
231
|
+
* permissive-on-null convention, since this gates actual control of
|
|
232
|
+
* hardware, not just a read. */
|
|
233
|
+
remoteControlEnabled: number | null;
|
|
234
|
+
cpuUsagePercent: number;
|
|
235
|
+
memUsagePercent: number;
|
|
236
|
+
diskFreeGb: number;
|
|
237
|
+
tailscaleHostname: string | null;
|
|
238
|
+
currentBuildId: string | null;
|
|
239
|
+
/** How many builds this Mac will run at once, self-reported at every
|
|
240
|
+
* heartbeat. 1 for a row an as-yet-unupgraded agent binary has never
|
|
241
|
+
* reported this on. See migrations/0017_agent_concurrency.sql. */
|
|
242
|
+
maxConcurrentBuilds: number;
|
|
243
|
+
/** JSON array of build ids this agent is running right now (e.g.
|
|
244
|
+
* '["bld_a","bld_b"]'), '[]' when idle — passed straight through from
|
|
245
|
+
* the D1 row (see apps/admin-api/src/routes/fleet.ts), not
|
|
246
|
+
* re-serialized, same as screenSharingEnabled just above. */
|
|
247
|
+
activeBuildIds: string;
|
|
248
|
+
lastSeenAt: string;
|
|
249
|
+
}
|
|
250
|
+
export type AgentCommandType = "DRAIN" | "RESUME" | "REBOOT_HOST" | "RESTART_AGENT" | "RUN_MAINTENANCE_SCRIPT" | "UPDATE_AGENT" | "CANCEL_BUILD";
|
|
251
|
+
export interface AgentCommand {
|
|
252
|
+
id: string;
|
|
253
|
+
agentId: string;
|
|
254
|
+
type: AgentCommandType;
|
|
255
|
+
/** Only used for RUN_MAINTENANCE_SCRIPT — must match a name in the agent's allowlist. */
|
|
256
|
+
scriptName?: string;
|
|
257
|
+
/** Only used for CANCEL_BUILD — which of this agent's (possibly several
|
|
258
|
+
* concurrent) builds to kill. See migrations/0018_cancel_build_command.sql. */
|
|
259
|
+
buildId?: string;
|
|
260
|
+
/** Only used for UPDATE_AGENT — an npm version (e.g. "0.2.0") or, for a
|
|
261
|
+
* repo-checkout install, a git ref (tag/sha) to check out. Undefined
|
|
262
|
+
* means "whatever the agent's own self-update step treats as latest"
|
|
263
|
+
* (npm dist-tag `latest` / the checkout's default branch) — see
|
|
264
|
+
* packages/agent/src/lib/self-update.ts. */
|
|
265
|
+
targetVersion?: string;
|
|
266
|
+
/** Only meaningful for UPDATE_AGENT — 1 when the fleet's own OTA
|
|
267
|
+
* Phase 4 auto-rollback issued this command after detecting a stuck
|
|
268
|
+
* self-update, distinct from one a human or Phase 3's ordinary
|
|
269
|
+
* auto-update policy issued. Null/0/undefined all mean "not an
|
|
270
|
+
* auto-rollback" — same nullable-number-as-boolean convention as
|
|
271
|
+
* `AgentInfo.remoteControlEnabled` above; see @appforge/db's
|
|
272
|
+
* AgentCommandRow.isAutoRollback for the full story. */
|
|
273
|
+
isAutoRollback?: number | null;
|
|
274
|
+
status: "pending" | "acked" | "done" | "failed";
|
|
275
|
+
createdAt: string;
|
|
276
|
+
result?: string;
|
|
277
|
+
/** The admin who issued this command — `GET /agents/:agentId/commands`
|
|
278
|
+
* (`packages/db/src/agents.ts`'s `listAgentCommands`) always joins it
|
|
279
|
+
* in, but this field was missing from this type even though the
|
|
280
|
+
* route/DB layer already returned it: `null` only for a command
|
|
281
|
+
* issued before this was tracked, or a since-deleted account — same
|
|
282
|
+
* convention as `VncSession.issuedByEmail` above. */
|
|
283
|
+
issuedByEmail?: string | null;
|
|
284
|
+
}
|
|
285
|
+
/** One VNC "Connect" session request — the audit trail's read side (see
|
|
286
|
+
* `apps/admin-api`'s `GET /orgs/:orgId/agents/:agentId/vnc-sessions`).
|
|
287
|
+
* `issuedByEmail` is null only for a since-deleted account. */
|
|
288
|
+
export interface VncSession {
|
|
289
|
+
id: string;
|
|
290
|
+
agentId: string;
|
|
291
|
+
issuedByEmail: string | null;
|
|
292
|
+
/** "view" (screen visibility only) or "control" (also forwards
|
|
293
|
+
* mouse/keyboard input) — see @appforge/db's
|
|
294
|
+
* migrations/0025_vnc_session_mode.sql. */
|
|
295
|
+
mode: "view" | "control";
|
|
296
|
+
/** Whether this request actually succeeded — 0 for a "control"
|
|
297
|
+
* request refused because the agent's `remoteControlEnabled` flag
|
|
298
|
+
* wasn't on (still recorded, not silently dropped — see
|
|
299
|
+
* @appforge/db's migrations/0027_vnc_session_audit_granted.sql). */
|
|
300
|
+
granted: number;
|
|
301
|
+
createdAt: string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* appforge.yaml — the build config a repo owner commits alongside their app.
|
|
305
|
+
* Intentionally similar in shape to codemagic.yaml / GitHub Actions so it's
|
|
306
|
+
* easy to port an existing pipeline.
|
|
307
|
+
*/
|
|
308
|
+
export interface AppforgeConfig {
|
|
309
|
+
platform: Platform;
|
|
310
|
+
workflows: Record<string, AppforgeWorkflow>;
|
|
311
|
+
}
|
|
312
|
+
export interface AppforgeWorkflow {
|
|
313
|
+
name?: string;
|
|
314
|
+
environment?: {
|
|
315
|
+
node?: string;
|
|
316
|
+
flutter?: string;
|
|
317
|
+
xcode?: string;
|
|
318
|
+
vars?: Record<string, string>;
|
|
319
|
+
};
|
|
320
|
+
triggers?: {
|
|
321
|
+
push?: {
|
|
322
|
+
branch: string[];
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
scripts: {
|
|
326
|
+
name?: string;
|
|
327
|
+
script: string;
|
|
328
|
+
}[];
|
|
329
|
+
artifacts?: string[];
|
|
330
|
+
/** Opt-in post-build smoke test (#9 v1) — off by default. */
|
|
331
|
+
smokeTest?: boolean;
|
|
332
|
+
/** Opt-in OTA (over-the-air) code-push update publishing. Off by default. */
|
|
333
|
+
ota?: {
|
|
334
|
+
channel?: string;
|
|
335
|
+
mandatory?: boolean;
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
export interface OtaRelease {
|
|
339
|
+
id: string;
|
|
340
|
+
appId: string;
|
|
341
|
+
channel: string;
|
|
342
|
+
platform: Platform;
|
|
343
|
+
buildId: string | null;
|
|
344
|
+
bundleHash: string;
|
|
345
|
+
r2Key: string;
|
|
346
|
+
mandatory: boolean;
|
|
347
|
+
rollout: number;
|
|
348
|
+
createdAt: string;
|
|
349
|
+
bundleUrl?: string;
|
|
350
|
+
}
|
|
351
|
+
export interface OtaChannel {
|
|
352
|
+
channel: string;
|
|
353
|
+
activeReleaseId: string | null;
|
|
354
|
+
activeRelease: OtaRelease | null;
|
|
355
|
+
updatedAt: string;
|
|
356
|
+
}
|
|
357
|
+
export interface OtaManifest {
|
|
358
|
+
releaseId: string;
|
|
359
|
+
appId: string;
|
|
360
|
+
channel: string;
|
|
361
|
+
platform: Platform;
|
|
362
|
+
bundleUrl: string;
|
|
363
|
+
bundleHash: string;
|
|
364
|
+
mandatory: boolean;
|
|
365
|
+
rollout: number;
|
|
366
|
+
createdAt: string;
|
|
367
|
+
}
|
|
368
|
+
/** Verdict from analyzing a build's smoke-test screenshot (#9) — see
|
|
369
|
+
* apps/api/src/lib/smoke-test-vision.ts. Best-effort, same "confidence"
|
|
370
|
+
* honesty as BuildFailureExplanation: a vision model looking at one
|
|
371
|
+
* screenshot can't be certain, so it isn't asked to pretend to be. */
|
|
372
|
+
export interface SmokeTestVerdict {
|
|
373
|
+
looksOk: boolean;
|
|
374
|
+
explanation: string;
|
|
375
|
+
}
|
|
376
|
+
export type LedgerReason = "topup" | "build_charge" | "refund" | "adjustment" | "invoice";
|
|
377
|
+
export interface LedgerEntry {
|
|
378
|
+
id: string;
|
|
379
|
+
orgId: string;
|
|
380
|
+
buildId: string | null;
|
|
381
|
+
deltaTokens: number;
|
|
382
|
+
reason: LedgerReason;
|
|
383
|
+
note: string | null;
|
|
384
|
+
createdAt: string;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Billing runs on tokens, not direct EUR-per-build, specifically so a
|
|
388
|
+
* bigger top-up can be cheaper per token without ever changing what a
|
|
389
|
+
* build costs. `BUILD_COST_TOKENS` is the default/baseline cost — same
|
|
390
|
+
* as every platform's actual cost today (see `BUILD_COST_TOKENS_BY_PLATFORM`
|
|
391
|
+
* below), so this PR ships the *mechanism* for per-platform pricing
|
|
392
|
+
* without changing anyone's bill; a maintainer who actually wants e.g. a
|
|
393
|
+
* bigger/faster iOS build machine to cost more tokens than a default
|
|
394
|
+
* Android one (the way Codemagic prices different machine types
|
|
395
|
+
* differently per minute) edits the map below, the same one-constant
|
|
396
|
+
* edit `TOKEN_PACKS` already is — no further code change needed.
|
|
397
|
+
*/
|
|
398
|
+
export declare const BUILD_COST_TOKENS = 100;
|
|
399
|
+
/** Every platform costs `BUILD_COST_TOKENS` today — see the note above.
|
|
400
|
+
* Kept as an explicit per-platform map (not a single constant) so that
|
|
401
|
+
* changing one platform's price is a one-line edit here, not a search
|
|
402
|
+
* for every `BUILD_COST_TOKENS` call site. */
|
|
403
|
+
export declare const BUILD_COST_TOKENS_BY_PLATFORM: Record<Platform, number>;
|
|
404
|
+
/**
|
|
405
|
+
* What a build on this platform actually costs. Takes the DB's storage
|
|
406
|
+
* format directly (`react_native`, underscore — matches the SQL CHECK
|
|
407
|
+
* constraint on `App.platform`) rather than making every caller convert
|
|
408
|
+
* to this module's hyphenated `Platform` type first; falls back to the
|
|
409
|
+
* flat default for anything unrecognized rather than throwing, since a
|
|
410
|
+
* charge must never hard-fail over a pricing lookup.
|
|
411
|
+
*/
|
|
412
|
+
export declare function getBuildCostTokens(dbPlatform: string): number;
|
|
413
|
+
export interface TokenPack {
|
|
414
|
+
id: string;
|
|
415
|
+
label: string;
|
|
416
|
+
tokens: number;
|
|
417
|
+
priceCents: number;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Fixed volume-discount packs, in ascending size. `BUILD_COST_TOKENS`
|
|
421
|
+
* tokens = 1 build regardless of which pack they came from, so the
|
|
422
|
+
* discount is purely "pay less per token by buying a bigger pack" —
|
|
423
|
+
* never a cheaper build.
|
|
424
|
+
*/
|
|
425
|
+
export declare const TOKEN_PACKS: TokenPack[];
|
|
426
|
+
/**
|
|
427
|
+
* The smallest pack's rate, used as: (a) the baseline every other pack's discount
|
|
428
|
+
* is computed against, and (b) the postpaid-monthly invoicing rate — monthly
|
|
429
|
+
* billing has no volume discount (same as Codemagic's pay-as-you-go minutes,
|
|
430
|
+
* which are priced flat per machine-type regardless of usage volume), so it
|
|
431
|
+
* always bills at list price.
|
|
432
|
+
*/
|
|
433
|
+
export declare const LIST_PRICE_PER_TOKEN_CENTS: number;
|
|
434
|
+
export declare function tokenPackDisplay(pack: TokenPack): {
|
|
435
|
+
priceEur: number;
|
|
436
|
+
pricePerTokenCents: number;
|
|
437
|
+
buildsIncluded: number;
|
|
438
|
+
discountPercent: number;
|
|
439
|
+
id: string;
|
|
440
|
+
label: string;
|
|
441
|
+
tokens: number;
|
|
442
|
+
priceCents: number;
|
|
443
|
+
};
|
|
444
|
+
export declare function findTokenPack(packId: string): TokenPack | undefined;
|
|
445
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared domain types used by the API, dashboard, CLI, and agent.
|
|
3
|
+
* These mirror (but do not import from) the Prisma schema in packages/db,
|
|
4
|
+
* so packages that never need a DB connection don't need Prisma as a dep.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Billing runs on tokens, not direct EUR-per-build, specifically so a
|
|
8
|
+
* bigger top-up can be cheaper per token without ever changing what a
|
|
9
|
+
* build costs. `BUILD_COST_TOKENS` is the default/baseline cost — same
|
|
10
|
+
* as every platform's actual cost today (see `BUILD_COST_TOKENS_BY_PLATFORM`
|
|
11
|
+
* below), so this PR ships the *mechanism* for per-platform pricing
|
|
12
|
+
* without changing anyone's bill; a maintainer who actually wants e.g. a
|
|
13
|
+
* bigger/faster iOS build machine to cost more tokens than a default
|
|
14
|
+
* Android one (the way Codemagic prices different machine types
|
|
15
|
+
* differently per minute) edits the map below, the same one-constant
|
|
16
|
+
* edit `TOKEN_PACKS` already is — no further code change needed.
|
|
17
|
+
*/
|
|
18
|
+
export const BUILD_COST_TOKENS = 100;
|
|
19
|
+
/** Every platform costs `BUILD_COST_TOKENS` today — see the note above.
|
|
20
|
+
* Kept as an explicit per-platform map (not a single constant) so that
|
|
21
|
+
* changing one platform's price is a one-line edit here, not a search
|
|
22
|
+
* for every `BUILD_COST_TOKENS` call site. */
|
|
23
|
+
export const BUILD_COST_TOKENS_BY_PLATFORM = {
|
|
24
|
+
ios: BUILD_COST_TOKENS,
|
|
25
|
+
android: BUILD_COST_TOKENS,
|
|
26
|
+
flutter: BUILD_COST_TOKENS,
|
|
27
|
+
"react-native": BUILD_COST_TOKENS,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* What a build on this platform actually costs. Takes the DB's storage
|
|
31
|
+
* format directly (`react_native`, underscore — matches the SQL CHECK
|
|
32
|
+
* constraint on `App.platform`) rather than making every caller convert
|
|
33
|
+
* to this module's hyphenated `Platform` type first; falls back to the
|
|
34
|
+
* flat default for anything unrecognized rather than throwing, since a
|
|
35
|
+
* charge must never hard-fail over a pricing lookup.
|
|
36
|
+
*/
|
|
37
|
+
export function getBuildCostTokens(dbPlatform) {
|
|
38
|
+
const platform = dbPlatform.replace("_", "-");
|
|
39
|
+
return BUILD_COST_TOKENS_BY_PLATFORM[platform] ?? BUILD_COST_TOKENS;
|
|
40
|
+
}
|
|
41
|
+
function pricePerTokenCents(pack) {
|
|
42
|
+
return pack.priceCents / pack.tokens;
|
|
43
|
+
}
|
|
44
|
+
/** List price is the smallest pack's per-token rate — every larger pack is defined as a discount off it. */
|
|
45
|
+
function discountPercent(pack, listPricePerToken) {
|
|
46
|
+
return Math.round((1 - pricePerTokenCents(pack) / listPricePerToken) * 100);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fixed volume-discount packs, in ascending size. `BUILD_COST_TOKENS`
|
|
50
|
+
* tokens = 1 build regardless of which pack they came from, so the
|
|
51
|
+
* discount is purely "pay less per token by buying a bigger pack" —
|
|
52
|
+
* never a cheaper build.
|
|
53
|
+
*/
|
|
54
|
+
export const TOKEN_PACKS = [
|
|
55
|
+
{ id: "starter", label: "Starter", tokens: 1_000, priceCents: 1_000 }, // €10.00 — 10 builds, list price
|
|
56
|
+
{ id: "growth", label: "Growth", tokens: 6_000, priceCents: 5_400 }, // €54.00 — 60 builds, 10% off
|
|
57
|
+
{ id: "scale", label: "Scale", tokens: 30_000, priceCents: 24_000 }, // €240.00 — 300 builds, 20% off
|
|
58
|
+
{ id: "studio", label: "Studio", tokens: 120_000, priceCents: 84_000 }, // €840.00 — 1200 builds, 30% off
|
|
59
|
+
];
|
|
60
|
+
/**
|
|
61
|
+
* The smallest pack's rate, used as: (a) the baseline every other pack's discount
|
|
62
|
+
* is computed against, and (b) the postpaid-monthly invoicing rate — monthly
|
|
63
|
+
* billing has no volume discount (same as Codemagic's pay-as-you-go minutes,
|
|
64
|
+
* which are priced flat per machine-type regardless of usage volume), so it
|
|
65
|
+
* always bills at list price.
|
|
66
|
+
*/
|
|
67
|
+
export const LIST_PRICE_PER_TOKEN_CENTS = pricePerTokenCents(TOKEN_PACKS[0]);
|
|
68
|
+
export function tokenPackDisplay(pack) {
|
|
69
|
+
return {
|
|
70
|
+
...pack,
|
|
71
|
+
priceEur: pack.priceCents / 100,
|
|
72
|
+
pricePerTokenCents: pricePerTokenCents(pack),
|
|
73
|
+
buildsIncluded: Math.floor(pack.tokens / BUILD_COST_TOKENS),
|
|
74
|
+
discountPercent: discountPercent(pack, LIST_PRICE_PER_TOKEN_CENTS),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function findTokenPack(packId) {
|
|
78
|
+
return TOKEN_PACKS.find((p) => p.id === packId);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0aH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC;;;+CAG+C;AAC/C,MAAM,CAAC,MAAM,6BAA6B,GAA6B;IACrE,GAAG,EAAE,iBAAiB;IACtB,OAAO,EAAE,iBAAiB;IAC1B,OAAO,EAAE,iBAAiB;IAC1B,cAAc,EAAE,iBAAiB;CAClC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAa,CAAC;IAC1D,OAAO,6BAA6B,CAAC,QAAQ,CAAC,IAAI,iBAAiB,CAAC;AACtE,CAAC;AASD,SAAS,kBAAkB,CAAC,IAAe;IACzC,OAAO,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;AACvC,CAAC;AAED,4GAA4G;AAC5G,SAAS,eAAe,CAAC,IAAe,EAAE,iBAAyB;IACjE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,GAAG,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,iCAAiC;IACxG,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,8BAA8B;IACnG,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,gCAAgC;IACrG,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,iCAAiC;CAC1G,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC;AAE9E,MAAM,UAAU,gBAAgB,CAAC,IAAe;IAC9C,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,EAAE,IAAI,CAAC,UAAU,GAAG,GAAG;QAC/B,kBAAkB,EAAE,kBAAkB,CAAC,IAAI,CAAC;QAC5C,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC;QAC3D,eAAe,EAAE,eAAe,CAAC,IAAI,EAAE,0BAA0B,CAAC;KACnE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;AAClD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@appforge-ci/core",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"zod": "^3.23.8"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.16.5",
|
|
25
|
+
"tsx": "^4.23.13",
|
|
26
|
+
"typescript": "^5.6.3"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
|
+
"lint": "echo 'no lint configured for core'",
|
|
33
|
+
"test": "tsx --import ../../scripts/test-home-sandbox.mjs --test $(find src -name '*.test.ts') --concurrency=4"
|
|
34
|
+
}
|
|
35
|
+
}
|