@vosjs/cli 0.78.0 → 0.80.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.
- package/README.md +152 -31
- package/dist/{assetManifest-S6H5F2VE.js → assetManifest-ETT3C7ZA.js} +3 -3
- package/dist/{chunk-JYCW4EOA.js → chunk-B373NVJE.js} +7 -3
- package/dist/chunk-B373NVJE.js.map +1 -0
- package/dist/{chunk-B4U4URE3.js → chunk-J3KHIZCH.js} +466 -412
- package/dist/chunk-J3KHIZCH.js.map +1 -0
- package/dist/{chunk-RUDDBHQ7.js → chunk-OWYOJB7K.js} +5 -1
- package/dist/chunk-OWYOJB7K.js.map +1 -0
- package/dist/{chunk-ZSJEISKL.js → chunk-PJ6MEJZV.js} +213 -28
- package/dist/chunk-PJ6MEJZV.js.map +1 -0
- package/dist/{chunk-INRUYQ6J.js → chunk-SP5OHTVB.js} +4285 -1560
- package/dist/chunk-SP5OHTVB.js.map +1 -0
- package/dist/cli.js +17 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.js +5 -5
- package/dist/manifest-VTOIEUHJ.js +8 -0
- package/dist/{run-TIFM3ZHI.js → run-2ELHMPTB.js} +5 -5
- package/dist/{shotList-SCA234OS.js → shotList-MSNWLVDN.js} +63 -9
- package/dist/shotList-MSNWLVDN.js.map +1 -0
- package/package.json +3 -3
- package/schema/actions.schema.json +498 -17
- package/schema/doc.schema.json +46 -5
- package/skills/VERSION +1 -1
- package/skills/launch-kit/SKILL.md +12 -3
- package/skills/product-video/SKILL.md +34 -0
- package/skills/product-video/references/taste.md +7 -3
- package/skills/vos-authoring/SKILL.md +8 -1
- package/skills/vos-create/SKILL.md +5 -0
- package/skills/vos-cut/SKILL.md +25 -2
- package/skills/vos-footage/SKILL.md +4 -0
- package/skills/vos-migrate/SKILL.md +4 -0
- package/dist/chunk-B4U4URE3.js.map +0 -1
- package/dist/chunk-INRUYQ6J.js.map +0 -1
- package/dist/chunk-JYCW4EOA.js.map +0 -1
- package/dist/chunk-RUDDBHQ7.js.map +0 -1
- package/dist/chunk-ZSJEISKL.js.map +0 -1
- package/dist/manifest-6DJSPKVH.js +0 -8
- package/dist/shotList-SCA234OS.js.map +0 -1
- /package/dist/{assetManifest-S6H5F2VE.js.map → assetManifest-ETT3C7ZA.js.map} +0 -0
- /package/dist/{manifest-6DJSPKVH.js.map → manifest-VTOIEUHJ.js.map} +0 -0
- /package/dist/{run-TIFM3ZHI.js.map → run-2ELHMPTB.js.map} +0 -0
package/schema/doc.schema.json
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"segments": {
|
|
29
29
|
"type": "array",
|
|
30
|
-
"description": "Kept SOURCE-time footage spans; the output timeline is their concatenation. Trim dead time by shrinking spans. Empty array = untrimmed. Never carry `rate` here (only lowered/derived segments do).",
|
|
30
|
+
"description": "Kept SOURCE-time footage spans; the output timeline is their concatenation, in list order. Trim dead time by shrinking spans. Empty array = untrimmed. Never carry `rate` here (only lowered/derived segments do). Unlike every other span list, segments may overlap: a segment may play seconds an earlier one already played (a cold open showing the end state, then the whole recording from its start), and every source-anchored span over those seconds (zoom, tilt, camMotion, speed, freeze, the cursor's clicks) plays in each occurrence. A layer anchored to a step starts in the pass where the step plays the longest. A segment with `camera: rest` keeps the camera at rest through its pass.",
|
|
31
31
|
"items": {
|
|
32
32
|
"type": "object",
|
|
33
33
|
"required": ["in", "out"],
|
|
@@ -55,6 +55,10 @@
|
|
|
55
55
|
"media": {
|
|
56
56
|
"type": "string",
|
|
57
57
|
"description": "the media this belongs to: a `media[].id`; absent = the primary, `source`"
|
|
58
|
+
},
|
|
59
|
+
"camera": {
|
|
60
|
+
"enum": ["rest"],
|
|
61
|
+
"description": "`rest`: the camera rests through this clip's pass. No zoom, tilt, cam move or follow recenter applies there, even over seconds another pass zooms on (a cold open of the settled whole before the recording plays with its zooms); the cursor, the click effects, the speed spans and the freezes still play. A span that runs into or out of the clip ramps at its edge as at any span's. Absent = the camera plays here as everywhere"
|
|
58
62
|
}
|
|
59
63
|
},
|
|
60
64
|
"additionalProperties": true
|
|
@@ -848,7 +852,8 @@
|
|
|
848
852
|
"description": "Screen-space layers drawn ABOVE the card, outside the zoom transform. OUTPUT-anchored (start is final-cut seconds \u2014 trims/speed never retime a title). A layer about something on the page names it with `pin` and is placed beside it, following it as the camera moves; a layer with no referent is a caption and sits in the margin. Optional; absent = none.",
|
|
849
853
|
"items": {
|
|
850
854
|
"type": "object",
|
|
851
|
-
"required": ["id", "kind", "
|
|
855
|
+
"required": ["id", "kind", "duration", "transform"],
|
|
856
|
+
"anyOf": [{ "required": ["start"] }, { "required": ["anchor"] }],
|
|
852
857
|
"additionalProperties": true,
|
|
853
858
|
"properties": {
|
|
854
859
|
"id": {
|
|
@@ -957,13 +962,13 @@
|
|
|
957
962
|
"start": {
|
|
958
963
|
"type": "number",
|
|
959
964
|
"minimum": 0,
|
|
960
|
-
"description": "OUTPUT seconds.
|
|
965
|
+
"description": "OUTPUT seconds. Required unless the layer carries an `anchor`; with one it is the fallback, used when the anchor cannot resolve, and may be left out (a layer whose anchor resolves nowhere and that has no start is refused by validate)"
|
|
961
966
|
},
|
|
962
967
|
"anchor": {
|
|
963
968
|
"type": "object",
|
|
964
969
|
"required": ["step"],
|
|
965
970
|
"additionalProperties": false,
|
|
966
|
-
"description": "When the layer starts, as a recorder step: the lowering maps the step's moment (meta.steps, SOURCE seconds of its media) through the segments, speed spans and freezes exactly as a source-anchored span is mapped, and adds `offset`. A trim, a speed change or a re-record moves the layer with its step; `start` is the fallback when the step is missing, skipped or cut out (validate says which), and `duration` stays the layer's own. A text layer's {elapsed} fills with the anchored step's off-camera wait (the meta.skips entry naming the step, else its offCameraMs; a step that waited for nothing reads the nearest earlier wait on its media) as a rounded duration, like \"12 minutes\". Not the re-record `anchor` on spans, which the lowering never reads",
|
|
971
|
+
"description": "When the layer starts, as a recorder step: the lowering maps the step's moment (meta.steps, SOURCE seconds of its media) through the segments, speed spans and freezes exactly as a source-anchored span is mapped, and adds `offset`. A trim, a speed change or a re-record moves the layer with its step; `start` is the fallback when the step is missing, skipped or cut out (validate says which) and may be left out, and `duration` stays the layer's own. On a cut that replays the step's seconds the layer starts in the pass where the step plays the longest (the first on a tie). A text layer's {elapsed} fills with the anchored step's off-camera wait (the meta.skips entry naming the step, else its offCameraMs; a step that waited for nothing reads the nearest earlier wait on its media) as a rounded duration, like \"12 minutes\". Not the re-record `anchor` on spans, which the lowering never reads",
|
|
967
972
|
"properties": {
|
|
968
973
|
"step": {
|
|
969
974
|
"type": ["string", "integer"],
|
|
@@ -1589,7 +1594,38 @@
|
|
|
1589
1594
|
"gain": {
|
|
1590
1595
|
"type": "number",
|
|
1591
1596
|
"minimum": 0,
|
|
1592
|
-
"maximum": 1
|
|
1597
|
+
"maximum": 1,
|
|
1598
|
+
"description": "linear gain; with a measured `normalize`, a trim on the target"
|
|
1599
|
+
},
|
|
1600
|
+
"normalize": {
|
|
1601
|
+
"type": "number",
|
|
1602
|
+
"minimum": -30,
|
|
1603
|
+
"maximum": -6,
|
|
1604
|
+
"description": "target integrated loudness, LUFS (e.g. -16). Applies only while `loudness.src` is this clip's `key`: the clip plays at 10^((normalize - loudness.lufs) / 20) × gain, which may exceed 1, capped so the file's sample peak stays under -1 dBFS and never lifted more than +24 dB. Unmeasured or measured for another key, it is ignored (vos validate warns: measure it with vos audio measure)"
|
|
1605
|
+
},
|
|
1606
|
+
"loudness": {
|
|
1607
|
+
"type": "object",
|
|
1608
|
+
"description": "what the file measures (BS.1770 integrated, K-weighted, gated, over the whole file), written by vos audio measure, vos score and vos join, never by hand",
|
|
1609
|
+
"required": ["lufs", "peakDb", "at", "src"],
|
|
1610
|
+
"properties": {
|
|
1611
|
+
"lufs": {
|
|
1612
|
+
"type": "number",
|
|
1613
|
+
"description": "integrated loudness, LUFS"
|
|
1614
|
+
},
|
|
1615
|
+
"peakDb": {
|
|
1616
|
+
"type": ["number", "null"],
|
|
1617
|
+
"description": "sample peak, dBFS (not oversampled)"
|
|
1618
|
+
},
|
|
1619
|
+
"at": {
|
|
1620
|
+
"type": "string",
|
|
1621
|
+
"description": "when it was measured (ISO date)"
|
|
1622
|
+
},
|
|
1623
|
+
"src": {
|
|
1624
|
+
"type": "string",
|
|
1625
|
+
"description": "the key it was measured for"
|
|
1626
|
+
}
|
|
1627
|
+
},
|
|
1628
|
+
"additionalProperties": false
|
|
1593
1629
|
},
|
|
1594
1630
|
"fadeIn": {
|
|
1595
1631
|
"type": "number",
|
|
@@ -1609,6 +1645,11 @@
|
|
|
1609
1645
|
"type": "boolean",
|
|
1610
1646
|
"description": "duck under the mic while speech is detected"
|
|
1611
1647
|
},
|
|
1648
|
+
"source": {
|
|
1649
|
+
"type": "string",
|
|
1650
|
+
"enum": ["page"],
|
|
1651
|
+
"description": "page: sound the recorded page itself played (a video or audio element heard during the take, meta.media), placed by the plan at the output time it was heard and keyed to the file fetched into page-sound/. A re-plan replaces only these; absent = a clip someone added"
|
|
1652
|
+
},
|
|
1612
1653
|
"media": {
|
|
1613
1654
|
"type": "string",
|
|
1614
1655
|
"description": "the media this belongs to: a `media[].id`; absent = the primary, `source`"
|
package/skills/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.26.0
|
|
@@ -91,7 +91,10 @@ labels typed, real-looking data, the feature mid-story.
|
|
|
91
91
|
**Write the story into `actions.json`.** Give steps an `id` (the re-render
|
|
92
92
|
loop and the template anchors address them by it) and a `caption` where a
|
|
93
93
|
beat deserves one (two to eight words; `plan` proposes it as a lower-third
|
|
94
|
-
at that step's moment on the cuts that take words)
|
|
94
|
+
at that step's moment on the cuts that take words), on the step whose
|
|
95
|
+
action it describes, and mark each beat's payoff `"zoom": true` so
|
|
96
|
+
`--zooms per-beat` keeps that zoom and no other (the `product-video`
|
|
97
|
+
skill, step 2, has the rule and the state check). After a click that
|
|
95
98
|
navigates, put a `wait` for the load, so the cut never dwells on a spinner.
|
|
96
99
|
|
|
97
100
|
- **Fresh recording**: the `product-video` skill's loop (explore →
|
|
@@ -180,7 +183,10 @@ not re-run `validate` after a push unless the human asks.
|
|
|
180
183
|
|
|
181
184
|
## 6. Push the release
|
|
182
185
|
|
|
183
|
-
|
|
186
|
+
Set `still` in `doc.json` to the payoff first (OUTPUT seconds): the
|
|
187
|
+
result the release is about, never the opening. Without one the cover is
|
|
188
|
+
derived near the start, usually a zoom on a navigation control under the
|
|
189
|
+
first caption. Then push the take labelled for the release:
|
|
184
190
|
|
|
185
191
|
```
|
|
186
192
|
vos push take --folder <project-slug> --label "v2.1 launch" --note "<what shipped, one line>" \
|
|
@@ -224,9 +230,12 @@ by data. Never spread before the seed is signed off.
|
|
|
224
230
|
anything records. Cost a full re-record once.
|
|
225
231
|
- A template's own palette on a deliverable: resolve `BRAND.md` (or witness
|
|
226
232
|
the site) before cutting, and put it beside the take.
|
|
233
|
+
- A push with no `still`: the release's watch page and share card open on
|
|
234
|
+
a frame near the start, not on what shipped.
|
|
227
235
|
- A cold open (`firstlast`): the first frame is the marketing page's
|
|
228
236
|
headline with the product nowhere; trim to the story or let the entrance
|
|
229
|
-
carry it.
|
|
237
|
+
carry it. A cold open of the RESULT is a repeated segment at camera rest
|
|
238
|
+
(the `vos-cut` skill, "Assemble").
|
|
230
239
|
- The loading plane on screen: a click that navigates needs the `wait`
|
|
231
240
|
after it.
|
|
232
241
|
- White words on a paper ground: the end card takes the brand's ink; a
|
|
@@ -134,6 +134,27 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
|
134
134
|
from hover-triggered menus (taste.md, flow rules). Check with
|
|
135
135
|
`vos validate actions.json`.
|
|
136
136
|
|
|
137
|
+
**Mark where the camera goes** (`@vosjs/cli` 0.80 and later). A step
|
|
138
|
+
with a `caption` starts a beat; put the caption on the step whose action
|
|
139
|
+
it describes. Mark each beat's PAYOFF `"zoom": true`: the moment the
|
|
140
|
+
viewer should see up close (the drag whose result is the point, the
|
|
141
|
+
click that opens the thing, the typing that names it). Mark a gesture
|
|
142
|
+
that must stay wide `"zoom": false`. Give every marked step an `id`.
|
|
143
|
+
Then `vos plan take --zooms per-beat` keeps exactly the marked zooms
|
|
144
|
+
and rejects the rest for good; the planner still picks level and
|
|
145
|
+
focus. Why marks: no rule inferred from a script matched a person's
|
|
146
|
+
zoom choices (the best got 3 of 5); marks reproduced all 5 and nothing
|
|
147
|
+
else.
|
|
148
|
+
|
|
149
|
+
**Check the app's own state** after any step that sets up the scene (a
|
|
150
|
+
document loaded, a count of items): `{ "do": "expect", "js":
|
|
151
|
+
"window.app.count()", "equals": 14 }`. A take can pass `--strict` with
|
|
152
|
+
the product in the wrong state (loaded twice, a duplicate on screen),
|
|
153
|
+
because the DOM says nothing about what a canvas app holds; with
|
|
154
|
+
`equals` a miss says what the app had. Read state only, never change
|
|
155
|
+
it. A collaboration product wants a second person on the page: `peers`
|
|
156
|
+
in `actions.json` (https://vos.so/llms-full.txt).
|
|
157
|
+
|
|
137
158
|
Then REHEARSE it (`@vosjs/cli` 0.39 and later):
|
|
138
159
|
`vos record --actions actions.json --out take --dry-run`. Every step runs
|
|
139
160
|
against the real page, in order, because a later selector usually exists
|
|
@@ -180,6 +201,8 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
|
180
201
|
- export: `{"resolution": "720p|1080p|2k|4k", "fps": 30}` — never above
|
|
181
202
|
the footage (validate warns).
|
|
182
203
|
Then `vos validate take --json` — lints must pass.
|
|
204
|
+
Several takes in one film, a cold open, an end card: the `vos-cut`
|
|
205
|
+
skill, "Assemble".
|
|
183
206
|
|
|
184
207
|
5. **Look before you render** (the taste.md quality loop):
|
|
185
208
|
- `vos frames take --at-zooms --times 0,25%,50%,75%,100% --json` → judge
|
|
@@ -201,6 +224,11 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
|
201
224
|
serves the take into the studio — your doc.json edits arrive intact and
|
|
202
225
|
every zoom span is draggable.
|
|
203
226
|
|
|
227
|
+
6c. **Pushing it to vos.so** (`vos push take`)? Set `still` in `doc.json`
|
|
228
|
+
(OUTPUT seconds) to the payoff first, the result the video is about,
|
|
229
|
+
never the opening: without one the cover is derived near the start,
|
|
230
|
+
usually a zoom on a navigation control under the first caption.
|
|
231
|
+
|
|
204
232
|
7. **Package for the destination**: `references/destinations.md`.
|
|
205
233
|
|
|
206
234
|
## Launch kit (one take → every cut of a release)
|
|
@@ -251,6 +279,8 @@ when the ask is a release, not one video.
|
|
|
251
279
|
frame the text, and open the span after the click.
|
|
252
280
|
- Routing the cursor through a hover-triggered menu between beats.
|
|
253
281
|
- A first frame or a last frame that cannot stand alone as a poster.
|
|
282
|
+
- A push with no `still`: the shelf, the watch page and the share card
|
|
283
|
+
show a frame near the start instead of the result.
|
|
254
284
|
- A frozen opening: a static landing page records as freeze-then-bang;
|
|
255
285
|
trim it or speed it, the story opens near the money shot.
|
|
256
286
|
- A sign-in in `steps`: every step there is in the footage and a typed
|
|
@@ -261,3 +291,7 @@ when the ask is a release, not one video.
|
|
|
261
291
|
`EXPOSED` list, and `mask` what is left.
|
|
262
292
|
- A `mask` with `as: "text"` over product copy or a number: that is no
|
|
263
293
|
longer a recording of the product.
|
|
294
|
+
- Picking zooms by hand from a long proposal list (a canvas take proposes
|
|
295
|
+
one per gesture): mark the payoff steps and plan `--zooms per-beat`.
|
|
296
|
+
- Trusting `--strict` for the scene: it proves the steps ran, not that the
|
|
297
|
+
app holds what the beat shows. Check with `expect` + `js` + `equals`.
|
|
@@ -47,8 +47,9 @@ The rubric for judging a take before its render ships. Judge stills
|
|
|
47
47
|
7. **Levels 1.4–2.8 read well**; above 3× is for genuine detail (a number, a
|
|
48
48
|
toggle), and must be brief. Below 1.3× reads as drift, not intent.
|
|
49
49
|
8. **One zoom per beat.** Zooms that chain across unrelated UI read as a lost
|
|
50
|
-
camera. If the planner over-suggested,
|
|
51
|
-
|
|
50
|
+
camera. If the planner over-suggested, mark the payoff steps and plan
|
|
51
|
+
`--zooms per-beat` (SKILL.md, step 2) rather than deleting spans by
|
|
52
|
+
hand — an un-zoomed second is better than an unmotivated one.
|
|
52
53
|
9. **Focus on the subject, not the cursor resting spot.** `cx/cy` are
|
|
53
54
|
normalized [0..1]; check the apex still shows the thing the beat is about,
|
|
54
55
|
with breathing room (the subject inside the middle ~60% of the crop).
|
|
@@ -99,7 +100,10 @@ The two enemies, both measurable:
|
|
|
99
100
|
per ~5s**, and let them happen WIDE (place zoom spans so the camera has
|
|
100
101
|
released before a click that re-layouts; zoom into the RESULT, not the
|
|
101
102
|
trigger). Census: `ffmpeg -vf "select='gt(scene,0.12)'"` on the final
|
|
102
|
-
render — each detection should be a transition you _chose_.
|
|
103
|
+
render — each detection should be a transition you _chose_. Prefer
|
|
104
|
+
`vos check-video <file>`: it also counts a hard cut between two light,
|
|
105
|
+
sparse pictures (thin lines on white cutting to an empty canvas),
|
|
106
|
+
which an average-difference threshold misses.
|
|
103
107
|
|
|
104
108
|
## Judge each still set against
|
|
105
109
|
|
|
@@ -57,10 +57,15 @@ the first line of `~/.config/vos/credentials`. With one, push:
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
vos push my-config.json --title "…" --desc "one line" \
|
|
60
|
-
--prompt "<the person's prompt, as they wrote it>" --wait
|
|
60
|
+
--prompt "<the person's prompt, as they wrote it>" --still <t> --wait
|
|
61
61
|
# --wait stays until the cover has rendered, then prints the links
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
**Set the cover at the payoff.** `--still <t>` (OUTPUT seconds) picks the
|
|
65
|
+
frame the shelf, the thumbnail and the share card show: the result the
|
|
66
|
+
film is about, never the opening. Without it the cover is an early frame,
|
|
67
|
+
before anything has arrived; later pushes keep the one you set.
|
|
68
|
+
|
|
64
69
|
**Show the prompt that made it.** Pass the person's prompt with `--prompt`
|
|
65
70
|
on the push that creates the vos: the watch page shows it by default, so
|
|
66
71
|
whoever watches sees what was asked. Use their words as they wrote them,
|
|
@@ -155,6 +160,8 @@ examples of every major type: `references/examples.md`.
|
|
|
155
160
|
7. **NO `Date.now()` / `Math.random()`** in frame paths — determinism is the
|
|
156
161
|
contract; derive everything from the timeline's time and `ctx.data`
|
|
157
162
|
8. **NO `onFrame`** for new configs — put all per-frame logic in `onUpdate`
|
|
163
|
+
9. **NO push without `--still`** at the payoff — the default cover is an
|
|
164
|
+
early frame, before the film has arrived
|
|
158
165
|
|
|
159
166
|
## Params and Looks (make it remixable)
|
|
160
167
|
|
|
@@ -171,6 +171,11 @@ vos push <file>.json --title "…" --desc "one line" --tags a,b \
|
|
|
171
171
|
|
|
172
172
|
`--prompt` shows the person's prompt on the watch page by default: their words as they wrote them, never yours, with no file path, key or email they did not mean to publish. Say it is shown, and that they can take it off from the watch page.
|
|
173
173
|
|
|
174
|
+
Add `--still <t>` (OUTPUT seconds) on the first push, at the payoff: the
|
|
175
|
+
frame the design is about, never the opening. Without it the cover is an
|
|
176
|
+
early frame, before the composition has arrived; every later push keeps
|
|
177
|
+
the one you set.
|
|
178
|
+
|
|
174
179
|
Pass `--label` and `--note` on EVERY push, the first one included: a
|
|
175
180
|
create stamps them on v1, and the history is the conversation the human
|
|
176
181
|
reads. The push creates a PRIVATE vos on the key owner's shelf, filed
|
package/skills/vos-cut/SKILL.md
CHANGED
|
@@ -135,7 +135,10 @@ Per beat, against `plan` and the recipe:
|
|
|
135
135
|
decides the level, obey it. `cx`/`cy` = the moment's `focus`. Ids you
|
|
136
136
|
mint: `u1`, `u2`… In an editor recording, zoom the LANE where a span
|
|
137
137
|
appears, never the canvas being dragged. Release a zoom BEFORE a click
|
|
138
|
-
that navigates, so the page swap plays wide.
|
|
138
|
+
that navigates, so the page swap plays wide. A scripted take whose
|
|
139
|
+
steps carry `"zoom": true` marks (the `product-video` skill, step 2):
|
|
140
|
+
`vos plan <take> --zooms per-beat` keeps exactly those, so add a mark
|
|
141
|
+
to `actions.json` and re-plan rather than choosing among proposals.
|
|
139
142
|
- **Speed.** Keep proposals on typing/idle/scroll; add `2×`-`3×` on a stretch
|
|
140
143
|
whose `activity` is low and no beat needs, or under drag clusters; never
|
|
141
144
|
over a beat's payoff, never over playback.
|
|
@@ -143,6 +146,13 @@ Per beat, against `plan` and the recipe:
|
|
|
143
146
|
personality) and a beat earns punctuation. One pose per ~5s, ±5..18°.
|
|
144
147
|
- **Trim.** Head and tail: cut to the first and last thing that matters
|
|
145
148
|
(`segments`), leaving ~0.5s of settle at the end. Loading screens are cut.
|
|
149
|
+
- **Assemble.** A cold open is a SEGMENT that repeats seconds the cut plays
|
|
150
|
+
again later, marked `"camera": "rest"` so it shows the settled whole
|
|
151
|
+
while the main play zooms; never a second join of the same recording.
|
|
152
|
+
An end card or caption with an `anchor` to a step needs no `start`.
|
|
153
|
+
Several takes are one `vos join`; to add, swap or reorder an input
|
|
154
|
+
later, run the same join with `--keep-edits`, which keeps every trim,
|
|
155
|
+
span, caption and still made since. Never rebuild the joined take.
|
|
146
156
|
- **Text.** A caption per beat that has something to say, at a cadence (one
|
|
147
157
|
every 5-10s, 2.5-4s each, never two at once), in the product's words and
|
|
148
158
|
the video's intention; lower-third `y ≈ 0.82`; OUTPUT seconds; never over
|
|
@@ -165,6 +175,7 @@ do not understand.
|
|
|
165
175
|
|
|
166
176
|
```bash
|
|
167
177
|
vos validate <take> # clean; READ the framing warnings, fix them
|
|
178
|
+
vos info <take> # every step's source seconds, and the cut in output seconds
|
|
168
179
|
vos digest <take> --no-frames # after retiming: fresh OUTPUT times, same frames
|
|
169
180
|
vos frames <take> --at-moments --at-zooms --times 0,25%,50%,75%,100%
|
|
170
181
|
vos frames <take> --times <every caption start + 1> # each caption on its own ground
|
|
@@ -184,6 +195,12 @@ beside "what the cut shows".
|
|
|
184
195
|
vos push <take> --label "<what, imperative, ≤60 chars>" --note "<the ask you cut to; the beats with source seconds; what you dropped and added>" [--folder <slug>]
|
|
185
196
|
```
|
|
186
197
|
|
|
198
|
+
Before pushing, set `still` in `doc.json` to the payoff, in OUTPUT seconds
|
|
199
|
+
(`vos plan <take> --still <t>` writes it, `vos frames <take> --at-still`
|
|
200
|
+
shows it): the result the cut is about, never the opening. Without one the
|
|
201
|
+
cover is derived near the start, usually a zoom on a navigation control
|
|
202
|
+
under the first caption.
|
|
203
|
+
|
|
187
204
|
The base comes from `vos.json`. `409 stale_base` → `vos pull`, re-apply on
|
|
188
205
|
top, push again. `409 protected_conflict` → keep the human's values unless
|
|
189
206
|
the ask named that node (`--override <id>` only then). A 400 prints the
|
|
@@ -233,9 +250,15 @@ never write a status line.
|
|
|
233
250
|
- speed up playback, or zoom a canvas being dragged;
|
|
234
251
|
- spread a series before its seed is signed off;
|
|
235
252
|
- push without `--label` and `--note`.
|
|
253
|
+
- push without a `still` at the payoff (the derived cover sits near the
|
|
254
|
+
start, under the first caption);
|
|
236
255
|
- open a zoom before the click it frames, or aim it at a `type` verb's
|
|
237
256
|
field centre (frame the text; start the span after the click);
|
|
238
257
|
- leave the cold open frozen (a static page records as freeze-then-bang:
|
|
239
258
|
trim it, or speed the load);
|
|
240
259
|
- end wide on an empty page: the last frame is the poster, so the closing
|
|
241
|
-
span holds the result and its proof
|
|
260
|
+
span holds the result and its proof;
|
|
261
|
+
- write a helper script to print step times or where a layer lands:
|
|
262
|
+
`vos info <take>` reads both clocks;
|
|
263
|
+
- rebuild a joined take to add a scene, or join a recording to itself for
|
|
264
|
+
a cold open (`--keep-edits`; a repeated segment).
|
|
@@ -68,6 +68,10 @@ sliced to the window); the editorial cut still trims `segments`.
|
|
|
68
68
|
|
|
69
69
|
## 4. The shelf is the record — push BEFORE the handoff
|
|
70
70
|
|
|
71
|
+
Set `still` in `doc.json` (OUTPUT seconds) to the payoff first, the result
|
|
72
|
+
the footage shows, never the opening: without one the cover is derived
|
|
73
|
+
near the start, usually a zoom on a navigation control.
|
|
74
|
+
|
|
71
75
|
```bash
|
|
72
76
|
vos push take --label "footage handoff" --note "<what it shows; which composition it feeds>" \
|
|
73
77
|
--prompt "<the person's prompt, as they wrote it>"
|
|
@@ -81,6 +81,10 @@ with `vos render take check.webm --range a..b --draft`.
|
|
|
81
81
|
|
|
82
82
|
## 4. Push — the old demo becomes a living document
|
|
83
83
|
|
|
84
|
+
Set `still` in `doc.json` (OUTPUT seconds) to the payoff first, the result
|
|
85
|
+
the demo is about, never the opening: without one the cover is derived near
|
|
86
|
+
the start, which on an old demo is usually its title or a navigation click.
|
|
87
|
+
|
|
84
88
|
```bash
|
|
85
89
|
vos push take --folder <project> --label "migrated from <source>" \
|
|
86
90
|
--note "<what it shows; that old edits are baked; what you re-cut>"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/flagUse.ts","../src/args.ts","../src/plugin/container.ts","../src/programAssets.ts","../src/plugin/safeWrite.ts","../src/plugin/args.ts","../src/plugin/platform.ts","../src/plugin/hostAgent.ts"],"sourcesContent":["/**\n * Which of the flags a person gave did the verb actually READ?\n *\n * The parsers accept any `--name`, so a mistyped or misplaced flag used to be\n * swallowed: `vos frames <take> --at 3,5,8` wrote five evenly spread stills\n * and exited 0, because the flag is `--times` and nothing ever looked at\n * `at`. It looked like it worked.\n *\n * A declared list per verb would be the obvious guard and the wrong one here:\n * several verbs read their options through data (a name table, a helper three\n * calls down), so a list drifts, and the day it drifts it REFUSES a real flag\n * for every agent at once. Reading is the truth, so reading is what is\n * recorded: both parsers hand back their flags behind a proxy that notes each\n * name a verb asks for, and the CLI's one exit point compares that with what\n * was given. It cannot reject a flag a verb uses, by construction.\n *\n * The cost of being certain is that the verdict lands AFTER the verb ran, so\n * the message says so plainly: the command ran, without that flag.\n */\n\nconst given = new Set<string>()\nconst used = new Set<string>()\n\n/** A flag bag that remembers which names were asked for. */\nexport function trackFlags<T extends Record<string, unknown>>(bag: T): T {\n return new Proxy(bag, {\n get(target, key, receiver) {\n if (typeof key === 'string') used.add(key)\n return Reflect.get(target, key, receiver)\n },\n has(target, key) {\n if (typeof key === 'string') used.add(key)\n return Reflect.has(target, key)\n },\n // hasOwnProperty.call(flags, name) lands here.\n getOwnPropertyDescriptor(target, key) {\n if (typeof key === 'string') used.add(key)\n return Reflect.getOwnPropertyDescriptor(target, key)\n },\n // A spread, Object.keys or JSON.stringify hands every flag onward; what\n // happens to them after that cannot be seen, so they all count as read.\n ownKeys(target) {\n for (const key of Reflect.ownKeys(target))\n if (typeof key === 'string') used.add(key)\n return Reflect.ownKeys(target)\n },\n })\n}\n\nexport function noteGivenFlag(name: string): void {\n given.add(name)\n}\n\n/** Flags that were given and never read, in the order they were given. */\nexport function unusedFlags(): string[] {\n return [...given].filter((name) => !used.has(name) && name !== 'help')\n}\n\nexport function resetFlagUse(): void {\n given.clear()\n used.clear()\n}\n\n/**\n * The `--flags` a verb documents: its `vos <verb>` line in a help text plus\n * the indented lines that continue it.\n */\nexport function helpFlagsFor(help: string, verb: string): string[] {\n const out = new Set<string>()\n let inVerb = false\n for (const line of help.split('\\n')) {\n const head = /^\\s{2}vos\\s+(\\S+)/.exec(line)\n if (head) inVerb = head[1] === verb\n else if (!/^\\s{3,}\\S/.test(line)) inVerb = false\n if (!inVerb) continue\n for (const m of line.matchAll(/--([a-z][a-z0-9-]*)/g)) out.add(m[1])\n }\n return [...out]\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, i) => i)\n for (let i = 1; i <= a.length; i++) {\n let prev = row[0]\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const keep = row[j]\n row[j] = Math.min(\n row[j] + 1,\n row[j - 1] + 1,\n prev + (a[i - 1] === b[j - 1] ? 0 : 1),\n )\n prev = keep\n }\n }\n return row[b.length]\n}\n\n/** The documented flag a mistyped one most likely meant, or null. */\nexport function nearestFlag(name: string, candidates: string[]): string | null {\n let best: string | null = null\n let bestScore = Infinity\n for (const c of candidates) {\n if (c === name) continue\n // A candidate that contains the name (or the reverse) is as close as a\n // one-letter slip: `--zooms` for `--at-zooms`, `--body` for `--body-px`.\n const d = c.includes(name) || name.includes(c) ? 1 : editDistance(name, c)\n if (d < bestScore) {\n bestScore = d\n best = c\n }\n }\n return best !== null && bestScore <= Math.max(2, Math.floor(name.length / 3))\n ? best\n : null\n}\n\n/** The sentence for a run that ignored what it was given, or null when nothing was. */\nexport function unusedFlagsMessage(\n verb: string,\n documented: string[],\n): string | null {\n const unused = unusedFlags()\n if (!unused.length) return null\n const parts = unused.map((name) => {\n const near = nearestFlag(name, documented)\n return `--${name}${near ? ` (did you mean --${near}?)` : ''}`\n })\n const list = documented.length\n ? ` It reads: ${documented.map((f) => `--${f}`).join(' ')}.`\n : ''\n return `vos ${verb} RAN, and ignored ${parts.join(', ')}: ${unused.length > 1 ? 'they are' : 'it is'} not something this verb reads, so what it printed above was made without ${unused.length > 1 ? 'them' : 'it'}.${list}`\n}\n","/**\n * Minimal argv parser — positionals + `--flag value` / `--flag=value` /\n * boolean flags. Deliberately tiny: the CLI has a small, stable surface and\n * agents benefit from predictable, dependency-free parsing.\n */\nimport { noteGivenFlag, trackFlags } from './flagUse'\n\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | true>\n}\n\nexport class UsageError extends Error {}\n\nexport function parseArgs(\n argv: string[],\n booleanFlags: ReadonlySet<string>,\n): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | true> = {}\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') {\n positionals.push(...argv.slice(i + 1))\n break\n }\n if (arg.startsWith('--')) {\n const eq = arg.indexOf('=')\n if (eq !== -1) {\n flags[arg.slice(2, eq)] = arg.slice(eq + 1)\n continue\n }\n const name = arg.slice(2)\n if (booleanFlags.has(name)) {\n flags[name] = true\n continue\n }\n const next = argv[i + 1]\n if (next === undefined || next.startsWith('--')) {\n throw new UsageError(`--${name} expects a value`)\n }\n flags[name] = next\n i++\n continue\n }\n positionals.push(arg)\n }\n for (const name of Object.keys(flags)) noteGivenFlag(name)\n return { positionals, flags: trackFlags(flags) }\n}\n\nexport function numFlag(\n flags: ParsedArgs['flags'],\n name: string,\n fallback: number,\n): number {\n const v = flags[name]\n if (v === undefined) return fallback\n const n = Number(v)\n if (!Number.isFinite(n))\n throw new UsageError(`--${name} expects a number, got \"${String(v)}\"`)\n return n\n}\n","/**\n * What a media file IS, read from its first bytes.\n *\n * A file's NAME and its Content-Type are both CLAIMS, and both drift from the\n * bytes: the in-page recorder remuxes a take to mp4 while keeping the name\n * `recording.webm`, and a hosted asset keeps whatever name it was uploaded\n * under. Believing a claim is how an mp4 gets uploaded as `video/webm`, and\n * every render of that version then dies inside the video element with a\n * format error — one machine and one day away from the mislabelling that\n * caused it.\n *\n * So the rule is one line: THE BYTES DECIDE. A claim is the fallback only for\n * bytes that say nothing (an SVG, a recipe, an unknown blob), which keeps the\n * override safe — a name is corrected only when the file positively contradicts\n * it, never on a guess.\n */\n\nconst OCTET_STREAM = 'application/octet-stream'\n\n/** The one extension↔type table. */\nconst TYPES_BY_EXTENSION: Record<string, string> = {\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n avif: 'image/avif',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n glb: 'model/gltf-binary',\n}\n\n/** The extension each type is written under, where the table is many-to-one. */\nconst EXTENSION_BY_TYPE: Record<string, string> = {\n 'image/jpeg': '.jpg',\n 'video/mp4': '.mp4',\n 'audio/mp4': '.m4a',\n}\n\n/**\n * Every container a take's own media (footage, mic, cam) can come home in —\n * the name `record` writes first, so a resolver is deterministic when a stale\n * sibling is somehow present.\n */\nexport const TAKE_MEDIA_EXTENSIONS = [\n '.webm',\n '.mp4',\n '.mov',\n '.m4a',\n '.mp3',\n '.ogg',\n '.wav',\n] as const\n\n/** Enough bytes for every signature below (the longest reads 12). */\nexport const MEDIA_HEAD_BYTES = 16\n\n/**\n * ISO base media brands that are NOT plain mp4. Everything else carrying an\n * `ftyp` box (isom, mp42, avc1, iso5, dash, M4V…) is video/mp4.\n */\nconst ISO_BRANDS: Record<string, string> = {\n 'qt ': 'video/quicktime',\n 'M4A ': 'audio/mp4',\n 'M4B ': 'audio/mp4',\n avif: 'image/avif',\n avis: 'image/avif',\n}\n\n/** RIFF forms, read from the four bytes after the size field. */\nconst RIFF_FORMS: Record<string, string> = {\n WEBP: 'image/webp',\n WAVE: 'audio/wav',\n 'AVI ': 'video/x-msvideo',\n}\n\n/** The media type a file's first bytes prove, or null when they prove nothing. */\nexport function sniffMediaType(head: Uint8Array): string | null {\n const ascii = (at: number, len: number): string =>\n head.length >= at + len\n ? String.fromCharCode(...head.subarray(at, at + len))\n : ''\n const startsWith = (...bytes: number[]): boolean =>\n bytes.every((b, i) => head[i] === b)\n\n // EBML: Matroska and its WebM profile. A .mkv sniffs as webm, which is what\n // every consumer here wants — the distinction is a codec constraint, not a\n // container one.\n if (startsWith(0x1a, 0x45, 0xdf, 0xa3)) return 'video/webm'\n if (ascii(4, 4) === 'ftyp') return ISO_BRANDS[ascii(8, 4)] ?? 'video/mp4'\n if (ascii(0, 4) === 'RIFF') return RIFF_FORMS[ascii(8, 4)] ?? null\n if (startsWith(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a))\n return 'image/png'\n if (startsWith(0xff, 0xd8, 0xff)) return 'image/jpeg'\n if (ascii(0, 4) === 'GIF8') return 'image/gif'\n if (ascii(0, 4) === 'OggS') return 'audio/ogg'\n if (ascii(0, 3) === 'ID3') return 'audio/mpeg'\n // Binary glTF: the magic is the four ASCII bytes `glTF`.\n if (ascii(0, 4) === 'glTF') return 'model/gltf-binary'\n return null\n}\n\n/** A Content-Type header reduced to its bare type, or '' when it says nothing. */\nfunction bareType(value: string | null | undefined): string {\n const type = (value ?? '').split(';')[0].trim().toLowerCase()\n // `binary/octet-stream` is a common spelling of the same non-answer.\n return !type || type === OCTET_STREAM || type === 'binary/octet-stream'\n ? ''\n : type\n}\n\n/** The content type a file's NAME claims, by extension. */\nexport function mediaContentType(file: string): string {\n const ext = (/\\.([a-z0-9]+)$/i.exec(file)?.[1] ?? '').toLowerCase()\n return TYPES_BY_EXTENSION[ext] ?? OCTET_STREAM\n}\n\n/** The extension a media type is written under ('' when it has none here). */\nexport function extensionFor(contentType: string): string {\n const type = (contentType ?? '').split(';')[0].trim().toLowerCase()\n if (EXTENSION_BY_TYPE[type]) return EXTENSION_BY_TYPE[type]\n const ext = Object.keys(TYPES_BY_EXTENSION).find(\n (e) => TYPES_BY_EXTENSION[e] === type,\n )\n return ext ? `.${ext}` : ''\n}\n\nexport interface MediaTypeClaims {\n /** The file's first bytes — `MEDIA_HEAD_BYTES` is enough. */\n head?: Uint8Array | null\n /** What a server said it was serving. */\n declared?: string | null\n /** What the name claims. */\n filename?: string | null\n}\n\nexport interface ResolvedMediaType {\n type: string\n /** Which claim won, so a caller can SAY when the bytes overruled a name. */\n from: 'bytes' | 'declared' | 'filename' | 'unknown'\n}\n\n/** The type to believe: the bytes, then the server, then the name. */\nexport function resolveMediaType(claims: MediaTypeClaims): ResolvedMediaType {\n const sniffed = claims.head?.length ? sniffMediaType(claims.head) : null\n if (sniffed) return { type: sniffed, from: 'bytes' }\n const declared = bareType(claims.declared)\n if (declared) return { type: declared, from: 'declared' }\n const named = mediaContentType(claims.filename ?? '')\n if (named !== OCTET_STREAM) return { type: named, from: 'filename' }\n return { type: OCTET_STREAM, from: 'unknown' }\n}\n\n/**\n * The same name wearing the extension its type earns. Unchanged when the two\n * already agree (`.jpeg` is not churned into `.jpg`) or when the type has no\n * extension here, so a correction is always a real one.\n */\nexport function nameForType(filename: string, type: string): string {\n const ext = extensionFor(type)\n if (!ext) return filename\n const current = /\\.[a-z0-9]+$/i.exec(filename)?.[0] ?? ''\n if (current && mediaContentType(`x${current}`) === type) return filename\n return `${filename.slice(0, filename.length - current.length)}${ext}`\n}\n\n/**\n * What to say about a file whose name and bytes disagree, or null when they\n * agree or the bytes prove nothing.\n *\n * Every reader in this CLI now believes the bytes (the take server sniffs, a\n * push declares from them), so a mismatch breaks nothing here — it is still a\n * file lying about itself to every player, editor and person who opens it, and\n * the rename is one move.\n */\nexport function containerMismatch(\n filename: string,\n head: Uint8Array,\n): string | null {\n const actual = sniffMediaType(head)\n if (!actual) return null\n const claimed = mediaContentType(filename)\n if (claimed === actual) return null\n return `${filename} is ${actual}, not the ${claimed} its name claims — rename it to ${nameForType(filename, actual)}`\n}\n","/**\n * The files a program declares (`config.assets`), for a LOCAL run of it.\n *\n * A capture page has an origin of its own, so a manifest entry that names a\n * file beside the config (`./logo.png`) or a hosted one (`asset:<id>`)\n * cannot be fetched from inside it as written. Each is made reachable here\n * and handed to the engine as the page's `ctx.assets`:\n *\n * - a local file is SERVED to the page from disk, under a path on the\n * page's own origin, so a video is streamed and never inlined;\n * - a hosted asset is downloaded once with the caller's credential into a\n * cache directory and served the same way;\n * - a URL is used as it is.\n *\n * A file that cannot be reached keeps its ref and is said, so the render\n * shows a missing picture with a reason instead of a silent blank.\n */\nimport { createHash } from 'node:crypto'\nimport {\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readSync,\n readdirSync,\n realpathSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { homedir } from 'node:os'\nimport { extname, join, resolve, sep } from 'node:path'\nimport { extensionFor } from './plugin/container'\n\n/** Where served files live on a page's own origin. */\nexport const ASSET_ROUTE = '/__assets/'\n\nexport interface ProgramAssetsOptions {\n /**\n * The program's directory: where the manifest's paths are read, and the\n * only place they may reach. Null for a config loaded from a URL, which\n * has no directory and reads no local file.\n */\n baseDir: string | null\n /** For a hosted ref: where it lives and who may read it. */\n origin?: string\n key?: string | null\n log: (line: string) => void\n /**\n * Where hosted files are kept between runs. Default `~/.cache/vos/assets`,\n * private to the user: a shared temp directory would leave a private\n * file readable by everyone else on the machine.\n */\n cacheDir?: string\n}\n\nexport interface ProgramAssets {\n /** Name → the URL (or URLs) the page reads as `ctx.assets.<name>`. */\n assets: Record<string, string | string[]>\n /** Served path (`/__assets/…`) → the file on disk behind it. */\n files: Record<string, string>\n}\n\nconst UUID = '[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}'\nconst HOSTED_URI = new RegExp(`^asset:(${UUID})$`)\nconst HOSTED_FILE = new RegExp(\n `^(https?://[^/?#]+)?/api/assets/(${UUID})/file(?:[?#].*)?$`,\n)\n\n/**\n * The hosted asset id a ref names, or null: `asset:<id>`, the app-relative\n * file path, or that path on one of `homeOrigins`. The same path on any\n * other host is someone else's URL and is left a URL: reading it as a\n * hosted file would name an asset that does not exist.\n */\nexport function hostedAssetId(\n ref: string,\n homeOrigins: readonly string[] = [],\n): string | null {\n const uri = HOSTED_URI.exec(ref)\n if (uri) return uri[1]\n const file = HOSTED_FILE.exec(ref)\n if (!file) return null\n return file[1] === undefined || homeOrigins.includes(file[1]) ? file[2] : null\n}\n\n/** A manifest name: an identifier, as the engine's schema holds it. */\nconst ASSET_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/** The type a served file wears, by extension (never by its bytes here). */\nconst SERVED_TYPES: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.avif': 'image/avif',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.mov': 'video/quicktime',\n '.mp3': 'audio/mpeg',\n '.ogg': 'audio/ogg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.glb': 'model/gltf-binary',\n '.gltf': 'model/gltf+json',\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.json': 'application/json',\n}\n\nexport interface ServedBytes {\n status: 200 | 206 | 416\n headers: Record<string, string>\n body: Buffer\n}\n\n/**\n * A declared file as an HTTP answer, honouring `Range`. A video element\n * seeks by range and hangs forever against a server that answers only\n * 200, so both the render page's route and the preview server answer\n * through here.\n */\nexport function serveFile(file: string, range?: string | null): ServedBytes {\n const size = statSync(file).size\n const type =\n SERVED_TYPES[extname(file).toLowerCase()] ?? 'application/octet-stream'\n const base = { 'content-type': type, 'accept-ranges': 'bytes' }\n const read = (start: number, end: number): Buffer => {\n const body = Buffer.alloc(Math.max(0, end - start + 1))\n const fd = openSync(file, 'r')\n try {\n readSync(fd, body, 0, body.length, start)\n } finally {\n closeSync(fd)\n }\n return body\n }\n const m = range ? /^bytes=(\\d*)-(\\d*)$/.exec(range.trim()) : null\n if (!m || (m[1] === '' && m[2] === '')) {\n return {\n status: 200,\n headers: { ...base, 'content-length': String(size) },\n body: read(0, size - 1),\n }\n }\n // `bytes=-N` is the last N bytes; `bytes=A-` runs to the end.\n const start = m[1] === '' ? Math.max(0, size - Number(m[2])) : Number(m[1])\n const end =\n m[1] === '' || m[2] === '' ? size - 1 : Math.min(Number(m[2]), size - 1)\n if (start >= size || start > end) {\n return {\n status: 416,\n headers: { ...base, 'content-range': `bytes */${size}` },\n body: Buffer.alloc(0),\n }\n }\n return {\n status: 206,\n headers: {\n ...base,\n 'content-range': `bytes ${start}-${end}/${size}`,\n 'content-length': String(end - start + 1),\n },\n body: read(start, end),\n }\n}\n\n/**\n * The file a manifest path names, when it is the program's to read.\n *\n * A program's functions run in a page, and whatever the page is served the\n * program can read and send anywhere. So a path is honoured only inside the\n * program's own directory (links resolved): a config from somewhere else\n * cannot name a key file or a `.env` two directories up and have a render\n * or a push carry it out. The cost is that a shared file has to be copied\n * (or the config moved up); the alternative is every config being able to\n * read the disk of whoever renders it.\n */\nexport function localFile(\n ref: string,\n baseDir: string | null,\n): { file: string } | { refused: string } {\n if (baseDir === null) {\n return {\n refused:\n 'a config loaded from a URL has no directory, so it reads no local file',\n }\n }\n const file = resolve(baseDir, ref)\n if (!existsSync(file) || !statSync(file).isFile()) {\n return { refused: 'which is not a file beside the config' }\n }\n const root = realpathSync(baseDir)\n const real = realpathSync(file)\n if (!real.startsWith(root + sep)) {\n return {\n refused: `which is outside the program's directory (${root}). A program reads only files in its own directory or below: copy the file there`,\n }\n }\n return { file: real }\n}\n\n/** A ref the page can fetch as written. */\nfunction isUrl(ref: string): boolean {\n return /^(https?:|data:|blob:)/.test(ref)\n}\n\nfunction manifestOf(\n config: Record<string, unknown>,\n): Record<string, { ref: string | string[] }> | null {\n const assets = config.assets\n if (!assets || typeof assets !== 'object' || Array.isArray(assets))\n return null\n return Object.keys(assets).length > 0\n ? (assets as Record<string, { ref: string | string[] }>)\n : null\n}\n\n/** Every ref of a manifest, in declaration order, with where it sits. */\nexport function manifestRefs(\n config: Record<string, unknown>,\n): { name: string; index: number | null; ref: string }[] {\n const out: { name: string; index: number | null; ref: string }[] = []\n for (const [name, decl] of Object.entries(manifestOf(config) ?? {})) {\n // A name becomes a file name when the manifest's files come home, so\n // it is held to the engine's rule HERE, for a config that reached this\n // machine without passing a schema (a fetched document's own config).\n if (!ASSET_NAME.test(name) || !decl || typeof decl !== 'object') continue\n if (Array.isArray(decl.ref)) {\n decl.ref.forEach((ref, index) => {\n if (typeof ref === 'string') out.push({ name, index, ref })\n })\n } else if (typeof decl.ref === 'string') {\n out.push({ name, index: null, ref: decl.ref })\n }\n }\n return out\n}\n\nasync function fetchHosted(\n id: string,\n opts: ProgramAssetsOptions,\n): Promise<string | null> {\n const dir = opts.cacheDir ?? join(homedir(), '.cache', 'vos', 'assets')\n // A hosted file is immutable, so a finished copy is good forever. Only a\n // finished one: a download lands under `.part` and is renamed whole.\n const cached = existsSync(dir)\n ? readdirSync(dir).find(\n (f) => (f === id || f.startsWith(`${id}.`)) && !f.endsWith('.part'),\n )\n : undefined\n if (cached) return join(dir, cached)\n if (!opts.origin || !opts.key) {\n opts.log(\n `note: asset ${id} is hosted and no credential is set, so the render cannot load it (vos login, or keep the file beside the config and name its path)`,\n )\n return null\n }\n const res = await fetch(`${opts.origin}/api/assets/${id}/file`, {\n headers: { authorization: `Bearer ${opts.key}` },\n })\n if (!res.ok) {\n opts.log(\n `note: asset ${id} could not be read (${res.status}), so the render cannot load it`,\n )\n return null\n }\n mkdirSync(dir, { recursive: true, mode: 0o700 })\n // The served type names the extension, so the page is told what it got.\n const type = (res.headers.get('content-type') ?? '').split(';')[0].trim()\n const file = join(dir, `${id}${extensionFor(type)}`)\n const partial = `${file}.part`\n try {\n writeFileSync(partial, new Uint8Array(await res.arrayBuffer()), {\n mode: 0o600,\n })\n renameSync(partial, file)\n } finally {\n rmSync(partial, { force: true })\n }\n return file\n}\n\n/**\n * The faces an HTML layer brings (`fonts` on the document, lowered into the\n * studio entry as `html.faces`), as refs a caller can re-point. The page\n * fetches each `url` itself, so a path is resolved against the page.\n */\nfunction htmlFaceRefs(config: Record<string, unknown>): {\n where: string\n url: string\n set: (next: string) => void\n drop: () => void\n}[] {\n const out: ReturnType<typeof htmlFaceRefs> = []\n const stack = Array.isArray(config.stack) ? config.stack : []\n for (const entry of stack as Record<string, unknown>[]) {\n const data = entry?.data as Record<string, unknown> | undefined\n const overlays = Array.isArray(data?.overlays) ? data.overlays : []\n for (const clip of overlays as Record<string, unknown>[]) {\n const html = clip?.html as Record<string, unknown> | undefined\n const faces = Array.isArray(html?.faces) ? html.faces : []\n for (const face of faces as Record<string, unknown>[]) {\n if (typeof face?.url !== 'string' || !face.url) continue\n out.push({\n where: `overlay ${String(clip.id)} font ${String(face.family)}`,\n url: face.url,\n set: (next) => {\n face.url = next\n },\n drop: () => {\n const at = faces.indexOf(face)\n if (at >= 0) faces.splice(at, 1)\n },\n })\n }\n }\n }\n return out\n}\n\n/** The one host a page fetches a face or an image from by URL. */\nconst FACE_HOST = /^https:\\/\\/assets\\.vos\\.so\\//\n\n/**\n * Make every declared file reachable for a local page: the manifest's, and\n * the faces an HTML layer brings from beside its document (re-pointed in\n * place, since the page fetches a face by its url). Null when there is\n * none, so a caller can leave the engine's defaults alone.\n */\nexport async function programAssets(\n config: Record<string, unknown>,\n opts: ProgramAssetsOptions,\n): Promise<ProgramAssets | null> {\n const refs = manifestRefs(config)\n const faces: ReturnType<typeof htmlFaceRefs> = []\n for (const f of htmlFaceRefs(config)) {\n const hosted = hostedAssetId(f.url, opts.origin ? [opts.origin] : [])\n if (!isUrl(f.url) || hosted) {\n faces.push(f)\n } else if (!FACE_HOST.test(f.url)) {\n // A face is fetched by the page and inlined, like its images, so it\n // is held to the same host; one on another host is dropped and said.\n opts.log(\n `note: ${f.where} names ${f.url}, which is not on https://assets.vos.so, so the render does not fetch it (a face comes from the hosted catalog, a hosted file, or a .woff2 beside the document)`,\n )\n f.drop()\n }\n }\n if (refs.length === 0 && faces.length === 0) return null\n const files: Record<string, string> = {}\n const served = new Map<string, string>()\n const serve = (file: string): string => {\n const known = served.get(file)\n if (known) return known\n // The path is a digest of the file's location plus its extension, and\n // never its name: a space or an accent in a name is percent-encoded in\n // the URL the page asks for and would miss a table keyed by the name.\n const tag = createHash('sha256').update(file).digest('hex').slice(0, 16)\n const ext = extname(file)\n const path = `${ASSET_ROUTE}${tag}${/^\\.[A-Za-z0-9]+$/.test(ext) ? ext.toLowerCase() : ''}`\n files[path] = file\n served.set(file, path)\n return path\n }\n\n const reach = async (ref: string, where: string): Promise<string> => {\n // A hosted file first, in any spelling: its absolute URL is a URL too,\n // but a private one answers only to the key, never to the page.\n const hosted = hostedAssetId(ref, opts.origin ? [opts.origin] : [])\n if (hosted) {\n const file = await fetchHosted(hosted, opts)\n return file ? serve(file) : ref\n }\n if (isUrl(ref)) return ref\n const local = localFile(ref, opts.baseDir)\n if ('refused' in local) {\n opts.log(\n `note: ${where} names ${ref}, ${local.refused}, so the render cannot load it`,\n )\n return ref\n }\n return serve(local.file)\n }\n\n for (const face of faces) face.set(await reach(face.url, face.where))\n\n const assets: Record<string, string | string[]> = {}\n for (const { name, index, ref } of refs) {\n const url = await reach(\n ref,\n index === null ? `assets.${name}` : `assets.${name}[${index}]`,\n )\n if (index === null) assets[name] = url\n else {\n const list = (assets[name] ??= []) as string[]\n list[index] = url\n }\n }\n return { assets, files }\n}\n\n/**\n * The manifest entries that name a file which is not there, for a check\n * that knows where the config lives. Hosted refs and URLs are not files.\n */\nexport function missingManifestFiles(\n config: Record<string, unknown>,\n baseDir: string,\n): string[] {\n const out: string[] = []\n for (const { name, index, ref } of manifestRefs(config)) {\n if (isUrl(ref) || hostedAssetId(ref)) continue\n const local = localFile(ref, baseDir)\n if ('file' in local) continue\n const where = index === null ? `assets.${name}` : `assets.${name}[${index}]`\n out.push(`${where} names ${ref}, ${local.refused}`)\n }\n return out\n}\n","import { lstat, writeFile } from 'node:fs/promises'\nimport { lstatSync, writeFileSync } from 'node:fs'\n\n/**\n * Write a file the CLI owns into a take or program directory, never\n * through a symbolic link. `writeFile` follows a link already at the path,\n * so a `doc.json` pointing out of the directory would carry the bytes\n * wherever it points; here the path is looked at first and a link is\n * refused in words. A regular file is overwritten as before.\n */\nexport async function writeFileSafe(\n path: string,\n data: string | Uint8Array,\n): Promise<void> {\n await refuseSymlink(path)\n await writeFile(path, data)\n}\n\n/** Throws in words when a symbolic link sits at the path; nothing else. */\nexport async function refuseSymlink(path: string): Promise<void> {\n let link = false\n try {\n link = (await lstat(path)).isSymbolicLink()\n } catch {\n // nothing there yet: a plain write\n }\n if (link) throw new Error(symlinkRefusal(path))\n}\n\n/** The synchronous twin, for the writers that are synchronous. */\nexport function writeFileSafeSync(\n path: string,\n data: string | Uint8Array,\n): void {\n let link = false\n try {\n link = lstatSync(path).isSymbolicLink()\n } catch {\n // nothing there yet\n }\n if (link) throw new Error(symlinkRefusal(path))\n writeFileSync(path, data)\n}\n\nexport function symlinkRefusal(path: string): string {\n return `${path} is a symbolic link, and the CLI writes only a regular file there: remove the link and run again`\n}\n","/** Minimal argv parser — mirrors @vosjs/cli's conventions. */\nimport { noteGivenFlag, trackFlags } from '../flagUse'\nimport { UsageError } from '../args'\n\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | true>\n /** Values of flags declared repeatable (multiFlags) — accumulated in order. */\n multi: Record<string, string[]>\n}\n\n// ONE usage error for the whole CLI: a verb that reaches a shared helper\n// (outputs.ts) must be reported as a usage error on either side.\nexport { UsageError }\n\nexport function parseArgs(\n argv: string[],\n booleanFlags: ReadonlySet<string>,\n multiFlags: ReadonlySet<string> = new Set(),\n): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | true> = {}\n const multi: Record<string, string[]> = {}\n const put = (name: string, value: string): void => {\n if (multiFlags.has(name)) (multi[name] ??= []).push(value)\n else flags[name] = value\n }\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') {\n positionals.push(...argv.slice(i + 1))\n break\n }\n if (arg.startsWith('--')) {\n const eq = arg.indexOf('=')\n if (eq !== -1) {\n put(arg.slice(2, eq), arg.slice(eq + 1))\n continue\n }\n const name = arg.slice(2)\n if (booleanFlags.has(name)) {\n flags[name] = true\n continue\n }\n if (i + 1 >= argv.length || argv[i + 1].startsWith('--')) {\n throw new UsageError(`--${name} expects a value`)\n }\n put(name, argv[i + 1])\n i++\n continue\n }\n positionals.push(arg)\n }\n for (const name of Object.keys(flags)) noteGivenFlag(name)\n for (const name of Object.keys(multi)) noteGivenFlag(name)\n return { positionals, flags: trackFlags(flags), multi: trackFlags(multi) }\n}\n\nexport function numFlag(\n flags: ParsedArgs['flags'],\n name: string,\n fallback: number,\n): number {\n const v = flags[name]\n if (typeof v !== 'string') return fallback\n const n = Number(v)\n if (!Number.isFinite(n))\n throw new UsageError(`--${name} expects a number, got \"${v}\"`)\n return n\n}\n\n/** String flag value, or undefined when absent (or boolean). */\nexport function strFlag(\n flags: ParsedArgs['flags'],\n name: string,\n): string | undefined {\n const v = flags[name]\n return typeof v === 'string' ? v : undefined\n}\n\nexport function hasFlag(flags: ParsedArgs['flags'], name: string): boolean {\n return Object.prototype.hasOwnProperty.call(flags, name)\n}\n","/**\n * vos.so platform client — THE one HTTP surface of the vos CLI.\n * Every platform verb (fetch/push/pull for programs AND takes, login) goes\n * through here: one origin resolution, one credential ladder, one sync-state\n * file, one 409 conflict shape.\n *\n * Contract notes (mirrored at https://vos.so/llms.txt):\n * - public/unlisted programs read with no auth; writes ride a bearer key\n * - credentials resolve --key → VOS_API_KEY → ~/.config/vos/credentials\n * (first line; `vos login` writes it) and are NEVER printed — not in\n * logs, not in errors, not in NDJSON events\n * - pushed configs travel as the MIGRATED object with params/presets\n * untouched; the platform validates and compiles server-side\n */\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { writeFileSafeSync } from './safeWrite'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { UsageError } from './args'\nimport { detectHostAgent } from './hostAgent'\nimport type { Env } from './hostAgent'\n\n/**\n * Origin resolution: --origin (or legacy --api) → VOS_ORIGIN → legacy\n * VOS_API_BASE (which historically carried a trailing /api) → https://vos.so.\n * Every shape converges on a bare origin; paths below carry the /api prefix.\n */\nexport function platformOrigin(\n flags: { origin?: string; api?: string } = {},\n): string {\n const legacyEnv = process.env.VOS_API_BASE?.trim()\n const raw =\n flags.origin ??\n flags.api ??\n process.env.VOS_ORIGIN?.trim() ??\n legacyEnv ??\n 'https://vos.so'\n if (!flags.origin && !flags.api && !process.env.VOS_ORIGIN && legacyEnv) {\n process.stderr.write(\n 'note: VOS_API_BASE is deprecated — set VOS_ORIGIN (an origin, no /api suffix)\\n',\n )\n }\n return raw.replace(/\\/+$/, '').replace(/\\/api$/, '')\n}\n\n/**\n * Accepts a bare vos id, a watch URL (vos.so/vos/{id}), an embed URL\n * (/embed/vos/{id}), or a studio/stage URL (?vos={id}).\n */\nexport function parseVosId(input: string): string {\n if (!/^https?:\\/\\//.test(input)) {\n if (/^[A-Za-z0-9_-]+$/.test(input)) return input\n throw new UsageError(`\"${input}\" is not a vos id or URL`)\n }\n let url: URL\n try {\n url = new URL(input)\n } catch {\n throw new UsageError(`\"${input}\" is not a valid URL`)\n }\n const query = url.searchParams.get('vos')\n if (query) return query\n const path = url.pathname.match(/\\/vos\\/([A-Za-z0-9_-]+)/)\n if (path) return path[1]\n throw new UsageError(\n `could not find a vos id in ${input} — expected /vos/{id} or ?vos={id}`,\n )\n}\n\nexport function deriveSlug(title: string): string {\n return (\n title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 50)\n .replace(/-+$/, '') || 'remix'\n )\n}\n\nexport const CREDENTIALS_PATH = join(homedir(), '.config', 'vos', 'credentials')\n\n/**\n * The full documented ladder: explicit --key → VOS_API_KEY → the first line\n * of ~/.config/vos/credentials. A vos_rg_ remix grant is just a key here.\n * Returns null when nothing resolves — callers decide whether auth is needed.\n */\nexport function resolveCredential(explicit?: string): string | null {\n const flag = explicit?.trim()\n if (flag) return flag\n const env = process.env.VOS_API_KEY?.trim()\n if (env) return env\n try {\n const first = readFileSync(CREDENTIALS_PATH, 'utf8').split('\\n')[0].trim()\n return first || null\n } catch {\n return null\n }\n}\n\nexport function requireCredential(explicit?: string): string {\n const key = resolveCredential(explicit)\n if (!key) {\n throw new Error(\n 'no credential found — pass --key, set VOS_API_KEY, or run `vos login` ' +\n '(mint a content key at https://vos.so/app/api; a vos_rg_ remix grant works too)',\n )\n }\n return key\n}\n\n/** `vos login` writes here: 0700 dir, 0600 file, first line is the key. */\nexport function writeCredential(key: string): string {\n mkdirSync(join(homedir(), '.config', 'vos'), { recursive: true, mode: 0o700 })\n writeFileSync(CREDENTIALS_PATH, `${key.trim()}\\n`, { mode: 0o600 })\n return CREDENTIALS_PATH\n}\n\n/**\n * The self-reported client string stamped onto pushed versions (User-Agent\n * grammar). Display-only on the platform — attribution trust\n * stays with the credential. The ladder: an explicit VOS_CLIENT (e.g.\n * VOS_CLIENT=claude-code/2.1), else the coding agent this CLI was spawned by\n * (`claude-code vos-cli`, read from the environment by `detectHostAgent`),\n * else the transport alone, so history rows name the tool that pushed.\n */\nexport function clientId(\n env: Env = process.env,\n exists?: (path: string) => boolean,\n): string {\n const explicit = env.VOS_CLIENT?.trim()\n if (explicit && explicit.length <= 60 && !/[\\r\\n]/.test(explicit))\n return explicit\n const host = detectHostAgent(env, exists)\n return host ? `${host} vos-cli` : 'vos-cli'\n}\n\nexport interface ApiResult {\n status: number\n body: Record<string, unknown>\n}\n\nexport async function apiJson(\n origin: string,\n path: string,\n init: {\n method?: string\n key?: string | null\n body?: unknown\n /** Raw request body (uploads) — used verbatim, with `headers`. */\n raw?: Uint8Array\n headers?: Record<string, string>\n } = {},\n): Promise<ApiResult> {\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...init.headers,\n }\n if (init.key) headers.authorization = `Bearer ${init.key}`\n if (init.body !== undefined) headers['content-type'] = 'application/json'\n const reqBody: BodyInit | undefined =\n init.raw !== undefined\n ? (init.raw as unknown as BodyInit)\n : init.body === undefined\n ? undefined\n : JSON.stringify(init.body)\n const res = await fetch(`${origin}${path}`, {\n method: init.method ?? 'GET',\n headers,\n body: reqBody,\n })\n let body: Record<string, unknown> = {}\n try {\n body = (await res.json()) as Record<string, unknown>\n } catch {\n // non-JSON error bodies stay {}\n }\n return { status: res.status, body }\n}\n\n/**\n * A refusal's issue list (`details: [{ path, message }]`, the shape every\n * vos.so route answers a failed schema with), one indented line per issue.\n * Empty when the body carries none.\n */\nexport function detailLine(body: Record<string, unknown>): string {\n const details = body.details\n if (!Array.isArray(details) || !details.length) return ''\n const lines = (details as Record<string, unknown>[]).map((d) => {\n const path = Array.isArray(d.path) ? d.path.join('.') : ''\n return `${path ? `${path}: ` : ''}${String(d.message ?? JSON.stringify(d))}`\n })\n return `\\n ${lines.join('\\n ')}`\n}\n\n/**\n * Human-readable error line for a failed platform response, with the\n * issues behind a generic `error` (\"Invalid input\") on the lines below it,\n * because the generic word alone names nothing to fix. Never echoes\n * credentials.\n */\nexport function apiError(what: string, r: ApiResult): string {\n const detail =\n typeof r.body.error === 'string'\n ? r.body.error + detailLine(r.body)\n : r.body.details !== undefined\n ? JSON.stringify(r.body.details)\n : ''\n const hint =\n r.status === 401\n ? ' (run `vos login`, or set VOS_API_KEY — mint a key at https://vos.so/app/api)'\n : ''\n return `${what} → ${r.status}${detail ? `: ${detail}` : ''}${hint}`\n}\n\n/**\n * The next step a push without `--prompt` prints: the prompt that made a\n * vos is shown on its watch page by default, in the person's own words.\n * An agent reads a command's output and acts on it, so the push says the\n * command outright. The vos lands private, so its owner sees the prompt\n * before anyone else can.\n */\nexport function promptHint(target: string, vosId: string): string {\n return `vos push ${target} --vos ${vosId} --prompt \"<the person's prompt, as they wrote it>\"`\n}\n\n// ---------------------------------------------------------------------------\n// Sync state — ONE file (vos.json) for both artifact kinds, legacy-read\n// compatible with the two files it replaces: push.json (take dirs) and\n// meta.json (fetched program dirs).\n// ---------------------------------------------------------------------------\n\nexport const SYNC_STATE_NAME = 'vos.json'\n\nexport interface SyncState {\n vosId: string\n /** The hosted version this directory is based on — the next push's base. */\n versionId: string | null\n pushedAt?: string\n title?: string\n slug?: string\n remixOfId?: string\n /**\n * The files `vos fetch --media` brought home, by their path in the\n * directory: the hosted address each came from and its sha256 then. A\n * claimable push of the take names these originals instead of uploading,\n * and only while the bytes are unchanged.\n */\n hostedMedia?: Record<string, { ref: string; sha256: string }>\n}\n\n/** The `hostedMedia` map, read defensively (a hand-edited vos.json). */\nfunction hostedMediaOf(\n value: unknown,\n): Record<string, { ref: string; sha256: string }> | undefined {\n if (!value || typeof value !== 'object') return undefined\n const out: Record<string, { ref: string; sha256: string }> = {}\n for (const [file, entry] of Object.entries(value)) {\n const e = entry as { ref?: unknown; sha256?: unknown } | null\n if (e && typeof e.ref === 'string' && typeof e.sha256 === 'string')\n out[file] = { ref: e.ref, sha256: e.sha256 }\n }\n return Object.keys(out).length ? out : undefined\n}\n\nfunction readJsonFile(file: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(file, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\n/**\n * vos.json → legacy push.json ({vosId, versionId}) → legacy meta.json\n * ({id, currentVersionId} — guarded on a string `id` so a take dir's\n * RecordingMeta meta.json is never misread as sync state).\n */\nexport function readSyncState(dir: string): SyncState | null {\n const own = readJsonFile(join(dir, SYNC_STATE_NAME))\n if (own && typeof own.vosId === 'string') {\n return {\n vosId: own.vosId,\n versionId: typeof own.versionId === 'string' ? own.versionId : null,\n ...(typeof own.pushedAt === 'string' ? { pushedAt: own.pushedAt } : {}),\n ...(typeof own.title === 'string' ? { title: own.title } : {}),\n ...(typeof own.slug === 'string' ? { slug: own.slug } : {}),\n ...(typeof own.remixOfId === 'string'\n ? { remixOfId: own.remixOfId }\n : {}),\n ...(hostedMediaOf(own.hostedMedia)\n ? { hostedMedia: hostedMediaOf(own.hostedMedia) }\n : {}),\n }\n }\n const push = readJsonFile(join(dir, 'push.json'))\n if (push && typeof push.vosId === 'string') {\n return {\n vosId: push.vosId,\n versionId: typeof push.versionId === 'string' ? push.versionId : null,\n ...(typeof push.pushedAt === 'string' ? { pushedAt: push.pushedAt } : {}),\n }\n }\n const meta = readJsonFile(join(dir, 'meta.json'))\n if (meta && typeof meta.id === 'string') {\n return {\n vosId: meta.id,\n versionId:\n typeof meta.currentVersionId === 'string'\n ? meta.currentVersionId\n : null,\n ...(typeof meta.title === 'string' ? { title: meta.title } : {}),\n ...(typeof meta.slug === 'string' ? { slug: meta.slug } : {}),\n ...(typeof meta.remixOfId === 'string'\n ? { remixOfId: meta.remixOfId }\n : {}),\n }\n }\n return null\n}\n\n/** Merge-writes vos.json; the legacy files are left alone (read-only compat). */\nexport function writeSyncState(\n dir: string,\n patch: Partial<SyncState> & { vosId: string },\n): SyncState {\n const merged = {\n ...(readSyncState(dir) ?? { versionId: null }),\n ...patch,\n } as SyncState\n writeFileSafeSync(\n join(dir, SYNC_STATE_NAME),\n `${JSON.stringify(merged, null, 2)}\\n`,\n )\n return merged\n}\n\n// ---------------------------------------------------------------------------\n// The /changes payload (shape mirrored from the platform)\n// ---------------------------------------------------------------------------\n\nexport interface VersionChange {\n versionId?: string\n versionNumber?: number\n origin?: string\n label?: string | null\n note?: string | null\n summary?: string\n}\n\n/** Human lines for a changes walk — origin/label attributed, note indented. */\nexport function formatChanges(changes: readonly VersionChange[]): string[] {\n const lines: string[] = []\n for (const ch of changes) {\n const label = ch.label ? ` · ${ch.label}` : ''\n const who = ch.origin === 'studio' ? 'human' : (ch.origin ?? 'unknown')\n lines.push(\n `v${String(ch.versionNumber ?? '?')} (${who}${label}): ${ch.summary ?? ''}`,\n )\n if (ch.note) lines.push(` note: ${ch.note}`)\n }\n return lines\n}\n\n/**\n * The warnings a write came back with (a knob or Look the platform dropped),\n * one line each. A push that saved is not a push that kept everything.\n */\nexport function serverWarnings(\n body: Record<string, unknown>,\n log: (line: string) => void,\n): void {\n const warnings = Array.isArray(body.warnings) ? body.warnings : []\n for (const w of warnings) if (typeof w === 'string') log(`warning ${w}`)\n}\n","import { existsSync } from 'node:fs'\n\n/**\n * Which coding agent is driving this CLI, read from the environment the agent\n * spawned it in. Used ONLY to name the tool on a pushed version (`clientId`),\n * which the platform treats as display-only: attribution trust stays with the\n * credential, so a wrong or spoofed answer changes a label and nothing else.\n *\n * The signals follow `@vercel/detect-agent` (Apache-2.0), kept as a small\n * synchronous table because every push reads it: the cross-tool `AI_AGENT`\n * convention first, then each host's own marker. Returns a lowercase slug,\n * or null when nothing says an agent is here.\n */\nexport type Env = Record<string, string | undefined>\n\nconst SLUG = /^[a-z0-9][a-z0-9.-]{0,39}$/\n\n/** The markers each host sets in the shells it runs, in precedence order. */\nconst MARKERS: Array<[slug: string, test: (env: Env) => boolean]> = [\n ['cursor', (e) => !!e.CURSOR_TRACE_ID],\n [\n 'cursor-cli',\n (e) => !!e.CURSOR_AGENT || e.CURSOR_EXTENSION_HOST_ROLE === 'agent-exec',\n ],\n ['gemini-cli', (e) => !!e.GEMINI_CLI],\n ['codex', (e) => !!(e.CODEX_SANDBOX || e.CODEX_CI || e.CODEX_THREAD_ID)],\n ['antigravity', (e) => !!e.ANTIGRAVITY_AGENT],\n ['augment-cli', (e) => !!e.AUGMENT_AGENT],\n ['opencode', (e) => !!e.OPENCODE_CLIENT],\n [\n 'claude-code',\n (e) => !!(e.CLAUDECODE || e.CLAUDE_CODE) && !e.CLAUDE_CODE_IS_COWORK,\n ],\n ['claude-cowork', (e) => !!(e.CLAUDECODE || e.CLAUDE_CODE)],\n ['replit', (e) => !!e.REPL_ID],\n [\n 'github-copilot',\n (e) => !!(e.COPILOT_MODEL || e.COPILOT_ALLOW_ALL || e.COPILOT_GITHUB_TOKEN),\n ],\n]\n\nconst DEVIN_LOCAL_PATH = '/opt/.devin'\n\n/**\n * `AI_AGENT` is free text by convention (`claude-code_2-1-284_agent`,\n * `github-copilot`, `v0`): the tool's name is its leading run of\n * `[a-z0-9.-]`, lowercased, so a version or a role suffix never reaches the\n * label and nothing but a short slug ever leaves the machine.\n */\nexport function slugFromAiAgent(value: string): string | null {\n const head = value.trim().toLowerCase().split('_')[0] ?? ''\n if (head === 'github-copilot-cli') return 'github-copilot'\n return SLUG.test(head) ? head : null\n}\n\nexport function detectHostAgent(\n env: Env = process.env,\n exists: (path: string) => boolean = existsSync,\n): string | null {\n if (env.AI_AGENT) {\n const slug = slugFromAiAgent(env.AI_AGENT)\n if (slug) return slug\n }\n for (const [slug, test] of MARKERS) if (test(env)) return slug\n try {\n if (exists(DEVIN_LOCAL_PATH)) return 'devin'\n } catch {\n // An unreadable path is not an agent.\n }\n return null\n}\n"],"mappings":"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","names":["start","end","writeFileSync","parseArgs","numFlag","mkdirSync","writeFileSync","homedir","join","existsSync","join","homedir","mkdirSync","writeFileSync"]}
|