quoxcli 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/lib/api/endpoints/admin.js +21 -0
  3. package/lib/api/endpoints/calendarq.js +8 -0
  4. package/lib/api/endpoints/governance.js +7 -0
  5. package/lib/api/endpoints/inboxq.js +39 -0
  6. package/lib/api/endpoints/index.js +4 -0
  7. package/lib/api/endpoints/lens.js +34 -0
  8. package/lib/api/endpoints/lucid.js +18 -0
  9. package/lib/api/endpoints/mcp.js +35 -0
  10. package/lib/api/endpoints/mon.js +6 -7
  11. package/lib/api/endpoints/notesq.js +2 -0
  12. package/lib/api/endpoints/quoxplan.js +51 -0
  13. package/lib/api/endpoints/v1.js +57 -0
  14. package/lib/commands/actions.js +51 -13
  15. package/lib/commands/admin.js +86 -0
  16. package/lib/commands/approvals.js +5 -1
  17. package/lib/commands/calendar.js +27 -0
  18. package/lib/commands/chat.js +1 -1
  19. package/lib/commands/contacts.js +2 -0
  20. package/lib/commands/fleet.js +164 -0
  21. package/lib/commands/fleetRecipe.js +174 -0
  22. package/lib/commands/lens.js +268 -0
  23. package/lib/commands/lucid.js +6 -0
  24. package/lib/commands/lucidGoals.js +423 -0
  25. package/lib/commands/mail.js +287 -7
  26. package/lib/commands/mcp.js +190 -0
  27. package/lib/commands/mon.js +14 -0
  28. package/lib/commands/monitoring.js +73 -1
  29. package/lib/commands/notesSeal.js +380 -22
  30. package/lib/commands/offload.js +51 -10
  31. package/lib/commands/plan.js +507 -54
  32. package/lib/commands/research.js +78 -0
  33. package/lib/commands/settings.js +7 -1
  34. package/lib/commands/volt.js +6 -1
  35. package/lib/commands/wazuh.js +74 -9
  36. package/lib/commands/wazuhIntake.js +55 -6
  37. package/lib/core/commandRegistrars.js +2 -0
  38. package/lib/core/config.js +14 -1
  39. package/lib/core/offload.js +83 -0
  40. package/lib/core/output.js +29 -4
  41. package/lib/core/selfUpdate.js +2 -0
  42. package/lib/core/version.js +15 -2
  43. package/lib/dragon/select.js +2 -2
  44. package/lib/quoxon/api.js +4 -0
  45. package/lib/quoxon/app.js +258 -49
  46. package/lib/quoxon/approvalsView.js +3 -1
  47. package/lib/quoxon/ask.js +4 -2
  48. package/lib/quoxon/bang.js +7 -1
  49. package/lib/quoxon/commandShort.js +1 -1
  50. package/lib/quoxon/commands.js +5 -5
  51. package/lib/quoxon/delegationDragon.js +49 -0
  52. package/lib/quoxon/dnd.js +47 -0
  53. package/lib/quoxon/dragonCompanion.js +24 -12
  54. package/lib/quoxon/events.js +12 -1
  55. package/lib/quoxon/extensions.js +2 -0
  56. package/lib/quoxon/foreignText.js +49 -0
  57. package/lib/quoxon/inboxView.js +5 -5
  58. package/lib/quoxon/launcher.js +2 -1
  59. package/lib/quoxon/loopCard.js +2 -1
  60. package/lib/quoxon/main.js +8 -2
  61. package/lib/quoxon/onboarding/dockerOffer.js +3 -1
  62. package/lib/quoxon/onboarding/view.js +1 -1
  63. package/lib/quoxon/render.js +5 -2
  64. package/lib/quoxon/resend.js +14 -0
  65. package/lib/quoxon/rows.js +3 -1
  66. package/lib/quoxon/secretShape.js +2 -0
  67. package/lib/quoxon/slash/broadcast.js +30 -6
  68. package/lib/quoxon/slash/broadcastRun.js +12 -6
  69. package/lib/quoxon/slash/cliLeaves.json +54 -0
  70. package/lib/quoxon/slash/dashboard.js +2 -0
  71. package/lib/quoxon/slash/listView.js +2 -1
  72. package/lib/quoxon/slash/palette.js +41 -0
  73. package/lib/quoxon/slash/paletteController.js +6 -2
  74. package/lib/quoxon/slash/restart.js +2 -0
  75. package/lib/quoxon/slotChat.js +7 -3
  76. package/lib/quoxon/slotRun.js +2 -0
  77. package/lib/quoxon/webPty.js +15 -0
  78. package/lib/quoxon/whatsnew.jsonl +7 -0
  79. package/lib/render/planBrief.js +614 -0
  80. package/lib/toolOutputShape.js +6 -3
  81. package/package.json +1 -1
  82. package/vendor/dragonlab/SOURCE +3 -3
  83. package/vendor/dragonlab/design/architect/v2/architect-master.svg +67 -0
  84. package/vendor/dragonlab/design/architect/v2/character.mjs +83 -0
  85. package/vendor/dragonlab/design/architect/v2/review.mjs +35 -0
  86. package/vendor/dragonlab/design/architect/v2/watch.mjs +56 -0
  87. package/vendor/dragonlab/design/archivist/v2/archivist-master.svg +77 -0
  88. package/vendor/dragonlab/design/archivist/v3/character.mjs +56 -0
  89. package/vendor/dragonlab/design/archivist/v4/character.mjs +46 -0
  90. package/vendor/dragonlab/design/archivist/v4/review.mjs +35 -0
  91. package/vendor/dragonlab/design/archivist/v4/watch.mjs +56 -0
  92. package/vendor/dragonlab/design/argus/v2/character.mjs +69 -0
  93. package/vendor/dragonlab/design/argus/v3/argus-master.svg +68 -0
  94. package/vendor/dragonlab/design/argus/v3/character.mjs +63 -0
  95. package/vendor/dragonlab/design/argus/v3/review.mjs +35 -0
  96. package/vendor/dragonlab/design/argus/v3/watch.mjs +56 -0
  97. package/vendor/dragonlab/design/daedalus/v2/character.mjs +49 -0
  98. package/vendor/dragonlab/design/daedalus/v3/daedalus-master.svg +65 -0
  99. package/vendor/dragonlab/design/daedalus/v4/character.mjs +47 -0
  100. package/vendor/dragonlab/design/daedalus/v5/character.mjs +66 -0
  101. package/vendor/dragonlab/design/daedalus/v6/character.mjs +32 -0
  102. package/vendor/dragonlab/design/daedalus/v6/review.mjs +35 -0
  103. package/vendor/dragonlab/design/daedalus/v6/watch.mjs +56 -0
  104. package/vendor/dragonlab/design/janus/v1/janus-master.svg +70 -0
  105. package/vendor/dragonlab/design/janus/v2/character.mjs +56 -0
  106. package/vendor/dragonlab/design/janus/v3/character.mjs +7 -0
  107. package/vendor/dragonlab/design/janus/v4/character.mjs +150 -0
  108. package/vendor/dragonlab/design/janus/v5/character.mjs +55 -0
  109. package/vendor/dragonlab/design/janus/v5/review.mjs +35 -0
  110. package/vendor/dragonlab/design/janus/v5/watch.mjs +56 -0
  111. package/vendor/dragonlab/design/roady/v1/roady-master.svg +67 -0
  112. package/vendor/dragonlab/design/roady/v2/character.mjs +69 -0
  113. package/vendor/dragonlab/design/roady/v3/character.mjs +91 -0
  114. package/vendor/dragonlab/design/roady/v3/review.mjs +35 -0
  115. package/vendor/dragonlab/design/roady/v3/watch.mjs +56 -0
  116. package/vendor/dragonlab/design/weaver/v3/weaver-master.svg +57 -0
  117. package/vendor/dragonlab/design/weaver/v4/character.mjs +56 -0
  118. package/vendor/dragonlab/design/weaver/v5/character.mjs +52 -0
  119. package/vendor/dragonlab/design/weaver/v6/character.mjs +88 -0
  120. package/vendor/dragonlab/design/weaver/v6/review.mjs +35 -0
  121. package/vendor/dragonlab/design/weaver/v6/watch.mjs +56 -0
  122. package/vendor/dragonlab/integration/companions-v1/architect.mjs +2 -0
  123. package/vendor/dragonlab/integration/companions-v1/archivist.mjs +2 -0
  124. package/vendor/dragonlab/integration/companions-v1/argus.mjs +2 -0
  125. package/vendor/dragonlab/integration/companions-v1/characters.mjs +86 -0
  126. package/vendor/dragonlab/integration/companions-v1/daedalus.mjs +2 -0
  127. package/vendor/dragonlab/integration/companions-v1/janus.mjs +2 -0
  128. package/vendor/dragonlab/integration/companions-v1/roady.mjs +2 -0
  129. package/vendor/dragonlab/integration/companions-v1/view.mjs +4 -1
  130. package/vendor/dragonlab/integration/companions-v1/weaver.mjs +2 -0
@@ -0,0 +1,68 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480"><title>Argus — the many-eyed watcher</title>
2
+ <g id="left-wing">
3
+ <path id="left-sail" d="M 335 288 Q 241 183 118 90 Q 141 161 87 218 Q 143 216 152 290 Q 208 264 243 327 L 340 332 Z" fill="#244d6b"/>
4
+ <path id="left-web" d="M 326 291 Q 228 192 118 90 L 200 232 L 152 290 L 251 281 L 243 327 Z" fill="#356987"/>
5
+ <path id="left-ribs" d="M 335 288 Q 241 183 118 90 M 335 288 L 87 218 M 335 288 L 152 290 M 335 288 L 243 327" fill="none" stroke="#83a5b2" stroke-width="7"/>
6
+ </g>
7
+ <g id="left-sensors">
8
+ <path id="left-sensor-rim-0" d="M 157 173 Q 179 148 205 173 Q 179 198 153 173 Z" fill="#080d19"/>
9
+ <path id="left-sensor-glow-0" d="M 160 173 C 160 166.372583 168.50658975 161 179 161 C 189.49341025 161 198 166.372583 198 173 C 198 179.627417 189.49341025 185 179 185 C 168.50658975 185 160 179.627417 160 173 Z" fill="#9ef4ef"/>
10
+ <path id="left-sensor-core-0" d="M 171 173 C 171 167.4771525 174.581722 163 179 163 C 183.418278 163 187 167.4771525 187 173 C 187 178.5228475 183.418278 183 179 183 C 174.581722 183 171 178.5228475 171 173 Z" fill="#080d19"/>
11
+ <path id="left-sensor-rim-1" d="M 135 236 Q 157 211 183 236 Q 157 261 131 236 Z" fill="#080d19"/>
12
+ <path id="left-sensor-glow-1" d="M 138 236 C 138 229.372583 146.50658975 224 157 224 C 167.49341025 224 176 229.372583 176 236 C 176 242.627417 167.49341025 248 157 248 C 146.50658975 248 138 242.627417 138 236 Z" fill="#9ef4ef"/>
13
+ <path id="left-sensor-core-1" d="M 149 236 C 149 230.4771525 152.581722 226 157 226 C 161.418278 226 165 230.4771525 165 236 C 165 241.5228475 161.418278 246 157 246 C 152.581722 246 149 241.5228475 149 236 Z" fill="#080d19"/>
14
+ <path id="left-sensor-rim-2" d="M 220 276 Q 242 251 268 276 Q 242 301 216 276 Z" fill="#080d19"/>
15
+ <path id="left-sensor-glow-2" d="M 223 276 C 223 269.372583 231.50658975 264 242 264 C 252.49341025 264 261 269.372583 261 276 C 261 282.627417 252.49341025 288 242 288 C 231.50658975 288 223 282.627417 223 276 Z" fill="#9ef4ef"/>
16
+ <path id="left-sensor-core-2" d="M 234 276 C 234 270.4771525 237.581722 266 242 266 C 246.418278 266 250 270.4771525 250 276 C 250 281.5228475 246.418278 286 242 286 C 237.581722 286 234 281.5228475 234 276 Z" fill="#080d19"/>
17
+ </g>
18
+ <g id="right-wing">
19
+ <path id="right-sail" d="M 421 288 Q 502 182 632 85 Q 615 160 672 213 Q 617 216 609 288 Q 555 262 517 327 L 420 332 Z" fill="#28546c"/>
20
+ <path id="right-web" d="M 428 288 Q 531 188 632 85 L 563 229 L 609 288 L 509 280 L 517 327 Z" fill="#3b708a"/>
21
+ <path id="right-ribs" d="M 421 288 Q 502 182 632 85 M 421 288 L 672 213 M 421 288 L 609 288 M 421 288 L 517 327" fill="none" stroke="#83a5b2" stroke-width="7"/>
22
+ </g>
23
+ <g id="right-sensors">
24
+ <path id="right-sensor-rim-0" d="M 552 170 Q 574 145 600 170 Q 574 195 548 170 Z" fill="#080d19"/>
25
+ <path id="right-sensor-glow-0" d="M 555 170 C 555 163.372583 563.50658975 158 574 158 C 584.49341025 158 593 163.372583 593 170 C 593 176.627417 584.49341025 182 574 182 C 563.50658975 182 555 176.627417 555 170 Z" fill="#9ef4ef"/>
26
+ <path id="right-sensor-core-0" d="M 566 170 C 566 164.4771525 569.581722 160 574 160 C 578.418278 160 582 164.4771525 582 170 C 582 175.5228475 578.418278 180 574 180 C 569.581722 180 566 175.5228475 566 170 Z" fill="#080d19"/>
27
+ <path id="right-sensor-rim-1" d="M 581 234 Q 603 209 629 234 Q 603 259 577 234 Z" fill="#080d19"/>
28
+ <path id="right-sensor-glow-1" d="M 584 234 C 584 227.372583 592.50658975 222 603 222 C 613.49341025 222 622 227.372583 622 234 C 622 240.627417 613.49341025 246 603 246 C 592.50658975 246 584 240.627417 584 234 Z" fill="#9ef4ef"/>
29
+ <path id="right-sensor-core-1" d="M 595 234 C 595 228.4771525 598.581722 224 603 224 C 607.418278 224 611 228.4771525 611 234 C 611 239.5228475 607.418278 244 603 244 C 598.581722 244 595 239.5228475 595 234 Z" fill="#080d19"/>
30
+ <path id="right-sensor-rim-2" d="M 492 276 Q 514 251 540 276 Q 514 301 488 276 Z" fill="#080d19"/>
31
+ <path id="right-sensor-glow-2" d="M 495 276 C 495 269.372583 503.50658975 264 514 264 C 524.49341025 264 533 269.372583 533 276 C 533 282.627417 524.49341025 288 514 288 C 503.50658975 288 495 282.627417 495 276 Z" fill="#9ef4ef"/>
32
+ <path id="right-sensor-core-2" d="M 506 276 C 506 270.4771525 509.581722 266 514 266 C 518.418278 266 522 270.4771525 522 276 C 522 281.5228475 518.418278 286 514 286 C 509.581722 286 506 281.5228475 506 276 Z" fill="#080d19"/>
33
+ </g>
34
+ <g id="tail">
35
+ <path id="tail-mass" d="M 321 345 Q 252 327 223 363 C 187 409 118 422 87 380 Q 73 361 86 338 C 49 358 54 398 91 420 C 152 456 245 407 282 393 L 340 385 Z" fill="#39617b"/>
36
+ <path id="tail-ridge" d="M 313 353 Q 253 342 225 378 Q 170 432 106 409" fill="none" stroke="#54a4b6" stroke-width="6"/>
37
+ </g>
38
+ <g id="body">
39
+ <path id="torso" d="M 332 261 Q 379 239 423 266 Q 477 292 480 346 Q 477 388 419 405 L 329 403 Q 278 386 283 341 Q 286 288 332 261 Z" fill="#31566f"/>
40
+ <path id="breast" d="M 346 267 L 404 265 Q 431 312 416 358 L 379 385 L 342 360 Q 323 313 346 267 Z" fill="#7e9da7"/>
41
+ <path id="breast-plates" d="M 340 298 Q 378 317 418 296 M 338 322 Q 378 344 420 320 M 345 348 Q 380 367 414 346" fill="none" stroke="#294756" stroke-width="5"/>
42
+ <path id="haunches" d="M 309 327 Q 252 330 270 389 L 256 411 L 329 415 L 348 393 L 326 366 Z M 437 327 Q 490 322 491 377 L 510 409 L 438 415 L 414 391 L 430 367 Z" fill="#466f89"/>
43
+ <path id="forelegs" d="M 342 329 L 356 349 L 342 407 L 301 415 L 301 401 L 320 388 Z M 405 328 L 428 329 L 433 389 L 458 403 L 454 416 L 409 408 L 397 355 Z" fill="#678c9e"/>
44
+ <path id="claws" d="M 304 403 L 294 420 L 314 414 L 321 403 L 318 421 L 335 414 M 423 402 L 419 420 L 435 414 L 442 404 L 443 421 L 460 414" fill="#ede9d9"/>
45
+ </g>
46
+ <g id="neck">
47
+ <path id="neck" d="M 341 199 L 405 195 Q 404 250 430 276 Q 381 307 331 277 Q 353 244 341 199 Z" fill="#527b92"/>
48
+ </g>
49
+ <g id="head">
50
+ <path id="far-horn" d="M 343 157 Q 296 135 297 93 Q 300 111 326 120 L 359 133 Z" fill="#dddcd1"/>
51
+ <path id="near-horn" d="M 401 151 Q 450 125 450 78 Q 465 135 422 167 Z" fill="#f1eddf"/>
52
+ <path id="cheek-fins" d="M 327 169 L 293 151 L 304 193 L 292 216 L 327 221 Z M 418 170 L 451 147 L 444 189 L 456 208 L 420 222 Z" fill="#4e8caa"/>
53
+ <path id="skull" d="M 326 155 Q 376 126 420 158 L 433 193 Q 432 227 397 243 L 351 241 Q 313 222 315 188 Z" fill="#759ba9"/>
54
+ <path id="brow" d="M 321 171 Q 338 148 364 154 L 375 170 L 386 154 Q 409 152 428 170 L 403 165 L 375 179 L 349 164 Z" fill="#244558"/>
55
+ <path id="snout" d="M 342 207 Q 376 193 412 205 L 428 225 Q 410 249 374 247 Q 338 247 327 227 Z" fill="#91adb2"/>
56
+ <path id="mouth" d="M 336 229 Q 376 245 419 225" fill="none" stroke="#080d19" stroke-width="6"/>
57
+ <path id="nostrils" d="M 345 216 L 354 214 L 352 222 Z M 398 214 L 409 216 L 401 222 Z" fill="#080d19"/>
58
+ <path id="chin" d="M 359 247 L 375 266 L 391 246 Z" fill="#d7dfd5"/>
59
+ </g>
60
+ <g id="eyes">
61
+ <path id="far-eye-socket" d="M 323 187 C 323 173.19288125 332.40202025 162 344 162 C 355.59797975 162 365 173.19288125 365 187 C 365 200.80711875 355.59797975 212 344 212 C 332.40202025 212 323 200.80711875 323 187 Z" fill="#080d19"/>
62
+ <path id="far-eye-white" d="M 330 187 C 330 177.0588745 336.2680135 169 344 169 C 351.7319865 169 358 177.0588745 358 187 C 358 196.9411255 351.7319865 205 344 205 C 336.2680135 205 330 196.9411255 330 187 Z" fill="#ffffff"/>
63
+ <path id="far-pupil" d="M 341 188 C 341 184.13400675 343.23857625 181 346 181 C 348.76142375 181 351 184.13400675 351 188 C 351 191.86599325 348.76142375 195 346 195 C 343.23857625 195 341 191.86599325 341 188 Z" fill="#080d19"/>
64
+ <path id="near-eye-socket" d="M 375 186 C 375 171.08831175 386.19288125 159 400 159 C 413.80711875 159 425 171.08831175 425 186 C 425 200.91168825 413.80711875 213 400 213 C 386.19288125 213 375 200.91168825 375 186 Z" fill="#080d19"/>
65
+ <path id="near-eye-white" d="M 382 186 C 382 174.954305 390.0588745 166 400 166 C 409.9411255 166 418 174.954305 418 186 C 418 197.045695 409.9411255 206 400 206 C 390.0588745 206 382 197.045695 382 186 Z" fill="#ffffff"/>
66
+ <path id="near-pupil" d="M 397 187 C 397 183.13400675 399.23857625 180 402 180 C 404.76142375 180 407 183.13400675 407 187 C 407 190.86599325 404.76142375 194 402 194 C 399.23857625 194 397 190.86599325 397 187 Z" fill="#080d19"/>
67
+ </g>
68
+ </svg>
@@ -0,0 +1,63 @@
1
+ import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
2
+ import {poseRig as oldPose,renderArgus as oldRender,ARGUS_CLIPS as oldClips} from '../v2/character.mjs';
3
+ export const ARGUS_CLIPS=Object.freeze([...oldClips,{id:'tongue',title:'Cheeky little blep',duration:3500},{id:'wazuh',title:'Claws up, let loose',duration:6500},{id:'fire',title:'A forward cyan puff',duration:5000}]);
4
+ export const STORY_DURATION=15000;
5
+ const framed=rig=>({...rig,bounds:{...rig.bounds,top:50}});
6
+ const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
7
+ const env=(t,d)=>ease(t/800)*(1-ease((t-d+1200)/1200));
8
+ const rotate=(x,y,cx,cy,a)=>[cx+(x-cx)*Math.cos(a)-(y-cy)*Math.sin(a),cy+(x-cx)*Math.sin(a)+(y-cy)*Math.cos(a)];
9
+ export function poseRig(rig,scene,ms,options={}){
10
+ rig=framed(rig);
11
+ if(!['tongue','wazuh','fire'].includes(scene))return oldPose(rig,scene,ms,options);
12
+ const d=ARGUS_CLIPS.find(c=>c.id===scene).duration;if(ms>=d)return oldPose(rig,'idle',ms-d,options);
13
+ const e=env(ms,d),shake=scene==='wazuh'?.13*Math.sin(ms/150)*e:scene==='tongue'?.07*e:0;
14
+ const head=(x,y)=>rotate(x,y,375,258,shake);
15
+ const arms=(x,y)=>{const left=x<375;return rotate(x,y,left?342:411,332,(left?1:-1)*2.55*e);};
16
+ const base=oldPose(rig,'idle',0,options);
17
+ const paths=base.paths.map(p=>({...p,polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
18
+ if(p.group==='head'||p.group==='eyes')return head(x,y);
19
+ if(p.group==='neck'){const w=Math.max(0,Math.min(1,(282-y)/75));return rotate(x,y,375,258,shake*w);}
20
+ if(scene==='wazuh'&&(p.id==='forelegs'||p.id==='claws'))return arms(x,y);
21
+ return [x,y];
22
+ }))}));
23
+ if(scene==='wazuh'&&e>0){
24
+ const arms=paths.filter(p=>p.id==='forelegs'||p.id==='claws');
25
+ for(const p of arms)paths.splice(paths.indexOf(p),1);
26
+ for(const p of arms){paths.push({...p,id:p.id+'-rim',rgb:[8,13,25],erase:true,stroke:7*e});paths.push({...p,rgb:p.id==='forelegs'?p.rgb.map((v,i)=>Math.round(v+([135,190,204][i]-v)*e)):p.rgb});}
27
+ }
28
+ const add=(id,rgb,poly,stroke=0,erase=false)=>paths.push({id,group:'personality',rgb,polygons:[poly],stroke,erase});
29
+ // The mouth remains the emission origin: forward-facing puff toward the viewer.
30
+ const mouth=poly=>poly.map(([x,y])=>head(x,y));
31
+ if(e>.001){
32
+ add('open-mouth',[8,13,25],mouth([[352,229],[375,225],[400,229],[394,229+19*e],[375,233+22*e],[356,229+19*e]]),0,true);
33
+ if(scene==='tongue'||scene==='wazuh'){
34
+ const poke=scene==='tongue'?e:e*.65;
35
+ add('tongue',[244,113,151],mouth([[368,237],[382,237],[384,240+28*poke],[377,248+28*poke],[368,245+28*poke]]));
36
+ add('tongue-crease',[139,46,88],mouth([[376,243],[377,249+17*poke]]),2*poke);
37
+ }
38
+ }
39
+ if(scene==='fire'){
40
+ const breath=ease((ms-1100)/400)*(1-ease((ms-3100)/900));
41
+ if(breath>.001){
42
+ const y=240,len=95*breath,w=(24+6*Math.sin(ms/85))*breath;
43
+ add('cyan-flame',[30,168,223],[[365,y],[385,y],[392+w,y+len*.7],[380+w,y+len*.55],[389+w*.6,y+len],[375,y+len*.8],[362-w*.6,y+len],[367-w,y+len*.6],[358-w,y+len*.73]]);
44
+ add('flame-heart',[179,255,244],[[370,y],[381,y],[389+w*.3,y+len*.65],[376,y+len*.48],[364-w*.2,y+len*.7]]);
45
+ }
46
+ }
47
+ const map=new Map(paths.map(p=>[p.id,p]));
48
+ return {...base,paths,eyes:rig.eyes.map(e=>({...e,socket:map.get(e.socket.id),white:map.get(e.white.id),pupil:map.get(e.pupil.id)}))};
49
+ }
50
+ export function renderArgus(rig,scene='idle',ms=0,options={}){
51
+ rig=framed(rig);
52
+ if(!['tongue','wazuh','fire'].includes(scene))return oldRender(rig,scene,ms,options);
53
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
54
+ const d=ARGUS_CLIPS.find(c=>c.id===scene).duration;
55
+ if(!options.still&&ms>=d)return oldRender(rig,'idle',ms-d,options);
56
+ const t=options.still?({tongue:1600,wazuh:2200,fire:2400}[scene]):ms;
57
+ return {...renderSvgRig(poseRig(rig,scene,t,options),options),scene,clip:scene,caption:{tongue:'A tiny breach of decorum.',wazuh:'Claws up. Absolutely no indoor voice.',fire:'One small puff. Feeling better.'}[scene]};
58
+ }
59
+ export function renderStory(rig,ms,options={}){
60
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
61
+ if(options.still)return renderArgus(rig,'wazuh',2200,options);
62
+ return ms<3500?renderArgus(rig,'tongue',ms,options):ms<10000?renderArgus(rig,'wazuh',ms-3500,options):renderArgus(rig,'fire',ms-10000,options);
63
+ }
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import {parseSvgRig} from '../../../factory/svg-rig-contour.mjs';
4
+ import {renderArgus,ARGUS_CLIPS} from './character.mjs';
5
+ import {artSlot,composeMessage} from '../../lucid/recovery/layout.mjs';
6
+ import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
7
+ const args=process.argv.slice(2),values=new Set(['--cols','--rows','--terminal-cols','--terminal-rows','--message','--scene','--at']);
8
+ for(let i=0;i<args.length;i++){
9
+ if(values.has(args[i])){if(!args[i+1]||args[i+1].startsWith('--'))throw Error('Missing option value');i++;}
10
+ else if(!['--large','--mono','--help','--watch','--attach','--intro','--still','--list'].includes(args[i]))throw Error('Unknown option: '+args[i]);
11
+ }
12
+ const value=(name,fallback)=>args.includes(name)?args[args.indexOf(name)+1]:fallback;
13
+ if(args.includes('--help')){console.log('Argus V3 forge studio. --watch --attach --intro --still --scene ID --at MS --list --large --mono --message TEXT --cols N --rows N --terminal-cols N --terminal-rows N');process.exit(0);}
14
+ if(args.includes('--list')){console.log(ARGUS_CLIPS.map(c=>c.id+' · '+c.title).join('\n'));process.exit(0);}
15
+ const w=Number(value('--terminal-cols',process.stdout.columns||80)),h=Number(value('--terminal-rows',process.stdout.rows||24));
16
+ if(!Number.isSafeInteger(w)||!Number.isSafeInteger(h)||w<1||h<1)throw Error('Invalid terminal size');
17
+ const slot=artSlot(w,h),large=args.includes('--large')||args.includes('--intro'),explicit=large||args.includes('--cols')||args.includes('--rows');
18
+ const cols=Number(value('--cols',large?Math.min(120,w-4):Math.min(39,slot.cols))),rows=Number(value('--rows',large?Math.min(36,h-4):Math.min(11,slot.rows)));
19
+ const message=value('--message','ARGUS\nWatch. Triage. Inspect.');
20
+ if(!slot.showArt&&!explicit){console.log(composeMessage(null,message,{terminalCols:w}).join('\n'));}
21
+ else{
22
+ if(cols>w-4||rows>h-2)throw Error('Requested art exceeds terminal margins');
23
+ const rig=parseSvgRig(await fs.readFile(new URL('./argus-master.svg',import.meta.url),'utf8'));
24
+ const opts={cols,rows,mono:args.includes('--mono'),still:args.includes('--still'),deck:large};
25
+ const scene=value('--scene',args.includes('--attach')?'attach':'idle');
26
+ const r=renderArgus(rig,scene,Number(value('--at',0)),opts);
27
+ if(!r.ok)throw Error(r.code);
28
+ if(args.includes('--watch')||args.includes('--attach')||args.includes('--intro')){
29
+ const {watch}=await import('./watch.mjs');
30
+ await watch(rig,opts,{terminalCols:w,terminalRows:h,large,scene:args.includes('--scene')||args.includes('--attach')?scene:null,arrival:args.includes('--attach'),message:args.includes('--message')?message:null});
31
+ }else{
32
+ const shownMessage=args.includes('--message')?message:'ARGUS\n'+r.caption;
33
+ console.log((large?[...cellsToAnsi(r.frame),...composeMessage(null,shownMessage,{terminalCols:w})]:composeMessage(r.frame,shownMessage,{terminalCols:w})).join('\n'));
34
+ }
35
+ }
@@ -0,0 +1,56 @@
1
+ import {performance} from 'node:perf_hooks';
2
+ import {ARGUS_CLIPS,renderArgus,renderStory,STORY_DURATION} from './character.mjs';
3
+ import {composeMessage} from '../../lucid/recovery/layout.mjs';
4
+ import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
5
+
6
+ /** One bounded performance. No alternate screen or full-screen clearing. */
7
+ export async function watch(paths,options,config){
8
+ if(!process.stdout.isTTY||!process.stdin.isTTY)throw new Error('--watch/--intro needs an interactive terminal. Use --scene and --at for snapshots.');
9
+ const {terminalCols,terminalRows,scene,large,message}=config;
10
+ const clip=scene?ARGUS_CLIPS.find(c=>c.id===scene):null;
11
+ const duration=(clip?.duration??STORY_DURATION)+(config.arrival?6000:0);
12
+ const realCols=process.stdout.columns,realRows=process.stdout.rows;
13
+ const start=performance.now();
14
+ const render=t=>{
15
+ const r=scene?renderArgus(paths,scene,t,options):renderStory(paths,t,options);
16
+ if(!r?.ok)throw new Error(r?.code??'Invalid story frame');
17
+ const caption=message??'ARGUS · '+r.clip+'\n'+r.caption;
18
+ return large?[...cellsToAnsi(r.frame),...caption.split('\n')]:composeMessage(r.frame,caption,{terminalCols});
19
+ };
20
+ // Reserve enough lines for captions too; keep within the real terminal.
21
+ const height=options.rows+3;
22
+ if(height>=terminalRows)throw new Error('Animation needs room for its caption; reduce --rows.');
23
+ let ended=false,drawn=false,timer,resolveDone,rejectDone;
24
+ const wasRaw=Boolean(process.stdin.isRaw);
25
+ const done=new Promise((resolve,reject)=>{resolveDone=resolve;rejectDone=reject;});
26
+ const cleanup=(error)=>{
27
+ if(ended)return;ended=true;clearInterval(timer);
28
+ process.stdin.off('data',key);process.stdout.off('resize',resized);
29
+ process.off('SIGINT',sigint);process.off('SIGTERM',sigterm);
30
+ process.stdin.setRawMode(wasRaw);process.stdin.pause();
31
+ if(drawn)process.stdout.write(`\x1b[${height}B\r`);
32
+ process.stdout.write('\x1b[0m\x1b[?25h');
33
+ error?rejectDone(error):resolveDone();
34
+ };
35
+ const key=data=>{if(data.includes(3)||data.toString().includes('q'))cleanup();};
36
+ const sigint=()=>cleanup(),sigterm=()=>cleanup();
37
+ const resized=()=>cleanup(new Error('Terminal resized; rerun for the new art slot.'));
38
+ try{
39
+ process.stdin.setRawMode(true);process.stdin.resume();process.stdin.on('data',key);
40
+ process.stdout.on('resize',resized);process.on('SIGINT',sigint);process.on('SIGTERM',sigterm);
41
+ process.stdout.write('\x1b[?25l');
42
+ // Allocate the block once; each frame replaces only these lines.
43
+ process.stdout.write('\n'.repeat(height)+`\x1b[${height}A`);drawn=true;
44
+ const tick=()=>{
45
+ try{
46
+ if(process.stdout.columns!==realCols||process.stdout.rows!==realRows)return resized();
47
+ const t=Math.min(performance.now()-start,duration-1),lines=render(t);
48
+ if(lines.length>height)throw new Error('Message exceeds the animation block; use a shorter message or a static preview.');
49
+ process.stdout.write(Array.from({length:height},(_,i)=>'\r\x1b[2K'+(lines[i]??'')+'\n').join('')+`\x1b[${height}A`);
50
+ if(performance.now()-start>=duration)cleanup();
51
+ }catch(e){cleanup(e);}
52
+ };
53
+ tick();if(!ended)timer=setInterval(tick,1000/6);
54
+ await done;
55
+ }catch(e){cleanup(e);await done;}
56
+ }
@@ -0,0 +1,49 @@
1
+ import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
2
+ export const DAEDALUS_CLIPS=Object.freeze([{id:'idle',title:'Architect at Rest',duration:12000},{id:'attach',title:'Open the Workshop',duration:3000},{id:'starforge',title:'Starforge',duration:10000}]);
3
+ export const STORY_DURATION=15000;
4
+ const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
5
+ const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
6
+ export function poseRig(rig,scene,ms){
7
+ const t=scene==='attach'?ms:ms%(scene==='starforge'?10000:12000);
8
+ const active=scene==='starforge',gather=active?ease((t-1000)/2200)*(1-ease((t-8000)/2000)):0;
9
+ const gesture=active?pulse(t,300,4300):scene==='attach'?pulse(t,300,2300):.2*pulse(t,2000,6500);
10
+ const paths=rig.paths.map(p=>({...p,polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
11
+ if(p.id==='component-left')return [x+29*gather,y+5*gather];
12
+ if(p.id==='component-right')return [x-37*gather,y-10*gather];
13
+ if(p.group==='head'||p.group==='eyes')return [x+3*gesture,y+6*gesture];
14
+ if(p.group==='neck'){const w=Math.max(0,Math.min(1,(290-y)/90));return [x+3*gesture*w,y+6*gesture*w];}
15
+ if(p.group==='arms'){const w=Math.max(0,Math.min(1,(x-350)/110));return [x+12*gesture*w,y+(y<290?8:-8)*gesture*w];}
16
+ if(p.group==='tail'){const w=Math.max(0,Math.min(1,(300-x)/210));return [x,y-5*gesture*w*w];}
17
+ return [x,y];
18
+ }))}));
19
+ const stages=['assembly-top','component-left','component-right'];
20
+ for(let i=0;i<stages.length;i++){
21
+ const p=paths.find(p=>p.id===stages[i]);
22
+ const lit=active?ease((t-3600-i*650)/450)*(1-ease((t-7300)/700)):0;
23
+ p.rgb=[Math.round(p.rgb[0]+(255-p.rgb[0])*lit),Math.round(p.rgb[1]+(239-p.rgb[1])*lit),Math.round(p.rgb[2]+(180-p.rgb[2])*lit)];
24
+ p.stroke+=2*lit;
25
+ }
26
+ if(active&&t>3200&&t<8000){
27
+ const alpha=ease((t-3200)/400)*(1-ease((t-7400)/600));
28
+ paths.push({id:'assembly-links',group:'effect',erase:false,stroke:4,rgb:[Math.round(240*alpha),Math.round(210*alpha),Math.round(140*alpha)],polygons:[[[592,351],[592,344],[607,344],[622,344],[622,351]]]});
29
+ if(t>=3600&&t<6000){const u=(t-3600)/2400,y=292+96*u;
30
+ paths.push({id:'deployment-wave',group:'effect',erase:false,stroke:4,rgb:[255,248,218],polygons:[[[577,y],[638,y]]]});
31
+ }
32
+ if(t>=6000&&t<7400){const r=4+10*pulse(t,6000,7400);paths.push({id:'completion-star',group:'effect',erase:false,stroke:0,rgb:[255,239,180],polygons:[[[607-r,402],[604,399],[607,402-r],[610,399],[607+r,402],[610,405],[607,402+r],[604,405]]]});}
33
+ }
34
+ const map=new Map(paths.map(p=>[p.id,p]));return {...rig,paths,eyes:rig.eyes.map(e=>({...e,socket:map.get(e.socket.id),white:map.get(e.white.id),pupil:map.get(e.pupil.id)}))};
35
+ }
36
+ export function renderDaedalus(rig,scene='idle',ms=0,options={}){
37
+ if(!DAEDALUS_CLIPS.some(c=>c.id===scene))return {ok:false,code:'UNKNOWN_CLIP'};
38
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
39
+ if(scene==='attach'&&ms>=3000&&!options.still)return renderDaedalus(rig,'idle',ms-3000,options);
40
+ const t=options.still?({idle:4000,attach:1400,starforge:4800}[scene]):ms;
41
+ const q=t%10000;
42
+ const caption=scene==='starforge'?(q<3200?'Draw the pieces together.':q<6000?'Lock the structure. Sweep the light.':q<8000?'Starforge complete.':'Release and reset.'):scene==='attach'?'The workshop opens.':'Imagine. Assemble. Deploy.';
43
+ return {...renderSvgRig(poseRig(rig,scene,t),options),clip:scene,scene,caption};
44
+ }
45
+ export function renderStory(rig,ms,options={}){
46
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
47
+ if(options.still)return renderDaedalus(rig,'starforge',4800,options);
48
+ return ms<3000?renderDaedalus(rig,'attach',ms,options):ms<13000?renderDaedalus(rig,'starforge',ms-3000,options):renderDaedalus(rig,'idle',ms-13000,options);
49
+ }
@@ -0,0 +1,65 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480">
2
+ <title>Daedalus — midnight architect, formerly Nova</title>
3
+ <g id="wings">
4
+ <path id="far-wing" fill="#344a76" d="M 308 279 L 226 244 L 177 75 L 161 160 L 112 124 L 143 231 L 120 223 L 174 278 L 267 308 Z"/>
5
+ <path id="far-spars" fill="none" stroke="#b5a679" stroke-width="8" d="M 300 277 L 226 244 L 177 75 L 161 160 L 143 231 M 226 244 L 112 124 M 226 244 L 174 278 L 267 308"/>
6
+ <path id="near-wing" fill="#405c91" d="M 345 278 Q 377 242 400 185 L 490 65 L 475 169 L 541 125 L 515 231 L 555 215 L 492 285 L 402 316 L 350 309 Z"/>
7
+ <path id="near-panel-shade" fill="#304571" d="M 359 289 L 475 169 L 400 185 Z M 359 289 L 515 231 L 492 285 L 402 316 Z"/>
8
+ <path id="near-spars" fill="none" stroke="#e7ce95" stroke-width="8" d="M 352 292 L 400 185 L 490 65 L 475 169 L 352 292 M 400 185 L 475 169 L 541 125 L 515 231 L 352 292 M 515 231 L 555 215 L 492 285 L 402 316 L 352 292"/>
9
+ <path id="wing-crossbraces" fill="none" stroke="#9faed4" stroke-width="5" d="M 429 148 L 480 136 M 405 238 L 467 218 M 427 285 L 492 285 M 168 177 L 208 184"/>
10
+ <path id="hinges" fill="#fff0c6" d="M 391 185 L 400 176 L 409 185 L 400 194 Z M 466 169 L 475 160 L 484 169 L 475 178 Z M 506 231 L 515 222 L 524 231 L 515 240 Z"/>
11
+ </g>
12
+ <g id="tail">
13
+ <path id="tail-coil" fill="#405b91" d="M 320 351 C 250 354 236 431 150 429 C 84 431 62 381 95 350 Q 116 334 140 349 Q 100 345 100 377 C 103 414 173 413 208 375 Q 253 321 319 324 Z"/>
14
+ <path id="tail-trace" fill="none" stroke="#dec78c" stroke-width="7" d="M 302 343 C 242 356 227 413 159 417 C 104 421 78 383 104 357"/>
15
+ <path id="tail-tip" fill="#e6d7ae" d="M 130 347 L 160 325 L 151 354 L 164 369 L 136 361 Z"/>
16
+ </g>
17
+ <g id="body">
18
+ <path id="torso" fill="#3e5687" d="M 300 235 Q 362 224 396 271 L 423 350 Q 414 399 369 413 L 288 394 L 263 353 Q 270 284 300 235 Z"/>
19
+ <path id="belly" fill="#c9d3eb" d="M 326 257 Q 358 259 374 288 L 394 347 L 372 390 L 325 377 L 304 331 Z"/>
20
+ <path id="belly-joints" fill="none" stroke="#667ba6" stroke-width="6" d="M 316 296 L 380 305 M 308 328 L 390 338 M 317 357 L 385 368"/>
21
+ <path id="haunch" fill="#4f6b9d" d="M 281 323 Q 317 311 333 342 L 321 384 L 337 419 L 266 429 L 248 410 L 262 362 Z"/>
22
+ <path id="far-foot" fill="#405783" d="M 369 364 L 404 359 L 425 410 L 460 424 L 457 439 L 379 436 L 356 410 Z"/>
23
+ <path id="feet" fill="#6881ac" d="M 266 401 L 306 402 L 339 421 L 334 438 L 252 439 L 240 424 Z"/>
24
+ <path id="talons" fill="#f5e5b6" d="M 256 422 L 247 441 L 265 435 L 279 421 L 276 440 L 296 435 L 310 422 L 309 439 L 333 433 M 392 422 L 382 439 L 402 435 L 418 422 L 416 440 L 437 435 L 449 426 L 448 440 L 465 435"/>
25
+ </g>
26
+ <g id="neck">
27
+ <path id="neck-mass" fill="#4d679a" d="M 260 168 L 317 164 Q 340 200 329 243 L 354 281 L 306 302 L 274 268 Q 291 218 260 168 Z"/>
28
+ <path id="throat" fill="#d7dff0" d="M 288 183 L 310 183 Q 325 216 315 247 L 336 278 L 311 285 L 291 262 Q 309 218 288 183 Z"/>
29
+ </g>
30
+ <g id="head">
31
+ <path id="horn-far" fill="#e6c98d" d="M 248 125 Q 210 92 230 52 Q 224 85 264 98 Z"/>
32
+ <path id="horn-near" fill="#fff0bf" d="M 295 109 Q 276 65 319 33 Q 298 70 322 100 Z"/>
33
+ <path id="head-mass" fill="#6a82af" d="M 241 121 Q 273 92 307 112 Q 335 130 328 165 L 307 193 L 266 193 L 231 166 Z"/>
34
+ <path id="ear-fin" fill="#d7dcf0" d="M 241 136 L 202 119 L 213 149 L 194 157 L 238 175 Z"/>
35
+ <path id="crest" fill="#e8d096" d="M 271 109 L 273 83 L 290 106 L 305 90 L 312 119 Z"/>
36
+ <path id="muzzle" fill="#c2cfea" d="M 299 160 Q 334 148 355 170 L 354 185 L 335 198 L 306 199 L 281 184 Z"/>
37
+ <path id="nose" fill="#080d19" d="M 334 169 L 343 176 L 331 179 Z"/>
38
+ <path id="mouth-opening" fill="#080d19" d="M 299 179 Q 324 187 354 176 L 350 191 Q 324 207 301 190 Z"/>
39
+ <path id="lower-jaw" fill="#91a8ce" d="M 301 191 Q 326 204 350 190 L 340 207 L 316 211 L 296 199 Z"/>
40
+ <path id="fang" fill="#fff0c5" d="M 337 184 L 345 181 L 339 192 Z"/>
41
+ </g>
42
+ <g id="shoulder">
43
+ <path id="wing-shoulder" fill="#647ea9" d="M 342 258 Q 365 244 380 263 L 382 284 L 364 302 L 342 288 Z"/>
44
+ <path id="shoulder-rim" fill="none" stroke="#e2c992" stroke-width="6" d="M 347 261 Q 369 252 376 273 L 364 291"/>
45
+ </g>
46
+ <g id="arms">
47
+ <path id="upper-arm" fill="#617ca8" d="M 326 241 Q 345 220 370 229 L 407 245 L 442 231 L 455 238 L 450 252 L 411 268 L 369 253 L 347 270 Z"/>
48
+ <path id="lower-arm" fill="#7993bd" d="M 343 296 Q 369 281 384 302 L 405 326 L 452 321 L 468 330 L 455 344 L 399 350 L 365 327 Z"/>
49
+ <path id="claw-tips" fill="#fff0c5" d="M 439 232 L 457 216 L 451 237 L 467 230 L 455 246 L 449 253 M 447 322 L 461 309 L 457 326 L 473 320 L 465 336 L 452 343"/>
50
+ <path id="cuffs" fill="none" stroke="#e2c992" stroke-width="7" d="M 402 244 L 411 264 M 401 326 L 405 348"/>
51
+ </g>
52
+ <g id="blueprint">
53
+ <path id="assembly-top" fill="none" stroke="#ead59d" stroke-width="6" d="M 580 306 L 607 289 L 634 306 L 607 323 Z M 580 306 L 580 335 L 607 352 L 634 335 L 634 306 M 607 323 L 607 352"/>
54
+ <path id="component-left" fill="none" stroke="#acbfe9" stroke-width="5" d="M 548 355 L 563 346 L 578 355 L 578 373 L 563 382 L 548 373 Z M 548 355 L 563 364 L 578 355 M 563 364 L 563 382"/>
55
+ <path id="component-right" fill="none" stroke="#acbfe9" stroke-width="5" d="M 644 370 L 659 361 L 674 370 L 674 388 L 659 397 L 644 388 Z M 644 370 L 659 379 L 674 370 M 659 379 L 659 397"/>
56
+ </g>
57
+ <g id="eyes">
58
+ <path id="far-eye-socket" fill="#080d19" d="M 245 147 C 245 134.8497355 253.50658975 125 264 125 C 274.49341025 125 283 134.8497355 283 147 C 283 159.1502645 274.49341025 169 264 169 C 253.50658975 169 245 159.1502645 245 147 Z"/>
59
+ <path id="far-eye-white" fill="#ffffff" d="M 250 147 C 250 137.61115925000001 256.2680135 130 264 130 C 271.7319865 130 278 137.61115925000001 278 147 C 278 156.38884074999999 271.7319865 164 264 164 C 256.2680135 164 250 156.38884074999999 250 147 Z"/>
60
+ <path id="far-pupil" fill="#080d19" d="M 263.5 147 C 263.5 143.13400675 265.514718625 140 268 140 C 270.485281375 140 272.5 143.13400675 272.5 147 C 272.5 150.86599325 270.485281375 154 268 154 C 265.514718625 154 263.5 150.86599325 263.5 147 Z"/>
61
+ <path id="near-eye-socket" fill="#080d19" d="M 279 146 C 279 132.19288125 289.29745075 121 302 121 C 314.70254925 121 325 132.19288125 325 146 C 325 159.80711875 314.70254925 171 302 171 C 289.29745075 171 279 159.80711875 279 146 Z"/>
62
+ <path id="near-eye-white" fill="#ffffff" d="M 284 146 C 284 134.954305 292.0588745 126 302 126 C 311.9411255 126 320 134.954305 320 146 C 320 157.045695 311.9411255 166 302 166 C 292.0588745 166 284 157.045695 284 146 Z"/>
63
+ <path id="near-pupil" fill="#080d19" d="M 301.5 146 C 301.5 142.13400675 303.514718625 139 306 139 C 308.485281375 139 310.5 142.13400675 310.5 146 C 310.5 149.86599325 308.485281375 153 306 153 C 303.514718625 153 301.5 149.86599325 301.5 146 Z"/>
64
+ </g>
65
+ </svg>
@@ -0,0 +1,47 @@
1
+ import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
2
+ import {poseRig as originalPose,renderDaedalus as originalRender,DAEDALUS_CLIPS as clips} from '../v2/character.mjs';
3
+ export const DAEDALUS_CLIPS=Object.freeze([...clips,{id:'repair',title:'Isolate. Replace. Resume.',duration:12000}]);
4
+ export const STORY_DURATION=15200;
5
+ const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
6
+ export function poseRig(rig,ms){
7
+ if(ms>=12000)return originalPose(rig,'idle',ms-12000);
8
+ const t=ms;
9
+ if(t>=10000)return originalPose(rig,'starforge',8000+t-10000);
10
+ const base=originalPose(rig,'starforge',3200);
11
+ const out=ease((t-2000)/1800),insert=ease((t-4400)/1800);
12
+ const paths=base.paths.filter(p=>p.group!=='effect').map(p=>({...p}));
13
+ const slot=paths.find(p=>p.id==='component-right');
14
+ const originalPolygons=slot.polygons;
15
+ if(t>=1500){
16
+ slot.id='quarantined-component';slot.rgb=[230,80,60];
17
+ slot.polygons=originalPolygons.map(poly=>poly.map(([x,y])=>[x+34*out,y-66*out]));
18
+ if(t>=4400){
19
+ paths.push({...slot,id:'replacement-component',rgb:insert<1?[150,198,255]:[255,236,176],polygons:originalPolygons.map(poly=>poly.map(([x,y])=>[x+34*(1-insert),y+27*(1-insert)]))});
20
+ }
21
+ if(t>=9000){slot.rgb=slot.rgb.map(c=>Math.round(c*(1-ease((t-9000)/1000))));}
22
+ }
23
+ const guide=t>=2000&&t<6500?Math.sin(Math.PI*(t-2000)/4500)**2:0;
24
+ for(const p of paths)if(p.group==='arms')p.polygons=p.polygons.map(poly=>poly.map(([x,y])=>{const w=Math.max(0,Math.min(1,(x-350)/110));return [x+10*guide*w,y-10*guide*w];}));
25
+ const add=(id,rgb,polygons,stroke)=>paths.push({id,group:'effect',erase:false,rgb,polygons,stroke});
26
+ // The deployment wave holds at the failed lower section until replacement seats.
27
+ const y=t<1500?292+68*ease(t/1500):t<6500?360:360+28*ease((t-6500)/1800);
28
+ if(t<8500)add('deployment-wave',t>=1500&&t<6500?[255,172,66]:[255,246,207],[[[577,y],[638,y]]],4);
29
+ add('assembly-links',[220,195,139],[[[592,351],[592,344],[607,344]],...(t>=2000&&t<6200?[]:[[[607,344],[622,344],[622,351]]])],4);
30
+ if(t>=1500&&t<9000){const x=622+34*out,cy=369-66*out;add('fault-cross',[255,100,70],[[[x-5,cy-5],[x+5,cy+5]],[[x+5,cy-5],[x-5,cy+5]]],3);}
31
+ if(t>=8500){const r=5+6*Math.sin(Math.PI*(t-8500)/1500)**2;add('recovery-star',[255,239,180],[[[607-r,409],[607,409-r],[607+r,409],[607,409+r]]],0);}
32
+ return {...base,paths};
33
+ }
34
+ export function renderDaedalus(rig,scene='idle',ms=0,options={}){
35
+ if(scene!=='repair')return originalRender(rig,scene,ms,options);
36
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
37
+ if(ms>=12000&&!options.still)return originalRender(rig,'idle',ms-12000,options);
38
+ const t=options.still?5000:ms;
39
+ const caption=t<1500?'Deployment begins.':t<2000?'Fault found. Hold the wave.':t<4400?'Isolate the failed component.':t<6500?'Seat the replacement.':t<8500?'Resume from the held step.':t<10000?'Repair complete.':'Release and reset.';
40
+ return {...renderSvgRig(poseRig(rig,t),options),scene,clip:scene,caption};
41
+ }
42
+ export function renderStory(rig,ms,options={}){
43
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
44
+ if(options.still)return renderDaedalus(rig,'repair',5000,options);
45
+ if(ms<3200)return renderDaedalus(rig,'starforge',ms,options);
46
+ return renderDaedalus(rig,'repair',ms-3200,options);
47
+ }
@@ -0,0 +1,66 @@
1
+ import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
2
+ import {poseRig as forgePose} from '../v2/character.mjs';
3
+ import {renderDaedalus as previousRender,DAEDALUS_CLIPS as clips} from '../v4/character.mjs';
4
+ export const DAEDALUS_CLIPS=Object.freeze([...clips,{id:'rollback',title:'Rewind to the checkpoint',duration:12000}]);
5
+ export const STORY_DURATION=15200;
6
+ const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
7
+ const gold=[255,236,176],blue=[139,193,255],red=[248,100,76];
8
+ const mix=(a,b,u)=>a.map((c,i)=>Math.round(c+(b[i]-c)*u));
9
+ const components=new Set(['assembly-top','component-left','component-right']);
10
+
11
+ export function poseRollback(rig,t){
12
+ if(t>=12000)return forgePose(rig,'idle',t-12000);
13
+ if(t>=10000)return forgePose(rig,'starforge',t-2000);
14
+ // Reverse authored construction, hold its checkpoint, then rebuild.
15
+ const clock=t<1800?3200:t<4800?3200-2200*ease((t-1800)/3000):t<6200?1000:t<9000?1000+2200*ease((t-6200)/2800):3200+4800*ease((t-9000)/1000);
16
+ const base=forgePose(rig,'starforge',clock);
17
+ const paths=base.paths.filter(p=>p.group!=='effect').map(p=>({...p}));
18
+ const ready=ease((t-8800)/400)*(1-ease((t-9600)/400));
19
+ for(const p of paths)if(components.has(p.id)){
20
+ const original=rig.paths.find(q=>q.id===p.id).rgb;
21
+ const color=t<1800?mix(original,red,ease(t/700)):t<4800?red:t<6200?mix(red,blue,ease((t-4800)/800)):mix(blue,gold,ease((t-6200)/2800));
22
+ p.rgb=t>9600?mix(color,original,ease((t-9600)/400)):color;
23
+ p.stroke+=2*ready;
24
+ }
25
+ const add=(id,rgb,polygons,stroke=3)=>paths.push({id,group:'effect',erase:false,rgb,polygons,stroke});
26
+ if(t>=1800&&t<6200){
27
+ const growth=ease((t-1800)/500)*(1-ease((t-5700)/500));
28
+ const theta=-(t-1800)/4400*Math.PI*4;
29
+ for(let j=0;j<2;j++){
30
+ const a=theta+j*Math.PI,points=[];
31
+ for(let i=0;i<=20;i++){const q=a+i/20*1.8;points.push([607+59*growth*Math.cos(q),350+66*growth*Math.sin(q)]);}
32
+ add('rewind-arc-'+j,gold,[points],3);
33
+ const [x,y]=points[0],dx=Math.sin(a),dy=-Math.cos(a);
34
+ add('rewind-arrow-'+j,gold,[[[x+7*dx,y+7*dy],[x-5*dx-5*dy,y-5*dy+5*dx],[x-5*dx+5*dy,y-5*dy-5*dx]]],0);
35
+ }
36
+ const y=388-96*ease((t-1800)/3000);
37
+ add('reverse-wave',gold,[[[551,y],[668,y]]],4);
38
+ }
39
+ if(t>=4800&&t<9000){
40
+ const glow=ease((t-4800)/600)*(1-ease((t-8500)/500));
41
+ if(glow>.01)add('checkpoint',[120,180,245],[[[548,402],[548,414],[667,414],[667,402]]],3*glow);
42
+ }
43
+ if(t>=6200&&t<9000){
44
+ const y=292+96*ease((t-6200)/2800);
45
+ add('verify-wave',gold,[[[551,y],[668,y]]],4);
46
+ }
47
+ if(t>=9000&&t<10000){
48
+ const s=Math.sin(Math.PI*(t-9000)/1000);
49
+ if(s>.01)add('checkpoint-confirmed',gold,[[[592,401],[602,411],[624,389]]],5*s);
50
+ }
51
+ return {...base,paths};
52
+ }
53
+
54
+ export function renderDaedalus(rig,scene='idle',ms=0,options={}){
55
+ if(scene!=='rollback')return previousRender(rig,scene,ms,options);
56
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
57
+ if(ms>=12000&&!options.still)return previousRender(rig,'idle',ms-12000,options);
58
+ const t=options.still?3500:ms;
59
+ const caption=t<1800?'Bad release. Stop deployment.':t<4800?'Rewind the assembly.':t<6200?'Recover the saved blueprint.':t<9000?'Rebuild. Verify each layer.':t<10000?'Checkpoint restored.':'Release and reset.';
60
+ return {...renderSvgRig(poseRollback(rig,t),options),scene,clip:scene,caption};
61
+ }
62
+ export function renderStory(rig,ms,options={}){
63
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
64
+ if(options.still)return renderDaedalus(rig,'rollback',3500,options);
65
+ return ms<3200?renderDaedalus(rig,'starforge',ms,options):renderDaedalus(rig,'rollback',ms-3200,options);
66
+ }
@@ -0,0 +1,32 @@
1
+ import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
2
+ import {poseRig as forgePose} from '../v2/character.mjs';
3
+ import {renderDaedalus as previousRender,DAEDALUS_CLIPS as clips} from '../v5/character.mjs';
4
+
5
+ /**
6
+ * Daedalus V6: companion power. Anatomy is the approved V3 SVG, untouched; every V2-V5 clip is unchanged.
7
+ * Adds 'assemble', the V2 Starforge choreography without its completion star: the pieces gather, link and
8
+ * are swept by light, then release. A companion plays it while the host reports work in progress, so it must
9
+ * not announce a finished deployment. Pure function of elapsed time; the host supplies every fact.
10
+ */
11
+ export const ASSEMBLE_MS=10000;
12
+ export const DAEDALUS_CLIPS=Object.freeze([...clips,{id:'assemble',title:'Starforge, no verdict',duration:ASSEMBLE_MS}]);
13
+ export const STORY_DURATION=3000+ASSEMBLE_MS;
14
+ export function poseAssemble(rig,t){
15
+ if(t>=ASSEMBLE_MS)return forgePose(rig,'idle',t-ASSEMBLE_MS);
16
+ const base=forgePose(rig,'starforge',t);
17
+ return {...base,paths:base.paths.filter(p=>p.id!=='completion-star')};
18
+ }
19
+ export function renderDaedalus(rig,scene='idle',ms=0,options={}){
20
+ if(scene!=='assemble')return previousRender(rig,scene,ms,options);
21
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
22
+ if(ms>=ASSEMBLE_MS&&!options.still)return previousRender(rig,'idle',ms-ASSEMBLE_MS,options);
23
+ // Still freezes every channel (gesture, components, links, wave) at the Starforge still pose.
24
+ const t=options.still?4800:ms;
25
+ const caption=t<3200?'Draw the pieces together.':t<8000?'Lock the structure. Sweep the light.':'Release and reset.';
26
+ return {...renderSvgRig(poseAssemble(rig,t),options),scene,clip:scene,caption};
27
+ }
28
+ export function renderStory(rig,ms,options={}){
29
+ if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
30
+ if(options.still)return renderDaedalus(rig,'assemble',4800,options);
31
+ return ms<3000?renderDaedalus(rig,'attach',ms,options):renderDaedalus(rig,'assemble',ms-3000,options);
32
+ }
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import {parseSvgRig} from '../../../factory/svg-rig.mjs';
4
+ import {renderDaedalus,DAEDALUS_CLIPS} from './character.mjs';
5
+ import {artSlot,composeMessage} from '../../lucid/recovery/layout.mjs';
6
+ import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
7
+ const args=process.argv.slice(2),values=new Set(['--cols','--rows','--terminal-cols','--terminal-rows','--message','--scene','--at']);
8
+ for(let i=0;i<args.length;i++){
9
+ if(values.has(args[i])){if(!args[i+1]||args[i+1].startsWith('--'))throw Error('Missing option value');i++;}
10
+ else if(!['--large','--mono','--help','--watch','--attach','--intro','--still','--list'].includes(args[i]))throw Error('Unknown option: '+args[i]);
11
+ }
12
+ const value=(name,fallback)=>args.includes(name)?args[args.indexOf(name)+1]:fallback;
13
+ if(args.includes('--help')){console.log('Daedalus V6 forge studio (companion Starforge). --watch --attach --intro --still --scene ID --at MS --list --large --mono --message TEXT --cols N --rows N --terminal-cols N --terminal-rows N');process.exit(0);}
14
+ if(args.includes('--list')){console.log(DAEDALUS_CLIPS.map(c=>c.id+' · '+c.title).join('\n'));process.exit(0);}
15
+ const w=Number(value('--terminal-cols',process.stdout.columns||80)),h=Number(value('--terminal-rows',process.stdout.rows||24));
16
+ if(!Number.isSafeInteger(w)||!Number.isSafeInteger(h)||w<1||h<1)throw Error('Invalid terminal size');
17
+ const slot=artSlot(w,h),large=args.includes('--large')||args.includes('--intro'),explicit=large||args.includes('--cols')||args.includes('--rows');
18
+ const cols=Number(value('--cols',large?Math.min(120,w-4):Math.min(39,slot.cols))),rows=Number(value('--rows',large?Math.min(36,h-4):Math.min(11,slot.rows)));
19
+ const message=value('--message','DAEDALUS\nImagine. Assemble. Deploy.');
20
+ if(!slot.showArt&&!explicit){console.log(composeMessage(null,message,{terminalCols:w}).join('\n'));}
21
+ else{
22
+ if(cols>w-4||rows>h-2)throw Error('Requested art exceeds terminal margins');
23
+ const rig=parseSvgRig(await fs.readFile(new URL('../v3/daedalus-master.svg',import.meta.url),'utf8'));
24
+ const opts={cols,rows,mono:args.includes('--mono'),still:args.includes('--still'),deck:large};
25
+ const scene=value('--scene',args.includes('--attach')?'attach':'idle');
26
+ const r=renderDaedalus(rig,scene,Number(value('--at',0)),opts);
27
+ if(!r.ok)throw Error(r.code);
28
+ if(args.includes('--watch')||args.includes('--attach')||args.includes('--intro')){
29
+ const {watch}=await import('./watch.mjs');
30
+ await watch(rig,opts,{terminalCols:w,terminalRows:h,large,scene:args.includes('--scene')||args.includes('--attach')?scene:null,arrival:args.includes('--attach'),message:args.includes('--message')?message:null});
31
+ }else{
32
+ const shownMessage=args.includes('--message')?message:'DAEDALUS\n'+r.caption;
33
+ console.log((large?[...cellsToAnsi(r.frame),...composeMessage(null,shownMessage,{terminalCols:w})]:composeMessage(r.frame,shownMessage,{terminalCols:w})).join('\n'));
34
+ }
35
+ }
@@ -0,0 +1,56 @@
1
+ import {performance} from 'node:perf_hooks';
2
+ import {DAEDALUS_CLIPS,renderDaedalus,renderStory,STORY_DURATION} from './character.mjs';
3
+ import {composeMessage} from '../../lucid/recovery/layout.mjs';
4
+ import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
5
+
6
+ /** One bounded performance. No alternate screen or full-screen clearing. */
7
+ export async function watch(paths,options,config){
8
+ if(!process.stdout.isTTY||!process.stdin.isTTY)throw new Error('--watch/--intro needs an interactive terminal. Use --scene and --at for snapshots.');
9
+ const {terminalCols,terminalRows,scene,large,message}=config;
10
+ const clip=scene?DAEDALUS_CLIPS.find(c=>c.id===scene):null;
11
+ const duration=(clip?.duration??STORY_DURATION)+(config.arrival?6000:0);
12
+ const realCols=process.stdout.columns,realRows=process.stdout.rows;
13
+ const start=performance.now();
14
+ const render=t=>{
15
+ const r=scene?renderDaedalus(paths,scene,t,options):renderStory(paths,t,options);
16
+ if(!r?.ok)throw new Error(r?.code??'Invalid story frame');
17
+ const caption=message??'DAEDALUS · '+r.clip+'\n'+r.caption;
18
+ return large?[...cellsToAnsi(r.frame),...caption.split('\n')]:composeMessage(r.frame,caption,{terminalCols});
19
+ };
20
+ // Reserve enough lines for captions too; keep within the real terminal.
21
+ const height=options.rows+3;
22
+ if(height>=terminalRows)throw new Error('Animation needs room for its caption; reduce --rows.');
23
+ let ended=false,drawn=false,timer,resolveDone,rejectDone;
24
+ const wasRaw=Boolean(process.stdin.isRaw);
25
+ const done=new Promise((resolve,reject)=>{resolveDone=resolve;rejectDone=reject;});
26
+ const cleanup=(error)=>{
27
+ if(ended)return;ended=true;clearInterval(timer);
28
+ process.stdin.off('data',key);process.stdout.off('resize',resized);
29
+ process.off('SIGINT',sigint);process.off('SIGTERM',sigterm);
30
+ process.stdin.setRawMode(wasRaw);process.stdin.pause();
31
+ if(drawn)process.stdout.write(`\x1b[${height}B\r`);
32
+ process.stdout.write('\x1b[0m\x1b[?25h');
33
+ error?rejectDone(error):resolveDone();
34
+ };
35
+ const key=data=>{if(data.includes(3)||data.toString().includes('q'))cleanup();};
36
+ const sigint=()=>cleanup(),sigterm=()=>cleanup();
37
+ const resized=()=>cleanup(new Error('Terminal resized; rerun for the new art slot.'));
38
+ try{
39
+ process.stdin.setRawMode(true);process.stdin.resume();process.stdin.on('data',key);
40
+ process.stdout.on('resize',resized);process.on('SIGINT',sigint);process.on('SIGTERM',sigterm);
41
+ process.stdout.write('\x1b[?25l');
42
+ // Allocate the block once; each frame replaces only these lines.
43
+ process.stdout.write('\n'.repeat(height)+`\x1b[${height}A`);drawn=true;
44
+ const tick=()=>{
45
+ try{
46
+ if(process.stdout.columns!==realCols||process.stdout.rows!==realRows)return resized();
47
+ const t=Math.min(performance.now()-start,duration-1),lines=render(t);
48
+ if(lines.length>height)throw new Error('Message exceeds the animation block; use a shorter message or a static preview.');
49
+ process.stdout.write(Array.from({length:height},(_,i)=>'\r\x1b[2K'+(lines[i]??'')+'\n').join('')+`\x1b[${height}A`);
50
+ if(performance.now()-start>=duration)cleanup();
51
+ }catch(e){cleanup(e);}
52
+ };
53
+ tick();if(!ended)timer=setInterval(tick,1000/6);
54
+ await done;
55
+ }catch(e){cleanup(e);await done;}
56
+ }