@12ui/design 0.2.15 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/dist/branch-execution.d.ts +5 -0
- package/dist/branch-execution.d.ts.map +1 -1
- package/dist/branch-execution.js +26 -8
- package/dist/branch-execution.js.map +1 -1
- package/dist/branch-page-conversion.d.ts +18 -2
- package/dist/branch-page-conversion.d.ts.map +1 -1
- package/dist/branch-page-conversion.js +36 -16
- package/dist/branch-page-conversion.js.map +1 -1
- package/dist/branch-run-record.d.ts +3 -0
- package/dist/branch-run-record.d.ts.map +1 -1
- package/dist/branch-run-record.js.map +1 -1
- package/dist/cli-capabilities.d.ts +15 -5
- package/dist/cli-capabilities.d.ts.map +1 -1
- package/dist/cli-capabilities.js +19 -5
- package/dist/cli-capabilities.js.map +1 -1
- package/dist/cli-draft-command.d.ts +2 -0
- package/dist/cli-draft-command.d.ts.map +1 -1
- package/dist/cli-draft-command.js +22 -4
- package/dist/cli-draft-command.js.map +1 -1
- package/dist/cli-package-command.d.ts +2 -0
- package/dist/cli-package-command.d.ts.map +1 -1
- package/dist/cli-package-command.js +11 -0
- package/dist/cli-package-command.js.map +1 -1
- package/dist/cli-skill-command.d.ts.map +1 -1
- package/dist/cli-skill-command.js +27 -12
- package/dist/cli-skill-command.js.map +1 -1
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/conversion-receipt-store.d.ts +75 -0
- package/dist/conversion-receipt-store.d.ts.map +1 -0
- package/dist/conversion-receipt-store.js +143 -0
- package/dist/conversion-receipt-store.js.map +1 -0
- package/dist/conversion-receipt.d.ts +139 -43
- package/dist/conversion-receipt.d.ts.map +1 -1
- package/dist/conversion-receipt.js +241 -73
- package/dist/conversion-receipt.js.map +1 -1
- package/dist/draft-adoption.d.ts +48 -0
- package/dist/draft-adoption.d.ts.map +1 -0
- package/dist/draft-adoption.js +237 -0
- package/dist/draft-adoption.js.map +1 -0
- package/dist/draft-run.d.ts +10 -0
- package/dist/draft-run.d.ts.map +1 -1
- package/dist/draft-run.js +16 -4
- package/dist/draft-run.js.map +1 -1
- package/dist/legacy-skill-catalog.d.ts.map +1 -1
- package/dist/legacy-skill-catalog.js +40 -0
- package/dist/legacy-skill-catalog.js.map +1 -1
- package/dist/package-submission.d.ts +12 -1
- package/dist/package-submission.d.ts.map +1 -1
- package/dist/package-submission.js +2 -0
- package/dist/package-submission.js.map +1 -1
- package/dist/skill-clients.d.ts +39 -0
- package/dist/skill-clients.d.ts.map +1 -0
- package/dist/skill-clients.js +156 -0
- package/dist/skill-clients.js.map +1 -0
- package/dist/skill-installer.d.ts +2 -3
- package/dist/skill-installer.d.ts.map +1 -1
- package/dist/skill-installer.js +9 -101
- package/dist/skill-installer.js.map +1 -1
- package/package.json +1 -1
- package/skills/design/SKILL.md +2 -2
- package/skills/design-branch/SKILL.md +3 -3
- package/skills/design-convert/SKILL.md +3 -3
- package/skills/design-draft/SKILL.md +3 -3
- package/skills/design-search/SKILL.md +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readFile, rm
|
|
2
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { conversionReceiptRecordPath, pruneConversionReceiptRecords, readConversionReceiptRecord, removeConversionReceiptRecord, resolveConversionReceiptStore, writeConversionReceiptRecord, } from './conversion-receipt-store.js';
|
|
3
4
|
/**
|
|
4
5
|
* The single owner of "an image's conversion was already bought, and here is
|
|
5
6
|
* the proof".
|
|
@@ -14,14 +15,29 @@ import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
|
14
15
|
* the upload — so a false claim is a failed page, not a cheap win.
|
|
15
16
|
*
|
|
16
17
|
* This is therefore a receipt of a proved fact, not a cache: it records the
|
|
17
|
-
* digest of the exact bytes a succeeded conversion was admitted with
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* digest of the exact bytes a succeeded conversion was admitted with. A later
|
|
19
|
+
* run reuses it only when the bytes it is about to submit hash identically.
|
|
20
|
+
* Absent proof it converts fresh.
|
|
20
21
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
* The records live in the per-user store (`conversion-receipt-store.ts`), keyed
|
|
23
|
+
* by that digest and the model, because the fact is about bytes and not about
|
|
24
|
+
* where a copy of them happens to sit. `12ui convert`, `12ui convert package`,
|
|
25
|
+
* and `12ui branch execute --convert` write them, `12ui branch execute
|
|
26
|
+
* --convert` reads them, and only this module decides what counts as proof.
|
|
27
|
+
*
|
|
28
|
+
* SUNSET — legacy sidecars. Receipts shipped in 0.2.15 as
|
|
29
|
+
* `<image>.conversion.json` written beside the input image. Those files are
|
|
30
|
+
* still READ beside a winner image for one release cycle and migrated into the
|
|
31
|
+
* store the first time they are seen, so an upgrade never re-buys a conversion
|
|
32
|
+
* a 0.2.15 convert already paid for. Nothing writes them any more. Delete the
|
|
33
|
+
* legacy read path — `LEGACY_CONVERSION_RECEIPT_SUFFIX`,
|
|
34
|
+
* `legacyConversionReceiptPath`, `readLegacyConversionReceipts`, and the
|
|
35
|
+
* `legacyImagePaths` argument of `invalidateConversionReceipts` — one release
|
|
36
|
+
* after 0.2.16; by then every receipt a 0.2.15 convert wrote has long outlived
|
|
37
|
+
* the ~24h conversion it names.
|
|
23
38
|
*/
|
|
24
|
-
export const
|
|
39
|
+
export const LEGACY_CONVERSION_RECEIPT_SUFFIX = '.conversion.json';
|
|
40
|
+
export { resolveConversionReceiptStore } from './conversion-receipt-store.js';
|
|
25
41
|
/**
|
|
26
42
|
* The output an attachable conversion must have produced. A package converts
|
|
27
43
|
* every viewport to a LayerDoc and buys the page export separately, so the
|
|
@@ -34,11 +50,13 @@ export const REUSABLE_CONVERSION_OUTPUT = 'layerdoc';
|
|
|
34
50
|
* attachment expiring within this window (`PACKAGE_SOURCE_MINIMUM_REMAINING_MS`)
|
|
35
51
|
* because composition reads the source run at the END of a package drive, and
|
|
36
52
|
* that refusal fails the page after its screens are already paid for.
|
|
37
|
-
* Conversions live 24h while a receipt sits
|
|
38
|
-
* so an expiry that is not checked here is a page failure waiting
|
|
53
|
+
* Conversions live 24h while a stored receipt sits in the user's home
|
|
54
|
+
* indefinitely, so an expiry that is not checked here is a page failure waiting
|
|
55
|
+
* for day two.
|
|
39
56
|
*/
|
|
40
57
|
export const CONVERSION_REUSE_MINIMUM_REMAINING_MS = 60 * 60_000;
|
|
41
|
-
|
|
58
|
+
/** Where 0.2.15 wrote a receipt. Read for one release cycle, never written. */
|
|
59
|
+
export const legacyConversionReceiptPath = (imagePath) => (`${imagePath}${LEGACY_CONVERSION_RECEIPT_SUFFIX}`);
|
|
42
60
|
const isReceipt = (value) => {
|
|
43
61
|
if (!value || typeof value !== 'object')
|
|
44
62
|
return false;
|
|
@@ -49,58 +67,149 @@ const isReceipt = (value) => {
|
|
|
49
67
|
&& typeof record.conversionId === 'string'
|
|
50
68
|
&& record.conversionId.length > 0
|
|
51
69
|
&& typeof record.model === 'string'
|
|
70
|
+
&& record.model.length > 0
|
|
52
71
|
&& typeof record.boughtBy === 'string'
|
|
53
72
|
&& record.boughtBy.length > 0
|
|
54
73
|
&& typeof record.expiresAt === 'string'
|
|
55
74
|
&& Number.isFinite(Date.parse(record.expiresAt));
|
|
56
75
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
export const conversionReceiptKey = (receipt) => ({
|
|
77
|
+
sha256: receipt.sha256,
|
|
78
|
+
model: receipt.model,
|
|
79
|
+
});
|
|
80
|
+
const storeOf = (store) => store ?? resolveConversionReceiptStore();
|
|
81
|
+
const expiredReceipt = (receipt, now) => (Date.parse(receipt.expiresAt) <= now.getTime());
|
|
82
|
+
/**
|
|
83
|
+
* Expiry hygiene, paid for by the invocations that already write to the store.
|
|
84
|
+
*
|
|
85
|
+
* Bounded on purpose: a user who converts every day accumulates a record per
|
|
86
|
+
* distinct image, and a full sweep on a hot path would make the cheapest
|
|
87
|
+
* command the one that reads the most files. A rotating sample covers the whole
|
|
88
|
+
* store across invocations, and a record that survives one pass costs nothing
|
|
89
|
+
* but its own inode until the next.
|
|
90
|
+
*/
|
|
91
|
+
const pruneExpired = async (store, now) => (await pruneConversionReceiptRecords({
|
|
92
|
+
store,
|
|
93
|
+
expired: (record) => isReceipt(record) && expiredReceipt(record, now),
|
|
94
|
+
}));
|
|
95
|
+
/**
|
|
96
|
+
* The stored record for exactly these bytes and this model, or nothing.
|
|
97
|
+
*
|
|
98
|
+
* An expired record is removed as it is read: it is the one expired record this
|
|
99
|
+
* invocation is certain about, and leaving it would have the next run read it
|
|
100
|
+
* again to reach the same conclusion.
|
|
101
|
+
*/
|
|
102
|
+
export const readStoredConversionReceipt = async (key, options = {}) => {
|
|
103
|
+
const store = storeOf(options.store);
|
|
104
|
+
const record = await readConversionReceiptRecord(store, key);
|
|
105
|
+
if (!isReceipt(record))
|
|
106
|
+
return undefined;
|
|
107
|
+
if (expiredReceipt(record, options.now ?? new Date())) {
|
|
108
|
+
await removeConversionReceiptRecord(store, key).catch(() => null);
|
|
64
109
|
return undefined;
|
|
65
110
|
}
|
|
66
|
-
|
|
67
|
-
export const readConversionReceipts = async (imagePaths) => {
|
|
68
|
-
const receipts = await Promise.all(imagePaths.map(readConversionReceipt));
|
|
69
|
-
return receipts.filter((receipt) => receipt !== undefined);
|
|
111
|
+
return record;
|
|
70
112
|
};
|
|
71
113
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* optimisation, and its absence only costs one conversion. Callers that can say
|
|
76
|
-
* so report the failures; the rest ignore them.
|
|
114
|
+
* Record a proved conversion. A write that fails is not a command failure: the
|
|
115
|
+
* receipt is an optimisation, and its absence only costs one conversion.
|
|
116
|
+
* Callers that can say so report the failure; the rest ignore it.
|
|
77
117
|
*/
|
|
78
|
-
export const
|
|
79
|
-
|
|
118
|
+
export const recordConversionReceipt = async (receipt, options = {}) => {
|
|
119
|
+
const store = storeOf(options.store);
|
|
120
|
+
// Never leave behind something that cannot be read back as proof: a record
|
|
80
121
|
// that fails the gate is indistinguishable from a corrupt file later.
|
|
81
122
|
if (!isReceipt(receipt)) {
|
|
82
123
|
return {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
path:
|
|
124
|
+
path: null,
|
|
125
|
+
failure: {
|
|
126
|
+
path: store,
|
|
86
127
|
detail: 'the conversion did not report the digest, id, model, and expiry a receipt proves',
|
|
87
|
-
}
|
|
128
|
+
},
|
|
88
129
|
};
|
|
89
130
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
const key = conversionReceiptKey(receipt);
|
|
132
|
+
try {
|
|
133
|
+
const written = await writeConversionReceiptRecord(store, key, receipt);
|
|
134
|
+
await pruneExpired(store, options.now ?? new Date());
|
|
135
|
+
return { path: written };
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return {
|
|
139
|
+
path: null,
|
|
140
|
+
failure: {
|
|
141
|
+
path: conversionReceiptRecordPath(store, key),
|
|
142
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const readLegacyConversionReceipt = async (imagePath) => {
|
|
148
|
+
try {
|
|
149
|
+
const parsed = JSON.parse(await readFile(legacyConversionReceiptPath(imagePath), 'utf8'));
|
|
150
|
+
return isReceipt(parsed) ? parsed : undefined;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* The 0.2.15 sidecars beside these images, carried forward.
|
|
158
|
+
*
|
|
159
|
+
* Every one that is still live is copied into the store the moment it is seen,
|
|
160
|
+
* so the upgrade costs nothing and the next run finds the proof by digest no
|
|
161
|
+
* matter which copy of the image it starts from. An existing store record wins:
|
|
162
|
+
* it was written by a later command about the same bytes, and overwriting it
|
|
163
|
+
* with the sidecar would replace a fresher expiry with an older one. Migration
|
|
164
|
+
* is best-effort — the receipt is still honoured this run either way.
|
|
165
|
+
*/
|
|
166
|
+
export const readLegacyConversionReceipts = async (imagePaths, options = {}) => {
|
|
167
|
+
const now = options.now ?? new Date();
|
|
168
|
+
const found = await Promise.all(imagePaths.map(readLegacyConversionReceipt));
|
|
169
|
+
const receipts = found.filter((receipt) => (receipt !== undefined && !expiredReceipt(receipt, now)));
|
|
170
|
+
const store = storeOf(options.store);
|
|
171
|
+
const migrated = new Set();
|
|
172
|
+
for (const receipt of receipts) {
|
|
173
|
+
const key = conversionReceiptKey(receipt);
|
|
174
|
+
const name = `${key.sha256}/${key.model}`;
|
|
175
|
+
if (migrated.has(name))
|
|
176
|
+
continue;
|
|
177
|
+
migrated.add(name);
|
|
178
|
+
const existing = await readStoredConversionReceipt(key, { store, now });
|
|
179
|
+
if (existing)
|
|
180
|
+
continue;
|
|
181
|
+
await writeConversionReceiptRecord(store, key, receipt).catch(() => null);
|
|
182
|
+
}
|
|
183
|
+
return receipts;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* The proof gate. Every condition is a fact about the bytes in hand, never an
|
|
187
|
+
* assumption about what an earlier command probably did.
|
|
188
|
+
*/
|
|
189
|
+
export const reusableConversionId = (args) => {
|
|
190
|
+
const deadline = (args.now ?? new Date()).getTime() + CONVERSION_REUSE_MINIMUM_REMAINING_MS;
|
|
191
|
+
return args.receipts.find((receipt) => (receipt.sha256 === args.sha256
|
|
192
|
+
&& receipt.model === args.model
|
|
193
|
+
&& receipt.boughtBy !== args.boughtBy
|
|
194
|
+
&& Date.parse(receipt.expiresAt) > deadline))?.conversionId;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* The gate, over the store and over whatever the caller already holds.
|
|
198
|
+
*
|
|
199
|
+
* The store is consulted first because it is the record every command writes;
|
|
200
|
+
* the in-hand receipts are the 0.2.15 sidecars this run read beside the winner,
|
|
201
|
+
* which stay proof for one release cycle even if their migration failed.
|
|
202
|
+
*/
|
|
203
|
+
export const findReusableConversionId = async (args) => {
|
|
204
|
+
const now = args.now ?? new Date();
|
|
205
|
+
const stored = await readStoredConversionReceipt({ sha256: args.sha256, model: args.model }, { ...(args.store ? { store: args.store } : {}), now });
|
|
206
|
+
return reusableConversionId({
|
|
207
|
+
receipts: [...(stored ? [stored] : []), ...(args.receipts ?? [])],
|
|
208
|
+
sha256: args.sha256,
|
|
209
|
+
model: args.model,
|
|
210
|
+
boughtBy: args.boughtBy,
|
|
211
|
+
now,
|
|
212
|
+
});
|
|
104
213
|
};
|
|
105
214
|
/**
|
|
106
215
|
* Withdraw a receipt the SERVER disproved, so the next run does not repeat the
|
|
@@ -113,17 +222,24 @@ export const writeConversionReceipts = async (imagePaths, receipt) => {
|
|
|
113
222
|
* says nothing about the referenced conversion, and deleting on one of those
|
|
114
223
|
* would throw away a live receipt and buy its conversion again for nothing.
|
|
115
224
|
*
|
|
116
|
-
* Only
|
|
117
|
-
*
|
|
225
|
+
* Only records naming the disproven conversion are withdrawn: a newer record
|
|
226
|
+
* under the same key describes different work and is untouched. Removal is
|
|
118
227
|
* best-effort, because a receipt that cannot be deleted only costs the next run
|
|
119
228
|
* one refused attachment and one retry — the same price this run paid.
|
|
120
229
|
*/
|
|
121
|
-
export const invalidateConversionReceipts = async (
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
230
|
+
export const invalidateConversionReceipts = async (args) => {
|
|
231
|
+
const store = storeOf(args.store);
|
|
232
|
+
const fromStore = await Promise.all((args.keys ?? []).map(async (key) => {
|
|
233
|
+
const record = await readConversionReceiptRecord(store, key);
|
|
234
|
+
if (!isReceipt(record) || record.conversionId !== args.conversionId)
|
|
125
235
|
return null;
|
|
126
|
-
|
|
236
|
+
return await removeConversionReceiptRecord(store, key);
|
|
237
|
+
}));
|
|
238
|
+
const fromSidecars = await Promise.all((args.legacyImagePaths ?? []).map(async (imagePath) => {
|
|
239
|
+
const receipt = await readLegacyConversionReceipt(imagePath);
|
|
240
|
+
if (receipt?.conversionId !== args.conversionId)
|
|
241
|
+
return null;
|
|
242
|
+
const receiptPath = legacyConversionReceiptPath(imagePath);
|
|
127
243
|
try {
|
|
128
244
|
await rm(receiptPath, { force: true });
|
|
129
245
|
return receiptPath;
|
|
@@ -132,24 +248,13 @@ export const invalidateConversionReceipts = async (imagePaths, conversionId) =>
|
|
|
132
248
|
return null;
|
|
133
249
|
}
|
|
134
250
|
}));
|
|
135
|
-
return
|
|
136
|
-
};
|
|
137
|
-
/**
|
|
138
|
-
* The proof gate. Every condition is a fact about the bytes in hand, never an
|
|
139
|
-
* assumption about what an earlier command probably did.
|
|
140
|
-
*/
|
|
141
|
-
export const reusableConversionId = (args) => {
|
|
142
|
-
const deadline = (args.now ?? new Date()).getTime() + CONVERSION_REUSE_MINIMUM_REMAINING_MS;
|
|
143
|
-
return args.receipts.find((receipt) => (receipt.sha256 === args.sha256
|
|
144
|
-
&& receipt.model === args.model
|
|
145
|
-
&& receipt.boughtBy !== args.boughtBy
|
|
146
|
-
&& Date.parse(receipt.expiresAt) > deadline))?.conversionId;
|
|
251
|
+
return [...fromStore, ...fromSidecars].filter((entry) => entry !== null);
|
|
147
252
|
};
|
|
148
253
|
/**
|
|
149
254
|
* What `12ui convert` leaves behind for the branch run that follows it.
|
|
150
255
|
*
|
|
151
256
|
* Only a succeeded image-to-LayerDoc conversion can stand in for a package
|
|
152
|
-
* viewport, so anything else
|
|
257
|
+
* viewport, so anything else records nothing at all: a receipt for an `html`
|
|
153
258
|
* conversion would be a claim the server refuses, and the refusal costs a whole
|
|
154
259
|
* page. The digest is of the bytes actually submitted, which is what `--width`
|
|
155
260
|
* normalization changes and what the server admitted the run with.
|
|
@@ -163,6 +268,7 @@ export const recordConvertedImageReceipt = async (args) => {
|
|
|
163
268
|
|| operation.input_kind === 'layerdoc') {
|
|
164
269
|
return undefined;
|
|
165
270
|
}
|
|
271
|
+
const now = args.now ?? new Date();
|
|
166
272
|
const receipt = {
|
|
167
273
|
version: 1,
|
|
168
274
|
sha256: createHash('sha256').update(args.bytes).digest('hex'),
|
|
@@ -170,15 +276,77 @@ export const recordConvertedImageReceipt = async (args) => {
|
|
|
170
276
|
model: operation.model,
|
|
171
277
|
boughtBy: operation.id,
|
|
172
278
|
expiresAt: operation.expires_at,
|
|
173
|
-
createdAt:
|
|
279
|
+
createdAt: now.toISOString(),
|
|
174
280
|
};
|
|
175
|
-
const write = await
|
|
176
|
-
|
|
281
|
+
const write = await recordConversionReceipt(receipt, {
|
|
282
|
+
...(args.store ? { store: args.store } : {}),
|
|
283
|
+
now,
|
|
284
|
+
});
|
|
285
|
+
if (!write.failure)
|
|
177
286
|
return receipt;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
287
|
+
args.warn?.(`12ui convert: could not record the conversion receipt in ${write.failure.path}`
|
|
288
|
+
+ ` (${write.failure.detail}). The conversion is unaffected; a later`
|
|
289
|
+
+ ' `12ui branch execute` from this image will buy its conversion again.');
|
|
182
290
|
return undefined;
|
|
183
291
|
};
|
|
292
|
+
/**
|
|
293
|
+
* What `12ui convert package` leaves behind: one receipt per viewport whose own
|
|
294
|
+
* child conversion the package bought.
|
|
295
|
+
*
|
|
296
|
+
* A package converts each viewport to a LayerDoc and composes the page from
|
|
297
|
+
* those children, so every viewport of a submitted page is exactly the artifact
|
|
298
|
+
* a later package can attach instead of buying — the same fact the branch path
|
|
299
|
+
* records for the winner, for every screenshot the user named.
|
|
300
|
+
*
|
|
301
|
+
* A viewport that ATTACHED a conversion bought nothing, so it records nothing.
|
|
302
|
+
* The expiry reported is the package's own, which its children cannot outlive
|
|
303
|
+
* backwards, and the model is the one the server says it ran; without either
|
|
304
|
+
* fact nothing here proves a conversion is still attachable, so nothing is
|
|
305
|
+
* recorded and the caller is told once.
|
|
306
|
+
*/
|
|
307
|
+
export const recordPackageConversionReceipts = async (args) => {
|
|
308
|
+
const { result } = args;
|
|
309
|
+
if (!result.model || !result.expiresAt) {
|
|
310
|
+
args.warn?.(`12ui convert package: package ${result.packageId} answered without a`
|
|
311
|
+
+ ` ${result.model ? 'viewport expiry' : 'model'}, so nothing here proves its viewport`
|
|
312
|
+
+ ' conversions are still attachable. They were bought and the pages are unaffected; a'
|
|
313
|
+
+ ' later run from the same screenshots will buy them again.');
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
const now = args.now ?? new Date();
|
|
317
|
+
const submitted = new Map(args.pages.map((page) => [page.id, page]));
|
|
318
|
+
const recorded = [];
|
|
319
|
+
for (const page of result.pages) {
|
|
320
|
+
for (const viewport of page.viewports ?? []) {
|
|
321
|
+
if (viewport.status !== 'succeeded' || !viewport.conversionId)
|
|
322
|
+
continue;
|
|
323
|
+
const source = submitted.get(page.id)?.viewports
|
|
324
|
+
.find((entry) => entry.id === viewport.id);
|
|
325
|
+
// Attached work was already proved by the receipt that named it.
|
|
326
|
+
if (!source || source.sourceConversionId)
|
|
327
|
+
continue;
|
|
328
|
+
const receipt = {
|
|
329
|
+
version: 1,
|
|
330
|
+
sha256: createHash('sha256').update(source.bytes).digest('hex'),
|
|
331
|
+
conversionId: viewport.conversionId,
|
|
332
|
+
model: result.model,
|
|
333
|
+
boughtBy: result.packageId,
|
|
334
|
+
expiresAt: result.expiresAt,
|
|
335
|
+
createdAt: now.toISOString(),
|
|
336
|
+
};
|
|
337
|
+
const write = await recordConversionReceipt(receipt, {
|
|
338
|
+
...(args.store ? { store: args.store } : {}),
|
|
339
|
+
now,
|
|
340
|
+
});
|
|
341
|
+
if (write.failure) {
|
|
342
|
+
args.warn?.(`12ui convert package: could not record the conversion receipt for ${page.id}/`
|
|
343
|
+
+ `${viewport.id} in ${write.failure.path} (${write.failure.detail}). The package is`
|
|
344
|
+
+ ' unaffected; a later run from this screenshot will buy its conversion again.');
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
recorded.push(receipt);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return recorded;
|
|
351
|
+
};
|
|
184
352
|
//# sourceMappingURL=conversion-receipt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversion-receipt.js","sourceRoot":"","sources":["../src/conversion-receipt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"conversion-receipt.js","sourceRoot":"","sources":["../src/conversion-receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type DraftPaths } from './draft-workspace.js';
|
|
2
|
+
/**
|
|
3
|
+
* Which directory a `12ui draft` run continues in.
|
|
4
|
+
*
|
|
5
|
+
* MEASURED overnight 2026-08-10: a draft whose corpus search died left
|
|
6
|
+
* `…-CPcNkH` holding nothing but `references/inspire-attempt.json`, and the
|
|
7
|
+
* retry opened a NEW same-slug sibling `…-hOnLBB`. Two same-slug directories
|
|
8
|
+
* then held genuinely different candidates, so every tool that resolves a run
|
|
9
|
+
* by slug glob (`ls -dt …`) silently switched between them. The derived corpus
|
|
10
|
+
* key already stops the retry from buying the search twice, so this is a
|
|
11
|
+
* tooling-confusion hazard rather than a cost one — and the fix is to continue
|
|
12
|
+
* the stranded directory instead of stranding it.
|
|
13
|
+
*
|
|
14
|
+
* Adoption is decided on the DERIVED corpus key, never on the slug: two
|
|
15
|
+
* different design problems whose first four words match share a slug, and
|
|
16
|
+
* they must keep separate directories.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DRAFT_CLAIM_FILENAME = "draft-claim.json";
|
|
19
|
+
/**
|
|
20
|
+
* The owner marker. It is created O_EXCL the moment a run directory is created
|
|
21
|
+
* or adopted, so exactly one process can ever own a directory, and it records
|
|
22
|
+
* the derived corpus key so a run that died BEFORE writing its corpus attempt
|
|
23
|
+
* record is still adoptable on its key.
|
|
24
|
+
*/
|
|
25
|
+
export type DraftRunClaim = {
|
|
26
|
+
version: 1;
|
|
27
|
+
corpusKey: string;
|
|
28
|
+
pid: number;
|
|
29
|
+
hostname: string;
|
|
30
|
+
claimedAt: string;
|
|
31
|
+
};
|
|
32
|
+
export type AcquiredDraftRun = {
|
|
33
|
+
paths: DraftPaths;
|
|
34
|
+
/** True when this run continues an incomplete previous directory in place. */
|
|
35
|
+
adopted: boolean;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The run directory `12ui draft` continues in: an incomplete previous run for
|
|
39
|
+
* the same derived corpus key, adopted in place, or a fresh unique sibling.
|
|
40
|
+
*/
|
|
41
|
+
export declare const acquireDraftRunDirectory: (args: {
|
|
42
|
+
query: string;
|
|
43
|
+
corpusKey: string;
|
|
44
|
+
/** Overridden only by tests; production always uses the system temp root. */
|
|
45
|
+
root?: string;
|
|
46
|
+
now?: () => Date;
|
|
47
|
+
}) => Promise<AcquiredDraftRun>;
|
|
48
|
+
//# sourceMappingURL=draft-adoption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-adoption.d.ts","sourceRoot":"","sources":["../src/draft-adoption.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAkNF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAU,MAAM;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,KAAG,OAAO,CAAC,gBAAgB,CAiB3B,CAAC"}
|