mover-os 4.7.6 → 4.7.8
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/install.js +763 -70
- package/package.json +15 -3
- package/src/dashboard/build.js +1503 -0
- package/src/dashboard/dashboard.js +276 -0
- package/src/dashboard/index.js +319 -0
- package/src/dashboard/lib/activation-log.js +297 -0
- package/src/dashboard/lib/active-context-parser.js +177 -0
- package/src/dashboard/lib/agent-command.js +93 -0
- package/src/dashboard/lib/agent-detect.js +255 -0
- package/src/dashboard/lib/agent-detector.js +92 -0
- package/src/dashboard/lib/agent-session.js +462 -0
- package/src/dashboard/lib/anti-identity-detector.js +116 -0
- package/src/dashboard/lib/auto-learnings-parser.js +183 -0
- package/src/dashboard/lib/cli-usage-parser.js +92 -0
- package/src/dashboard/lib/config-parser.js +109 -0
- package/src/dashboard/lib/connect-recommender.js +131 -0
- package/src/dashboard/lib/correlations-parser.js +231 -0
- package/src/dashboard/lib/daily-note-resolver.js +211 -0
- package/src/dashboard/lib/date-utils.js +35 -0
- package/src/dashboard/lib/distribution-parser.js +78 -0
- package/src/dashboard/lib/dossier-parser.js +64 -0
- package/src/dashboard/lib/drift-history.js +83 -0
- package/src/dashboard/lib/drift-score.js +119 -0
- package/src/dashboard/lib/engine-health.js +170 -0
- package/src/dashboard/lib/engine-writer.js +1685 -0
- package/src/dashboard/lib/execution-plan.js +125 -0
- package/src/dashboard/lib/experiments-parser.js +429 -0
- package/src/dashboard/lib/feed-parser.js +294 -0
- package/src/dashboard/lib/forked-future.js +60 -0
- package/src/dashboard/lib/goal-forecast.js +304 -0
- package/src/dashboard/lib/goals-parser.js +67 -0
- package/src/dashboard/lib/health-protocols-parser.js +133 -0
- package/src/dashboard/lib/hook-activity.js +48 -0
- package/src/dashboard/lib/hook-indexer.js +169 -0
- package/src/dashboard/lib/hourly-activity-parser.js +143 -0
- package/src/dashboard/lib/identity-parser.js +85 -0
- package/src/dashboard/lib/ingestion.js +418 -0
- package/src/dashboard/lib/library-indexer-v2.js +212 -0
- package/src/dashboard/lib/library-indexer.js +105 -0
- package/src/dashboard/lib/log-activation.sh +61 -0
- package/src/dashboard/lib/memory-curator.js +97 -0
- package/src/dashboard/lib/memory-gardener.js +177 -0
- package/src/dashboard/lib/memory-gepa.js +102 -0
- package/src/dashboard/lib/memory-index.js +470 -0
- package/src/dashboard/lib/memory-rerank.js +72 -0
- package/src/dashboard/lib/memory-text.js +136 -0
- package/src/dashboard/lib/metrics-log-parser.js +76 -0
- package/src/dashboard/lib/moves-usage-parser.js +184 -0
- package/src/dashboard/lib/onboarding-forge.js +70 -0
- package/src/dashboard/lib/override-outcome-parser.js +68 -0
- package/src/dashboard/lib/override-summary.js +73 -0
- package/src/dashboard/lib/paths.js +192 -0
- package/src/dashboard/lib/pattern-manifest-loader.js +29 -0
- package/src/dashboard/lib/phantom-strategy.js +129 -0
- package/src/dashboard/lib/project-scanner.js +121 -0
- package/src/dashboard/lib/promise-wall.js +88 -0
- package/src/dashboard/lib/record-score.js +173 -0
- package/src/dashboard/lib/redaction.js +140 -0
- package/src/dashboard/lib/refusal-parser.js +44 -0
- package/src/dashboard/lib/regenerate-manifest.js +206 -0
- package/src/dashboard/lib/rewind-snapshots.js +689 -0
- package/src/dashboard/lib/roast-wall-parser.js +200 -0
- package/src/dashboard/lib/run-registry.js +226 -0
- package/src/dashboard/lib/safe-write.js +63 -0
- package/src/dashboard/lib/session-log-parser.js +145 -0
- package/src/dashboard/lib/session-time-parser.js +158 -0
- package/src/dashboard/lib/skill-index.js +171 -0
- package/src/dashboard/lib/skill-indexer.js +118 -0
- package/src/dashboard/lib/skill-recommender.js +689 -0
- package/src/dashboard/lib/strategy-parser.js +245 -0
- package/src/dashboard/lib/strategy-protocol-parser.js +135 -0
- package/src/dashboard/lib/streak-parser.js +95 -0
- package/src/dashboard/lib/suggested-now.js +254 -0
- package/src/dashboard/lib/tool-awareness.js +125 -0
- package/src/dashboard/lib/transcript-parser.js +331 -0
- package/src/dashboard/lib/vault-graph-parser.js +205 -0
- package/src/dashboard/lib/view-generator.js +163 -0
- package/src/dashboard/lib/voice-dna-parser.js +57 -0
- package/src/dashboard/lib/walkthrough-script.js +140 -0
- package/src/dashboard/lib/workflow-graph-parser.js +170 -0
- package/src/dashboard/lib/workflow-library-parser.js +146 -0
- package/src/dashboard/server.js +2024 -0
- package/src/dashboard/shortcut.js +0 -0
- package/src/dashboard/static/setup-poc.html +306 -0
- package/src/dashboard/static/walkthrough-poc.html +580 -0
- package/src/dashboard/styles.css +1201 -0
- package/src/dashboard/templates/index.html +278 -0
- package/src/dashboard/ui/dist/assets/hanken-grotesk-latin-ext-wght-normal-Dg-wlmqe.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/hanken-grotesk-latin-wght-normal-CaVRRdDk.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/hanken-grotesk-vietnamese-wght-normal-CHiFlh_0.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/hero.png +0 -0
- package/src/dashboard/ui/dist/assets/index-598CSGOZ.js +157 -0
- package/src/dashboard/ui/dist/assets/index-BP--M69H.css +1 -0
- package/src/dashboard/ui/dist/assets/index-CidzmwSW.js +34 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/mover-system.css +62 -0
- package/src/dashboard/ui/dist/assets/xterm-CqkleIqs.js +1 -0
- package/src/dashboard/ui/dist/icon.svg +4 -0
- package/src/dashboard/ui/dist/index.html +18 -0
- package/src/dashboard/ui/dist/manifest.webmanifest +1 -0
- package/src/dashboard/ui/dist/registerSW.js +1 -0
- package/src/dashboard/ui/dist/sw.js +1 -0
- package/src/dashboard/ui/dist/workbox-39fa566e.js +1 -0
|
Binary file
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Mover OS · Setup</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600&family=Newsreader:opsz,wght@6..72,400;6..72,500&display=swap" rel="stylesheet">
|
|
10
|
+
<!-- Shared Mover OS tokens (same palette as the dashboard) so /setup is
|
|
11
|
+
native, not alien. Canonical values live in styles/v6/v3.css. -->
|
|
12
|
+
<link rel="stylesheet" href="/assets/mover-system.css">
|
|
13
|
+
<style>
|
|
14
|
+
/* page layout + class skinning — colours come from mover-system.css tokens */
|
|
15
|
+
* { box-sizing: border-box; }
|
|
16
|
+
body { margin: 0; background: var(--m-bg); color: var(--m-ink);
|
|
17
|
+
font: 15px/1.6 var(--m-sans); -webkit-font-smoothing: antialiased; }
|
|
18
|
+
body::before { /* faint warm wash, matches the app's atmosphere */
|
|
19
|
+
content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
|
|
20
|
+
background: radial-gradient(70% 50% at 82% 0%, var(--m-gold-glow), transparent 60%);
|
|
21
|
+
opacity: 0.5; }
|
|
22
|
+
.wrap { max-width: 680px; margin: 0 auto; padding: 32px 22px 116px; }
|
|
23
|
+
header { display: flex; align-items: center; justify-content: space-between;
|
|
24
|
+
padding: 4px 0 22px; border-bottom: 1px solid var(--m-line); margin-bottom: 26px; }
|
|
25
|
+
.brand { font-family: var(--m-serif); font-size: 23px; color: var(--m-ink); }
|
|
26
|
+
.brand small { font-family: var(--m-sans); color: var(--m-ink-3); font-size: 12px;
|
|
27
|
+
margin-left: 11px; letter-spacing: .04em; }
|
|
28
|
+
.status { display: flex; align-items: center; gap: 8px; color: var(--m-ink-2);
|
|
29
|
+
font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
30
|
+
.dot { width: 8px; height: 8px; border-radius: 50%; background: #4a4742; }
|
|
31
|
+
.dot:not(.off) { background: var(--m-good); box-shadow: 0 0 8px rgba(169,191,130,0.55); }
|
|
32
|
+
.dot.off { background: #4a4742; box-shadow: none; }
|
|
33
|
+
.card { background: var(--m-card); border: 1px solid var(--m-line); border-radius: 16px;
|
|
34
|
+
padding: 18px 20px; margin: 14px 0;
|
|
35
|
+
box-shadow: 0 1px 0 rgba(255,240,214,0.05) inset, 0 30px 60px -34px rgba(0,0,0,0.9);
|
|
36
|
+
animation: rise .24s var(--m-ease); }
|
|
37
|
+
.q { font-family: var(--m-serif); font-size: 21px; line-height: 1.4; color: var(--m-ink); }
|
|
38
|
+
.a { color: var(--m-ink-2); font-size: 14px; margin-top: 6px; }
|
|
39
|
+
.perm { border-color: var(--m-gold-line); }
|
|
40
|
+
.perm .lbl { color: var(--m-gold-2); font-size: 10px; text-transform: uppercase;
|
|
41
|
+
letter-spacing: .14em; margin-bottom: 8px; }
|
|
42
|
+
.row { display: flex; gap: 10px; margin-top: 16px; }
|
|
43
|
+
button { font: inherit; border: 1px solid var(--m-line-2); background: rgba(255,240,214,0.03);
|
|
44
|
+
color: var(--m-ink); padding: 10px 17px; border-radius: 11px; cursor: pointer;
|
|
45
|
+
transition: border-color .15s, background .15s; }
|
|
46
|
+
button:hover:not(:disabled) { border-color: var(--m-gold-line); }
|
|
47
|
+
button.primary { background: linear-gradient(180deg, var(--m-gold-2), var(--m-gold));
|
|
48
|
+
color: var(--m-on-gold); border-color: transparent; font-weight: 600;
|
|
49
|
+
box-shadow: 0 0 24px -8px var(--m-gold-glow); }
|
|
50
|
+
button:disabled { opacity: .4; cursor: default; box-shadow: none; }
|
|
51
|
+
.composer { position: fixed; left: 0; right: 0; bottom: 0;
|
|
52
|
+
background: linear-gradient(transparent, var(--m-bg) 34%); padding: 18px 22px; }
|
|
53
|
+
.composer .inner { max-width: 680px; margin: 0 auto; display: flex; gap: 10px; }
|
|
54
|
+
textarea { flex: 1; background: var(--m-bg-2); border: 1px solid var(--m-line-2);
|
|
55
|
+
border-radius: 12px; color: var(--m-ink); padding: 13px; font: inherit; resize: none; height: 50px; }
|
|
56
|
+
textarea:focus { outline: none; border-color: var(--m-gold-line); }
|
|
57
|
+
@keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
|
58
|
+
/* Thinking indicator — the wait sells the product: it's not loading, it's
|
|
59
|
+
reading you. A live gold orb + rotating perceptive verbs. */
|
|
60
|
+
.thinking { display: flex; align-items: center; gap: 14px; }
|
|
61
|
+
.thinking .orb { position: relative; width: 22px; height: 22px; flex: none; }
|
|
62
|
+
.thinking .orb span { position: absolute; inset: 0; border-radius: 50%;
|
|
63
|
+
border: 1.5px solid var(--m-gold-line); animation: ripple 1.9s var(--m-ease) infinite; }
|
|
64
|
+
.thinking .orb span:nth-child(2) { animation-delay: .63s; }
|
|
65
|
+
.thinking .orb span:nth-child(3) { animation-delay: 1.26s; }
|
|
66
|
+
.thinking .orb::after { content: ""; position: absolute; inset: 7px; border-radius: 50%;
|
|
67
|
+
background: var(--m-gold); box-shadow: 0 0 12px -1px var(--m-gold-glow);
|
|
68
|
+
animation: breathe 1.9s var(--m-ease) infinite; }
|
|
69
|
+
.thinking .verb { font-family: var(--m-serif); font-size: 18px; color: var(--m-gold-2); }
|
|
70
|
+
.thinking .verb em { font-style: normal; opacity: .55; animation: dots 1.4s ease-in-out infinite; }
|
|
71
|
+
.thinking .verb.swap { animation: fade .7s var(--m-ease); }
|
|
72
|
+
@keyframes ripple { from { transform: scale(.5); opacity: .85; } to { transform: scale(1.4); opacity: 0; } }
|
|
73
|
+
@keyframes breathe { 0%,100% { opacity: .5; transform: scale(.82); } 50% { opacity: 1; transform: scale(1.1); } }
|
|
74
|
+
@keyframes dots { 0%,100% { opacity: .25; } 50% { opacity: .7; } }
|
|
75
|
+
@keyframes fade { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
|
|
76
|
+
@media (prefers-reduced-motion: reduce) {
|
|
77
|
+
.thinking .orb span, .thinking .orb::after, .thinking .verb em, .thinking .verb.swap { animation: none; }
|
|
78
|
+
.thinking .orb::after { opacity: .85; }
|
|
79
|
+
}
|
|
80
|
+
/* Synthesis loader + value prophecy (D5) — the payoff moment: the system builds
|
|
81
|
+
the Engine from THEIR answers (live checklist), then names what it can now drive. */
|
|
82
|
+
.synth-h { font-family: var(--m-serif); font-size: 18px; color: var(--m-gold-2); margin-bottom: 14px; }
|
|
83
|
+
.synth-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
|
|
84
|
+
.synth-list li { display: flex; align-items: center; gap: 12px; color: var(--m-ink-3); font-size: 15px;
|
|
85
|
+
transition: color .35s var(--m-ease); }
|
|
86
|
+
.synth-list li.on { color: var(--m-ink); }
|
|
87
|
+
.synth-list li.done { color: var(--m-ink-2); }
|
|
88
|
+
.synth-list .tick { width: 17px; height: 17px; border-radius: 50%; border: 1.5px solid var(--m-line-2);
|
|
89
|
+
flex: none; position: relative; transition: border-color .3s, background .3s; }
|
|
90
|
+
.synth-list li.on .tick { border-color: var(--m-gold); border-top-color: transparent;
|
|
91
|
+
box-shadow: 0 0 10px -2px var(--m-gold-glow); animation: spin .8s linear infinite; }
|
|
92
|
+
.synth-list li.done .tick { border-color: var(--m-gold); background: var(--m-gold); animation: none; }
|
|
93
|
+
.synth-list li.done .tick::after { content: ""; position: absolute; left: 5.5px; top: 2px; width: 4px; height: 8px;
|
|
94
|
+
border: solid var(--m-on-gold); border-width: 0 1.6px 1.6px 0; transform: rotate(45deg); }
|
|
95
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
96
|
+
.prophecy { border-color: var(--m-gold-line); }
|
|
97
|
+
.proph-eyebrow { font-family: var(--m-sans); font-size: 10px; text-transform: uppercase; letter-spacing: .15em;
|
|
98
|
+
color: var(--m-gold-2); margin-bottom: 10px; }
|
|
99
|
+
.proph-body { font-family: var(--m-serif); font-size: 20px; line-height: 1.45; color: var(--m-ink); }
|
|
100
|
+
@media (prefers-reduced-motion: reduce) { .synth-list li.on .tick { animation: none; } }
|
|
101
|
+
/* Boot flow — D7 connect · D3 import · D4 recommend */
|
|
102
|
+
.drop-h, .rec-h { font-family: var(--m-serif); font-size: 18px; color: var(--m-gold-2); }
|
|
103
|
+
.drop-sub { color: var(--m-ink-2); font-size: 13px; margin: 6px 0 14px; }
|
|
104
|
+
.dropzone { border: 1.5px dashed var(--m-line-2); border-radius: 12px; padding: 26px; text-align: center;
|
|
105
|
+
color: var(--m-ink-3); font-size: 14px; cursor: pointer; transition: border-color .15s, color .15s, background .15s; }
|
|
106
|
+
.dropzone:hover, .dropzone.over { border-color: var(--m-gold-line); color: var(--m-ink-2); background: rgba(227,196,137,0.04); }
|
|
107
|
+
.droplist { color: var(--m-ink-2); font-size: 12px; margin-top: 10px; min-height: 1px; }
|
|
108
|
+
.rec-list { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 11px; }
|
|
109
|
+
.rec-list li { display: flex; flex-direction: column; gap: 2px; }
|
|
110
|
+
.rec-name { font-family: var(--m-serif); font-size: 15px; color: var(--m-ink); }
|
|
111
|
+
.rec-why { color: var(--m-ink-3); font-size: 12.5px; }
|
|
112
|
+
.conn-row { display: flex; align-items: center; gap: 9px; margin-top: 8px; color: var(--m-ink-2); font-size: 14px; }
|
|
113
|
+
.conn-row .ok { color: var(--m-good); } .conn-row .no { color: var(--m-ink-3); }
|
|
114
|
+
</style>
|
|
115
|
+
</head>
|
|
116
|
+
<body>
|
|
117
|
+
<div class="wrap">
|
|
118
|
+
<header>
|
|
119
|
+
<div class="brand">Mover OS<small>Setup · local</small></div>
|
|
120
|
+
<div class="status"><span class="dot off" id="dot"></span><span id="st">connecting…</span></div>
|
|
121
|
+
</header>
|
|
122
|
+
<div id="feed"></div>
|
|
123
|
+
</div>
|
|
124
|
+
<div class="composer"><div class="inner">
|
|
125
|
+
<textarea id="inp" placeholder="Your answer…" disabled></textarea>
|
|
126
|
+
<button class="primary" id="send" disabled>Send</button>
|
|
127
|
+
<button id="end" disabled>End</button>
|
|
128
|
+
</div></div>
|
|
129
|
+
<script>
|
|
130
|
+
const feed=document.getElementById('feed'), inp=document.getElementById('inp'),
|
|
131
|
+
sendBtn=document.getElementById('send'), endBtn=document.getElementById('end'),
|
|
132
|
+
dot=document.getElementById('dot'), st=document.getElementById('st');
|
|
133
|
+
let token=null, sessionId=null, es=null, busy=false, completed=false;
|
|
134
|
+
|
|
135
|
+
function card(cls){ const d=document.createElement('div'); d.className='card '+(cls||''); feed.appendChild(d); window.scrollTo(0,document.body.scrollHeight); return d; }
|
|
136
|
+
function setBusy(b){ busy=b; sendBtn.disabled=b||!sessionId; inp.disabled=b||!sessionId; }
|
|
137
|
+
function status(t,on){ st.textContent=t; dot.className='dot'+(on?'':' off'); }
|
|
138
|
+
async function post(p,body){ const r=await fetch(p,{method:'POST',headers:{'Content-Type':'application/json','X-Mover-Token':token},body:JSON.stringify(body)}); return r.json(); }
|
|
139
|
+
|
|
140
|
+
// Thinking indicator — perceptive, "getting to know you" verbs. The cold-start
|
|
141
|
+
// wait (~30-60s on the first question) is where the promise lands: the screen
|
|
142
|
+
// shouldn't look dead, it should look like the system is reading you.
|
|
143
|
+
const THINK_VERBS=["Listening","Reading you","Connecting threads","Forming a picture","Taking it in","Joining the dots","Turning it over","Sizing it up","Piecing it together","Sketching you in","Reading between the lines","Weighing it","Filing that away","Getting to know you","Mulling it over","Squaring it up"];
|
|
144
|
+
let thinkingEl=null, thinkTimer=null, lastVerb=-1;
|
|
145
|
+
function nextVerb(){ let i; do{ i=Math.floor(Math.random()*THINK_VERBS.length); }while(i===lastVerb&&THINK_VERBS.length>1); lastVerb=i; return THINK_VERBS[i]; }
|
|
146
|
+
function paintVerb(){ if(!thinkingEl)return; const v=thinkingEl.querySelector('.verb'); v.innerHTML=nextVerb()+'<em>…</em>'; v.classList.remove('swap'); void v.offsetWidth; v.classList.add('swap'); }
|
|
147
|
+
function showThinking(){ if(thinkingEl)return; thinkingEl=card('thinking'); thinkingEl.innerHTML='<div class="orb"><span></span><span></span><span></span></div><div class="verb"></div>'; paintVerb(); thinkTimer=setInterval(paintVerb,2300); window.scrollTo(0,document.body.scrollHeight); }
|
|
148
|
+
function hideThinking(){ if(thinkTimer){clearInterval(thinkTimer);thinkTimer=null;} if(thinkingEl){thinkingEl.remove();thinkingEl=null;} }
|
|
149
|
+
|
|
150
|
+
// D5 — synthesis loader + value prophecy (Mobbin BitePal/Brilliant pattern). When the
|
|
151
|
+
// interview agent emits [[SETUP_COMPLETE]], stop asking and show the system building the
|
|
152
|
+
// Engine from THEIR answers (live checklist), then name the outcome it can now drive.
|
|
153
|
+
const SYNTH_STEPS=["Reading your answers","Writing your Identity","Setting your Strategy","Mapping your leverage","Drafting tomorrow's plan"];
|
|
154
|
+
function runSynthesis(prophecy){
|
|
155
|
+
completed=true; hideThinking(); setBusy(true); endBtn.disabled=true; status('building your Engine',true);
|
|
156
|
+
const c=card('synth'); c.innerHTML='<div class="synth-h">Building your Engine</div><ul class="synth-list"></ul>';
|
|
157
|
+
const ul=c.querySelector('.synth-list');
|
|
158
|
+
SYNTH_STEPS.forEach(s=>{ const li=document.createElement('li'); const t=document.createElement('span'); t.className='tick'; const lbl=document.createElement('span'); lbl.textContent=s; li.appendChild(t); li.appendChild(lbl); ul.appendChild(li); });
|
|
159
|
+
const items=[...ul.children]; let i=0;
|
|
160
|
+
const step=()=>{ if(i>0){ items[i-1].classList.remove('on'); items[i-1].classList.add('done'); }
|
|
161
|
+
if(i<items.length){ items[i].classList.add('on'); i++; window.scrollTo(0,document.body.scrollHeight); setTimeout(step,780); }
|
|
162
|
+
else setTimeout(()=>revealProphecy(prophecy),460); };
|
|
163
|
+
step();
|
|
164
|
+
}
|
|
165
|
+
function revealProphecy(text){
|
|
166
|
+
const c=card('prophecy');
|
|
167
|
+
const eb=document.createElement('div'); eb.className='proph-eyebrow'; eb.textContent='Your Engine is ready';
|
|
168
|
+
const body=document.createElement('div'); body.className='proph-body'; body.textContent=(text||'Everything you told me is captured. Your Engine is ready.').slice(0,600);
|
|
169
|
+
c.appendChild(eb); c.appendChild(body);
|
|
170
|
+
status('setup complete',true); window.scrollTo(0,document.body.scrollHeight);
|
|
171
|
+
finalizeEngine(text); // A engine preview, then D4 recs, then the CTA
|
|
172
|
+
}
|
|
173
|
+
// A — preview the Engine the system seeded from the interview (dry-run, sandboxed on
|
|
174
|
+
// this machine; the real write happens only on a fresh install). Honest receipt for the
|
|
175
|
+
// synthesis loader: shows the actual files prepared, never claims a write that didn't run.
|
|
176
|
+
function finalizeEngine(workText){
|
|
177
|
+
post('/api/session/finalize',{sessionId,dryRun:true}).then(r=>{
|
|
178
|
+
if(r&&r.ok&&r.fileCount){
|
|
179
|
+
const c=card('rec');
|
|
180
|
+
const h=document.createElement('div'); h.className='rec-h'; h.textContent='Seeded from your answers'; c.appendChild(h);
|
|
181
|
+
const sub=document.createElement('div'); sub.className='drop-sub'; sub.style.margin='6px 0 0';
|
|
182
|
+
sub.textContent=r.fileCount+' Engine files prepared: '+(r.files||[]).map(f=>(f.path||'').split('/').pop().replace('.md','')).filter(Boolean).join(', ')+'.';
|
|
183
|
+
c.appendChild(sub);
|
|
184
|
+
}
|
|
185
|
+
}).catch(()=>{}).finally(()=>fetchRec(workText));
|
|
186
|
+
}
|
|
187
|
+
// D4 — recommendation card off the interview, keyed on the prophecy text.
|
|
188
|
+
function fetchRec(workText){
|
|
189
|
+
post('/api/setup/recommend',{profile:{work:workText||''}}).then(r=>{
|
|
190
|
+
if(r&&r.ok){
|
|
191
|
+
const recs=(r.recommended||[]).slice(0,5);
|
|
192
|
+
if(r.installAll||recs.length){
|
|
193
|
+
const c=card('rec');
|
|
194
|
+
const h=document.createElement('div'); h.className='rec-h'; h.textContent=r.installAll?'Recommended: the full core toolkit':'Skills matched to your work'; c.appendChild(h);
|
|
195
|
+
if(!r.installAll){ const ul=document.createElement('ul'); ul.className='rec-list';
|
|
196
|
+
recs.forEach(x=>{ const li=document.createElement('li'); const n=document.createElement('span'); n.className='rec-name'; n.textContent=x.label||x.skill;
|
|
197
|
+
const w=document.createElement('span'); w.className='rec-why'; w.textContent=x.reason||''; li.appendChild(n); li.appendChild(w); ul.appendChild(li); });
|
|
198
|
+
c.appendChild(ul); }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
finalCta();
|
|
202
|
+
}).catch(()=>finalCta());
|
|
203
|
+
}
|
|
204
|
+
function finalCta(){
|
|
205
|
+
const c=card();
|
|
206
|
+
const row=document.createElement('div'); row.className='row';
|
|
207
|
+
const cta=document.createElement('button'); cta.className='primary'; cta.textContent='Open your dashboard';
|
|
208
|
+
cta.onclick=async()=>{ try{ if(es)es.close(); await post('/api/session/end',{sessionId}); }catch(_){} location.href='/'; };
|
|
209
|
+
row.appendChild(cta); c.appendChild(row); window.scrollTo(0,document.body.scrollHeight);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function boot(){
|
|
213
|
+
const h=await (await fetch('/api/health')).json(); token=h.token;
|
|
214
|
+
// D7 — confirm a runnable agent before starting; else show the connect step.
|
|
215
|
+
let det=null; try{ det=await (await fetch('/api/agents/detect')).json(); }catch(_){}
|
|
216
|
+
const claude=det&&det.agents&&det.agents.find(a=>a.id==='claude-code');
|
|
217
|
+
if(!claude||!claude.runnable){ showConnect(det); return; }
|
|
218
|
+
startSession();
|
|
219
|
+
}
|
|
220
|
+
function showConnect(det){
|
|
221
|
+
const c=card('perm');
|
|
222
|
+
c.innerHTML='<div class="lbl">Connect your agent</div><div class="q">Mover OS runs setup through your own AI agent.</div>';
|
|
223
|
+
((det&&det.agents)||[]).forEach(a=>{ const r=document.createElement('div'); r.className='conn-row';
|
|
224
|
+
r.innerHTML='<span class="'+(a.runnable?'ok':'no')+'">'+(a.runnable?'●':'○')+'</span> '+a.name+' — '+(a.runnable?'ready':(a.present?'installed, not connected':'not installed')); c.appendChild(r); });
|
|
225
|
+
const row=document.createElement('div'); row.className='row';
|
|
226
|
+
const retry=document.createElement('button'); retry.className='primary'; retry.textContent='Retry';
|
|
227
|
+
retry.onclick=()=>{ c.remove(); boot(); };
|
|
228
|
+
row.appendChild(retry); c.appendChild(row);
|
|
229
|
+
}
|
|
230
|
+
async function startSession(){
|
|
231
|
+
const r=await post('/api/session/start',{agent:'claude-code'});
|
|
232
|
+
if(!r.ok){ status('start failed: '+r.error,false); return; }
|
|
233
|
+
sessionId=r.sessionId; status('session live',true); endBtn.disabled=false;
|
|
234
|
+
es=new EventSource('/api/session/events?id='+encodeURIComponent(sessionId));
|
|
235
|
+
es.onmessage=(m)=>{ let ev; try{ev=JSON.parse(m.data)}catch(_){return} handle(ev); };
|
|
236
|
+
es.onerror=()=>status('stream dropped',false);
|
|
237
|
+
offerImport();
|
|
238
|
+
}
|
|
239
|
+
function offerImport(){
|
|
240
|
+
setBusy(true); // lock the composer until the interview actually starts
|
|
241
|
+
const c=card('drop');
|
|
242
|
+
c.innerHTML='<div class="drop-h">Bring your existing notes?</div>'
|
|
243
|
+
+'<div class="drop-sub">Drop exports from Apple Notes, Google Keep, Obsidian, or plain text. They stay on your machine, used only to skip questions the system can already answer.</div>'
|
|
244
|
+
+'<div class="dropzone" id="dz">Drop files here, or click to choose</div>'
|
|
245
|
+
+'<input type="file" id="dzin" multiple hidden>'
|
|
246
|
+
+'<div class="droplist" id="dl"></div>'
|
|
247
|
+
+'<div class="row"><button class="primary" id="imp" disabled>Import</button><button id="skip">Skip</button></div>';
|
|
248
|
+
const dz=c.querySelector('#dz'), inp2=c.querySelector('#dzin'), dl=c.querySelector('#dl'), imp=c.querySelector('#imp'), skip=c.querySelector('#skip');
|
|
249
|
+
let picked=[];
|
|
250
|
+
const refresh=()=>{ dl.textContent=picked.length?('Ready: '+picked.map(f=>f.name).join(', ')):''; imp.disabled=!picked.length; };
|
|
251
|
+
dz.onclick=()=>inp2.click();
|
|
252
|
+
inp2.onchange=()=>{ picked=[...inp2.files]; refresh(); };
|
|
253
|
+
dz.ondragover=(e)=>{ e.preventDefault(); dz.classList.add('over'); };
|
|
254
|
+
dz.ondragleave=()=>dz.classList.remove('over');
|
|
255
|
+
dz.ondrop=(e)=>{ e.preventDefault(); dz.classList.remove('over'); picked=[...e.dataTransfer.files]; refresh(); };
|
|
256
|
+
skip.onclick=()=>{ c.remove(); beginInterview(); };
|
|
257
|
+
imp.onclick=async()=>{
|
|
258
|
+
imp.disabled=true; skip.disabled=true; imp.textContent='Reading…';
|
|
259
|
+
const files=[]; for(const f of picked){ try{ files.push({name:f.name, content:await f.text()}); }catch(_){} }
|
|
260
|
+
try{ const r=await post('/api/session/ingest',{sessionId,files}); if(r&&r.injected) status('imported '+(r.chars||0)+' chars',true); }catch(_){}
|
|
261
|
+
c.remove(); beginInterview();
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function beginInterview(){
|
|
265
|
+
setBusy(true); showThinking();
|
|
266
|
+
post('/api/session/send',{sessionId,text:'Begin the setup interview. Ask your first question.'});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function handle(ev){
|
|
270
|
+
if(ev.kind==='assistant'){
|
|
271
|
+
hideThinking();
|
|
272
|
+
const SENT='[[SETUP_COMPLETE]]';
|
|
273
|
+
if(ev.text.indexOf(SENT)>=0){
|
|
274
|
+
const parts=ev.text.split(SENT); const pre=(parts[0]||'').trim(); const prophecy=parts.slice(1).join(' ').trim();
|
|
275
|
+
if(pre){ const c=card(); const q=document.createElement('div'); q.className='q'; q.textContent=pre; c.appendChild(q); }
|
|
276
|
+
runSynthesis(prophecy); return;
|
|
277
|
+
}
|
|
278
|
+
const c=card(); const q=document.createElement('div'); q.className='q'; q.textContent=ev.text; c.appendChild(q);
|
|
279
|
+
}
|
|
280
|
+
else if(ev.kind==='result'){ hideThinking(); if(!completed){ setBusy(false); inp.focus(); } }
|
|
281
|
+
else if(ev.kind==='permission'){
|
|
282
|
+
hideThinking();
|
|
283
|
+
const id=ev.requestId; const c=card('perm');
|
|
284
|
+
c.innerHTML='<div class="lbl">Permission requested</div>';
|
|
285
|
+
const q=document.createElement('div'); q.className='q'; q.textContent=(JSON.stringify(ev.request)||'').slice(0,240); c.appendChild(q);
|
|
286
|
+
const row=document.createElement('div'); row.className='row';
|
|
287
|
+
const allow=document.createElement('button'); allow.className='primary'; allow.textContent='Allow';
|
|
288
|
+
const deny=document.createElement('button'); deny.textContent='Deny';
|
|
289
|
+
// Don't optimistically resolve: disable + show a pending note so a wrong
|
|
290
|
+
// control_response shape can't look "answered" (Codex MF3). The session
|
|
291
|
+
// proceeding (next event) is the real confirmation.
|
|
292
|
+
const decide=(d)=>{ allow.disabled=true; deny.disabled=true; const note=document.createElement('span'); note.className='a'; note.style.marginLeft='8px'; note.textContent=d+' sent…'; row.appendChild(note); post('/api/session/approve',{sessionId,requestId:id,decision:d}); };
|
|
293
|
+
allow.onclick=()=>decide('allow');
|
|
294
|
+
deny.onclick=()=>decide('deny');
|
|
295
|
+
row.appendChild(allow); row.appendChild(deny); c.appendChild(row);
|
|
296
|
+
}
|
|
297
|
+
else if(ev.kind==='closed'||ev.kind==='error'){ hideThinking(); status('session ended',false); setBusy(true); endBtn.disabled=true; if(es)es.close(); }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
sendBtn.onclick=async()=>{ const t=inp.value.trim(); if(!t||busy)return; const c=card(); const a=document.createElement('div'); a.className='a'; a.textContent='You: '+t; c.appendChild(a); inp.value=''; setBusy(true); showThinking(); await post('/api/session/send',{sessionId,text:t}); };
|
|
301
|
+
inp.addEventListener('keydown',e=>{ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendBtn.click();} });
|
|
302
|
+
endBtn.onclick=async()=>{ if(es)es.close(); await post('/api/session/end',{sessionId}); status('ended',false); setBusy(true); endBtn.disabled=true; };
|
|
303
|
+
boot();
|
|
304
|
+
</script>
|
|
305
|
+
</body>
|
|
306
|
+
</html>
|