@officexapp/vidfarm-devcli 0.21.11 → 0.21.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/hyperframes-core/SKILL.md +2 -0
- package/.agents/skills/hyperframes-creative/SKILL.md +1 -0
- package/.agents/skills/hyperframes-creative/references/beat-direction.md +17 -0
- package/.agents/skills/vidfarm-director/SKILL.md +4 -0
- package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +1 -1
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +23 -6
- package/.agents/skills/vidfarm-director/references/core-workflows.md +1 -1
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +15 -2
- package/.agents/skills/vidfarm-director/references/primitives.md +1 -1
- package/.agents/skills/vidfarm-director/references/rest-api.md +1 -1
- package/.agents/skills/vidfarm-media/SKILL.md +16 -6
- package/README.md +2 -2
- package/SKILL.director.md +45 -11
- package/demo/dist/app.js +247 -226
- package/dist/src/cli.js +198 -30
- package/dist/src/devcli/composition-edit.js +99 -23
- package/dist/src/devcli/cost-mode.js +13 -4
- package/dist/src/devcli/doctor.js +65 -9
- package/dist/src/devcli/local-frontend-server.js +342 -50
- package/dist/src/devcli/port-utils.js +43 -0
- package/dist/src/devcli/process-scan.js +173 -0
- package/package.json +3 -1
- package/public/serve-shells/editor.html +75 -13
- package/public/serve-shells/library-files.html +75 -13
- package/public/serve-shells/library-raws.html +75 -13
- package/public/serve-shells/tools-clipper.html +75 -13
- package/public/serve-shells/tools-image.html +75 -13
- package/public/serve-shells/tools-video.html +75 -13
|
@@ -2529,12 +2529,25 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2529
2529
|
// Desktop-agents nudge banner: a full-width alert pinned to the top of the
|
|
2530
2530
|
// /editor and /chat surfaces pointing to the setup guide. Both surfaces embed
|
|
2531
2531
|
// this chrome script, so gate strictly by pathname. Dismissible per session.
|
|
2532
|
+
// The nudge is aimed at the DIRECTOR (the paid operator who drives Vidfarm with
|
|
2533
|
+
// a desktop AI agent). A client reviewing a shared video in the editor should
|
|
2534
|
+
// NOT be told to go install Claude Code / Codex — so on the editor we suppress
|
|
2535
|
+
// it for free-tier (client / reviewer) sessions, read from the editor boot JSON.
|
|
2532
2536
|
(function mountDesktopAgentsBanner() {
|
|
2533
2537
|
try {
|
|
2534
2538
|
var path = location.pathname;
|
|
2535
2539
|
var onEditor = path === '/editor' || path.indexOf('/editor/') === 0;
|
|
2536
2540
|
var onChat = path === '/chat' || path.indexOf('/chat/') === 0;
|
|
2537
2541
|
if (!onEditor && !onChat) return;
|
|
2542
|
+
if (onEditor) {
|
|
2543
|
+
var bootEl = document.getElementById('hf-boot');
|
|
2544
|
+
if (bootEl) {
|
|
2545
|
+
try {
|
|
2546
|
+
var boot = JSON.parse(bootEl.textContent || '{}');
|
|
2547
|
+
if (boot && boot.freeTier) return;
|
|
2548
|
+
} catch (e) {}
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2538
2551
|
if (document.querySelector('.vf-topbanner')) return;
|
|
2539
2552
|
if (sessionStorage.getItem('vf-topbanner-dismissed') === '1') return;
|
|
2540
2553
|
var GUIDE = '/blog/desktop-ai-agents';
|
|
@@ -2740,6 +2753,23 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2740
2753
|
setLeftMode(''); // show the conversation, not the Files/History drawer
|
|
2741
2754
|
openThread(id);
|
|
2742
2755
|
}
|
|
2756
|
+
var hadHandoff = !!handoffThread;
|
|
2757
|
+
// Sticky active thread per template so a page refresh reopens the SAME
|
|
2758
|
+
// conversation instead of a blank chat (server routes deliberately never
|
|
2759
|
+
// attach ?thread= to editor URLs — the browser owns "which chat is active").
|
|
2760
|
+
function activeThreadKey() { return 'rk-chat-active:' + TEMPLATE_ID; }
|
|
2761
|
+
function saveActiveThread(id) {
|
|
2762
|
+
try { if (id) localStorage.setItem(activeThreadKey(), id); else localStorage.removeItem(activeThreadKey()); } catch (e) {}
|
|
2763
|
+
}
|
|
2764
|
+
var restoredActive = false;
|
|
2765
|
+
function restoreActiveThread() {
|
|
2766
|
+
if (restoredActive || hadHandoff) return;
|
|
2767
|
+
restoredActive = true;
|
|
2768
|
+
if (convo.length) return; // user already chatting — don't clobber
|
|
2769
|
+
var saved = null;
|
|
2770
|
+
try { saved = localStorage.getItem(activeThreadKey()); } catch (e) {}
|
|
2771
|
+
if (saved) openThread(saved);
|
|
2772
|
+
}
|
|
2743
2773
|
var busy = false;
|
|
2744
2774
|
var pendingAbort = null; // AbortController for the in-flight reply (Stop button)
|
|
2745
2775
|
|
|
@@ -2790,14 +2820,20 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2790
2820
|
// outgoing user turn — verbatim to what the SPA's own chat sends — so the agent
|
|
2791
2821
|
// knows which fork to read (video_context) and mutate (editor_action). Only the
|
|
2792
2822
|
// /editor dock has this bridge; elsewhere it returns ''.
|
|
2823
|
+
// Async: the Option-B bridge's getSnapshot() returns a PROMISE (it re-reads the
|
|
2824
|
+
// composition through the files API). The old sync call JSON.stringify'd the
|
|
2825
|
+
// Promise itself, sending the model a literal "{}" editor_context — no fork id,
|
|
2826
|
+
// no layers, no viral DNA. Always resolve before serializing.
|
|
2793
2827
|
function editorContextBlock() {
|
|
2794
|
-
if (!isEditorDock) return '';
|
|
2828
|
+
if (!isEditorDock) return Promise.resolve('');
|
|
2795
2829
|
var bridge = (typeof window !== 'undefined') ? window.__vidfarmEditorAction : null;
|
|
2796
|
-
if (!bridge || typeof bridge.getSnapshot !== 'function') return '';
|
|
2830
|
+
if (!bridge || typeof bridge.getSnapshot !== 'function') return Promise.resolve('');
|
|
2797
2831
|
var snap; try { snap = bridge.getSnapshot(); } catch (e) { snap = null; }
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2832
|
+
return Promise.resolve(snap).then(function (s) {
|
|
2833
|
+
if (!s) return '';
|
|
2834
|
+
try { return '\n\n<editor_context>\n' + JSON.stringify(s, null, 2) + '\n</editor_context>'; }
|
|
2835
|
+
catch (e) { return ''; }
|
|
2836
|
+
}, function () { return ''; });
|
|
2801
2837
|
}
|
|
2802
2838
|
function loadBoot() {
|
|
2803
2839
|
if (BOOT_STATE === 'ready' || BOOT_STATE === 'loading') return;
|
|
@@ -2820,6 +2856,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2820
2856
|
loadThreads();
|
|
2821
2857
|
if (leftMode() === 'cloud') loadTasks();
|
|
2822
2858
|
consumeHandoff();
|
|
2859
|
+
restoreActiveThread();
|
|
2823
2860
|
})
|
|
2824
2861
|
.catch(function () { BOOT_STATE = 'error'; });
|
|
2825
2862
|
}
|
|
@@ -3373,8 +3410,10 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3373
3410
|
setBusy(true);
|
|
3374
3411
|
if (!threadId) threadId = genId('thread');
|
|
3375
3412
|
// Attach a fresh <editor_context> to the current (last) user turn only, so the
|
|
3376
|
-
// model sees the composition state without bloating persisted history.
|
|
3377
|
-
|
|
3413
|
+
// model sees the composition state without bloating persisted history. The
|
|
3414
|
+
// block resolves asynchronously (files-API read) — wait for it before building
|
|
3415
|
+
// the outgoing messages so the model actually receives the composition state.
|
|
3416
|
+
editorContextBlock().then(function (ctxBlock) {
|
|
3378
3417
|
// Attachments (pasted files OR files picked from the directory explorer) must
|
|
3379
3418
|
// ride in the model messages as file content parts + a URL text line — the
|
|
3380
3419
|
// backend only feeds the model messages[].content, NOT user_message.attachments
|
|
@@ -3392,6 +3431,9 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3392
3431
|
}
|
|
3393
3432
|
return { role: m.role, content: content };
|
|
3394
3433
|
});
|
|
3434
|
+
// The send is what turns a freshly minted thread id into a real saved
|
|
3435
|
+
// thread — make it the sticky-restore target from this moment on.
|
|
3436
|
+
saveActiveThread(threadId);
|
|
3395
3437
|
var body = {
|
|
3396
3438
|
messages: outMessages,
|
|
3397
3439
|
thread_id: threadId,
|
|
@@ -3455,6 +3497,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3455
3497
|
if (!API_KEY) msg = msg + '\n\nAdd an AI provider key in Settings to chat on your own keys.';
|
|
3456
3498
|
view.fail(msg); setBusy(false); if (input) input.focus();
|
|
3457
3499
|
});
|
|
3500
|
+
}); // end editorContextBlock().then
|
|
3458
3501
|
}
|
|
3459
3502
|
|
|
3460
3503
|
function resetConversation() {
|
|
@@ -3708,10 +3751,25 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3708
3751
|
// fall through to chat-attach for folders / non-placeable files.
|
|
3709
3752
|
if (isEditorDock && it && it.viewUrl) {
|
|
3710
3753
|
var bridge = (typeof window !== 'undefined') ? window.__vidfarmEditorAction : null;
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3754
|
+
// Placeability must be decided SYNCHRONOUSLY (the Option-B bridge returns a
|
|
3755
|
+
// Promise, so we can't branch on its result to decide chat-attach fallback —
|
|
3756
|
+
// checking ".ok" on the Promise made EVERY click fall through, placing the
|
|
3757
|
+
// media AND attaching it to chat, with no toast). Only image/video/audio go
|
|
3758
|
+
// on the timeline; folders/docs still fall through to chat-attach.
|
|
3759
|
+
var ct = String(it.contentType || '');
|
|
3760
|
+
if (bridge && typeof bridge.placeMediaAtPlayhead === 'function'
|
|
3761
|
+
&& (ct.indexOf('image/') === 0 || ct.indexOf('video/') === 0 || ct.indexOf('audio/') === 0)) {
|
|
3762
|
+
var placedName = it.name || 'media';
|
|
3763
|
+
var settlePlace = function (r) {
|
|
3764
|
+
if (r && r.ok) { editorPlaceToast('Added \u201c' + placedName + '\u201d to the timeline'); }
|
|
3765
|
+
else { editorPlaceToast('Couldn\u2019t add \u201c' + placedName + '\u201d' + ((r && r.error) ? ': ' + r.error : ''), true); }
|
|
3766
|
+
};
|
|
3767
|
+
try {
|
|
3768
|
+
var res = bridge.placeMediaAtPlayhead({ url: it.viewUrl, name: it.name, contentType: it.contentType });
|
|
3769
|
+
if (res && typeof res.then === 'function') { res.then(settlePlace, function (e) { settlePlace({ ok: false, error: (e && e.message) ? e.message : String(e) }); }); }
|
|
3770
|
+
else { settlePlace(res); }
|
|
3771
|
+
} catch (e) { settlePlace({ ok: false, error: (e && e.message) ? e.message : String(e) }); }
|
|
3772
|
+
return;
|
|
3715
3773
|
}
|
|
3716
3774
|
}
|
|
3717
3775
|
// Files-only drawer (opened from the /chat page) has no chat composer of its
|
|
@@ -3828,7 +3886,11 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3828
3886
|
return wrap;
|
|
3829
3887
|
}
|
|
3830
3888
|
function setActiveThread(id) {
|
|
3831
|
-
|
|
3889
|
+
// null = "fresh unsaved chat": KEEP the freshly minted threadId (sends must
|
|
3890
|
+
// always carry a real thread_id or the server silently skips persistence)
|
|
3891
|
+
// and clear the sticky restore key; a real id becomes both current + sticky.
|
|
3892
|
+
if (id) { threadId = id; saveActiveThread(id); }
|
|
3893
|
+
else { saveActiveThread(null); }
|
|
3832
3894
|
if (!histBody) return;
|
|
3833
3895
|
var rows = histBody.querySelectorAll('.rk-aichat-frow');
|
|
3834
3896
|
for (var i = 0; i < rows.length; i++) rows[i].classList.toggle('is-active', rows[i].getAttribute('data-id') === id);
|
|
@@ -3921,7 +3983,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3921
3983
|
.then(function (r) {
|
|
3922
3984
|
if (!r.ok && r.status !== 404) throw new Error('http ' + r.status);
|
|
3923
3985
|
threads = threads.filter(function (t) { return t.id !== id; });
|
|
3924
|
-
if (id === threadId) resetConversation();
|
|
3986
|
+
if (id === threadId) { resetConversation(); saveActiveThread(null); }
|
|
3925
3987
|
renderHistory();
|
|
3926
3988
|
})
|
|
3927
3989
|
.catch(function () {});
|