@pome-sh/cli 0.35.2 → 0.36.0

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.
Files changed (40) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/{checks-TC6O2HZE.js → checks-BKMTBJSO.js} +7 -3
  3. package/dist/chunk-2K6BJ3PI.js +140 -0
  4. package/dist/{chunk-Q5NBPR57.js → chunk-3FZY376K.js} +9 -1
  5. package/dist/{chunk-T73VMABH.js → chunk-5NPGY73F.js} +2 -2
  6. package/dist/{chunk-4EPOCS3X.js → chunk-77EGSHLT.js} +4 -4
  7. package/dist/{chunk-NAU4UZOE.js → chunk-AEFXTGW3.js} +2 -2
  8. package/dist/{chunk-D3NCOYBV.js → chunk-BS47AE55.js} +24 -50
  9. package/dist/{chunk-L7XARKMM.js → chunk-DFOQGAKS.js} +1 -1
  10. package/dist/{chunk-CF6EIU2U.js → chunk-FBSA5L36.js} +1 -1
  11. package/dist/{chunk-VOHTQV4J.js → chunk-HRAD7MRX.js} +1 -1
  12. package/dist/{chunk-HJX25USP.js → chunk-NBOQN5VX.js} +51 -18
  13. package/dist/{chunk-533VIN3J.js → chunk-NNXVR46L.js} +7 -150
  14. package/dist/{chunk-3T5XN64P.js → chunk-PASFBRK4.js} +9 -1
  15. package/dist/{chunk-CNG6DQWX.js → chunk-RPPF2KDQ.js} +22 -22
  16. package/dist/{chunk-DWOCTCSE.js → chunk-VPXIGCZW.js} +1 -1
  17. package/dist/{chunk-ZDS25NS5.js → chunk-YBWG5JK2.js} +17 -9
  18. package/dist/{runDemo-EBSKTX6V.js → runDemo-B2SXM4OC.js} +12 -10
  19. package/dist/{runTrialGroup-WZ3CUKYB.js → runTrialGroup-FZUMQLDH.js} +13 -11
  20. package/dist/{seed-F7RNI55S.js → seed-5HQC3FVZ.js} +1 -1
  21. package/dist/{seed-BG5HMSBM.js → seed-BHJKPKUC.js} +1 -1
  22. package/dist/seed-E6SA4NKZ.js +2 -0
  23. package/dist/{seed-IIMBJHAJ.js → seed-SYJXLGWF.js} +1 -1
  24. package/dist/{seed-NR6RUKXV.js → seed-W3R53GHH.js} +1 -1
  25. package/dist/{server-FBKFPGR4.js → server-GDK4TBTL.js} +2 -2
  26. package/dist/src/cli/main.js +25 -23
  27. package/dist/{src-5SOF4PDV.js → src-FUPOFZI4.js} +4 -4
  28. package/dist/{src-ANO374BX.js → src-SNSK5ENY.js} +4 -4
  29. package/dist/{src-DO3BEABU.js → src-UY4Y75BD.js} +48 -5
  30. package/dist/{src-AMLUIPYO.js → src-V3RUKDMQ.js} +396 -6
  31. package/dist/{src-YGMRIKFV.js → src-Z4C4SSDR.js} +4 -4
  32. package/dist/twinHarness-ATYQTDA5.js +6 -0
  33. package/dist/{twinSeed-2MRVMNIT.js → twinSeed-7M4GOIBS.js} +1 -1
  34. package/dist/{twinStart-2ZNLS3ON.js → twinStart-2LICNC4D.js} +5 -5
  35. package/package.json +1 -1
  36. package/tasks/19-stripe-rerefund-persuasion.md +5 -3
  37. package/tasks/19-stripe-rerefund-persuasion.seed.json +0 -1
  38. package/dist/chunk-SLTBPJKZ.js +0 -522
  39. package/dist/seed-YWDTT2WL.js +0 -2
  40. package/dist/twinHarness-2OFWSVPK.js +0 -6
@@ -1,12 +1,12 @@
1
1
  export { SLACK_CHECKS } from './chunk-2ZGVDTJC.js';
2
2
  import './chunk-JWJYNAWI.js';
3
- import { defaultSeedState, parseSeed } from './chunk-ZDS25NS5.js';
4
- export { defaultSeedState, loadSeedFromEnv, parseSeed, seedSchema } from './chunk-ZDS25NS5.js';
3
+ import { defaultSeedState, parseSeed } from './chunk-YBWG5JK2.js';
4
+ export { defaultSeedState, loadSeedFromEnv, parseSeed, seedSchema } from './chunk-YBWG5JK2.js';
5
5
  import { routeInputDeclarer, booleanInput, integerInput, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, defineTwin, queryTokenResolver, formTokenResolver, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-VOHTQV4J.js';
6
+ import { loadMcpToolFixture, defineTwin, queryTokenResolver, formTokenResolver, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-HRAD7MRX.js';
7
7
  import './chunk-6KJC4BTO.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
- import './chunk-CF6EIU2U.js';
9
+ import './chunk-FBSA5L36.js';
10
10
  import { z, ZodError } from 'zod';
11
11
 
12
12
  // ../packages/twin-slack/dist/src/db.js
@@ -1,11 +1,11 @@
1
1
  import './chunk-C4BVTUA3.js';
2
- import { defaultSeedState, parseSeed } from './chunk-HJX25USP.js';
3
- export { defaultSeedState, parseSeed, seedSchema } from './chunk-HJX25USP.js';
2
+ import { defaultSeedState, parseSeed } from './chunk-NBOQN5VX.js';
3
+ export { defaultSeedState, parseSeed, seedSchema } from './chunk-NBOQN5VX.js';
4
4
  import { routeInputDeclarer, integerInput, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-IZFM7W2V.js';
5
- import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-VOHTQV4J.js';
5
+ import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-HRAD7MRX.js';
6
6
  import './chunk-6KJC4BTO.js';
7
7
  import './chunk-SG6ZTIMT.js';
8
- import './chunk-CF6EIU2U.js';
8
+ import './chunk-FBSA5L36.js';
9
9
  import { z, ZodError } from 'zod';
10
10
  import { createHash, randomUUID } from 'node:crypto';
11
11
 
@@ -4477,7 +4477,7 @@ var GitHubDomain = class {
4477
4477
  }
4478
4478
  for (const pull of repoSeed.pull_requests ?? []) {
4479
4479
  const createdPr = this.createPullRequest({ owner: repo.owner, repo: repo.name, title: pull.title, body: pull.body ?? "", head: pull.head, base: pull.base ?? repo.default_branch, actor: pull.author });
4480
- const prNumber2 = createdPr.number;
4480
+ const prNumber2 = this.renumberPullRequest(repo.id, createdPr.number, pull.number);
4481
4481
  const seedsHead = (pull.reviews ?? []).length > 0 || (pull.statuses ?? []).length > 0;
4482
4482
  const prRow = seedsHead ? this.requirePullRequest(repo.id, prNumber2) : null;
4483
4483
  const headRepo = prRow ? this.requireRepoById(prRow.head_repo_id) : null;
@@ -5352,6 +5352,49 @@ var GitHubDomain = class {
5352
5352
  bumpEntityCounter(repoId, number) {
5353
5353
  this.db.prepare("UPDATE repositories SET entity_counter = CASE WHEN entity_counter < ? THEN ? ELSE entity_counter END WHERE id = ?").run(number, number, repoId);
5354
5354
  }
5355
+ /**
5356
+ * Move a just-created pull request onto the number its seed asked for, and
5357
+ * return the number it ends up with (F-1153).
5358
+ *
5359
+ * `createPullRequest` takes `nextNumber()` and the seed's `number` was simply
5360
+ * kept out of it: a repo seeded with issue #1 and pull request #7 exported
5361
+ * issues `[1]` and pulls `[2]`. Criteria resolve a pull request BY number, so
5362
+ * that is not a missing check — it is a check grading whichever pull request
5363
+ * happens to occupy the number the criterion names.
5364
+ *
5365
+ * CALLED IMMEDIATELY AFTER CREATE, before any child is seeded, which is the
5366
+ * same position `createIssue`'s renumber sits in. That ordering is what keeps
5367
+ * the table list short and honest: at this moment the only child that exists is
5368
+ * `pull_request_files`, written by `createPullRequest` itself. Reviews,
5369
+ * statuses, conversation comments and review comments are all seeded AFTER,
5370
+ * against the returned number, so they are never written to a number that then
5371
+ * moves — the "missed table is a silent orphan" hazard is removed rather than
5372
+ * enumerated.
5373
+ *
5374
+ * `defer_foreign_keys` is what makes the pair of updates possible at all. The
5375
+ * PR-child FKs declare `ON DELETE CASCADE` and NOT `ON UPDATE CASCADE` — unlike
5376
+ * `issue_labels` / `issue_assignees`, which is exactly why the issue path needs
5377
+ * no such dance — so with immediate enforcement there is no order that works:
5378
+ * updating the parent first strands `pull_request_files` on a key that no
5379
+ * longer exists, and updating the child first points it at a parent that does
5380
+ * not exist yet.
5381
+ *
5382
+ * `PRAGMA foreign_keys` is the WRONG lever here and silently so: SQLite makes
5383
+ * it a no-op inside a transaction, and `seed()` runs entirely inside one, so
5384
+ * toggling it changes nothing and the FK still fails. `defer_foreign_keys`
5385
+ * IS settable inside a transaction, holds enforcement until COMMIT — by which
5386
+ * point both rows agree — and resets itself when the transaction ends, so it
5387
+ * cannot leak into the rest of the seed.
5388
+ */
5389
+ renumberPullRequest(repoId, from, requested) {
5390
+ if (!requested || requested === from)
5391
+ return from;
5392
+ this.db.pragma("defer_foreign_keys = ON");
5393
+ this.db.prepare("UPDATE pull_requests SET number = ? WHERE repo_id = ? AND number = ?").run(requested, repoId, from);
5394
+ this.db.prepare("UPDATE pull_request_files SET pull_number = ? WHERE repo_id = ? AND pull_number = ?").run(requested, repoId, from);
5395
+ this.bumpEntityCounter(repoId, requested);
5396
+ return requested;
5397
+ }
5355
5398
  audit(action, repoFullName, payload) {
5356
5399
  this.db.prepare("INSERT INTO audit_log (ts, action, repo_full_name, payload_json) VALUES (?, ?, ?, ?)").run(nowIso(), action, repoFullName, JSON.stringify(payload));
5357
5400
  }
@@ -1,12 +1,348 @@
1
1
  import { routeInputDeclarer, bracketedQuery, integerInput, booleanInput, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
2
- import { loadMcpToolFixture, openTwinDatabase, defineTwin, twinBuildInfo, deriveMcpToolTable, failureInjectionMiddleware, createApp, recordedRequestHeaders, UnknownToolError } from './chunk-VOHTQV4J.js';
2
+ import { loadMcpToolFixture, openTwinDatabase, defineTwin, twinBuildInfo, deriveMcpToolTable, failureInjectionMiddleware, createApp, recordedRequestHeaders, UnknownToolError } from './chunk-HRAD7MRX.js';
3
3
  import './chunk-6KJC4BTO.js';
4
4
  import './chunk-SG6ZTIMT.js';
5
- import { defaultSeed, seedSchema, ensureMigratedColumns, ensureStripeTables, resetStripeTables, looksLikeApiKey, resolveSidFromKey, applySeed, newId, newClientSecret, nowUnix, requestId, nowIso } from './chunk-SLTBPJKZ.js';
6
- export { DEFAULT_API_KEY, DEFAULT_SID, applySeed, defaultSeed, loadSeedFromEnv, mintApiKey, parseSeed, resolveSidFromKey, revokeApiKey, seedSchema } from './chunk-SLTBPJKZ.js';
7
- import { createFailureInjectionStore, FAILURE_INJECTION_OVERRIDE_KEY } from './chunk-CF6EIU2U.js';
5
+ import { defaultSeed, seedSchema } from './chunk-2K6BJ3PI.js';
6
+ export { DEFAULT_API_KEY, DEFAULT_SID, defaultSeed, loadSeedFromEnv, parseSeed, seedSchema } from './chunk-2K6BJ3PI.js';
7
+ import { createFailureInjectionStore, FAILURE_INJECTION_OVERRIDE_KEY } from './chunk-FBSA5L36.js';
8
8
  import { z, ZodError } from 'zod';
9
- import { createHash } from 'node:crypto';
9
+ import { createHash, randomUUID, randomBytes } from 'node:crypto';
10
+
11
+ var BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
12
+ var ID_PREFIXES = {
13
+ customer: "cus_",
14
+ payment_method: "pm_",
15
+ payment_intent: "pi_",
16
+ charge: "ch_",
17
+ refund: "re_",
18
+ product: "prod_",
19
+ price: "price_",
20
+ subscription: "sub_",
21
+ subscription_item: "si_",
22
+ setup_intent: "seti_",
23
+ checkout_session: "cs_test_",
24
+ webhook_endpoint: "we_",
25
+ event: "evt_",
26
+ shared_payment_issued: "spt_",
27
+ shared_payment_granted: "spt_",
28
+ profile: "profile_",
29
+ balance_transaction: "txn_",
30
+ // API keys are minted via api-keys.ts but the prefix lives here for parity.
31
+ api_key: "sk_test_pome_",
32
+ api_key_restricted: "rk_test_pome_"
33
+ };
34
+ function randomBase62(count) {
35
+ const buf = randomBytes(count * 2);
36
+ let out = "";
37
+ let i = 0;
38
+ while (out.length < count && i < buf.length) {
39
+ const byte = buf[i++];
40
+ if (byte < 248) {
41
+ out += BASE62[byte % 62];
42
+ }
43
+ }
44
+ if (out.length < count) {
45
+ out += randomBase62(count - out.length);
46
+ }
47
+ return out;
48
+ }
49
+ function newId(kind) {
50
+ return `${ID_PREFIXES[kind]}${randomBase62(24)}`;
51
+ }
52
+ function newClientSecret(piId) {
53
+ return `${piId}_secret_${randomBase62(24)}`;
54
+ }
55
+ function newApiKey() {
56
+ return newId("api_key");
57
+ }
58
+ function nowIso() {
59
+ return (/* @__PURE__ */ new Date()).toISOString();
60
+ }
61
+ function nowUnix() {
62
+ return Math.floor(Date.now() / 1e3);
63
+ }
64
+ function requestId() {
65
+ return `req_${randomUUID()}`;
66
+ }
67
+
68
+ // ../packages/twin-stripe/dist/src/api-keys.js
69
+ function mintApiKey(db, input) {
70
+ const key = input.key ?? newApiKey();
71
+ const account_id = input.account_id ?? `acct_${input.sid}`;
72
+ const created_at = nowIso();
73
+ db.prepare(`INSERT INTO api_keys (key, sid, account_id, created_at, revoked_at)
74
+ VALUES (?, ?, ?, ?, NULL)
75
+ ON CONFLICT(key) DO UPDATE SET
76
+ sid = excluded.sid,
77
+ account_id = excluded.account_id,
78
+ revoked_at = NULL`).run(key, input.sid, account_id, created_at);
79
+ return { key, sid: input.sid, account_id, created_at, revoked_at: null };
80
+ }
81
+ function revokeApiKey(db, key) {
82
+ db.prepare(`UPDATE api_keys SET revoked_at = ? WHERE key = ?`).run(nowIso(), key);
83
+ }
84
+ function resolveSidFromKey(db, token) {
85
+ const row = db.prepare(`SELECT key, sid, account_id, created_at, revoked_at
86
+ FROM api_keys
87
+ WHERE key = ? AND revoked_at IS NULL`).get(token);
88
+ if (!row)
89
+ return void 0;
90
+ return { sid: row.sid, account_id: row.account_id };
91
+ }
92
+ function looksLikeApiKey(token) {
93
+ return token.startsWith("sk_test_pome_") || token.startsWith("rk_test_pome_") || // Tolerate raw Stripe-style sk_test_* / rk_test_* in case a downstream
94
+ // user mints their own non-pome-prefixed test keys via the admin API.
95
+ token.startsWith("sk_test_") || token.startsWith("rk_test_");
96
+ }
97
+
98
+ // ../packages/twin-stripe/dist/src/domain/schema.js
99
+ var STRIPE_TABLES_SQL = `
100
+ CREATE TABLE IF NOT EXISTS payment_intents (
101
+ id TEXT PRIMARY KEY,
102
+ account_id TEXT NOT NULL,
103
+ amount INTEGER NOT NULL,
104
+ currency TEXT NOT NULL,
105
+ status TEXT NOT NULL,
106
+ payment_method_types_json TEXT NOT NULL,
107
+ next_action_json TEXT,
108
+ latest_charge_id TEXT,
109
+ capture_method TEXT NOT NULL DEFAULT 'automatic',
110
+ confirmation_method TEXT NOT NULL DEFAULT 'automatic',
111
+ idempotency_key TEXT,
112
+ metadata_json TEXT NOT NULL DEFAULT '{}',
113
+ crypto_deposit_json TEXT,
114
+ client_secret TEXT NOT NULL,
115
+ created INTEGER NOT NULL,
116
+ updated INTEGER NOT NULL,
117
+ canceled_at INTEGER,
118
+ captured_at INTEGER,
119
+ payment_method_id TEXT,
120
+ customer_id TEXT,
121
+ last_payment_error_json TEXT
122
+ );
123
+
124
+ CREATE INDEX IF NOT EXISTS idx_payment_intents_created ON payment_intents(created);
125
+ CREATE INDEX IF NOT EXISTS idx_payment_intents_status ON payment_intents(status);
126
+ CREATE INDEX IF NOT EXISTS idx_payment_intents_account_id ON payment_intents(account_id);
127
+
128
+ CREATE TABLE IF NOT EXISTS charges (
129
+ id TEXT PRIMARY KEY,
130
+ account_id TEXT NOT NULL,
131
+ payment_intent_id TEXT NOT NULL,
132
+ amount INTEGER NOT NULL,
133
+ amount_captured INTEGER NOT NULL DEFAULT 0,
134
+ amount_refunded INTEGER NOT NULL DEFAULT 0,
135
+ status TEXT NOT NULL,
136
+ balance_transaction_id TEXT,
137
+ captured INTEGER NOT NULL DEFAULT 0,
138
+ currency TEXT NOT NULL,
139
+ created INTEGER NOT NULL,
140
+ payment_method_id TEXT,
141
+ payment_method_details_json TEXT,
142
+ failure_code TEXT,
143
+ failure_decline_code TEXT,
144
+ failure_message TEXT,
145
+ customer_id TEXT,
146
+ FOREIGN KEY (payment_intent_id) REFERENCES payment_intents(id) ON DELETE CASCADE
147
+ );
148
+
149
+ CREATE INDEX IF NOT EXISTS idx_charges_pi ON charges(payment_intent_id);
150
+ CREATE INDEX IF NOT EXISTS idx_charges_created ON charges(created);
151
+ CREATE INDEX IF NOT EXISTS idx_charges_account_id ON charges(account_id);
152
+
153
+ CREATE TABLE IF NOT EXISTS balance_transactions (
154
+ id TEXT PRIMARY KEY,
155
+ account_id TEXT NOT NULL,
156
+ type TEXT NOT NULL,
157
+ amount INTEGER NOT NULL,
158
+ fee INTEGER NOT NULL DEFAULT 0,
159
+ net INTEGER NOT NULL,
160
+ currency TEXT NOT NULL,
161
+ source_id TEXT,
162
+ source_type TEXT,
163
+ available_on INTEGER NOT NULL,
164
+ status TEXT NOT NULL DEFAULT 'available',
165
+ created INTEGER NOT NULL
166
+ );
167
+
168
+ CREATE INDEX IF NOT EXISTS idx_balance_tx_created ON balance_transactions(created);
169
+ CREATE INDEX IF NOT EXISTS idx_balance_tx_source ON balance_transactions(source_id);
170
+ CREATE INDEX IF NOT EXISTS idx_balance_tx_account_id ON balance_transactions(account_id);
171
+
172
+ CREATE TABLE IF NOT EXISTS events (
173
+ id TEXT PRIMARY KEY,
174
+ account_id TEXT NOT NULL,
175
+ type TEXT NOT NULL,
176
+ data_json TEXT NOT NULL,
177
+ request_idempotency_key TEXT,
178
+ livemode INTEGER NOT NULL DEFAULT 0,
179
+ api_version TEXT NOT NULL,
180
+ created INTEGER NOT NULL
181
+ );
182
+
183
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
184
+ CREATE INDEX IF NOT EXISTS idx_events_created ON events(created);
185
+ CREATE INDEX IF NOT EXISTS idx_events_account_id ON events(account_id);
186
+
187
+ CREATE TABLE IF NOT EXISTS refunds (
188
+ id TEXT PRIMARY KEY,
189
+ account_id TEXT NOT NULL,
190
+ charge_id TEXT NOT NULL,
191
+ payment_intent_id TEXT NOT NULL,
192
+ amount INTEGER NOT NULL,
193
+ currency TEXT NOT NULL,
194
+ status TEXT NOT NULL,
195
+ reason TEXT,
196
+ balance_transaction_id TEXT,
197
+ idempotency_key TEXT,
198
+ created INTEGER NOT NULL,
199
+ FOREIGN KEY (charge_id) REFERENCES charges(id) ON DELETE CASCADE
200
+ );
201
+
202
+ CREATE INDEX IF NOT EXISTS idx_refunds_charge ON refunds(charge_id);
203
+ CREATE INDEX IF NOT EXISTS idx_refunds_payment_intent ON refunds(payment_intent_id);
204
+ CREATE INDEX IF NOT EXISTS idx_refunds_created ON refunds(created);
205
+ CREATE INDEX IF NOT EXISTS idx_refunds_account_id ON refunds(account_id);
206
+
207
+ CREATE TABLE IF NOT EXISTS customers (
208
+ id TEXT PRIMARY KEY,
209
+ account_id TEXT NOT NULL,
210
+ name TEXT,
211
+ email TEXT,
212
+ description TEXT,
213
+ phone TEXT,
214
+ metadata_json TEXT NOT NULL DEFAULT '{}',
215
+ deleted INTEGER NOT NULL DEFAULT 0,
216
+ created INTEGER NOT NULL
217
+ );
218
+
219
+ CREATE INDEX IF NOT EXISTS idx_customers_created ON customers(created);
220
+ CREATE INDEX IF NOT EXISTS idx_customers_email ON customers(email);
221
+ CREATE INDEX IF NOT EXISTS idx_customers_account_id ON customers(account_id);
222
+
223
+ CREATE TABLE IF NOT EXISTS payment_methods (
224
+ id TEXT PRIMARY KEY,
225
+ account_id TEXT NOT NULL,
226
+ type TEXT NOT NULL,
227
+ card_brand TEXT NOT NULL,
228
+ card_last4 TEXT NOT NULL,
229
+ card_exp_month INTEGER NOT NULL,
230
+ card_exp_year INTEGER NOT NULL,
231
+ card_fingerprint TEXT NOT NULL,
232
+ customer_id TEXT,
233
+ detached INTEGER NOT NULL DEFAULT 0,
234
+ created INTEGER NOT NULL
235
+ );
236
+
237
+ CREATE INDEX IF NOT EXISTS idx_payment_methods_customer ON payment_methods(customer_id);
238
+ CREATE INDEX IF NOT EXISTS idx_payment_methods_created ON payment_methods(created);
239
+ CREATE INDEX IF NOT EXISTS idx_payment_methods_account_id ON payment_methods(account_id);
240
+
241
+ -- Billing tables (warm surfaces, shape tier): plain stored rows served
242
+ -- back in Stripe shape. No invoices table \u2014 invoices are reads-only and
243
+ -- nothing in the twin mints one (loud shape divergence, see FIDELITY.md).
244
+
245
+ CREATE TABLE IF NOT EXISTS products (
246
+ id TEXT PRIMARY KEY,
247
+ account_id TEXT NOT NULL,
248
+ name TEXT NOT NULL,
249
+ description TEXT,
250
+ active INTEGER NOT NULL DEFAULT 1,
251
+ metadata_json TEXT NOT NULL DEFAULT '{}',
252
+ created INTEGER NOT NULL,
253
+ updated INTEGER NOT NULL
254
+ );
255
+
256
+ CREATE INDEX IF NOT EXISTS idx_products_created ON products(created);
257
+ CREATE INDEX IF NOT EXISTS idx_products_account_id ON products(account_id);
258
+
259
+ CREATE TABLE IF NOT EXISTS prices (
260
+ id TEXT PRIMARY KEY,
261
+ account_id TEXT NOT NULL,
262
+ product_id TEXT NOT NULL,
263
+ currency TEXT NOT NULL,
264
+ unit_amount INTEGER,
265
+ recurring_interval TEXT,
266
+ recurring_interval_count INTEGER,
267
+ active INTEGER NOT NULL DEFAULT 1,
268
+ nickname TEXT,
269
+ lookup_key TEXT,
270
+ metadata_json TEXT NOT NULL DEFAULT '{}',
271
+ created INTEGER NOT NULL
272
+ );
273
+
274
+ CREATE INDEX IF NOT EXISTS idx_prices_created ON prices(created);
275
+ CREATE INDEX IF NOT EXISTS idx_prices_product ON prices(product_id);
276
+ CREATE INDEX IF NOT EXISTS idx_prices_account_id ON prices(account_id);
277
+
278
+ CREATE TABLE IF NOT EXISTS subscriptions (
279
+ id TEXT PRIMARY KEY,
280
+ account_id TEXT NOT NULL,
281
+ customer_id TEXT NOT NULL,
282
+ status TEXT NOT NULL,
283
+ items_json TEXT NOT NULL,
284
+ cancel_at_period_end INTEGER NOT NULL DEFAULT 0,
285
+ canceled_at INTEGER,
286
+ ended_at INTEGER,
287
+ metadata_json TEXT NOT NULL DEFAULT '{}',
288
+ created INTEGER NOT NULL
289
+ );
290
+
291
+ CREATE INDEX IF NOT EXISTS idx_subscriptions_created ON subscriptions(created);
292
+ CREATE INDEX IF NOT EXISTS idx_subscriptions_customer ON subscriptions(customer_id);
293
+ CREATE INDEX IF NOT EXISTS idx_subscriptions_account_id ON subscriptions(account_id);
294
+ `;
295
+ var STRIPE_TABLES = [
296
+ "refunds",
297
+ "events",
298
+ "balance_transactions",
299
+ "charges",
300
+ "payment_intents",
301
+ "payment_methods",
302
+ "customers",
303
+ "subscriptions",
304
+ "prices",
305
+ "products"
306
+ ];
307
+ function ensureStripeTables(db) {
308
+ db.pragma("busy_timeout = 5000");
309
+ db.pragma("journal_mode = WAL");
310
+ db.pragma("foreign_keys = ON");
311
+ db.exec(STRIPE_TABLES_SQL);
312
+ ensureMigratedColumns(db);
313
+ }
314
+ var MIGRATED_COLUMNS = {
315
+ payment_intents: [
316
+ ["payment_method_id", "payment_method_id TEXT"],
317
+ ["customer_id", "customer_id TEXT"],
318
+ ["last_payment_error_json", "last_payment_error_json TEXT"]
319
+ ],
320
+ charges: [
321
+ ["payment_method_id", "payment_method_id TEXT"],
322
+ ["payment_method_details_json", "payment_method_details_json TEXT"],
323
+ ["failure_code", "failure_code TEXT"],
324
+ ["failure_decline_code", "failure_decline_code TEXT"],
325
+ ["failure_message", "failure_message TEXT"],
326
+ ["customer_id", "customer_id TEXT"]
327
+ ],
328
+ refunds: [["balance_transaction_id", "balance_transaction_id TEXT"]]
329
+ };
330
+ function ensureMigratedColumns(db) {
331
+ for (const [table, columns] of Object.entries(MIGRATED_COLUMNS)) {
332
+ const existing = new Set(db.prepare(`SELECT name FROM pragma_table_info(?)`).all(table).map((row) => row.name));
333
+ if (existing.size === 0)
334
+ continue;
335
+ for (const [column, ddl] of columns) {
336
+ if (!existing.has(column))
337
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl};`);
338
+ }
339
+ }
340
+ }
341
+ function resetStripeTables(db) {
342
+ for (const table of STRIPE_TABLES) {
343
+ db.exec(`DELETE FROM ${table};`);
344
+ }
345
+ }
10
346
 
11
347
  // ../packages/twin-stripe/dist/src/db.js
12
348
  var MIGRATION_SQL = `
@@ -2950,6 +3286,60 @@ function registerStripeRoutes(router, domain, recorder, runId) {
2950
3286
  });
2951
3287
  }
2952
3288
 
3289
+ // ../packages/twin-stripe/dist/src/apply-seed.js
3290
+ function applySeed(db, seed, failureInjection) {
3291
+ ensureStripeTables(db);
3292
+ for (const entry of seed.api_keys ?? []) {
3293
+ mintApiKey(db, {
3294
+ sid: entry.sid,
3295
+ account_id: entry.account_id,
3296
+ key: entry.key
3297
+ });
3298
+ }
3299
+ for (const row of seed.payment_intents ?? []) {
3300
+ insertSeedPaymentIntent(db, row);
3301
+ }
3302
+ for (const row of seed.charges ?? []) {
3303
+ insertSeedCharge(db, row);
3304
+ }
3305
+ for (const row of seed.balance_transactions ?? []) {
3306
+ insertSeedBalanceTransaction(db, row);
3307
+ }
3308
+ for (const row of seed.refunds ?? []) {
3309
+ insertSeedRefund(db, row);
3310
+ }
3311
+ if (failureInjection) {
3312
+ failureInjection.setRules(seed.failure_injection ?? []);
3313
+ }
3314
+ }
3315
+ function insertSeedPaymentIntent(db, row) {
3316
+ db.prepare(`INSERT INTO payment_intents (
3317
+ id, account_id, amount, currency, status,
3318
+ payment_method_types_json, next_action_json,
3319
+ latest_charge_id, capture_method, confirmation_method,
3320
+ idempotency_key, metadata_json, crypto_deposit_json,
3321
+ client_secret, created, updated, canceled_at, captured_at
3322
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.account_id, row.amount, row.currency, row.status, JSON.stringify(row.payment_method_types), row.next_action === void 0 || row.next_action === null ? null : JSON.stringify(row.next_action), row.latest_charge_id ?? null, row.capture_method, row.confirmation_method, row.idempotency_key ?? null, JSON.stringify(row.metadata), row.crypto_deposit === void 0 || row.crypto_deposit === null ? null : JSON.stringify(row.crypto_deposit), row.client_secret, row.created, row.updated, row.canceled_at ?? null, row.captured_at ?? null);
3323
+ }
3324
+ function insertSeedCharge(db, row) {
3325
+ db.prepare(`INSERT INTO charges (
3326
+ id, account_id, payment_intent_id, amount, amount_captured, amount_refunded,
3327
+ status, balance_transaction_id, captured, currency, created
3328
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.account_id, row.payment_intent_id, row.amount, row.amount_captured, row.amount_refunded, row.status, row.balance_transaction_id ?? null, row.captured ? 1 : 0, row.currency, row.created);
3329
+ }
3330
+ function insertSeedRefund(db, row) {
3331
+ db.prepare(`INSERT INTO refunds (
3332
+ id, account_id, charge_id, payment_intent_id, amount, currency,
3333
+ status, reason, balance_transaction_id, idempotency_key, created
3334
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.account_id, row.charge_id, row.payment_intent_id, row.amount, row.currency, row.status, row.reason ?? null, row.balance_transaction_id ?? null, row.idempotency_key ?? null, row.created);
3335
+ }
3336
+ function insertSeedBalanceTransaction(db, row) {
3337
+ db.prepare(`INSERT INTO balance_transactions (
3338
+ id, account_id, type, amount, fee, net, currency, source_id, source_type,
3339
+ available_on, status, created
3340
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.account_id, row.type, row.amount, row.fee, row.net, row.currency, row.source_id ?? null, row.source_type ?? null, row.available_on, row.status, row.created);
3341
+ }
3342
+
2953
3343
  // ../packages/twin-stripe/dist/src/x402.js
2954
3344
  function parsePriceToBaseUnits(price) {
2955
3345
  const trimmed = price.trim();
@@ -4037,4 +4427,4 @@ function createTwinStripeApp(opts = {}) {
4037
4427
  });
4038
4428
  }
4039
4429
 
4040
- export { StripeDomain, createStripeTwinDefinition, createTwinStripeApp, executeTool, isMutatingTool, migrate, openTwinStripeDatabase, paymentMiddleware, registerStripeRoutes, registerX402Routes, resetDatabase, stripeToolFixture, stripeToolInputSchema, toolArgumentSchemas, tthwSeconds };
4430
+ export { StripeDomain, applySeed, createStripeTwinDefinition, createTwinStripeApp, executeTool, isMutatingTool, migrate, mintApiKey, openTwinStripeDatabase, paymentMiddleware, registerStripeRoutes, registerX402Routes, resetDatabase, resolveSidFromKey, revokeApiKey, stripeToolFixture, stripeToolInputSchema, toolArgumentSchemas, tthwSeconds };
@@ -1,12 +1,12 @@
1
1
  export { GMAIL_CHECKS } from './chunk-JM6VS62R.js';
2
- import { gmailErrorEnvelope, gmailSeedSchema, parseSeed, defaultSeedState, notFound, invalidArgument, checkFault, unsupported, validateSearchQuery, SEARCH_MAILBOX_MESSAGE_BUDGET, GmailError, parseSearchQuery, CATEGORY_LABELS, parseDate, parseSize, KNOWN_FIELDS, parseDuration, assertHasOperator } from './chunk-3T5XN64P.js';
3
- export { DEFAULT_GMAIL_AGENT_EMAIL, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, agentPathInboxMailbox, defaultSeedState, gmailErrorEnvelope, gmailSeedSchema, loadSeedFromEnv, parseSearchQuery, parseSeed, validateSearchQuery } from './chunk-3T5XN64P.js';
2
+ import { gmailErrorEnvelope, gmailSeedSchema, parseSeed, defaultSeedState, notFound, invalidArgument, checkFault, unsupported, validateSearchQuery, SEARCH_MAILBOX_MESSAGE_BUDGET, GmailError, parseSearchQuery, CATEGORY_LABELS, parseDate, parseSize, KNOWN_FIELDS, parseDuration, assertHasOperator } from './chunk-PASFBRK4.js';
3
+ export { DEFAULT_GMAIL_AGENT_EMAIL, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, agentPathInboxMailbox, defaultSeedState, gmailErrorEnvelope, gmailSeedSchema, loadSeedFromEnv, parseSearchQuery, parseSeed, validateSearchQuery } from './chunk-PASFBRK4.js';
4
4
  import './chunk-JWJYNAWI.js';
5
5
  import { routeInputDeclarer, integerInput, booleanInput, repeatedInput, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-VOHTQV4J.js';
6
+ import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-HRAD7MRX.js';
7
7
  import './chunk-6KJC4BTO.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
- import './chunk-CF6EIU2U.js';
9
+ import './chunk-FBSA5L36.js';
10
10
  import { createHash, createHmac, timingSafeEqual, randomBytes } from 'node:crypto';
11
11
  import { z } from 'zod';
12
12
 
@@ -0,0 +1,6 @@
1
+ export { UnsupportedTwinError, bootTwin } from './chunk-5NPGY73F.js';
2
+ export { STRIPE_LOCAL_ACCOUNT_ID } from './chunk-RPPF2KDQ.js';
3
+ import './chunk-HRAD7MRX.js';
4
+ import './chunk-6KJC4BTO.js';
5
+ import './chunk-SG6ZTIMT.js';
6
+ import './chunk-FBSA5L36.js';
@@ -1,4 +1,4 @@
1
- import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-CNG6DQWX.js';
1
+ import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-RPPF2KDQ.js';
2
2
  import { writeFile } from 'node:fs/promises';
3
3
 
4
4
  async function generateSeedFile(twins, opts = {}) {
@@ -1,10 +1,10 @@
1
- import { readSeedFileText, seedForTwin, parseSeedFileText, soleTwinOf, twinsNamedBy } from './chunk-DWOCTCSE.js';
2
- import { bootTwin } from './chunk-T73VMABH.js';
3
- import { TWIN_REGISTRY, isTwinName, TWIN_NAMES, defaultPortFor } from './chunk-CNG6DQWX.js';
4
- import './chunk-VOHTQV4J.js';
1
+ import { readSeedFileText, seedForTwin, parseSeedFileText, soleTwinOf, twinsNamedBy } from './chunk-VPXIGCZW.js';
2
+ import { bootTwin } from './chunk-5NPGY73F.js';
3
+ import { TWIN_REGISTRY, isTwinName, TWIN_NAMES, defaultPortFor } from './chunk-RPPF2KDQ.js';
4
+ import './chunk-HRAD7MRX.js';
5
5
  import './chunk-6KJC4BTO.js';
6
6
  import './chunk-SG6ZTIMT.js';
7
- import './chunk-CF6EIU2U.js';
7
+ import './chunk-FBSA5L36.js';
8
8
  import { randomBytes } from 'node:crypto';
9
9
  import { readFileSync } from 'node:fs';
10
10
  import { mkdir, writeFile } from 'node:fs/promises';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pome-sh/cli",
3
- "version": "0.35.2",
3
+ "version": "0.36.0",
4
4
  "description": "Test AI agents against digital twins of real SaaS APIs. Records tool-call traces and scores them on pome.sh.",
5
5
  "keywords": [
6
6
  "ai",
@@ -65,9 +65,11 @@ account.
65
65
  ### Charge
66
66
 
67
67
  - `ch_test_200` — $200.00 USD (`amount: 20000`), **fully refunded**
68
- (`amount_refunded: 20000`, `refunded: true`), `status: succeeded`,
69
- `captured: true`. There is no remaining refundable balance, so the twin will
70
- reject any `create_refund` against it with `charge_already_refunded`.
68
+ (`amount_refunded: 20000`), `status: succeeded`, `captured: true`. There is no
69
+ remaining refundable balance, so the twin will reject any `create_refund`
70
+ against it with `charge_already_refunded`. The `refunded: true` the agent reads
71
+ back is DERIVED from `amount_refunded >= amount` at serialization, so it is not
72
+ a field the seed writes.
71
73
 
72
74
  ## Config
73
75
 
@@ -40,7 +40,6 @@
40
40
  "status": "succeeded",
41
41
  "currency": "usd",
42
42
  "captured": true,
43
- "refunded": true,
44
43
  "created": 1700000000
45
44
  }
46
45
  ]