@officexapp/vidfarm-devcli 0.21.63 → 0.21.64

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vidfarm
3
- skill_version: 0.21.63
3
+ skill_version: 0.21.64
4
4
  description: Use Vidfarm as a director. Run a strategy **consultation** (the `brainstorm/*` chain — cold-start interview, awareness stages, persuasive angles, hooks, product placement). Answer "give me content ideas" / "what should I post" / "I need 30 videos this month" from the bundled idea banks (50 content frames x 5 awareness stages x 44 problem angles). Browse/add inspiration videos, browse the free public raws catalog BY CATEGORY (curated shelves like scroll-stoppers/greenscreen/reaction — the cheapest way to source footage for one video, and a ready-made clip pool for bulk scripting N variants), fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
5
5
  ---
6
6
 
@@ -256,9 +256,74 @@ devcli: `vidfarm channels` lists destinations, `vidfarm schedule <postId> --at <
256
256
 
257
257
  Deeper FlockPoster work (connecting accounts, channel management, direct posting/analytics outside vidfarm's schedule wrapper) is FlockPoster's own API, not vidfarm's — grab its skill first: `npx skills add https://github.com/OfficeXApp/flockposter-skill` (mirrored as `vidfarm skills add flockposter`).
258
258
 
259
- ## Marketplaceorder videos from specialist agents
260
-
261
- The marketplace is a **paid, web-only surface**. NEVER render or reimplement it locally (not in the terminal, not on a serve box) — send the human to **https://vidfarm.cc/marketplace** (featured agents), `/marketplace/browse` (portfolios by category tag), `/marketplace/manage` (their orders). `vidfarm marketplace` prints exactly those links.
259
+ ## Pack marketplace buy a pack, or sell one
260
+
261
+ **There are two marketplaces and they are not the same product.** This one is the **shelf**: a vendor
262
+ puts **pack cards** on `https://vidfarm.cc/marketplace` — one card is one offer, "7 videos, $35, 3-5
263
+ days" — and a buyer orders a **pack** from it. No brief bank, no bell, no swipe deck. The older gig
264
+ marketplace (briefs broadcast to a crowd) is the section after this one.
265
+
266
+ It was web-only until 2026-08-29. It is now REST, under `/api/v1/marketplace/`, and the devcli wraps
267
+ every route. Auth is the ordinary `vidfarm-api-key` header or a session; the whole surface is
268
+ paid-only and answers **402** to a free key.
269
+
270
+ ```
271
+ GET /api/v1/marketplace/shelf PUBLIC — what is for sale
272
+ GET /api/v1/marketplace/shelf/:shop/:machine one card + examples + reviews
273
+ GET /api/v1/marketplace/shop your shop, cards, limits
274
+ POST /api/v1/marketplace/shop/profile create/edit the shop
275
+ POST /api/v1/marketplace/shop/machines create/edit a pack card
276
+ POST /api/v1/marketplace/shop/machines/:id/visibility on/off for buyers
277
+ GET /api/v1/marketplace/shop/orders the vendor queue
278
+ POST /api/v1/marketplace/shop/orders/:id/fulfil DELIVER — { items: [...] }
279
+ POST /api/v1/marketplace/purchases place an order
280
+ GET /api/v1/marketplace/purchases/:id one order + thread + files + `can`
281
+ POST /api/v1/marketplace/purchases/:id/accept pays, and is FINAL
282
+ POST /api/v1/marketplace/purchases/:id/request-changes free; nothing moves
283
+ GET /api/v1/marketplace/free-order-status/:shopSlug is MY first order here free?
284
+ ```
285
+
286
+ devcli — **two verbs, and they do not overlap**: `vidfarm shop` is the vendor
287
+ (`shop orders`, `shop deliver <id> --item "<preview>::<master>::<note>"`, `shop earnings`,
288
+ `shop withdraw`), `vidfarm purchases` is the buyer (`purchases shelf`, `vidfarm buy
289
+ <shop>/<card> -y`, `purchases accept <id> -y`, `purchases changes <id> "<fix>"`). Full lists:
290
+ `vidfarm shop help`, `vidfarm purchases help`. Everything takes `--json`.
291
+
292
+ **An order ends three ways, and only one is reversible.** `accept` approves and settles in full and
293
+ is FINAL in both directions. `request-changes` is FREE and reaches nothing on chain — the deposit
294
+ stays in escrow and the vendor re-delivers. `refund` returns the escrow; whoever signs pays the gas.
295
+ Never route a fixable note through Dollar Platoon's reject — theirs is final and would kill a funded
296
+ order. **Clean masters are withheld until the payout is stamped**; everything before that is
297
+ watermarked, and the rule is applied server-side.
298
+
299
+ ### First order free — the NEW SHOP flag
300
+
301
+ The platform can mark a shop so that **every buyer's FIRST order there is free** — one pack, $0,
302
+ one per SHOP rather than one per card. It is superagency-only
303
+ (`POST /api/v1/admin/shops/new-status`); a vendor can neither grant nor revoke it.
304
+
305
+ It changes three rules, and an agent that misses them will mislead its human:
306
+
307
+ - **No deposit and no escrow.** There is nothing to refund. A "refund" on a free order is a
308
+ **cancel** — it closes the order and returns nothing.
309
+ - **The buyer's approval is FINAL and releases the files by itself.** On the paid rail the PAYOUT
310
+ unlocks the masters; on the free rail the APPROVAL does, because there is no payout after it. So
311
+ accepting is the entire transaction and there is no undo behind it.
312
+ - **A spent free order does not come back.** Cancelling one does not restore the entitlement.
313
+
314
+ **Read `is_free` on the order — NEVER `total_usd === 0`.** A zero total reached any other way still
315
+ has escrow semantics, and treating it as free skips the payout. Underneath, a free order runs on a
316
+ **separate Dollar Platoon gig** (`dp_free_gig_id`): their API locks a gig's price mode at creation,
317
+ so one gig cannot sell both.
318
+
319
+ `GET …/shelf` is shared-cached and therefore only ever carries the **offer** (`shop.new_shop`).
320
+ Whether *this* buyer still has theirs is per-account and lives on its own `no-store` route. It is
321
+ **advisory**: price at the paid number and lower it on a yes, never the other way round.
322
+ `vidfarm purchases pack <shop>/<card>` does exactly that and prints the terms.
323
+
324
+ ## Older gig marketplace — broadcast a brief to a crowd
325
+
326
+ Web-only. NEVER render or reimplement it locally (not in the terminal, not on a serve box) — send the human to **https://vidfarm.cc/marketplace** (featured agents), `/marketplace/browse` (portfolios by category tag), `/marketplace/manage` (their orders). `vidfarm marketplace` prints exactly those links.
262
327
 
263
328
  An **order IS a Dollar Platoon gig** (tagged `vidfarm_order_<uuidv7>` plus category tags). Vidfarm exposes a deliberately thin REST wrapper — auth via session or the `vidfarm-api-key` header:
264
329
 
package/SKILL.director.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vidfarm
3
- skill_version: 0.21.63
3
+ skill_version: 0.21.64
4
4
  description: Use Vidfarm as a director. Run a strategy **consultation** (the `brainstorm/*` chain — cold-start interview, awareness stages, persuasive angles, hooks, product placement). Answer "give me content ideas" / "what should I post" / "I need 30 videos this month" from the bundled idea banks (50 content frames x 5 awareness stages x 44 problem angles). Browse/add inspiration videos, browse the free public raws catalog BY CATEGORY (curated shelves like scroll-stoppers/greenscreen/reaction — the cheapest way to source footage for one video, and a ready-made clip pool for bulk scripting N variants), fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
5
5
  ---
6
6
 
@@ -854,9 +854,74 @@ devcli: `vidfarm channels` lists destinations, `vidfarm schedule <postId> --at <
854
854
 
855
855
  Deeper FlockPoster work (connecting accounts, channel management, direct posting/analytics outside vidfarm's schedule wrapper) is FlockPoster's own API, not vidfarm's — grab its skill first: `npx skills add https://github.com/OfficeXApp/flockposter-skill` (mirrored as `vidfarm skills add flockposter`).
856
856
 
857
- ## Marketplaceorder videos from specialist agents
858
-
859
- The marketplace is a **paid, web-only surface**. NEVER render or reimplement it locally (not in the terminal, not on a serve box) — send the human to **https://vidfarm.cc/marketplace** (featured agents), `/marketplace/browse` (portfolios by category tag), `/marketplace/manage` (their orders). `vidfarm marketplace` prints exactly those links.
857
+ ## Pack marketplace buy a pack, or sell one
858
+
859
+ **There are two marketplaces and they are not the same product.** This one is the **shelf**: a vendor
860
+ puts **pack cards** on `https://vidfarm.cc/marketplace` — one card is one offer, "7 videos, $35, 3-5
861
+ days" — and a buyer orders a **pack** from it. No brief bank, no bell, no swipe deck. The older gig
862
+ marketplace (briefs broadcast to a crowd) is the section after this one.
863
+
864
+ It was web-only until 2026-08-29. It is now REST, under `/api/v1/marketplace/`, and the devcli wraps
865
+ every route. Auth is the ordinary `vidfarm-api-key` header or a session; the whole surface is
866
+ paid-only and answers **402** to a free key.
867
+
868
+ ```
869
+ GET /api/v1/marketplace/shelf PUBLIC — what is for sale
870
+ GET /api/v1/marketplace/shelf/:shop/:machine one card + examples + reviews
871
+ GET /api/v1/marketplace/shop your shop, cards, limits
872
+ POST /api/v1/marketplace/shop/profile create/edit the shop
873
+ POST /api/v1/marketplace/shop/machines create/edit a pack card
874
+ POST /api/v1/marketplace/shop/machines/:id/visibility on/off for buyers
875
+ GET /api/v1/marketplace/shop/orders the vendor queue
876
+ POST /api/v1/marketplace/shop/orders/:id/fulfil DELIVER — { items: [...] }
877
+ POST /api/v1/marketplace/purchases place an order
878
+ GET /api/v1/marketplace/purchases/:id one order + thread + files + `can`
879
+ POST /api/v1/marketplace/purchases/:id/accept pays, and is FINAL
880
+ POST /api/v1/marketplace/purchases/:id/request-changes free; nothing moves
881
+ GET /api/v1/marketplace/free-order-status/:shopSlug is MY first order here free?
882
+ ```
883
+
884
+ devcli — **two verbs, and they do not overlap**: `vidfarm shop` is the vendor
885
+ (`shop orders`, `shop deliver <id> --item "<preview>::<master>::<note>"`, `shop earnings`,
886
+ `shop withdraw`), `vidfarm purchases` is the buyer (`purchases shelf`, `vidfarm buy
887
+ <shop>/<card> -y`, `purchases accept <id> -y`, `purchases changes <id> "<fix>"`). Full lists:
888
+ `vidfarm shop help`, `vidfarm purchases help`. Everything takes `--json`.
889
+
890
+ **An order ends three ways, and only one is reversible.** `accept` approves and settles in full and
891
+ is FINAL in both directions. `request-changes` is FREE and reaches nothing on chain — the deposit
892
+ stays in escrow and the vendor re-delivers. `refund` returns the escrow; whoever signs pays the gas.
893
+ Never route a fixable note through Dollar Platoon's reject — theirs is final and would kill a funded
894
+ order. **Clean masters are withheld until the payout is stamped**; everything before that is
895
+ watermarked, and the rule is applied server-side.
896
+
897
+ ### First order free — the NEW SHOP flag
898
+
899
+ The platform can mark a shop so that **every buyer's FIRST order there is free** — one pack, $0,
900
+ one per SHOP rather than one per card. It is superagency-only
901
+ (`POST /api/v1/admin/shops/new-status`); a vendor can neither grant nor revoke it.
902
+
903
+ It changes three rules, and an agent that misses them will mislead its human:
904
+
905
+ - **No deposit and no escrow.** There is nothing to refund. A "refund" on a free order is a
906
+ **cancel** — it closes the order and returns nothing.
907
+ - **The buyer's approval is FINAL and releases the files by itself.** On the paid rail the PAYOUT
908
+ unlocks the masters; on the free rail the APPROVAL does, because there is no payout after it. So
909
+ accepting is the entire transaction and there is no undo behind it.
910
+ - **A spent free order does not come back.** Cancelling one does not restore the entitlement.
911
+
912
+ **Read `is_free` on the order — NEVER `total_usd === 0`.** A zero total reached any other way still
913
+ has escrow semantics, and treating it as free skips the payout. Underneath, a free order runs on a
914
+ **separate Dollar Platoon gig** (`dp_free_gig_id`): their API locks a gig's price mode at creation,
915
+ so one gig cannot sell both.
916
+
917
+ `GET …/shelf` is shared-cached and therefore only ever carries the **offer** (`shop.new_shop`).
918
+ Whether *this* buyer still has theirs is per-account and lives on its own `no-store` route. It is
919
+ **advisory**: price at the paid number and lower it on a yes, never the other way round.
920
+ `vidfarm purchases pack <shop>/<card>` does exactly that and prints the terms.
921
+
922
+ ## Older gig marketplace — broadcast a brief to a crowd
923
+
924
+ Web-only. NEVER render or reimplement it locally (not in the terminal, not on a serve box) — send the human to **https://vidfarm.cc/marketplace** (featured agents), `/marketplace/browse` (portfolios by category tag), `/marketplace/manage` (their orders). `vidfarm marketplace` prints exactly those links.
860
925
 
861
926
  An **order IS a Dollar Platoon gig** (tagged `vidfarm_order_<uuidv7>` plus category tags). Vidfarm exposes a deliberately thin REST wrapper — auth via session or the `vidfarm-api-key` header:
862
927
 
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vidfarm
3
- skill_version: 0.21.63
3
+ skill_version: 0.21.64
4
4
  description: Entry point for making, editing, and rendering short-form video with Vidfarm. Use when the task is to create a video, ad, explainer, or social clip; to edit or re-theme an existing composition; to source footage or audio; or to run a batch of video variants. Routes to the full director skill and the `vidfarm` CLI.
5
5
  ---
6
6
 
@@ -121,6 +121,17 @@ function money(value) {
121
121
  const parsed = Number(value);
122
122
  return Number.isFinite(parsed) ? `$${parsed.toFixed(2)}` : "$—";
123
123
  }
124
+ /**
125
+ * What an order cost, in words.
126
+ *
127
+ * "$0.00" on a free order reads as a bug, and worse, it reads as a PAID order
128
+ * that went wrong — which is the one thing it must never look like, because
129
+ * the two rails have opposite undo rules. Never derive this from the number:
130
+ * a zero total reached any other way still has escrow behind it.
131
+ */
132
+ export function orderPrice(order) {
133
+ return order.is_free ? "FREE" : money(order.total_usd);
134
+ }
124
135
  function shortDate(value) {
125
136
  const raw = String(value ?? "");
126
137
  return raw ? raw.slice(0, 10) : "—";
@@ -135,16 +146,22 @@ function shortDate(value) {
135
146
  */
136
147
  export function orderStage(order) {
137
148
  if (order.refunded_at)
138
- return { label: "refunded", colour: DIM };
149
+ return { label: order.is_free ? "cancelled" : "refunded", colour: DIM };
150
+ // A FREE order is never "paid out" — nobody was paid. It closed, and the
151
+ // buyer's files were released by the approval itself.
139
152
  if (order.settled_at)
140
- return { label: "paid out", colour: GREEN };
141
- if (order.refund_requested_at)
142
- return { label: "refund asked", colour: YELLOW };
153
+ return { label: order.is_free ? "closed" : "paid out", colour: GREEN };
154
+ if (order.refund_requested_at) {
155
+ return { label: order.is_free ? "cancel asked" : "refund asked", colour: YELLOW };
156
+ }
143
157
  if (order.changes_requested_at)
144
158
  return { label: "changes asked", colour: YELLOW };
145
159
  if (order.delivery_id)
146
160
  return { label: "delivered", colour: CYAN };
147
- if (!order.funded)
161
+ // "UNFUNDED" is the vendor's do-not-work signal, and it is meaningless on the
162
+ // free rail: a free order carries no deposit BY DESIGN and is still real work
163
+ // the vendor agreed to. Flagging it red would train vendors to abandon it.
164
+ if (!order.funded && !order.is_free)
148
165
  return { label: "UNFUNDED", colour: RED };
149
166
  return { label: "waiting on vendor", colour: YELLOW };
150
167
  }
@@ -154,9 +171,10 @@ function printOrderRow(order, opts = {}) {
154
171
  ? ` · ${String(order.buyer_name || order.buyer_email)}`
155
172
  : "";
156
173
  console.log(` ${BOLD}${order.order_id}${RESET} ${order.machine_title}`
157
- + ` ${DIM}${order.packs}× pack · ${order.video_count} videos · ${money(order.total_usd)}`
174
+ + ` ${DIM}${order.packs}× pack · ${order.video_count} videos · ${orderPrice(order)}`
158
175
  + `${who} · ${shortDate(order.created_at)}${RESET}`
159
- + ` ${stage.colour}${stage.label}${RESET}`);
176
+ + ` ${stage.colour}${stage.label}${RESET}`
177
+ + (order.is_free ? ` ${CYAN}free first order${RESET}` : ""));
160
178
  }
161
179
  function printCardRow(card) {
162
180
  const shelf = card.listed
@@ -317,6 +335,11 @@ export const SHOP_HELP = `vidfarm shop — your storefront on the vidfarm market
317
335
  a buyer orders a pack from it. Every card waits for a manual review before it
318
336
  reaches the public shelf; \`shop\` tells you which state each one is in.
319
337
 
338
+ NEW SHOP. The platform can mark a shop so that every buyer's FIRST order there
339
+ is free — one pack, $0. You cannot set it or clear it; \`shop\` reports it, and
340
+ an operator grants it. Those orders are REAL WORK with no deposit behind them,
341
+ they never pay out, and the buyer's approval is final. Work them normally.
342
+
320
343
  Earning WITHOUT a shop? You do not need one — read vidfarm.cc/agentic-clipper.md
321
344
  and use \`vidfarm gigs\`. A shop is how buyers come to you instead.
322
345
 
@@ -374,6 +397,15 @@ async function cmdShopShow(ctx) {
374
397
  if (shop.headline)
375
398
  console.log(` ${shop.headline}`);
376
399
  console.log(` ${CYAN}${shop.shop_url}${RESET}`);
400
+ if (shop.new_shop) {
401
+ // The vendor cannot set this and cannot clear it — it is a promise the
402
+ // PLATFORM makes to buyers, so the console reports it rather than offering
403
+ // it. Reported at all because it changes what the vendor is agreeing to:
404
+ // real work, no deposit behind it.
405
+ console.log(` ${GREEN}NEW SHOP${RESET} — every buyer's first order here is free, one pack at $0.`);
406
+ console.log(` ${DIM}Those orders carry no deposit and the buyer's approval is final.`
407
+ + ` Work them like any other order. Granted by the platform; ask an operator to change it.${RESET}`);
408
+ }
377
409
  if (!result.body.dollarplatoon_connected) {
378
410
  console.log(` ${YELLOW}No Dollar Platoon key on this account — your cards cannot take a paid order.${RESET}`);
379
411
  console.log(` ${DIM}Connect it at ${ctx.host}/settings/marketplace${RESET}`);
@@ -539,6 +571,13 @@ async function cmdOrderDetail(ctx, orderId) {
539
571
  return;
540
572
  const { order, buyer, delivery, thread, review } = result.body;
541
573
  printOrderRow({ ...order, buyer_name: buyer?.name, buyer_email: buyer?.email }, { showBuyer: true });
574
+ if (order.is_free) {
575
+ // A vendor reading "$0" needs to know it is the promotion and not a broken
576
+ // order — and needs to know the undo rules are inverted before they ask a
577
+ // buyer to "just approve it".
578
+ console.log(` ${CYAN}This is the buyer's free first order.${RESET}`
579
+ + ` ${DIM}No deposit, no payout, and their approval closes it for good.${RESET}`);
580
+ }
542
581
  if (order.notes)
543
582
  console.log(`\n${BOLD}Their brief${RESET}\n ${order.notes.replace(/\n/g, "\n ")}`);
544
583
  if (order.answers?.length) {
@@ -660,6 +699,11 @@ async function cmdEarnings(ctx, values) {
660
699
  console.log(` In escrow ${YELLOW}${money(totals.pendingUsd)}${RESET} ${DIM}${totals.pending ?? 0} order(s)${RESET}`);
661
700
  console.log(` Refunded ${DIM}${money(totals.refundedUsd)} · ${totals.refunded ?? 0} order(s)${RESET}`);
662
701
  console.log(` Ordered ${money(totals.ordersUsd)} ${DIM}${totals.orders ?? 0} order(s)${RESET}`);
702
+ if (result.body.shop_slug && totals.orders && !totals.ordersUsd) {
703
+ // Real orders totalling nothing is what a New Shop's early history looks
704
+ // like. Without this line it reads as a broken rollup.
705
+ console.log(` ${DIM}Orders at $0 are free first orders — they close without a payout.${RESET}`);
706
+ }
663
707
  for (const machine of result.body.machines ?? []) {
664
708
  console.log(` ${machine.label || machine.machineId} ${DIM}${money(machine.totals?.payoutsUsd)} paid out${RESET}`);
665
709
  }
@@ -821,10 +865,20 @@ export const PURCHASES_HELP = `vidfarm purchases — buy packs of videos on the
821
865
  accept approve and pay. FINAL, in both directions.
822
866
  changes free. Nothing moves — the vendor re-delivers and you rule again.
823
867
  refund the escrow comes back to you. Whoever signs it pays the gas.
868
+ On a FREE order this is a cancel: it closes the order and returns
869
+ nothing, because nothing was taken.
870
+
871
+ FIRST ORDER FREE — some shops are marked NEW SHOP by the platform, and your
872
+ first order there is one pack at $0. \`purchases shelf\` flags the offer;
873
+ \`purchases pack\` says whether YOU still have yours. It is one per shop, not
874
+ one per card, and it changes the rules: no deposit, nothing to refund, and
875
+ YOUR APPROVAL IS FINAL — it releases the files at once, with no payout step
876
+ after it to hold anything back. Cancelling does not give the free order back.
824
877
 
825
878
  THE SHELF
826
879
  purchases shelf [--shop <slug>] What is for sale → GET ${API}/shelf
827
- purchases pack <shop>/<machineId> One card, its examples and reviews
880
+ purchases pack <shop>/<machineId> One card, its examples, reviews, and
881
+ whether your free first order is still there
828
882
 
829
883
  BUYING
830
884
  purchases buy <shop>/<machineId> [--packs <n>] [--notes "<brief>"]
@@ -863,6 +917,29 @@ export function splitPackRef(ref, values) {
863
917
  return { shop: explicitShop, machine: raw };
864
918
  throw new MarketplaceError("Name the pack as <shopSlug>/<machineId> — `vidfarm purchases shelf` lists both.");
865
919
  }
920
+ /**
921
+ * Does THIS account still have its free first order at this shop?
922
+ *
923
+ * A separate call on purpose. The shelf is shared-cached and can only say that
924
+ * the OFFER exists (`shop.new_shop`); the entitlement is per-account, so it has
925
+ * its own no-store route.
926
+ *
927
+ * Fails CLOSED — an unreachable answer is `false`, which prices the order at
928
+ * the paid number. Showing $0 and then charging is the one direction that is
929
+ * unforgivable, so this only ever LOWERS a price it already showed.
930
+ */
931
+ async function freeOrderAvailable(ctx, shopSlug) {
932
+ const result = await call(ctx, "GET", `${API}/free-order-status/${encodeURIComponent(shopSlug)}`)
933
+ .catch(() => null);
934
+ return result?.ok === true && result.body?.free_available === true;
935
+ }
936
+ /** The three sentences a buyer must read BEFORE they consent to a free order. */
937
+ const FREE_ORDER_TERMS = [
938
+ "It is ONE pack, at $0, and you get one per shop — not one per card.",
939
+ "Nothing is deposited, so there is no escrow and nothing to refund.",
940
+ "YOUR APPROVAL IS FINAL and releases the files at once. There is no payout to hold back,"
941
+ + " so accepting is the whole transaction."
942
+ ];
866
943
  async function cmdShelf(ctx, values) {
867
944
  const result = await call(ctx, "GET", `${API}/shelf`, {
868
945
  query: { shop: optionalText(values.shop), limit: optionalText(values.limit) }
@@ -877,7 +954,10 @@ async function cmdShelf(ctx, values) {
877
954
  }
878
955
  console.log(`${BOLD}${packs.length} pack${packs.length === 1 ? "" : "s"} for sale${RESET}`);
879
956
  for (const pack of packs) {
880
- console.log(`\n ${BOLD}${pack.title}${RESET} ${DIM}by ${pack.shop.display_name}${RESET}`);
957
+ console.log(`\n ${BOLD}${pack.title}${RESET} ${DIM}by ${pack.shop.display_name}${RESET}`
958
+ // The OFFER, which is the same for everybody and safe on a cached list.
959
+ // Whether YOU still have yours is answered by `purchases pack`.
960
+ + (pack.shop.new_shop ? ` ${GREEN}FIRST ORDER FREE${RESET}` : ""));
881
961
  console.log(` ${money(pack.price_per_lot_usd)} for ${pack.lot_size} videos`
882
962
  + `${pack.delivery_estimate ? ` ${DIM}· ${pack.delivery_estimate}${RESET}` : ""}`);
883
963
  if (pack.note)
@@ -897,6 +977,19 @@ async function cmdPack(ctx, values, ref) {
897
977
  + `${pack.delivery_estimate ? ` · ${pack.delivery_estimate}` : ""}`);
898
978
  if (pack.note)
899
979
  console.log(` ${pack.note}`);
980
+ // The card above is a cached, public answer. THIS is the per-account one, and
981
+ // it is the only place that can say the word "you".
982
+ if (result.body.shop.new_shop) {
983
+ if (await freeOrderAvailable(ctx, result.body.shop.shop_slug)) {
984
+ console.log(`\n ${GREEN}Your first order at this shop is FREE — one pack, $0.${RESET}`);
985
+ for (const term of FREE_ORDER_TERMS)
986
+ console.log(` ${DIM}· ${term}${RESET}`);
987
+ }
988
+ else {
989
+ console.log(`\n ${DIM}This shop offers a free first order, but you have already used yours.`
990
+ + ` This one is ${money(pack.price_per_lot_usd)}.${RESET}`);
991
+ }
992
+ }
900
993
  if (pack.before_you_buy_note) {
901
994
  console.log(`\n ${YELLOW}Before you buy${RESET}\n ${pack.before_you_buy_note}`);
902
995
  }
@@ -938,9 +1031,37 @@ async function cmdBuy(ctx, values, ref) {
938
1031
  ].join("\n"));
939
1032
  }
940
1033
  const total = Math.round(packs * Number(pack.price_per_lot_usd) * 100) / 100;
1034
+ // ── is this one free? ──
1035
+ //
1036
+ // ADVISORY, and only ever downward. The order route claims the free pack
1037
+ // again with a conditional write, so a stale yes here cannot mint a free
1038
+ // order — it can only produce a confirmation that was too generous, and the
1039
+ // server then charges the real price. Which is why this is read BEFORE the
1040
+ // confirmation and never after: quoting $0 and then charging is the one
1041
+ // direction a buyer cannot forgive.
1042
+ const free = card.body.shop.new_shop === true
1043
+ && await freeOrderAvailable(ctx, card.body.shop.shop_slug);
1044
+ // A free order is ONE pack whatever was asked for — the free rail prices a
1045
+ // whole order at $0 and cannot discount part of one. Say so before they
1046
+ // consent, rather than silently delivering a third of what they typed.
1047
+ if (free && packs > 1 && !ctx.json) {
1048
+ console.error(`${YELLOW}Your free first order is ONE pack. `
1049
+ + `Ordering ${packs} would be charged in full — drop --packs to take the free one.${RESET}`);
1050
+ }
941
1051
  if (!values.yes && !ctx.json) {
942
1052
  console.log(`${BOLD}${packs} × ${pack.title}${RESET} from ${card.body.shop.display_name}`);
943
- console.log(` ${packs * pack.lot_size} videos · ${BOLD}${money(total)}${RESET} into escrow now`);
1053
+ if (free && packs === 1) {
1054
+ console.log(` ${pack.lot_size} videos · ${GREEN}${BOLD}FREE${RESET}`
1055
+ + ` ${DIM}— your first order at this shop${RESET}`);
1056
+ // THE CONSENT POINT. "Free" is the part a buyer reads; the part they must
1057
+ // read is that free costs them the undo. On this rail there is no escrow
1058
+ // to hold back and approval ends the order outright.
1059
+ for (const term of FREE_ORDER_TERMS)
1060
+ console.log(` ${YELLOW}· ${term}${RESET}`);
1061
+ }
1062
+ else {
1063
+ console.log(` ${packs * pack.lot_size} videos · ${BOLD}${money(total)}${RESET} into escrow now`);
1064
+ }
944
1065
  if (pack.before_you_buy_note)
945
1066
  console.log(` ${YELLOW}${pack.before_you_buy_note}${RESET}`);
946
1067
  console.log(`\n ${DIM}Add -y to place it.${RESET}`);
@@ -975,8 +1096,21 @@ async function cmdBuy(ctx, values, ref) {
975
1096
  return;
976
1097
  console.log(`${GREEN}${result.body.replay ? "Already ordered." : "Ordered."}${RESET}`
977
1098
  + ` ${BOLD}${result.body.order_id}${RESET}`);
978
- console.log(` ${result.body.video_count} videos · ${money(result.body.total_usd)}`
979
- + ` · ${result.body.funded ? `${GREEN}funded${RESET}` : `${YELLOW}NOT funded no deposit was taken${RESET}`}`);
1099
+ // THE SERVER DECIDED, not the check above. The claim is a conditional write
1100
+ // and this is its answer, so a race that lost prints the price it was charged.
1101
+ console.log(` ${result.body.video_count} videos · `
1102
+ + (result.body.free ? `${GREEN}FREE — your first order at this shop${RESET}` : money(result.body.total_usd))
1103
+ + ` · ${result.body.funded
1104
+ ? (result.body.free ? `${GREEN}live${RESET}` : `${GREEN}funded${RESET}`)
1105
+ // On the free rail there is nothing to fund, so an unfunded free order
1106
+ // means it never reached Dollar Platoon at all — a different fault, and
1107
+ // it needs a different sentence.
1108
+ : (result.body.free
1109
+ ? `${YELLOW}NOT live — it did not reach Dollar Platoon${RESET}`
1110
+ : `${YELLOW}NOT funded — no deposit was taken${RESET}`)}`);
1111
+ if (result.body.free) {
1112
+ console.log(` ${YELLOW}Your approval on this order is FINAL and releases the files at once.${RESET}`);
1113
+ }
980
1114
  console.log(` ${CYAN}${ctx.host}${result.body.order_url}${RESET}`);
981
1115
  }
982
1116
  async function cmdPurchasesList(ctx) {
@@ -991,7 +1125,7 @@ async function cmdPurchasesList(ctx) {
991
1125
  }
992
1126
  for (const row of rows) {
993
1127
  console.log(` ${BOLD}${row.order_id ?? row.delivery_id}${RESET} ${row.machine_title}`
994
- + ` ${DIM}from ${row.shop_name} · ${money(row.total_usd)} · ${shortDate(row.created_at)}${RESET}`
1128
+ + ` ${DIM}from ${row.shop_name} · ${orderPrice(row)} · ${shortDate(row.created_at)}${RESET}`
995
1129
  + ` ${row.opened_at ? `${DIM}opened${RESET}` : `${CYAN}sealed${RESET}`} ${row.status}`);
996
1130
  }
997
1131
  }
@@ -1013,7 +1147,12 @@ async function cmdPurchaseDetail(ctx, orderId) {
1013
1147
  console.log(`\n${BOLD}The pack${RESET} ${DIM}${delivery.items.length} item(s)`
1014
1148
  + `${delivery.opened_at ? "" : " · still sealed"}${RESET}`);
1015
1149
  if (!delivery.masters_released) {
1016
- console.log(` ${YELLOW}Masters are locked until you accept and the payout lands.${RESET}`);
1150
+ // Two rails, two release rules, and the difference matters to a buyer
1151
+ // deciding whether to accept: on a paid order the payout unlocks the
1152
+ // masters, on a free one the approval does it by itself.
1153
+ console.log(order.is_free
1154
+ ? ` ${YELLOW}Masters are locked until you accept. On a free order accepting IS the end — there is no payout after it.${RESET}`
1155
+ : ` ${YELLOW}Masters are locked until you accept and the payout lands.${RESET}`);
1017
1156
  }
1018
1157
  for (const item of delivery.items) {
1019
1158
  const verdict = item.verdict === "approved"
@@ -1042,9 +1181,13 @@ async function cmdPurchaseDetail(ctx, orderId) {
1042
1181
  if (review)
1043
1182
  console.log(`\n${BOLD}Your review${RESET} ${review.overall}/5`);
1044
1183
  const moves = [
1045
- can?.accept ? `accept (pays ${money(order.total_usd)}, FINAL)` : null,
1184
+ can?.accept
1185
+ ? (order.is_free ? "accept (FINAL, nothing charged)" : `accept (pays ${money(order.total_usd)}, FINAL)`)
1186
+ : null,
1046
1187
  can?.request_changes ? "changes (free)" : null,
1047
- can?.refund ? "refund" : null,
1188
+ // "refund" on an order that cost nothing reads as a bug. Same route either
1189
+ // way — it closes the order — but the word has to match what happened.
1190
+ can?.refund ? (order.is_free ? "cancel" : "refund") : null,
1048
1191
  can?.review ? "review" : null
1049
1192
  ].filter(Boolean);
1050
1193
  if (moves.length)
@@ -1112,14 +1255,24 @@ async function cmdRate(ctx, values, orderId, itemId, verdictWord) {
1112
1255
  }
1113
1256
  async function cmdAccept(ctx, values, orderId) {
1114
1257
  const body = await fetchOrder(ctx, orderId);
1258
+ const free = body.order.is_free === true;
1115
1259
  if (!values.yes && !ctx.json) {
1116
- // The one command in this file that spends money irreversibly. It gets a
1117
- // confirmation for the same reason the web card has a dialog.
1118
- console.log(`${BOLD}Accepting pays ${money(body.order.total_usd)} to ${body.order.shop_name}, in full.${RESET}`);
1119
- console.log(` Dollar Platoon's verdict is FINAL there is no undo, and no partial payment.`);
1260
+ // The one command in this file that cannot be undone. It gets a
1261
+ // confirmation for the same reason the web card has a dialog — and a FREE
1262
+ // order needs it MORE, not less: nothing is charged, so the only thing the
1263
+ // buyer is spending is their right to send the pack back.
1264
+ console.log(free
1265
+ ? `${BOLD}Accepting closes this order for good. Nothing is charged — it is your free first order.${RESET}`
1266
+ : `${BOLD}Accepting pays ${money(body.order.total_usd)} to ${body.order.shop_name}, in full.${RESET}`);
1267
+ console.log(free
1268
+ ? ` Your verdict is FINAL and releases the files at once. There is no payout to hold back,`
1269
+ + ` so this IS the end of the order — you cannot ask for changes afterwards.`
1270
+ : ` Dollar Platoon's verdict is FINAL — there is no undo, and no partial payment.`);
1120
1271
  const rejected = (body.delivery?.items ?? []).filter((item) => item.verdict === "rejected").length;
1121
1272
  if (rejected) {
1122
- console.log(` ${YELLOW}You marked ${rejected} video(s) wrong. Accepting pays for them anyway.${RESET}`);
1273
+ console.log(free
1274
+ ? ` ${YELLOW}You marked ${rejected} video(s) wrong. Accepting takes the pack as it is.${RESET}`
1275
+ : ` ${YELLOW}You marked ${rejected} video(s) wrong. Accepting pays for them anyway.${RESET}`);
1123
1276
  console.log(` ${YELLOW}Free alternative: vidfarm purchases changes ${orderId} "<what to fix>"${RESET}`);
1124
1277
  }
1125
1278
  console.log(`\n ${DIM}Add -y to accept.${RESET}`);
@@ -1130,9 +1283,11 @@ async function cmdAccept(ctx, values, orderId) {
1130
1283
  if (emit(ctx, result.body))
1131
1284
  return;
1132
1285
  console.log(`${GREEN}${result.body.message}${RESET}`);
1133
- if (!result.body.settled) {
1134
- // Approved-but-unsettled is real and recoverable. Never tell this buyer to
1135
- // accept again there is nothing left to approve.
1286
+ // Approved-but-unsettled is real and recoverable, and it is a PAID-rail
1287
+ // state only: on the free rail the approval is the whole transaction, so
1288
+ // there is no second call left to fail.
1289
+ if (!result.body.settled && !free) {
1290
+ // Never tell this buyer to accept again — there is nothing left to approve.
1136
1291
  console.log(`${YELLOW}The verdict landed but the payout did not. Do NOT accept again — retry the payout shortly.${RESET}`);
1137
1292
  }
1138
1293
  }
@@ -1154,9 +1309,19 @@ async function cmdBuyerRefund(ctx, values, orderId) {
1154
1309
  assertOk(result, "purchases refund");
1155
1310
  if (emit(ctx, result.body))
1156
1311
  return;
1312
+ // The server's own sentence already knows which rail it was on — a free
1313
+ // order gets "cancelled", a paid one "refunded" — so it is printed as-is
1314
+ // rather than reworded here.
1157
1315
  console.log(`${GREEN}${result.body.message}${RESET}`);
1158
1316
  if (result.body.tx_hash)
1159
1317
  console.log(` ${DIM}tx ${result.body.tx_hash}${RESET}`);
1318
+ // A spent free order does NOT come back. Said here because this is the one
1319
+ // command that spends it, and a buyer cancelling to "re-order properly"
1320
+ // would otherwise find out by being charged.
1321
+ if (result.body.free_spent) {
1322
+ console.log(` ${YELLOW}Your free first order at this shop stays used. `
1323
+ + `A new order here will be charged.${RESET}`);
1324
+ }
1160
1325
  }
1161
1326
  async function cmdReview(ctx, values, orderId) {
1162
1327
  const axes = ["value", "speed", "quality", "support"];
package/marketplace.md CHANGED
@@ -52,6 +52,12 @@ Both take `--json` on every subcommand, and both authenticate on the ordinary vi
52
52
  (`vidfarm login`, `VIDFARM_API_KEY`, or `--api-key`). The marketplace is paid-only, so a free key
53
53
  reaches the routes and is refused with `402`.
54
54
 
55
+ **One shop-level rule cuts across both verbs: NEW SHOP.** The platform can mark a shop so every
56
+ buyer's *first* order there is free — one pack, $0, one per shop. Those orders carry no deposit and
57
+ never pay out, and **the buyer's approval is final and releases the files by itself** rather than
58
+ waiting for a payout. Buyers: § *first order free* in `marketplace-buyer.md`. Vendors: § *a NEW SHOP
59
+ order pays nothing* below. Either way, read `is_free` on the order — never `total_usd == 0`.
60
+
55
61
  **This document is the shared reference underneath both harnesses** — the keys, the REST surface,
56
62
  the machines, the proof rules, the payout rules. The harness tells you *what loop to run*; this file
57
63
  tells you *what each call does*. Read the harness for your side first, then use this as the manual.
@@ -1699,6 +1705,24 @@ dollar platoon task**, which is why the order page and the task thread are the s
1699
1705
  | you deliver | proof — watermarked evidence + **sealed note** |
1700
1706
  | the buyer's pack | `https://vidfarm.cc/delivery/<id>` |
1701
1707
 
1708
+ #### a NEW SHOP order pays nothing, and it is still real work
1709
+
1710
+ if your shop is marked **new shop**, every buyer's **first order** at it is free — one pack, $0. the
1711
+ platform grants that, not you; `vidfarm shop` reports it and an operator changes it. what matters
1712
+ while you are working one:
1713
+
1714
+ - **it carries no deposit and it never pays out.** `vidfarm shop orders` shows it as `FREE`, not as
1715
+ `UNFUNDED` — those are different things, and the red one means *do not work this*. a free order is
1716
+ an order the vendor agreed to take. work it normally.
1717
+ - **the buyer's approval is final and releases your files at once.** on a paid order the payout
1718
+ unlocks the clean masters; here the approval does it alone, because there is no payout afterwards.
1719
+ so there is no window in which to renegotiate — deliver what you would deliver for money.
1720
+ - **your earnings chart will show orders totalling $0.** that is the promotion, not a broken rollup.
1721
+
1722
+ underneath it is a **separate dollar platoon gig** (`dpFreeGigId`) with `list_price: 0`. their API
1723
+ locks a gig's price mode at creation, so one gig cannot sell both free and paid — which is why a
1724
+ machine that does both owns two. you never choose which: the order lands in the right one.
1725
+
1702
1726
  #### the delivery url — vidfarm mints it, you never invent it
1703
1727
 
1704
1728
  **you do not construct a `/delivery/` url.** it is minted when you fulfil the order, and it is the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@officexapp/vidfarm-devcli",
3
- "version": "0.21.63",
3
+ "version": "0.21.64",
4
4
  "description": "Local bridge for the Vidfarm Trackpad Editor. `vidfarm serve <template_id>` boots the FULL editor on localhost (disk-backed records/storage, free in-process render); edit composition.html on disk (Claude Code, Codex, etc.) and the browser live-morphs it.",
5
5
  "type": "module",
6
6
  "bin": {