@12ui/design 0.2.13 → 0.2.14

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 (172) hide show
  1. package/README.md +35 -9
  2. package/dist/branch-client.d.ts +155 -0
  3. package/dist/branch-client.d.ts.map +1 -0
  4. package/dist/branch-client.js +179 -0
  5. package/dist/branch-client.js.map +1 -0
  6. package/dist/branch-completion.d.ts +37 -0
  7. package/dist/branch-completion.d.ts.map +1 -0
  8. package/dist/branch-completion.js +137 -0
  9. package/dist/branch-completion.js.map +1 -0
  10. package/dist/branch-dispatch-loop.d.ts +84 -0
  11. package/dist/branch-dispatch-loop.d.ts.map +1 -0
  12. package/dist/branch-dispatch-loop.js +206 -0
  13. package/dist/branch-dispatch-loop.js.map +1 -0
  14. package/dist/branch-downloads.d.ts +43 -0
  15. package/dist/branch-downloads.d.ts.map +1 -0
  16. package/dist/branch-downloads.js +94 -0
  17. package/dist/branch-downloads.js.map +1 -0
  18. package/dist/branch-execution-types.d.ts +54 -0
  19. package/dist/branch-execution-types.d.ts.map +1 -0
  20. package/dist/branch-execution-types.js +2 -0
  21. package/dist/branch-execution-types.js.map +1 -0
  22. package/dist/branch-execution.d.ts +18 -0
  23. package/dist/branch-execution.d.ts.map +1 -0
  24. package/dist/branch-execution.js +261 -0
  25. package/dist/branch-execution.js.map +1 -0
  26. package/dist/branch-identity.d.ts +71 -0
  27. package/dist/branch-identity.d.ts.map +1 -0
  28. package/dist/branch-identity.js +72 -0
  29. package/dist/branch-identity.js.map +1 -0
  30. package/dist/branch-materialize.d.ts +22 -0
  31. package/dist/branch-materialize.d.ts.map +1 -0
  32. package/dist/branch-materialize.js +47 -0
  33. package/dist/branch-materialize.js.map +1 -0
  34. package/dist/branch-page-conversion.d.ts +39 -0
  35. package/dist/branch-page-conversion.d.ts.map +1 -0
  36. package/dist/branch-page-conversion.js +206 -0
  37. package/dist/branch-page-conversion.js.map +1 -0
  38. package/dist/branch-plan.d.ts +63 -0
  39. package/dist/branch-plan.d.ts.map +1 -0
  40. package/dist/branch-plan.js +95 -0
  41. package/dist/branch-plan.js.map +1 -0
  42. package/dist/branch-progress.d.ts +75 -0
  43. package/dist/branch-progress.d.ts.map +1 -0
  44. package/dist/branch-progress.js +159 -0
  45. package/dist/branch-progress.js.map +1 -0
  46. package/dist/branch-run-record.d.ts +153 -0
  47. package/dist/branch-run-record.d.ts.map +1 -0
  48. package/dist/branch-run-record.js +94 -0
  49. package/dist/branch-run-record.js.map +1 -0
  50. package/dist/branch-schedule.d.ts +74 -0
  51. package/dist/branch-schedule.d.ts.map +1 -0
  52. package/dist/branch-schedule.js +130 -0
  53. package/dist/branch-schedule.js.map +1 -0
  54. package/dist/branch-status.d.ts +11 -0
  55. package/dist/branch-status.d.ts.map +1 -0
  56. package/dist/branch-status.js +115 -0
  57. package/dist/branch-status.js.map +1 -0
  58. package/dist/branch-viewport-normalization.d.ts +74 -0
  59. package/dist/branch-viewport-normalization.d.ts.map +1 -0
  60. package/dist/branch-viewport-normalization.js +120 -0
  61. package/dist/branch-viewport-normalization.js.map +1 -0
  62. package/dist/cli-arguments.d.ts.map +1 -1
  63. package/dist/cli-arguments.js +4 -0
  64. package/dist/cli-arguments.js.map +1 -1
  65. package/dist/cli-branch-command.d.ts +46 -0
  66. package/dist/cli-branch-command.d.ts.map +1 -0
  67. package/dist/cli-branch-command.js +426 -0
  68. package/dist/cli-branch-command.js.map +1 -0
  69. package/dist/cli-capabilities.d.ts +28 -0
  70. package/dist/cli-capabilities.d.ts.map +1 -1
  71. package/dist/cli-capabilities.js +29 -1
  72. package/dist/cli-capabilities.js.map +1 -1
  73. package/dist/cli-create-command.d.ts +21 -0
  74. package/dist/cli-create-command.d.ts.map +1 -0
  75. package/dist/cli-create-command.js +319 -0
  76. package/dist/cli-create-command.js.map +1 -0
  77. package/dist/cli-detach.d.ts +62 -0
  78. package/dist/cli-detach.d.ts.map +1 -0
  79. package/dist/cli-detach.js +123 -0
  80. package/dist/cli-detach.js.map +1 -0
  81. package/dist/cli-draft-command.d.ts +63 -0
  82. package/dist/cli-draft-command.d.ts.map +1 -0
  83. package/dist/cli-draft-command.js +220 -0
  84. package/dist/cli-draft-command.js.map +1 -0
  85. package/dist/cli-export-command.d.ts.map +1 -1
  86. package/dist/cli-export-command.js +2 -3
  87. package/dist/cli-export-command.js.map +1 -1
  88. package/dist/cli-image-batch-command.d.ts +21 -0
  89. package/dist/cli-image-batch-command.d.ts.map +1 -1
  90. package/dist/cli-image-batch-command.js +65 -4
  91. package/dist/cli-image-batch-command.js.map +1 -1
  92. package/dist/cli-image-batch-resume.d.ts +7 -0
  93. package/dist/cli-image-batch-resume.d.ts.map +1 -1
  94. package/dist/cli-image-batch-resume.js +19 -2
  95. package/dist/cli-image-batch-resume.js.map +1 -1
  96. package/dist/cli-package-command.d.ts +18 -0
  97. package/dist/cli-package-command.d.ts.map +1 -0
  98. package/dist/cli-package-command.js +112 -0
  99. package/dist/cli-package-command.js.map +1 -0
  100. package/dist/cli-progress.d.ts +61 -0
  101. package/dist/cli-progress.d.ts.map +1 -1
  102. package/dist/cli-progress.js +53 -0
  103. package/dist/cli-progress.js.map +1 -1
  104. package/dist/cli-select-command.d.ts +12 -0
  105. package/dist/cli-select-command.d.ts.map +1 -0
  106. package/dist/cli-select-command.js +66 -0
  107. package/dist/cli-select-command.js.map +1 -0
  108. package/dist/cli.js +140 -7
  109. package/dist/cli.js.map +1 -1
  110. package/dist/create-identity.d.ts +25 -0
  111. package/dist/create-identity.d.ts.map +1 -0
  112. package/dist/create-identity.js +0 -0
  113. package/dist/create-identity.js.map +1 -0
  114. package/dist/draft-identity.d.ts +30 -0
  115. package/dist/draft-identity.d.ts.map +1 -0
  116. package/dist/draft-identity.js +0 -0
  117. package/dist/draft-identity.js.map +1 -0
  118. package/dist/draft-prompts.d.ts +35 -0
  119. package/dist/draft-prompts.d.ts.map +1 -0
  120. package/dist/draft-prompts.js +51 -0
  121. package/dist/draft-prompts.js.map +1 -0
  122. package/dist/draft-run.d.ts +32 -0
  123. package/dist/draft-run.d.ts.map +1 -0
  124. package/dist/draft-run.js +152 -0
  125. package/dist/draft-run.js.map +1 -0
  126. package/dist/draft-workspace.d.ts +90 -0
  127. package/dist/draft-workspace.d.ts.map +1 -0
  128. package/dist/draft-workspace.js +64 -0
  129. package/dist/draft-workspace.js.map +1 -0
  130. package/dist/image-batch-status.d.ts +42 -0
  131. package/dist/image-batch-status.d.ts.map +1 -0
  132. package/dist/image-batch-status.js +142 -0
  133. package/dist/image-batch-status.js.map +1 -0
  134. package/dist/image-dimensions.d.ts +12 -0
  135. package/dist/image-dimensions.d.ts.map +1 -0
  136. package/dist/image-dimensions.js +14 -0
  137. package/dist/image-dimensions.js.map +1 -0
  138. package/dist/image-generation-resume.d.ts +13 -0
  139. package/dist/image-generation-resume.d.ts.map +1 -1
  140. package/dist/image-generation-resume.js +13 -0
  141. package/dist/image-generation-resume.js.map +1 -1
  142. package/dist/index.d.ts +5 -0
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +4 -0
  145. package/dist/index.js.map +1 -1
  146. package/dist/legacy-skill-catalog.d.ts.map +1 -1
  147. package/dist/legacy-skill-catalog.js +115 -0
  148. package/dist/legacy-skill-catalog.js.map +1 -1
  149. package/dist/package-manifest-file.d.ts +21 -0
  150. package/dist/package-manifest-file.d.ts.map +1 -0
  151. package/dist/package-manifest-file.js +119 -0
  152. package/dist/package-manifest-file.js.map +1 -0
  153. package/dist/package-manifest-template.d.ts +23 -0
  154. package/dist/package-manifest-template.d.ts.map +1 -0
  155. package/dist/package-manifest-template.js +60 -0
  156. package/dist/package-manifest-template.js.map +1 -0
  157. package/dist/package-submission.d.ts +103 -0
  158. package/dist/package-submission.d.ts.map +1 -0
  159. package/dist/package-submission.js +173 -0
  160. package/dist/package-submission.js.map +1 -0
  161. package/dist/selection-record.d.ts +95 -0
  162. package/dist/selection-record.d.ts.map +1 -0
  163. package/dist/selection-record.js +121 -0
  164. package/dist/selection-record.js.map +1 -0
  165. package/package.json +1 -1
  166. package/skills/design/SKILL.md +40 -266
  167. package/skills/design-branch/SKILL.md +36 -116
  168. package/skills/design-convert/SKILL.md +62 -148
  169. package/skills/design-draft/SKILL.md +71 -135
  170. package/skills/design-search/SKILL.md +29 -78
  171. package/skills/design/references/continuation-contract.md +0 -60
  172. package/skills/design/references/selection-contract.md +0 -112
@@ -0,0 +1,84 @@
1
+ import { type TwelveUiBranchClient } from './branch-client.js';
2
+ import { type BranchNormalizedPlan } from './branch-plan.js';
3
+ /**
4
+ * The dispatch driver: the scheduler decides WHICH screens may run, this decides
5
+ * WHEN to ask again. It is `apps/web/src/pages/branch/useBranchRun.ts` with the
6
+ * React state machine removed and its three intervals kept as the same
7
+ * constants, because they are the cadence the server was tuned against.
8
+ *
9
+ * Spending rules, unchanged from the board:
10
+ * - automatic dispatch sends NO idempotency key, so any replay is free;
11
+ * - a settled slot is never re-offered; a redraw is an explicit action;
12
+ * - a gate refusal is not an error — the slot is held and offered again.
13
+ */
14
+ export declare const BRANCH_POLL_INTERVAL_MS = 4000;
15
+ export declare const BRANCH_PUMP_INTERVAL_MS = 2000;
16
+ export declare const BRANCH_GATE_BACKOFF_MS = 4000;
17
+ /** A screen's own server budget is 150 s; a whole run is bounded far above it. */
18
+ export declare const BRANCH_RUN_TIMEOUT_MS: number;
19
+ export type BranchDispatchEvent = {
20
+ kind: 'dispatched';
21
+ slot: string;
22
+ } | {
23
+ kind: 'succeeded';
24
+ slot: string;
25
+ } | {
26
+ kind: 'failed';
27
+ slot: string;
28
+ code: string;
29
+ message: string;
30
+ } | {
31
+ kind: 'held';
32
+ slot: string;
33
+ code: string;
34
+ waitingOn?: string;
35
+ } | {
36
+ kind: 'in-flight';
37
+ slot: string;
38
+ } | {
39
+ kind: 'blocked';
40
+ slot: string;
41
+ waitingOn: string;
42
+ reason: string;
43
+ } | {
44
+ kind: 'stopped';
45
+ code: string;
46
+ message: string;
47
+ };
48
+ export type BranchDispatchStop = {
49
+ code: string;
50
+ message: string;
51
+ };
52
+ export type BranchDispatchResult = {
53
+ plan: BranchNormalizedPlan;
54
+ stopped: BranchDispatchStop | null;
55
+ };
56
+ /** Everything a status surface needs to narrate motion, taken at one instant. */
57
+ export type BranchDispatchSnapshot = {
58
+ plan: BranchNormalizedPlan;
59
+ dispatchedAt: Readonly<Record<string, string>>;
60
+ };
61
+ /** Cadence and clock overrides. Tests drive these; the CLI never sets them. */
62
+ export type BranchDispatchTuning = {
63
+ now?: () => number;
64
+ sleep?: (milliseconds: number) => Promise<void>;
65
+ pollIntervalMs?: number;
66
+ pumpIntervalMs?: number;
67
+ gateBackoffMs?: number;
68
+ timeoutMs?: number;
69
+ };
70
+ export declare const runBranchDispatch: (args: {
71
+ client: TwelveUiBranchClient;
72
+ plan: BranchNormalizedPlan;
73
+ onEvent?: (event: BranchDispatchEvent) => void;
74
+ /** Called on every loop turn, changed or not, so a poller always sees motion. */
75
+ onSnapshot?: (snapshot: BranchDispatchSnapshot) => void;
76
+ now?: () => number;
77
+ sleep?: (milliseconds: number) => Promise<void>;
78
+ pollIntervalMs?: number;
79
+ pumpIntervalMs?: number;
80
+ gateBackoffMs?: number;
81
+ timeoutMs?: number;
82
+ signal?: AbortSignal;
83
+ }) => Promise<BranchDispatchResult>;
84
+ //# sourceMappingURL=branch-dispatch-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-dispatch-loop.d.ts","sourceRoot":"","sources":["../src/branch-dispatch-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ import { BranchApiError } from './branch-client.js';
2
+ import { branchBlockedSlots, branchDispatchableSlots, branchRunSettled, } from './branch-schedule.js';
3
+ import { applyBranchEnvelope } from './branch-plan.js';
4
+ /**
5
+ * The dispatch driver: the scheduler decides WHICH screens may run, this decides
6
+ * WHEN to ask again. It is `apps/web/src/pages/branch/useBranchRun.ts` with the
7
+ * React state machine removed and its three intervals kept as the same
8
+ * constants, because they are the cadence the server was tuned against.
9
+ *
10
+ * Spending rules, unchanged from the board:
11
+ * - automatic dispatch sends NO idempotency key, so any replay is free;
12
+ * - a settled slot is never re-offered; a redraw is an explicit action;
13
+ * - a gate refusal is not an error — the slot is held and offered again.
14
+ */
15
+ export const BRANCH_POLL_INTERVAL_MS = 4_000;
16
+ export const BRANCH_PUMP_INTERVAL_MS = 2_000;
17
+ export const BRANCH_GATE_BACKOFF_MS = 4_000;
18
+ /** A screen's own server budget is 150 s; a whole run is bounded far above it. */
19
+ export const BRANCH_RUN_TIMEOUT_MS = 60 * 60_000;
20
+ /** Refusals that end the run rather than holding one screen back. */
21
+ const TERMINAL_CODES = new Set([
22
+ 'quota_exhausted',
23
+ 'insufficient_funds',
24
+ 'expired',
25
+ 'rate_limited',
26
+ 'not_found',
27
+ 'unauthorized',
28
+ 'forbidden',
29
+ ]);
30
+ const scheduleView = (plan) => (plan.screens.map((screen) => ({
31
+ slot: screen.slot,
32
+ status: screen.status,
33
+ footer: screen.footer,
34
+ dependsOnSlot: screen.dependsOnSlot,
35
+ })));
36
+ const defaultSleep = async (milliseconds) => (new Promise((resolve) => { setTimeout(resolve, milliseconds); }));
37
+ export const runBranchDispatch = async (args) => {
38
+ const now = args.now ?? Date.now;
39
+ const sleep = args.sleep ?? defaultSleep;
40
+ const pollIntervalMs = args.pollIntervalMs ?? BRANCH_POLL_INTERVAL_MS;
41
+ const pumpIntervalMs = args.pumpIntervalMs ?? BRANCH_PUMP_INTERVAL_MS;
42
+ const gateBackoffMs = args.gateBackoffMs ?? BRANCH_GATE_BACKOFF_MS;
43
+ const emit = args.onEvent ?? (() => undefined);
44
+ const deadline = now() + (args.timeoutMs ?? BRANCH_RUN_TIMEOUT_MS);
45
+ let plan = args.plan;
46
+ let stopped = null;
47
+ const inFlight = new Set();
48
+ const heldUntil = {};
49
+ const active = new Set();
50
+ const announcedBlocked = new Set();
51
+ const dispatchedAt = {};
52
+ const snapshot = () => args.onSnapshot?.({ plan, dispatchedAt });
53
+ const markDispatched = (slot) => {
54
+ plan = {
55
+ ...plan,
56
+ screens: plan.screens.map((screen) => (screen.slot === slot && screen.status === 'pending'
57
+ ? { ...screen, status: 'dispatched' }
58
+ : screen)),
59
+ };
60
+ };
61
+ const markPending = (slot) => {
62
+ plan = {
63
+ ...plan,
64
+ screens: plan.screens.map((screen) => (screen.slot === slot ? { ...screen, status: 'pending' } : screen)),
65
+ };
66
+ };
67
+ const markFailed = (slot, code, message) => {
68
+ plan = {
69
+ ...plan,
70
+ screens: plan.screens.map((screen) => (screen.slot === slot
71
+ ? { ...screen, status: 'failed', error: { code, message } }
72
+ : screen)),
73
+ };
74
+ };
75
+ const announceBlocked = () => {
76
+ for (const blocked of branchBlockedSlots(scheduleView(plan))) {
77
+ if (announcedBlocked.has(blocked.slot))
78
+ continue;
79
+ announcedBlocked.add(blocked.slot);
80
+ emit({
81
+ kind: 'blocked',
82
+ slot: blocked.slot,
83
+ waitingOn: blocked.waitingOn,
84
+ reason: blocked.reason,
85
+ });
86
+ }
87
+ };
88
+ const dispatch = async (slot) => {
89
+ inFlight.add(slot);
90
+ markDispatched(slot);
91
+ dispatchedAt[slot] = new Date(now()).toISOString();
92
+ emit({ kind: 'dispatched', slot });
93
+ snapshot();
94
+ try {
95
+ // No idempotency key: an unkeyed repeat replays the settled screen free,
96
+ // while a new key would buy another image.
97
+ const envelope = await args.client.dispatchScreen(plan.runId, slot, {
98
+ ...(args.signal ? { signal: args.signal } : {}),
99
+ });
100
+ plan = applyBranchEnvelope(plan, envelope);
101
+ const resolved = plan.screens.find((screen) => screen.slot === slot);
102
+ if (resolved?.status === 'succeeded')
103
+ emit({ kind: 'succeeded', slot });
104
+ if (resolved?.status === 'failed') {
105
+ emit({
106
+ kind: 'failed',
107
+ slot,
108
+ code: resolved.error?.code ?? 'failed',
109
+ message: resolved.error?.message ?? 'This screen could not be generated.',
110
+ });
111
+ }
112
+ }
113
+ catch (error) {
114
+ const code = error instanceof BranchApiError ? error.code : 'error';
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ if (code === 'dependency_pending' || code === 'footer_reference_pending') {
117
+ // The worker knows more than this client did. Hold the slot and let the
118
+ // scheduler offer it again once its reference lands.
119
+ heldUntil[slot] = now() + gateBackoffMs;
120
+ markPending(slot);
121
+ emit({
122
+ kind: 'held',
123
+ slot,
124
+ code,
125
+ ...(error instanceof BranchApiError && error.dependsOn
126
+ ? { waitingOn: error.dependsOn }
127
+ : {}),
128
+ });
129
+ }
130
+ else if (code === 'candidate_in_flight') {
131
+ // Another caller owns the paid call. Leave the slot dispatched; the run
132
+ // poll reports its result. Note the FLAT error shape here.
133
+ emit({ kind: 'in-flight', slot });
134
+ }
135
+ else if (TERMINAL_CODES.has(code) || (error instanceof BranchApiError && error.status >= 500)) {
136
+ markPending(slot);
137
+ stopped ??= { code, message };
138
+ }
139
+ else {
140
+ markFailed(slot, code, message);
141
+ emit({ kind: 'failed', slot, code, message });
142
+ }
143
+ }
144
+ finally {
145
+ inFlight.delete(slot);
146
+ snapshot();
147
+ }
148
+ };
149
+ const poll = async () => {
150
+ try {
151
+ plan = applyBranchEnvelope(plan, await args.client.getRun(plan.runId, args.signal));
152
+ }
153
+ catch (error) {
154
+ const code = error instanceof BranchApiError ? error.code : 'error';
155
+ if (TERMINAL_CODES.has(code))
156
+ stopped ??= { code, message: error.message };
157
+ }
158
+ };
159
+ const track = (promise) => {
160
+ const tracked = promise.finally(() => { active.delete(tracked); });
161
+ active.add(tracked);
162
+ };
163
+ while (plan.screens.length > 0 && !branchRunSettled(scheduleView(plan))) {
164
+ if (stopped)
165
+ break;
166
+ if (now() >= deadline) {
167
+ stopped = {
168
+ code: 'run_timeout',
169
+ message: `Branch run ${plan.runId} did not settle within the run budget.`,
170
+ };
171
+ break;
172
+ }
173
+ announceBlocked();
174
+ for (const slot of branchDispatchableSlots(scheduleView(plan), {
175
+ inFlight,
176
+ heldUntil,
177
+ now: now(),
178
+ })) {
179
+ track(dispatch(slot));
180
+ }
181
+ // Every turn publishes a snapshot, changed or not: a poller that sees only
182
+ // "nothing yet" is the one that gives up and improvises.
183
+ snapshot();
184
+ if (active.size > 0) {
185
+ await Promise.race([...active, sleep(pumpIntervalMs)]);
186
+ continue;
187
+ }
188
+ // Nothing of ours is running. Either a sibling caller owns a slot, or every
189
+ // remaining screen is inside its gate backoff; both resolve by waiting and
190
+ // re-reading rather than by dispatching again.
191
+ await poll();
192
+ if (stopped || branchRunSettled(scheduleView(plan)))
193
+ break;
194
+ const nextOffer = Math.min(...Object.values(heldUntil).filter((until) => until > now()), now() + pollIntervalMs);
195
+ await sleep(Math.max(pumpIntervalMs, Math.min(pollIntervalMs, nextOffer - now())));
196
+ }
197
+ // Whatever ended the loop, in-flight work still owns paid calls; let it land
198
+ // so its result reaches the sentinel instead of being lost.
199
+ await Promise.allSettled([...active]);
200
+ announceBlocked();
201
+ snapshot();
202
+ if (stopped)
203
+ emit({ kind: 'stopped', ...stopped });
204
+ return { plan, stopped };
205
+ };
206
+ //# sourceMappingURL=branch-dispatch-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-dispatch-loop.js","sourceRoot":"","sources":["../src/branch-dispatch-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { TwelveUiBranchClient } from './branch-client.js';
2
+ /**
3
+ * Screens are written into a hidden staging directory and moved into
4
+ * `screens/` in one pass once the run is terminal, exactly as
5
+ * `image generate-batch` materializes its candidates. A caller listing
6
+ * `screens/` therefore never sees a half-written run and mistake it for
7
+ * failure; the directory goes from empty to complete.
8
+ *
9
+ * The staging directory is a child of the destination on purpose: a
10
+ * same-directory rename is same-filesystem, so materialization is atomic and
11
+ * can never fail with EXDEV.
12
+ */
13
+ export declare const BRANCH_STAGING_DIRECTORY_NAME = ".12ui-branch-staging";
14
+ export type BranchDownloadedScreen = {
15
+ slot: string;
16
+ path: string;
17
+ sha256: string;
18
+ bytes: number;
19
+ width: number;
20
+ height: number;
21
+ };
22
+ export declare const branchScreenPath: (screensDirectory: string, slot: string) => string;
23
+ /** Bytes already on disk are already paid for; a resume never re-downloads. */
24
+ export declare const readMaterializedScreen: (screensDirectory: string, slot: string) => Promise<BranchDownloadedScreen | null>;
25
+ export declare const stageBranchScreenBytes: (args: {
26
+ screensDirectory: string;
27
+ slot: string;
28
+ bytes: Uint8Array;
29
+ }) => Promise<BranchDownloadedScreen>;
30
+ export declare const stageBranchScreen: (args: {
31
+ client: TwelveUiBranchClient;
32
+ runId: string;
33
+ slot: string;
34
+ screensDirectory: string;
35
+ signal?: AbortSignal;
36
+ }) => Promise<BranchDownloadedScreen>;
37
+ /**
38
+ * Deliver every staged screen at once. Anything already materialized — a
39
+ * resumed run's earlier screens — stays where it is rather than being
40
+ * overwritten with bytes that would have to be proved identical first.
41
+ */
42
+ export declare const materializeBranchScreens: (screensDirectory: string, slots: readonly string[]) => Promise<void>;
43
+ //# sourceMappingURL=branch-downloads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-downloads.d.ts","sourceRoot":"","sources":["../src/branch-downloads.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG/D;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,6BAA6B,yBAAyB,CAAC;AAEpE,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,kBAAkB,MAAM,EAAE,MAAM,MAAM,KAAG,MAEzE,CAAC;AAgBF,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,GACjC,kBAAkB,MAAM,EACxB,MAAM,MAAM,KACX,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAiBvC,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,MAAM;IACjD,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;CACnB,KAAG,OAAO,CAAC,sBAAsB,CAajC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAC5C,MAAM,EAAE,oBAAoB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAG,OAAO,CAAC,sBAAsB,CAOjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,kBAAkB,MAAM,EACxB,OAAO,SAAS,MAAM,EAAE,KACvB,OAAO,CAAC,IAAI,CAgBd,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, rename, rm, rmdir, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { readImageDimensions } from './image-dimensions.js';
5
+ /**
6
+ * Screens are written into a hidden staging directory and moved into
7
+ * `screens/` in one pass once the run is terminal, exactly as
8
+ * `image generate-batch` materializes its candidates. A caller listing
9
+ * `screens/` therefore never sees a half-written run and mistake it for
10
+ * failure; the directory goes from empty to complete.
11
+ *
12
+ * The staging directory is a child of the destination on purpose: a
13
+ * same-directory rename is same-filesystem, so materialization is atomic and
14
+ * can never fail with EXDEV.
15
+ */
16
+ export const BRANCH_STAGING_DIRECTORY_NAME = '.12ui-branch-staging';
17
+ export const branchScreenPath = (screensDirectory, slot) => (path.join(screensDirectory, `${slot}.png`));
18
+ const stagedScreenPath = (screensDirectory, slot) => (path.join(screensDirectory, BRANCH_STAGING_DIRECTORY_NAME, `${slot}.png.part`));
19
+ const pathExists = async (target) => {
20
+ try {
21
+ await readFile(target);
22
+ return true;
23
+ }
24
+ catch (error) {
25
+ if (error.code === 'ENOENT')
26
+ return false;
27
+ throw error;
28
+ }
29
+ };
30
+ /** Bytes already on disk are already paid for; a resume never re-downloads. */
31
+ export const readMaterializedScreen = async (screensDirectory, slot) => {
32
+ const file = branchScreenPath(screensDirectory, slot);
33
+ let bytes;
34
+ try {
35
+ bytes = await readFile(file);
36
+ }
37
+ catch (error) {
38
+ if (error.code === 'ENOENT')
39
+ return null;
40
+ throw error;
41
+ }
42
+ const dimensions = await readImageDimensions(bytes);
43
+ return {
44
+ slot,
45
+ path: file,
46
+ sha256: createHash('sha256').update(bytes).digest('hex'),
47
+ bytes: bytes.byteLength,
48
+ ...dimensions,
49
+ };
50
+ };
51
+ export const stageBranchScreenBytes = async (args) => {
52
+ const staged = stagedScreenPath(args.screensDirectory, args.slot);
53
+ await mkdir(path.dirname(staged), { recursive: true });
54
+ await rm(staged, { force: true });
55
+ await writeFile(staged, args.bytes, { flag: 'wx', mode: 0o600 });
56
+ const dimensions = await readImageDimensions(args.bytes);
57
+ return {
58
+ slot: args.slot,
59
+ path: branchScreenPath(args.screensDirectory, args.slot),
60
+ sha256: createHash('sha256').update(args.bytes).digest('hex'),
61
+ bytes: args.bytes.byteLength,
62
+ ...dimensions,
63
+ };
64
+ };
65
+ export const stageBranchScreen = async (args) => {
66
+ const downloaded = await args.client.downloadScreen(args.runId, args.slot, args.signal);
67
+ return stageBranchScreenBytes({
68
+ screensDirectory: args.screensDirectory,
69
+ slot: args.slot,
70
+ bytes: downloaded.bytes,
71
+ });
72
+ };
73
+ /**
74
+ * Deliver every staged screen at once. Anything already materialized — a
75
+ * resumed run's earlier screens — stays where it is rather than being
76
+ * overwritten with bytes that would have to be proved identical first.
77
+ */
78
+ export const materializeBranchScreens = async (screensDirectory, slots) => {
79
+ await Promise.all(slots.map(async (slot) => {
80
+ const staged = stagedScreenPath(screensDirectory, slot);
81
+ if (!(await pathExists(staged)))
82
+ return;
83
+ const destination = branchScreenPath(screensDirectory, slot);
84
+ if (await pathExists(destination)) {
85
+ throw new Error(`Refusing to overwrite an existing screen at ${destination} with staged bytes from`
86
+ + ` ${staged}; inspect both before continuing.`);
87
+ }
88
+ await rename(staged, destination);
89
+ }));
90
+ // rmdir refuses a non-empty directory, so this both cleans up and proves
91
+ // nothing was left undelivered.
92
+ await rmdir(path.join(screensDirectory, BRANCH_STAGING_DIRECTORY_NAME)).catch(() => undefined);
93
+ };
94
+ //# sourceMappingURL=branch-downloads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-downloads.js","sourceRoot":"","sources":["../src/branch-downloads.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { BranchAspect } from './branch-client.js';
2
+ import type { BranchScope } from './branch-identity.js';
3
+ import type { BranchDispatchEvent } from './branch-dispatch-loop.js';
4
+ import type { BranchConversionSettings } from './branch-page-conversion.js';
5
+ import type { BranchProgressPhase, BranchProgressScreen } from './branch-progress.js';
6
+ import type { BranchCompletion } from './branch-run-record.js';
7
+ import type { PackageProgressEvent } from './package-submission.js';
8
+ /**
9
+ * The inputs and events of one branch run, in their own module so the phases
10
+ * that consume them — download, completion, the CLI — can import them without
11
+ * importing the orchestrator that drives them.
12
+ */
13
+ export type BranchWinnerInput = {
14
+ kind: 'run';
15
+ runId: string;
16
+ slot: string;
17
+ } | {
18
+ kind: 'image';
19
+ imagePath: string;
20
+ bytes: Uint8Array;
21
+ mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
22
+ sha256: string;
23
+ };
24
+ export type BranchExecutionInputs = {
25
+ outDir: string;
26
+ scope: BranchScope;
27
+ concept: string;
28
+ winner: BranchWinnerInput;
29
+ aspect?: BranchAspect;
30
+ maxScreens?: number;
31
+ convert?: BranchConversionSettings;
32
+ idempotencyKey?: string;
33
+ };
34
+ export type BranchExecutionEvent = BranchDispatchEvent | {
35
+ kind: 'phase';
36
+ phase: string;
37
+ detail?: string;
38
+ } | {
39
+ /** Published every loop turn so an observer always sees motion, not absence. */
40
+ kind: 'progress';
41
+ phase: BranchProgressPhase;
42
+ screens: BranchProgressScreen[];
43
+ } | {
44
+ kind: 'package';
45
+ pageId: string;
46
+ progress: PackageProgressEvent;
47
+ };
48
+ export type BranchExecutionResult = {
49
+ completion: BranchCompletion;
50
+ completionPath: string;
51
+ /** Non-null when the run must exit non-zero. A parked partial run does not. */
52
+ failure: Error | null;
53
+ };
54
+ //# sourceMappingURL=branch-execution-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-execution-types.d.ts","sourceRoot":"","sources":["../src/branch-execution-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEpE;;;;GAIG;AAEH,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5C;IACA,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEJ,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,wBAAwB,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,mBAAmB,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IACA,gFAAgF;IAChF,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC,GACC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,OAAO,EAAE,KAAK,GAAG,IAAI,CAAC;CACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=branch-execution-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-execution-types.js","sourceRoot":"","sources":["../src/branch-execution-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { type TwelveUiBranchClient } from './branch-client.js';
2
+ import { type BranchDispatchTuning } from './branch-dispatch-loop.js';
3
+ import type { TwelveUiConversionPackageClient } from './package-client.js';
4
+ import type { BranchExecutionEvent, BranchExecutionInputs, BranchExecutionResult } from './branch-execution-types.js';
5
+ export type { BranchExecutionEvent, BranchExecutionInputs, BranchExecutionResult, BranchWinnerInput, } from './branch-execution-types.js';
6
+ export declare const deriveBranchRunKey: (inputs: BranchExecutionInputs) => string;
7
+ export declare const executeBranchRun: (args: {
8
+ branchClient: TwelveUiBranchClient;
9
+ packageClient?: TwelveUiConversionPackageClient;
10
+ inputs: BranchExecutionInputs;
11
+ origin: string;
12
+ resume?: boolean;
13
+ onEvent?: (event: BranchExecutionEvent) => void;
14
+ dispatch?: BranchDispatchTuning;
15
+ packageTimeoutMs?: number;
16
+ signal?: AbortSignal;
17
+ }) => Promise<BranchExecutionResult>;
18
+ //# sourceMappingURL=branch-execution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-execution.d.ts","sourceRoot":"","sources":["../src/branch-execution.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAc5B,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,6BAA6B,CAAC;AAErC,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AAWrC,eAAO,MAAM,kBAAkB,GAAI,QAAQ,qBAAqB,KAAG,MAQlE,CAAC;AAkCF,eAAO,MAAM,gBAAgB,GAAU,MAAM;IAC3C,YAAY,EAAE,oBAAoB,CAAC;IACnC,aAAa,CAAC,EAAE,+BAA+B,CAAC;IAChD,MAAM,EAAE,qBAAqB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAG,OAAO,CAAC,qBAAqB,CAoOhC,CAAC"}