@rexvane/piweb 0.3.15 → 0.3.17

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 (143) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +4 -4
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/react-loadable-manifest.json +3 -3
  6. package/.next/required-server-files.js +1 -3
  7. package/.next/required-server-files.json +1 -3
  8. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/server/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  21. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  22. package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
  23. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  24. package/.next/server/app/api/growth/route.js +1 -1
  25. package/.next/server/app/api/growth/route.js.nft.json +1 -1
  26. package/.next/server/app/api/models/route.js +1 -1
  27. package/.next/server/app/api/models/route.js.nft.json +1 -1
  28. package/.next/server/app/api/pi-settings/route.js +1 -1
  29. package/.next/server/app/api/plugins/route.js.nft.json +1 -1
  30. package/.next/server/app/api/prompts/route.js.nft.json +1 -1
  31. package/.next/server/app/api/security/route.js.nft.json +1 -1
  32. package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
  33. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  34. package/.next/server/app/api/sessions/import/route.js +3 -3
  35. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  36. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  37. package/.next/server/app/api/update/route.js +1 -1
  38. package/.next/server/app/api/update/route.js.nft.json +1 -1
  39. package/.next/server/app/api/version/route.js +1 -1
  40. package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  41. package/.next/server/app/index.html +1 -1
  42. package/.next/server/app/index.rsc +4 -4
  43. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  44. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  46. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  47. package/.next/server/app/login.html +1 -1
  48. package/.next/server/app/login.rsc +3 -3
  49. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  50. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  51. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
  52. package/.next/server/app/page.js +1 -1
  53. package/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/server/app-paths-manifest.json +4 -4
  55. package/.next/server/chunks/496.js +1 -1
  56. package/.next/server/chunks/686.js +4 -4
  57. package/.next/server/middleware-build-manifest.js +1 -1
  58. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  59. package/.next/server/pages/404.html +1 -1
  60. package/.next/server/pages/500.html +1 -1
  61. package/.next/server/server-reference-manifest.json +1 -1
  62. package/.next/static/chunks/2716.93be8ad5c970c516.js +1 -0
  63. package/.next/static/chunks/4632.112d2e893d48d188.js +1 -0
  64. package/.next/static/chunks/4673.c1fb1580efc6ace7.js +1 -0
  65. package/.next/static/chunks/4686-f4a037d609cea3f2.js +1 -0
  66. package/.next/static/chunks/app/page-b51087831582ee37.js +1 -0
  67. package/.next/static/chunks/{webpack-42c5aae79b0e5d8c.js → webpack-c31263fd83d75ce6.js} +1 -1
  68. package/.next/static/css/850c2fd130ffa2fc.css +1 -0
  69. package/README.md +17 -19
  70. package/assets/archive.jpg +0 -0
  71. package/assets/growth-1.jpg +0 -0
  72. package/assets/growth-2.jpg +0 -0
  73. package/assets/import.jpg +0 -0
  74. package/assets/main.jpg +0 -0
  75. package/assets/outline.jpg +0 -0
  76. package/assets/prompts-1.jpg +0 -0
  77. package/assets/prompts-2.jpg +0 -0
  78. package/assets/providers-1.jpg +0 -0
  79. package/assets/providers-2.jpg +0 -0
  80. package/assets/providers-3.jpg +0 -0
  81. package/assets/trace-1.jpg +0 -0
  82. package/assets/trace-2.png +0 -0
  83. package/assets/trace-3.png +0 -0
  84. package/assets/trace-4.png +0 -0
  85. package/assets/viewer-1.jpg +0 -0
  86. package/assets/viewer-2.jpg +0 -0
  87. package/bin/pi.js +50 -0
  88. package/next.config.ts +32 -7
  89. package/package.json +2 -6
  90. package/scripts/launcher.mjs +1 -1
  91. package/scripts/verify-package.mjs +0 -53
  92. package/src/app/api/growth/route.ts +14 -14
  93. package/src/app/api/models/route.ts +2 -1
  94. package/src/app/globals.css +90 -0
  95. package/src/components/AppShell.tsx +30 -16
  96. package/src/components/ChatWindow.tsx +192 -6
  97. package/src/components/FileViewer.tsx +10 -9
  98. package/src/components/ProjectPanel.tsx +111 -94
  99. package/src/components/SessionImportSection.tsx +20 -36
  100. package/src/hooks/useGrowth.ts +123 -156
  101. package/src/hooks/usePiWeb.ts +7 -10
  102. package/src/i18n.tsx +39 -39
  103. package/src/lib/agent-manager.ts +10 -8
  104. package/src/lib/growth-service.ts +390 -166
  105. package/src/lib/growth-tracker.ts +64 -264
  106. package/src/lib/growth-turns.ts +31 -1
  107. package/src/lib/models/model-scope.ts +15 -0
  108. package/src/lib/models-service.ts +16 -0
  109. package/src/lib/pi-settings.ts +7 -0
  110. package/src/lib/session-import/claude.ts +2 -13
  111. package/src/lib/session-import/codex.ts +8 -44
  112. package/src/lib/session-import/files.ts +0 -14
  113. package/src/lib/session-import/grok.ts +24 -54
  114. package/src/lib/session-import/types.ts +0 -12
  115. package/src/lib/types.ts +27 -22
  116. package/tsconfig.json +2 -1
  117. package/.next/static/chunks/2716.a9e808190a4e2424.js +0 -1
  118. package/.next/static/chunks/4632.0ed92ae42905ce40.js +0 -1
  119. package/.next/static/chunks/4673.d3d0a7f2599080fb.js +0 -1
  120. package/.next/static/chunks/4686-7d694def920ff093.js +0 -1
  121. package/.next/static/chunks/app/page-9a5440047c5a415c.js +0 -1
  122. package/.next/static/css/b2d35abc828d4713.css +0 -1
  123. package/assets/archive.png +0 -0
  124. package/assets/growth-1.png +0 -0
  125. package/assets/growth-2.png +0 -0
  126. package/assets/import.png +0 -0
  127. package/assets/main.png +0 -0
  128. package/assets/outline.png +0 -0
  129. package/assets/prompts-1.png +0 -0
  130. package/assets/prompts-2.png +0 -0
  131. package/assets/providers-1.png +0 -0
  132. package/assets/providers-2.png +0 -0
  133. package/assets/providers-3.png +0 -0
  134. package/assets/trace-1.png +0 -0
  135. package/assets/viewer-1.png +0 -0
  136. package/assets/viewer-2.png +0 -0
  137. package/scripts/managed-niubash-assets.mjs +0 -146
  138. package/scripts/prepare-niubash-packages.mjs +0 -182
  139. package/scripts/smoke-managed-niubash.mjs +0 -62
  140. package/src/lib/growth-rounds.ts +0 -84
  141. package/src/lib/managed-niubash.ts +0 -227
  142. /package/.next/static/{DdBwPYoHBKtaJ97QIr6Co → zsynuO_9Kx6nrXgkTUJ04}/_buildManifest.js +0 -0
  143. /package/.next/static/{DdBwPYoHBKtaJ97QIr6Co → zsynuO_9Kx6nrXgkTUJ04}/_ssgManifest.js +0 -0
@@ -1 +0,0 @@
1
- /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,::backdrop,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:host,:root{--font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-medium:500;--font-weight-bold:700;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--animate-spin:spin 1s linear infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,::backdrop,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}:host,html{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.\!relative{position:relative!important}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.-top-1{top:calc(var(--spacing) * -1)}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-0{top:0}.top-0\.5{top:calc(var(--spacing) * .5)}.top-1\/2{top:50%}.top-3{top:calc(var(--spacing) * 3)}.top-5{top:calc(var(--spacing) * 5)}.top-6{top:calc(var(--spacing) * 6)}.top-8{top:calc(var(--spacing) * 8)}.top-9{top:calc(var(--spacing) * 9)}.top-full{top:100%}.-right-1{right:calc(var(--spacing) * -1)}.-right-1\.5{right:calc(var(--spacing) * -1.5)}.right-0{right:0}.right-1{right:var(--spacing)}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.right-5{right:calc(var(--spacing) * 5)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-9{bottom:calc(var(--spacing) * 9)}.bottom-10{bottom:calc(var(--spacing) * 10)}.bottom-24{bottom:calc(var(--spacing) * 24)}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.z-20{z-index:20}.z-50{z-index:50}.z-\[70\]{z-index:70}.z-\[80\]{z-index:80}.z-\[85\]{z-index:85}.z-\[95\]{z-index:95}.z-\[100\]{z-index:100}.z-\[105\]{z-index:105}.z-\[110\]{z-index:110}.z-\[120\]{z-index:120}.z-\[130\]{z-index:130}.z-\[9999\]{z-index:9999}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-1{margin-inline:var(--spacing)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-1\.5{margin-block:calc(var(--spacing) * 1.5)}.my-2{margin-block:calc(var(--spacing) * 2)}.-mt-3{margin-top:calc(var(--spacing) * -3)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-8{height:calc(var(--spacing) * 8)}.h-11{height:calc(var(--spacing) * 11)}.h-14{height:calc(var(--spacing) * 14)}.h-\[42px\]{height:42px}.h-full{height:100%}.h-screen{height:100vh}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[70vh\]{max-height:70vh}.max-h-\[340px\]{max-height:340px}.min-h-0{min-height:0}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-14{width:calc(var(--spacing) * 14)}.w-36{width:calc(var(--spacing) * 36)}.w-44{width:calc(var(--spacing) * 44)}.w-52{width:calc(var(--spacing) * 52)}.w-56{width:calc(var(--spacing) * 56)}.w-64{width:calc(var(--spacing) * 64)}.w-\[260px\]{width:260px}.w-\[312px\]{width:312px}.w-\[calc\(100\%-8px\)\]{width:calc(100% - 8px)}.w-\[min\(92vw\,360px\)\]{width:min(92vw,360px)}.w-\[min\(92vw\,380px\)\]{width:min(92vw,380px)}.w-\[min\(92vw\,480px\)\]{width:min(92vw,480px)}.w-full{width:100%}.w-max{width:max-content}.w-screen{width:100vw}.max-w-\[45\%\]{max-width:45%}.max-w-\[85\%\]{max-width:85%}.max-w-\[90vw\]{max-width:90vw}.max-w-\[180px\]{max-width:180px}.max-w-\[220px\]{max-width:220px}.max-w-\[420px\]{max-width:420px}.max-w-\[calc\(100vw-24px\)\]{max-width:calc(100vw - 24px)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:0}.min-w-4{min-width:calc(var(--spacing) * 4)}.min-w-\[86px\]{min-width:86px}.flex-1{flex:1}.flex-none{flex:none}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1)}.-translate-x-1\/2,.-translate-y-1\/2{translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-3\.5{gap:calc(var(--spacing) * 3.5)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-\[6px\]{gap:6px}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-\[10px\]{border-radius:10px}.rounded-\[24px\]{border-radius:24px}.rounded-full{border-radius:3.40282e+38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-\[var\(--dsw-border-l1\)\]{border-color:var(--dsw-border-l1)}.border-\[var\(--dsw-label-tertiary\)\]{border-color:var(--dsw-label-tertiary)}.border-current{border-color:currentColor}.border-transparent{border-color:#0000}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.border-white\/15{border-color:#ffffff26}@supports (color:color-mix(in lab,red,red)){.border-white\/15{border-color:color-mix(in oklab,var(--color-white) 15%,transparent)}}.border-t-transparent{border-top-color:#0000}.bg-\[\#2c2c2c\]{background-color:#2c2c2c}.bg-\[\#212121\]{background-color:#212121}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/\[0\.05\]{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.05\]{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-7{padding:calc(var(--spacing) * 7)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-12{padding-inline:calc(var(--spacing) * 12)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-1{padding-right:var(--spacing)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-0{padding-bottom:0}.pb-1{padding-bottom:var(--spacing)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-1{padding-left:var(--spacing)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[14\.5px\]{font-size:14.5px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.text-ellipsis{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#f87171\]{color:#f87171}.text-\[var\(--dsw-label-primary\)\]{color:var(--dsw-label-primary)}.text-black{color:var(--color-black)}.text-white{color:var(--color-white)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040)}.shadow-2xl,.shadow-xl{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.group-focus-within\:flex:is(:where(.group):focus-within *){display:flex}.group-focus-within\:hidden:is(:where(.group):focus-within *){display:none}@media (hover:hover){.group-hover\:block:is(:where(.group):hover *){display:block}.group-hover\:flex:is(:where(.group):hover *){display:flex}.group-hover\:hidden:is(:where(.group):hover *){display:none}.group-hover\/tool\:opacity-100:is(:where(.group\/tool):hover *){opacity:1}}.first\:mt-0:first-child{margin-top:0}@media (hover:hover){.hover\:border-\[\#f87171\]\/40:hover{border-color:oklab(71.0627% .153814 .0628211/.4)}.hover\:border-white\/25:hover{border-color:#ffffff40}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/25:hover{border-color:color-mix(in oklab,var(--color-white) 25%,transparent)}}.hover\:bg-\[\#383838\]:hover{background-color:#383838}.hover\:bg-\[var\(--dsw-hover\)\]:hover{background-color:var(--dsw-hover)}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.hover\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\:text-\[\#ef4444\]:hover{color:#ef4444}.hover\:text-white:hover{color:var(--color-white)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-white\/40:focus{border-color:#fff6}@supports (color:color-mix(in lab,red,red)){.focus\:border-white\/40:focus{border-color:color-mix(in oklab,var(--color-white) 40%,transparent)}}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-white\/30:focus{--tw-ring-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.focus\:ring-white\/30:focus{--tw-ring-color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.focus-visible\:opacity-100:focus-visible{opacity:1}.disabled\:cursor-wait:disabled{cursor:wait}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}:root{--dsw-deepseek-400:#679efe;--dsw-deepseek-450:#5686fe;--dsw-deepseek-500:#4176e6;--dsw-amber-500:#f59e0b;--dsw-green-500:#22c55e;--dsw-red-600:#ec1313;--dsw-bg-base:#fff;--dsw-bg-elevated:#fff;--dsw-sidebar-fill:#f9fafb;--dsw-input-major:#fff;--dsw-bubble:#eff4ff;--dsw-hover:#0000000a;--dsw-active:#0000000f;--dsw-label-primary:#000;--dsw-label-secondary:#404042;--dsw-label-tertiary:#6e6e73;--dsw-label-caption:#96969b;--dsw-border-l1:#0000000a;--dsw-border-l2:#0000001a;--dsw-border-l3:#0000001f;--dsw-border-l4:#00000029;--dsw-accent:var(--dsw-deepseek-500);--dsw-accent-soft:#4176e61a;--dsw-warn:var(--dsw-amber-500);--dsw-success:var(--dsw-green-500);--dsw-danger:var(--dsw-red-600);--dsw-selector:#0000000d;--dsw-layer-2:#fff;--dsw-layer-3:#fff;--dsw-btn-elevated:#fff;--dsw-btn-floating-hover:#f0f1f3;--dsw-module-platform:#eeeff1;--dsw-border-strong:#adb2b8;--dsw-glass-popover:#fff;--dsw-glass-input:#fff;--dsw-glass-modal:#fff;--dsw-mask:#0000003d;--dsw-mask-blur:blur(6px);--dsw-elevation-stroke:var(--dsw-border-l2);--dsw-elevation-soft:0 0 0 .5px var(--dsw-elevation-stroke),0 4px 16px #00000008,0 0 24px #00000008;--dsw-elevation-panel:0 0 0 .5px var(--dsw-elevation-stroke),0 8px 32px #00000014;--dsw-elevation-prominent:0 0 0 .5px var(--dsw-elevation-stroke),0 16px 48px #00000029;--ds-ease-in-out:cubic-bezier(.4,0,.2,1);--ds-duration-fast:.1s;--ds-duration-normal:.2s;--ds-duration-slow:.3s;--dsh-content-font-size:14px;--dsh-content-font-delta:0px;--dsh-content-font-size-secondary:13px;--piweb-chat-font-size:var(--dsh-content-font-size);--piweb-chat-font-l:calc(var(--piweb-chat-font-size) * .96);--piweb-chat-font-m:calc(var(--piweb-chat-font-size) * .93);--piweb-chat-font-s:calc(var(--piweb-chat-font-size) * .89);--piweb-chat-font-t:calc(var(--piweb-chat-font-size) * .86);--piweb-chat-font-xs:calc(var(--piweb-chat-font-size) * .82);--font-ui:"Maple Mono CN",ui-monospace,SFMono-Regular,Menlo,Consolas,"PingFang SC","Microsoft YaHei",monospace;--font-mono:"Maple Mono CN",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;--dsh-chat-content-width:clamp(544px,51.2%,736px);--dsh-composer-card-max-width:calc(var(--dsh-chat-content-width) + 32px)}@media (prefers-color-scheme:dark){:root:not([data-ds-light-theme]),body:not([data-ds-light-theme]){--dsw-bg-base:#151517;--dsw-bg-elevated:#1e1e20;--dsw-sidebar-fill:#1b1b1c;--dsw-input-major:#2c2c2e;--dsw-bubble:#26282e;--dsw-hover:#ffffff0d;--dsw-active:#ffffff14;--dsw-label-primary:#f9fafb;--dsw-label-secondary:#cfd3d6;--dsw-label-tertiary:#adb2b8;--dsw-label-caption:#81858c;--dsw-border-l1:#ffffff0f;--dsw-border-l2:#ffffff1f;--dsw-border-l3:#ffffff29;--dsw-border-l4:#fff3;--dsw-accent:var(--dsw-deepseek-400);--dsw-accent-soft:#679efe24;--dsw-danger:#f25a5a;--dsw-selector:#ffffff14;--dsw-layer-2:#2c2c2e;--dsw-layer-3:#353638;--dsw-btn-elevated:#43454a;--dsw-btn-floating-hover:#353638;--dsw-module-platform:#353638;--dsw-glass-popover:#353638;--dsw-glass-input:#2c2c2e;--dsw-glass-modal:#2c2c2e;--dsw-mask:#00000080;--dsw-elevation-stroke:var(--dsw-border-l2);--dsw-elevation-soft:0 0 0 .5px var(--dsw-elevation-stroke),0 4px 16px #0003,0 0 24px #0003;--dsw-elevation-panel:0 0 0 .5px var(--dsw-elevation-stroke),0 8px 32px #0006;--dsw-elevation-prominent:0 0 0 .5px var(--dsw-elevation-stroke),0 16px 48px #0000008c}}body[data-ds-dark-theme],html[data-ds-dark-theme]{--dsw-bg-base:#151517;--dsw-bg-elevated:#1e1e20;--dsw-sidebar-fill:#1b1b1c;--dsw-input-major:#2c2c2e;--dsw-bubble:#26282e;--dsw-hover:#ffffff0d;--dsw-active:#ffffff14;--dsw-label-primary:#f9fafb;--dsw-label-secondary:#cfd3d6;--dsw-label-tertiary:#adb2b8;--dsw-label-caption:#81858c;--dsw-border-l1:#ffffff0f;--dsw-border-l2:#ffffff1f;--dsw-border-l3:#ffffff29;--dsw-border-l4:#fff3;--dsw-accent:var(--dsw-deepseek-400);--dsw-accent-soft:#679efe24;--dsw-danger:#f25a5a;--dsw-selector:#ffffff14;--dsw-layer-2:#2c2c2e;--dsw-layer-3:#353638;--dsw-btn-elevated:#43454a;--dsw-btn-floating-hover:#353638;--dsw-module-platform:#353638;--dsw-glass-popover:#353638;--dsw-glass-input:#2c2c2e;--dsw-glass-modal:#2c2c2e;--dsw-mask:#00000080;--dsw-elevation-stroke:var(--dsw-border-l2);--dsw-elevation-soft:0 0 0 .5px var(--dsw-elevation-stroke),0 4px 16px #0003,0 0 24px #0003;--dsw-elevation-panel:0 0 0 .5px var(--dsw-elevation-stroke),0 8px 32px #0006;--dsw-elevation-prominent:0 0 0 .5px var(--dsw-elevation-stroke),0 16px 48px #0000008c}body,html{height:100%}body{background:var(--dsw-bg-base);color:var(--dsw-label-primary);font-family:var(--font-ui);font-size:var(--dsh-content-font-size);line-height:calc(24px + var(--dsh-content-font-delta));-webkit-font-smoothing:antialiased;overflow:hidden}button{font-size:inherit;cursor:pointer;padding:0}button,input,select,textarea{font-family:inherit;color:inherit;background:0 0;border:none}input,select,textarea{outline:none}select{color-scheme:light}select option{background:var(--dsw-layer-2);color:var(--dsw-label-primary)}body[data-ds-dark-theme] select{color-scheme:dark}input::placeholder,textarea::placeholder{color:var(--dsw-label-caption)}*{box-sizing:border-box;corner-shape:superellipse(1.5)}@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}@media (max-width:840px){:root{--dsh-chat-content-width:100%;--dsh-composer-card-max-width:100%}.pi-main>.hairline-b{padding-left:56px;padding-right:12px}.settings-dialog{flex-direction:column;border-radius:22px!important;width:calc(100vw - 16px)!important;max-width:none!important;height:calc(100vh - 16px)!important}.settings-nav{border-bottom:.5px solid var(--dsw-border-l2);overflow-x:auto;flex-direction:row!important;width:100%!important;padding:10px!important}.settings-nav>button{flex:none;height:36px}.settings-title{display:none}}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:var(--dsw-active);background-clip:content-box;border:2.5px solid #0000;border-radius:8px}::-webkit-scrollbar-thumb:hover{background:var(--dsw-border-l3);background-clip:content-box;border:2px solid #0000}::-webkit-scrollbar-track{background:0 0}.icon-btn{width:28px;height:28px;color:var(--dsw-label-secondary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:8px;justify-content:center;align-items:center;display:inline-flex}.icon-btn:hover{background:var(--dsw-hover)}.chip,.icon-btn:hover{color:var(--dsw-label-primary)}.chip{height:28px;white-space:nowrap;transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:24px;align-items:center;gap:5px;padding:0 10px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.chip:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.chip .chevron{color:var(--dsw-label-caption);transition:transform .12s var(--ds-ease-in-out)}.chip[data-open=true] .chevron{transform:rotate(180deg)}.hero-chip{height:28px;color:var(--dsw-label-primary);white-space:nowrap;cursor:pointer;transition:background var(--ds-duration-fast) var(--ds-ease-in-out);background:0 0;border-radius:14px;align-items:center;gap:6px;padding:0 8px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.hero-chip:hover,.hero-chip[data-open=true]{background:var(--dsw-hover)}.hero-chip .hero-chip-icon{color:var(--dsw-label-primary);flex:none}.hero-chip .chevron{color:var(--dsw-label-caption);transition:transform .12s var(--ds-ease-in-out);flex:none}.hero-chip[data-open=true] .chevron{transform:rotate(180deg)}.btn-outline{border:.5px solid var(--dsw-border-l3);height:36px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);background:0 0;border-radius:18px;justify-content:center;align-items:center;gap:6px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.btn-outline:hover{background:var(--dsw-hover)}.btn-primary-white{background:var(--dsw-label-primary);height:36px;color:var(--dsw-bg-base);transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:18px;justify-content:center;align-items:center;gap:6px;padding:0 14px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.btn-primary-white:disabled{opacity:.5;cursor:default}.btn-new-session{border:.5px solid var(--dsw-border-l3);background:var(--dsw-btn-elevated);height:38px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:12px;justify-content:center;align-items:center;gap:6px;padding:8px 16px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.btn-new-session:hover{background:var(--dsw-btn-floating-hover)}.select-chip{border:.5px solid var(--dsw-border-l2);height:32px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:16px;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.select-chip:hover{background:var(--dsw-hover)}.tab-underline{color:var(--dsw-label-tertiary);transition:color var(--ds-duration-fast) var(--ds-ease-in-out);padding:6px 2px;font-size:13.5px;position:relative}.tab-underline:hover{color:var(--dsw-label-primary)}.tab-underline[data-active=true]{color:var(--dsw-accent);font-weight:600}.tab-underline:after{content:"";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:-1px;left:0;right:0}.tab-underline[data-active=true]:after{background:var(--dsw-accent)}.settings-tab-underline{color:var(--dsw-label-tertiary);transition:color var(--ds-duration-fast) var(--ds-ease-in-out);padding:6px 2px 10px;font-size:13.5px;position:relative}.settings-tab-underline:hover{color:var(--dsw-label-primary)}.settings-tab-underline[data-active=true]{color:var(--dsw-label-primary);font-weight:500}.settings-tab-underline:after{content:"";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:-1px;left:0;right:0}.settings-tab-underline[data-active=true]:after{background:var(--dsw-label-primary)}.btn-primary-circle{background:var(--dsw-accent);color:#fff;width:34px;height:34px;transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out),transform var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:999px;flex:none;justify-content:center;align-items:center;margin-top:-3px;margin-bottom:-3px;display:inline-flex;transform:translateY(-2px)}.btn-primary-circle:disabled{opacity:.4;cursor:default}.btn-ghost-circle{width:30px;height:30px;color:var(--dsw-label-secondary);border:.5px solid var(--dsw-border-l2);background:0 0;border-radius:999px;flex:none;justify-content:center;align-items:center;margin-top:-3px;margin-bottom:-3px;display:inline-flex;transform:translateY(-2px)}.btn-ghost-circle:hover{color:var(--dsw-danger);border-color:var(--dsw-danger)}.pw-queue{flex-direction:column;gap:3px;margin:0 14px 8px;font-size:12.5px;display:flex}.pw-queue-row{min-width:0;color:var(--dsw-label-secondary);align-items:center;gap:8px;display:flex}.pw-queue-tag{text-transform:capitalize;color:var(--dsw-label-tertiary);border:.5px solid var(--dsw-border-l2);border-radius:4px;flex:none;padding:0 6px;font-size:10.5px;line-height:16px}.pw-queue-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.pw-queue-foot{color:var(--dsw-label-caption);gap:12px;margin-top:1px;padding-left:2px;font-size:11.5px;display:flex}.pw-queue-foot button:hover{color:var(--dsw-label-secondary)}.pw-busy-hint{color:var(--dsw-label-caption);margin:6px 14px 0;font-size:11.5px;line-height:16px}.hairline-t{border-top:.5px solid var(--dsw-border-l2)}.hairline-b{border-bottom:.5px solid var(--dsw-border-l2)}.hairline-r{border-right:.5px solid var(--dsw-border-l2)}.rail-btn{width:36px;height:36px;color:var(--dsw-label-secondary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:12px;justify-content:center;align-items:center;display:inline-flex}.rail-btn:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.state-dot{border-radius:999px;flex:none;width:8px;height:8px;position:relative}.state-dot.running{background:var(--dsw-deepseek-450);animation:dot-breathe 1s ease-in-out infinite}.state-dot.pending{background:var(--dsw-warn);box-shadow:0 0 0 3px #f59e0b1a}.state-dot.done{background:var(--dsw-success)}@keyframes dot-breathe{0%,to{opacity:1}50%{opacity:.45}}.popover{background:var(--dsw-glass-popover);box-shadow:var(--dsw-elevation-prominent);border:.5px solid var(--dsw-border-l1);border-radius:16px;padding:6px;overflow:hidden}.modal-mask{background:var(--dsw-mask);-webkit-backdrop-filter:var(--dsw-mask-blur)}.glass-modal{background:var(--dsw-glass-modal)}.md{overflow-wrap:break-word;min-width:0;font-size:var(--piweb-chat-font-size);line-height:1.65}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4{margin:1em 0 .4em;font-weight:600;line-height:1.35}.md h1{font-size:1.35em}.md h2{font-size:1.22em}.md h3{font-size:1.1em}.md ol,.md ul{margin:.5em 0;padding-left:1.5em}.md li{margin:.2em 0}.md a{color:var(--dsw-accent);text-decoration:none}.md a:hover{text-decoration:underline}.md blockquote{border-left:3px solid var(--dsw-border-l3);color:var(--dsw-label-secondary);margin:.6em 0;padding:.1em 0 .1em .9em}.md code{font-family:var(--font-mono);border-radius:5px;padding:.1em .35em;font-size:.9em}.md code,.md pre{background:var(--dsw-selector)}.md pre{border:.5px solid var(--dsw-border-l1);border-radius:12px;margin:.7em 0;padding:12px 14px;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:13px;line-height:1.55}.md table{border-collapse:collapse;width:100%;margin:.7em 0;font-size:.95em}.md td,.md th{border:.5px solid var(--dsw-border-l2);text-align:left;padding:5px 10px}.md th{background:var(--dsw-hover)}.md hr{border:none;border-top:.5px solid var(--dsw-border-l2);margin:1em 0}:root{--hl-comment:#828892;--hl-keyword:#a046c8;--hl-string:#1e825a;--hl-number:#be6414;--hl-func:#3764dc;--hl-type:#148296;--hl-attr:#8c5a1e;--hl-tag:#c8325a;--hl-regex:#c83c28;--hl-meta:#786e28;--hl-punct:var(--dsw-label-tertiary);--hl-variable:#1e64aa}body[data-ds-dark-theme],html[data-ds-dark-theme]{--hl-comment:#78808c;--hl-keyword:#c896ff;--hl-string:#96d2a5;--hl-number:#f0af6e;--hl-func:#8cb9ff;--hl-type:#64c8d2;--hl-attr:#dcc382;--hl-tag:#f08296;--hl-regex:#eb826e;--hl-meta:#c8be78;--hl-punct:var(--dsw-label-tertiary);--hl-variable:#96c8fa}.hljs{color:var(--dsw-label-secondary)}.hljs-comment,.hljs-quote{color:var(--hl-comment);font-style:italic}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-section,.hljs-selector-tag,.hljs-strong{color:var(--hl-keyword)}.hljs-strong{font-weight:600}.hljs-addition,.hljs-bullet,.hljs-link,.hljs-string,.hljs-symbol{color:var(--hl-string)}.hljs-number,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-template-variable{color:var(--hl-number)}.hljs-code,.hljs-name,.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:var(--hl-func)}.hljs-built_in,.hljs-builtin-name,.hljs-class .hljs-title,.hljs-type{color:var(--hl-type)}.hljs-attr,.hljs-attribute,.hljs-property{color:var(--hl-attr)}.hljs-deletion,.hljs-selector-attr,.hljs-tag{color:var(--hl-tag)}.hljs-char.escape_,.hljs-regexp,.hljs-subst{color:var(--hl-regex)}.hljs-formula,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:var(--hl-meta)}.hljs-operator,.hljs-params,.hljs-variable,.hljs-variable.language_{color:var(--hl-variable)}.hljs-punctuation{color:var(--hl-punct)}.hljs-emphasis{font-style:italic}.hljs-addition,.hljs-deletion{background:0 0}.msg-user-bubble{background:var(--dsw-input-major);max-width:85%;font-size:var(--piweb-chat-font-size);border-radius:18px;padding:10px 16px;line-height:1.65;display:inline-block}.msg-actions{opacity:.45;transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out);align-items:center;gap:6px;margin-top:6px;display:flex}.msg-actions:hover{opacity:1}.msg-clock{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-label-tertiary)}.msg-clock-start{padding-right:12px}:root{--pw-danger-soft:#ec13131f;--pw-shimmer-hi:#96beff}body[data-ds-dark-theme],html[data-ds-dark-theme]{--pw-danger-soft:#f25a5a29;--pw-shimmer-hi:#e1ecff}.pw-turn{padding:6px 0 2px}.pw-run,.pw-turn{flex-direction:column;gap:1px;display:flex}.pw-step{min-height:26px;animation:pw-in .16s var(--ds-ease-in-out,ease-out) both;align-items:flex-start;gap:10px;display:flex}@keyframes pw-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:none}}.pw-icon{width:16px;height:26px;color:var(--dsw-label-caption);flex:none;justify-content:center;align-items:center;display:inline-flex}.pw-step[data-tone=live] .pw-icon{color:var(--dsw-accent);animation:dot-breathe 1.4s ease-in-out infinite}.pw-step[data-tone=fail] .pw-icon{color:var(--dsw-danger)}.pw-step[data-tone=warn] .pw-icon{color:var(--dsw-warn)}.pw-step[data-kind=retry] .pw-icon{animation:pw-rotate 1.2s linear infinite}@keyframes pw-rotate{to{transform:rotate(1turn)}}.pw-pi{font-family:var(--font-mono);color:var(--dsw-accent);font-size:15px;font-weight:600;line-height:1;transform:translateY(-1px)}.pw-body{flex:1;min-width:0}.pw-line{min-height:26px;line-height:26px;font-size:var(--piweb-chat-font-m);color:var(--dsw-label-tertiary);align-items:center;gap:6px;display:flex}button.pw-line{text-align:left;cursor:pointer;width:100%;font:inherit;font-size:var(--piweb-chat-font-m);background:0 0;border:0;padding:0}button.pw-line:hover{color:var(--dsw-label-secondary)}.pw-verb{flex:none}.pw-verb[data-failed=true]{color:var(--dsw-danger)}.pw-arg{font-family:var(--font-mono);font-size:var(--piweb-chat-font-s);color:var(--dsw-label-secondary);flex:0 auto}.pw-arg,.pw-gist{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.pw-gist{flex:1}.pw-meta{font-size:var(--piweb-chat-font-t);white-space:nowrap}.pw-line .pw-chev,.pw-meta{color:var(--dsw-label-caption);flex:none}.pw-line .pw-chev{transition:transform .12s var(--ds-ease-in-out,ease)}.pw-line[aria-expanded=true] .pw-chev{transform:rotate(90deg)}.pw-summary strong{color:var(--dsw-label-secondary);font-weight:600}.pw-run[data-open]>.pw-step:not(:first-child){margin-left:4px}.pw-shimmer{background:linear-gradient(90deg,var(--dsw-accent) 0,var(--pw-shimmer-hi) 35%,var(--dsw-accent) 70%);color:#0000;background-size:220% 100%;-webkit-background-clip:text;background-clip:text;flex:none;animation:pw-shimmer 2.2s linear infinite}@keyframes pw-shimmer{0%{background-position:120% 0}to{background-position:-100% 0}}.pw-tail{font-size:var(--piweb-chat-font-s);color:var(--dsw-label-caption);word-break:break-word;-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:-4px 0 5px;line-height:1.5;display:-webkit-box;overflow:hidden}.pw-result{font-family:var(--font-mono);font-size:var(--piweb-chat-font-t);color:var(--dsw-label-tertiary);align-items:flex-start;gap:6px;margin:-4px 0 5px;line-height:1.55;display:flex}.pw-result[role=button]{cursor:pointer}.pw-rail{text-align:center;width:14px;color:var(--dsw-label-caption);-webkit-user-select:none;user-select:none;flex:none}.pw-lines{white-space:pre-wrap;word-break:break-word;flex:1;min-width:0}.pw-lines>div{text-overflow:ellipsis;white-space:pre;overflow:hidden}.pw-lines .pw-head{color:var(--dsw-label-tertiary)}.pw-lines .pw-more{color:var(--dsw-label-caption)}.pw-result[role=button]:hover .pw-more{text-decoration:underline}.pw-result[data-failed=true],.pw-result[data-failed=true] .pw-head,.pw-result[data-failed=true] .pw-rail{color:var(--dsw-danger)}.pw-diff{max-height:420px;margin:-2px 0 6px 20px;padding:6px 8px}.pw-diff,.pw-output{background:var(--dsw-markdown-code-block,var(--dsw-hover));border-radius:10px;overflow:auto}.pw-output{max-height:360px;font-family:var(--font-mono);font-size:var(--piweb-chat-font-t);color:var(--dsw-label-secondary);white-space:pre-wrap;word-break:break-word;margin:0 0 6px 20px;padding:8px 12px;line-height:1.55}.pw-output.pw-think-full{color:var(--dsw-label-tertiary);font-family:inherit}.pw-output .cmdline{color:var(--dsw-label-tertiary);border-bottom:.5px solid var(--dsw-border-l2);white-space:pre-wrap;word-break:break-all;margin-bottom:6px;padding-bottom:6px}.pw-output .cmdline:before{content:"$ ";color:var(--dsw-label-caption)}button.pw-more{font:inherit;font-family:var(--font-mono);font-size:var(--piweb-chat-font-t);color:var(--dsw-label-caption);cursor:pointer;text-align:left;background:0 0;border:0;margin-top:4px;padding:0;display:block}button.pw-more:hover{color:var(--dsw-label-secondary);text-decoration:underline}.pw-narr{font-size:var(--piweb-chat-font-l);color:var(--dsw-label-primary);margin:7px 0 3px;line-height:1.65}.pw-turn>.pw-narr:first-child{margin-top:1px}.pw-narr[data-error=true]{color:var(--dsw-danger)}.pw-narr .md p{margin:0}.pw-narr .md p+p{margin-top:.5em}.pw-narr .md pre{margin:.5em 0}.pw-warn-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-warn);flex:1;overflow:hidden}.pw-step[data-kind=abort] .pw-line,.pw-step[data-kind=context] .pw-line{color:var(--dsw-label-caption);font-size:var(--piweb-chat-font-s)}body[data-pebrel-theme],html[data-pebrel-theme]{--dsw-bg-base:#fff;--dsw-bg-elevated:#fff;--dsw-input-major:#fff;--dsw-bubble:#64748b0d;--dsw-hover:#64748b21;--dsw-active:#64748b33;--dsw-label-primary:#334155;--dsw-label-secondary:#64748b;--dsw-label-tertiary:#94a3b8;--dsw-label-caption:#a3adbb;--dsw-border-l1:#33415514;--dsw-border-l2:#3341551f;--dsw-border-l3:#33415529;--dsw-border-l4:#3341553d;--dsw-warn:#d97706;--dsw-success:#548c9e;--dsw-danger:#b23a48;--dsw-selector:#64748b14;--dsw-layer-2:#fff;--dsw-layer-3:#f5f5f6;--dsw-btn-elevated:#fff;--dsw-btn-floating-hover:#64748b21;--dsw-module-platform:#f5f5f6;--dsw-border-strong:#64748b;--dsw-glass-popover:#fff;--dsw-glass-input:#fff;--dsw-glass-modal:#fff;--dsw-mask:#0f172a38;--dsw-elevation-stroke:var(--dsw-border-l2)}body[data-pebrel-theme=pebrel],body[data-pebrel-theme=silver-steel],html[data-pebrel-theme=pebrel],html[data-pebrel-theme=silver-steel]{--dsw-sidebar-fill:#f3f4f6;--dsw-accent:#495057;--dsw-accent-soft:#4950571f}body[data-pebrel-theme=limestone-coal],html[data-pebrel-theme=limestone-coal]{--dsw-sidebar-fill:#f0efeb;--dsw-layer-3:#f0efeb;--dsw-module-platform:#f0efeb;--dsw-accent:#58554c;--dsw-accent-soft:#58554c1f}body[data-pebrel-theme=linen-moss],html[data-pebrel-theme=linen-moss]{--dsw-sidebar-fill:#f2f2ec;--dsw-layer-3:#f2f2ec;--dsw-module-platform:#f2f2ec;--dsw-accent:#5f635f;--dsw-accent-soft:#5f635f1f}body[data-pebrel-theme=nord-paper],html[data-pebrel-theme=nord-paper]{--dsw-bg-base:#fcfbf9;--dsw-bg-elevated:#f5f4f0;--dsw-sidebar-fill:#f5f4f0;--dsw-input-major:#fcfbf9;--dsw-bubble:#8c8a8014;--dsw-hover:#ebeae5;--dsw-active:#2b5a382e;--dsw-label-primary:#1a1a1a;--dsw-label-secondary:#474646;--dsw-label-tertiary:#8c8a80;--dsw-label-caption:#a8a49a;--dsw-border-l1:#eceae4;--dsw-border-l2:#e0dfd5;--dsw-border-l3:#d5d3c9;--dsw-border-l4:#c1beb5;--dsw-accent:#2b5a38;--dsw-accent-soft:#2b5a382e;--dsw-warn:#a85a20;--dsw-success:#2b5a38;--dsw-danger:#a33a3a;--dsw-selector:#ebeae5;--dsw-layer-2:#fcfbf9;--dsw-layer-3:#f5f4f0;--dsw-btn-elevated:#fcfbf9;--dsw-btn-floating-hover:#ebeae5;--dsw-module-platform:#f0efe9;--dsw-border-strong:#8c8a80;--dsw-glass-popover:#fcfbf9;--dsw-glass-input:#fcfbf9;--dsw-glass-modal:#f5f4f0;--dsw-mask:#1a1a1a30}body[data-pebrel-theme][data-ds-dark-theme],html[data-pebrel-theme][data-ds-dark-theme]{--dsw-label-primary:#e2e8f0;--dsw-label-secondary:#b9c3cf;--dsw-label-tertiary:#94a3b8;--dsw-label-caption:#64748b;--dsw-bubble:#ffffff0d;--dsw-hover:#cbd5e124;--dsw-active:#cbd5e136;--dsw-border-l1:#cbd5e114;--dsw-border-l2:#cbd5e11f;--dsw-border-l3:#cbd5e129;--dsw-border-l4:#cbd5e13d;--dsw-warn:#f59e0b;--dsw-success:#7db2c2;--dsw-danger:#c44a58;--dsw-selector:#cbd5e114;--dsw-border-strong:#94a3b8;--dsw-mask:#02061780;--dsw-elevation-stroke:var(--dsw-border-l2)}body[data-pebrel-theme=silver-steel][data-ds-dark-theme],html[data-pebrel-theme=silver-steel][data-ds-dark-theme]{--dsw-bg-base:#16181e;--dsw-bg-elevated:#1e2129;--dsw-sidebar-fill:#16181e;--dsw-input-major:#1a1c24;--dsw-layer-2:#1e2129;--dsw-layer-3:#343a48;--dsw-btn-elevated:#1e2129;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#1e2129;--dsw-glass-popover:#1e2129;--dsw-glass-input:#1a1c24;--dsw-glass-modal:#1e2129;--dsw-accent:#94a3b8;--dsw-accent-soft:#94a3b826}body[data-pebrel-theme=limestone-coal][data-ds-dark-theme],html[data-pebrel-theme=limestone-coal][data-ds-dark-theme]{--dsw-bg-base:#161616;--dsw-bg-elevated:#1e1e1e;--dsw-sidebar-fill:#161616;--dsw-input-major:#171717;--dsw-layer-2:#1e1e1e;--dsw-layer-3:#383836;--dsw-btn-elevated:#1e1e1e;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#1e1e1e;--dsw-glass-popover:#1e1e1e;--dsw-glass-input:#171717;--dsw-glass-modal:#1e1e1e;--dsw-accent:#d4d4d4;--dsw-accent-soft:#d4d4d424;--dsw-warn:#ceb27e}body[data-pebrel-theme=linen-moss][data-ds-dark-theme],html[data-pebrel-theme=linen-moss][data-ds-dark-theme]{--dsw-bg-base:#191c19;--dsw-bg-elevated:#212521;--dsw-sidebar-fill:#191c19;--dsw-input-major:#1e211e;--dsw-layer-2:#212521;--dsw-layer-3:#363d36;--dsw-btn-elevated:#212521;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#212521;--dsw-glass-popover:#212521;--dsw-glass-input:#1e211e;--dsw-glass-modal:#212521;--dsw-accent:#a3b3a3;--dsw-accent-soft:#a3b3a326}body[data-pebrel-theme=nord-paper][data-ds-dark-theme],html[data-pebrel-theme=nord-paper][data-ds-dark-theme]{--dsw-bg-base:#2e3440;--dsw-bg-elevated:#3b4252;--dsw-sidebar-fill:#2e3440;--dsw-input-major:#3b4252;--dsw-bubble:#3b4252;--dsw-hover:#ffffff0f;--dsw-active:#88c0d02e;--dsw-label-primary:#e5e9f0;--dsw-label-secondary:#c0c7d3;--dsw-label-tertiary:#7b8294;--dsw-label-caption:#6f7688;--dsw-border-l1:#ffffff0d;--dsw-border-l2:#ffffff0f;--dsw-border-l3:#434c5e;--dsw-border-l4:#4c566a;--dsw-accent:#88c0d0;--dsw-accent-soft:#88c0d02e;--dsw-warn:#ebcb8b;--dsw-success:#a3be8c;--dsw-danger:#bf616a;--dsw-selector:#ffffff0f;--dsw-layer-2:#3b4252;--dsw-layer-3:#434c5e;--dsw-btn-elevated:#3b4252;--dsw-btn-floating-hover:#434c5e;--dsw-module-platform:#3b4252;--dsw-border-strong:#7b8294;--dsw-glass-popover:#3b4252;--dsw-glass-input:#3b4252;--dsw-glass-modal:#3b4252;--dsw-mask:#0206175c}body[data-pebrel-theme=pebrel][data-ds-dark-theme],html[data-pebrel-theme=pebrel][data-ds-dark-theme]{--dsw-bg-base:#222630;--dsw-bg-elevated:#2b303b;--dsw-sidebar-fill:#222630;--dsw-input-major:#0f111a;--dsw-layer-2:#2b303b;--dsw-layer-3:#414858;--dsw-btn-elevated:#2b303b;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#2b303b;--dsw-glass-popover:#2b303b;--dsw-glass-input:#0f111a;--dsw-glass-modal:#2b303b;--dsw-accent:#52a8ff;--dsw-accent-soft:#52a8ff26}@keyframes piweb-spin-kf{to{transform:rotate(1turn)}}.piweb-spin{animation:piweb-spin-kf .8s linear infinite}@keyframes piweb-pulse-kf{0%,to{opacity:1}50%{opacity:.3}}.piweb-pulse{animation:piweb-pulse-kf 1.1s ease-in-out infinite}@keyframes stream-cursor-kf{0%,49%{opacity:1}50%,to{opacity:0}}.stream-cursor{vertical-align:-.15em;background:var(--dsw-accent);width:2px;height:1em;margin-left:2px;animation:stream-cursor-kf 1s step-end infinite;display:inline-block}.pw-seg{background:var(--dsw-hover);border-radius:8px;flex:none;gap:1px;padding:2px;display:inline-flex}.pw-seg>button{height:20px;color:var(--dsw-label-tertiary);white-space:nowrap;transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:6px;padding:0 8px;font-size:11.5px;line-height:20px}.pw-seg>button[data-active=true]{background:var(--dsw-bg-elevated);color:var(--dsw-label-primary);box-shadow:0 0 0 .5px var(--dsw-border-l2),0 1px 2px #0000000f}.pw-chip{border:.5px solid var(--dsw-border-l3);height:20px;color:var(--dsw-label-tertiary);white-space:nowrap;transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out),border-color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:19px}.pw-chip:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.pw-chip[data-active=true]{background:var(--dsw-accent-soft);color:var(--dsw-accent);border-color:#0000}.pw-chip:disabled{opacity:.4;pointer-events:none}.pw-project{background:var(--dsw-sidebar-fill);border-right:.5px solid var(--dsw-border-l2)}.pw-project input,.pw-viewer input{color:var(--dsw-label-primary);outline:none}.pw-tree{-webkit-user-select:none;user-select:none;font-size:12.5px;line-height:22px}.pw-tree-row{cursor:pointer;white-space:nowrap;height:22px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:6px;align-items:center;gap:4px;padding-right:8px;display:flex;position:absolute;left:0;right:0}.pw-tree-row:hover{background:var(--dsw-hover)}.pw-tree-row[data-focused],.pw-tree-row[data-selected]{background:var(--dsw-active)}.pw-tree:focus-visible .pw-tree-row[data-focused]{box-shadow:inset 0 0 0 1px var(--dsw-accent)}.pw-tree-chevron,.pw-tree-row[data-lazy]>.pw-tree-name{color:var(--dsw-label-caption)}.pw-tree-chevron{width:12px;flex:none;justify-content:center;align-items:center;display:inline-flex}.pw-tree-icon{object-fit:contain;flex:none;width:16px;height:16px}.pw-tree-row>.pw-tree-icon{filter:saturate(.78);transition:filter var(--ds-duration-fast) var(--ds-ease-in-out),opacity var(--ds-duration-fast) var(--ds-ease-in-out)}.pw-tree-row>.pw-tree-icon[data-kind=dir]{opacity:.88;transform:translateY(-.25px)scale(.87)}.pw-tree-row>.pw-tree-icon[data-kind=file]{opacity:.93;transform:scale(.91)}.pw-tree-row:hover>.pw-tree-icon{filter:saturate(.95);opacity:1}.pw-viewer .pw-tree-icon{filter:saturate(.82);opacity:.92;transform:scale(.9)}.pw-tree-name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.pw-tree-from{color:var(--dsw-label-caption);margin-left:6px;font-size:11px}.pw-tree-stat{font-variant-numeric:tabular-nums;color:var(--dsw-label-caption);flex:none;gap:4px;font-size:10.5px;display:inline-flex}.pw-tree-badge{text-align:center;font-variant-numeric:tabular-nums;border:.5px solid;border-radius:4px;flex:none;min-width:14px;height:14px;padding:0 3px;font-size:10px;font-weight:600;line-height:13px}.pw-tree-actions{flex:none;align-items:center;gap:1px;display:none}.pw-tree-row:hover .pw-tree-actions{display:inline-flex}.pw-tree-row:hover .pw-tree-stat{display:none}.pw-tree-pulse{background:var(--dsw-accent);border-radius:999px;flex:none;width:6px;height:6px;animation:dot-breathe 1s ease-in-out infinite}.pw-tree-row[data-pending]>.pw-tree-name{color:var(--dsw-accent)}@keyframes pw-tree-in{0%{opacity:0;transform:translate(-6px)}to{opacity:1;transform:none}}@keyframes pw-tree-glow-add{0%{background:#22c55e47}to{background:0 0}}@keyframes pw-tree-glow-mod{0%{background:#f59e0b42}to{background:0 0}}@keyframes pw-tree-glow-del{0%{background:#ec13133d}to{background:0 0}}.pw-tree-row[data-fresh]{animation:pw-tree-in .25s var(--ds-ease-in-out) both,pw-tree-glow-mod 2.2s ease-out both}.pw-tree-row[data-fresh=A]{animation-name:pw-tree-in,pw-tree-glow-add}.pw-tree-row[data-fresh=D]{animation-name:pw-tree-in,pw-tree-glow-del}.pw-timeline{flex:none}.pw-viewer{width:80vw;height:80vh;box-shadow:var(--dsw-elevation-prominent);animation:pw-in .16s var(--ds-ease-in-out) both;transition:width var(--ds-duration-normal) var(--ds-ease-in-out),height var(--ds-duration-normal) var(--ds-ease-in-out),border-radius var(--ds-duration-normal) var(--ds-ease-in-out);border-radius:20px}.pw-viewer[data-fullscreen]{border-radius:0;width:100vw;height:100vh}.pw-viewer-tabs{flex:none;height:40px;padding:0 8px 0 10px}.pw-viewer-tabs,.pw-viewer-tabstrip{align-items:center;gap:2px;display:flex}.pw-viewer-tabstrip{scrollbar-width:none;min-width:0;overflow-x:auto}.pw-viewer-tabstrip::-webkit-scrollbar{display:none}.pw-viewer-tab{max-width:200px;height:28px;color:var(--dsw-label-tertiary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:8px;flex:none;align-items:center;gap:6px;padding:0 8px;font-size:12.5px;display:inline-flex}.pw-viewer-tab:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.pw-viewer-tab[data-active=true]{background:var(--dsw-active);color:var(--dsw-label-primary)}.pw-viewer-tab-main{min-width:0;color:inherit;align-items:center;gap:6px;display:inline-flex}.pw-viewer-tab-close{width:16px;height:16px;color:var(--dsw-label-caption);opacity:0;transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:4px;justify-content:center;align-items:center;margin-right:-4px;display:inline-flex}.pw-viewer-tab-close:focus-visible,.pw-viewer-tab:hover .pw-viewer-tab-close,.pw-viewer-tab[data-active=true] .pw-viewer-tab-close{opacity:1}.pw-viewer-tab-close:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.pw-viewer-toolbar{flex:none;gap:6px;height:36px;padding:0 12px}.pw-crumb,.pw-viewer-toolbar{align-items:center;display:flex}.pw-crumb{color:var(--dsw-label-tertiary);white-space:nowrap;flex-wrap:nowrap;font-size:12.5px;overflow:hidden}.pw-crumb button:hover{color:var(--dsw-accent)}.pw-crumb-sep{color:var(--dsw-label-caption);margin:0 4px}.pw-viewer-notice{color:var(--dsw-label-secondary);background:var(--dsw-hover);border-bottom:.5px solid var(--dsw-border-l1);flex:none;padding:4px 14px;font-size:11.5px}.pw-viewer-notice[data-kind=deleted]{color:var(--dsw-danger);background:#ec131314}.pw-viewer-notice[data-kind=warn]{color:var(--dsw-warn)}.pw-viewer .md pre{overflow-x:auto}.pw-quick{z-index:5;background:#00000014;justify-content:center;align-items:flex-start;padding-top:56px;display:flex;position:absolute;inset:0}.pw-quick-box{border-radius:14px;width:min(560px,90%);padding:4px}.pw-quick-list{max-height:50vh;padding:2px;overflow-y:auto}.pw-quick-item{width:100%;height:30px;color:var(--dsw-label-primary);text-align:left;border-radius:8px;align-items:center;gap:8px;padding:0 8px;font-size:12.5px;display:flex}.pw-quick-item[data-active=true]{background:var(--dsw-active)}.pw-fdiff{font-family:var(--font-mono);font-size:12px;line-height:20px}.pw-fdiff-row{white-space:pre;height:20px;color:var(--dsw-label-secondary);align-items:stretch;display:flex;position:absolute;left:0;right:0}.pw-fdiff-row[data-kind=add]{box-shadow:inset 2px 0 0 var(--dsw-success);background:#22c55e24}.pw-fdiff-row[data-kind=del]{box-shadow:inset 2px 0 0 var(--dsw-danger);background:#ec13131f}.pw-fdiff-gutter{color:var(--dsw-label-caption);-webkit-user-select:none;user-select:none;font-variant-numeric:tabular-nums;background:inherit;flex:none;gap:1ch;padding:0 8px 0 10px;display:inline-flex;position:sticky;left:0}.pw-fdiff-gutter>span{display:inline-block}.pw-fdiff-sign{-webkit-user-select:none;user-select:none;width:1.5ch;color:var(--dsw-label-caption);flex:none}.pw-fdiff-row[data-kind=add] .pw-fdiff-sign{color:var(--dsw-success)}.pw-fdiff-row[data-kind=del] .pw-fdiff-sign{color:var(--dsw-danger)}.pw-fdiff-text{background:0 0;flex:1;padding-right:24px}.pw-fdiff-fold{height:20px;color:var(--dsw-label-caption);background:var(--dsw-hover);text-align:center;letter-spacing:.02em;font-size:11px;position:absolute;left:0;right:0}.pw-fdiff-fold:hover{color:var(--dsw-accent)}.pw-fdiff-ticks{pointer-events:none;width:8px;position:absolute;top:0;bottom:0;right:2px}.pw-fdiff-tick{pointer-events:auto;cursor:pointer;background:var(--dsw-success);opacity:.6;border-radius:1px;width:8px;height:3px;position:absolute;right:0}.pw-fdiff-tick[data-kind=del]{background:var(--dsw-danger)}.pw-fdiff-tick[data-current]{opacity:1;box-shadow:0 0 0 1px var(--dsw-label-tertiary)}@media (max-width:840px){.pw-viewer{border-radius:0;width:100vw;height:100vh}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(1turn)}}
Binary file
Binary file
Binary file
package/assets/import.png DELETED
Binary file
package/assets/main.png DELETED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,146 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
-
5
- export const NIUBASH_VERSION = "1.1.4";
6
- export const NIUBASH_TAG = `v${NIUBASH_VERSION}`;
7
- export const NIUBASH_DISTRIBUTION_VERSION = `${NIUBASH_VERSION}-piweb.0`;
8
- export const NIUBASH_REPOSITORY = "https://github.com/unixwin/niubash";
9
-
10
- export const MANAGED_NIUBASH_ASSETS = Object.freeze({
11
- x64: Object.freeze({
12
- arch: "x64",
13
- machine: 0x8664,
14
- packageName: "@rexvane/piweb-niubash-win32-x64",
15
- directory: "piweb-niubash-win32-x64",
16
- asset: "niubash-v1.1.4-win-x64.zip",
17
- sha256: "9f543634288a316f7ed8e5a76ff794eca402cb21e74b8f8a551daffafde162f5",
18
- }),
19
- arm64: Object.freeze({
20
- arch: "arm64",
21
- machine: 0xaa64,
22
- packageName: "@rexvane/piweb-niubash-win32-arm64",
23
- directory: "piweb-niubash-win32-arm64",
24
- asset: "niubash-v1.1.4-win-arm64.zip",
25
- sha256: "77e98ba8d7f6be70c35388bcc28bdbc883c06bcaafffbe186a88788ad32ccf00",
26
- }),
27
- });
28
-
29
- export const NIUBASH_LICENSES = Object.freeze([
30
- Object.freeze({
31
- filename: "NIUBASH.txt",
32
- url: "https://raw.githubusercontent.com/unixwin/niubash/v1.1.4/LICENSE",
33
- sha256: "1290aae99e71239ee3154e7847830e49e7a36c758fa6d659d73fa752dc9f81c8",
34
- }),
35
- Object.freeze({
36
- filename: "RUBASH.txt",
37
- url: "https://raw.githubusercontent.com/unixwin/rubash/8b81c7501646/LICENSE",
38
- sha256: "e426a75a20633f3088da22aef61a5a44d06f1f884c600f6011d00702c3989079",
39
- }),
40
- Object.freeze({
41
- filename: "WINUXCMD.txt",
42
- url: "https://raw.githubusercontent.com/unixwin/WinuxCmd/v1.0.8/LICENSE",
43
- sha256: "8ffc4ed56f5e6ab8183a7ece2e3aa6ea4800b0a4e8170d11bd60856f2dd760f3",
44
- }),
45
- ]);
46
-
47
- export function niubashAssetUrl(asset) {
48
- return `${NIUBASH_REPOSITORY}/releases/download/${NIUBASH_TAG}/${asset.asset}`;
49
- }
50
-
51
- export function sha256(data) {
52
- return crypto.createHash("sha256").update(data).digest("hex");
53
- }
54
-
55
- export function sha256File(filename) {
56
- return sha256(fs.readFileSync(filename));
57
- }
58
-
59
- export function listFiles(root) {
60
- const files = [];
61
- const visit = (directory, prefix = "") => {
62
- for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
63
- const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
64
- const absolute = path.join(directory, entry.name);
65
- if (entry.isDirectory()) visit(absolute, relative);
66
- else if (entry.isFile()) files.push(relative);
67
- else throw new Error(`managed niubash runtime contains an unsupported entry: ${relative}`);
68
- }
69
- };
70
- visit(root);
71
- return files;
72
- }
73
-
74
- export function readPeMachine(filename) {
75
- const data = fs.readFileSync(filename);
76
- if (data.length < 64 || data[0] !== 0x4d || data[1] !== 0x5a) throw new Error(`${filename} is not a PE executable`);
77
- const peOffset = data.readUInt32LE(0x3c);
78
- if (peOffset + 6 > data.length || data.toString("ascii", peOffset, peOffset + 4) !== "PE\0\0") {
79
- throw new Error(`${filename} has an invalid PE header`);
80
- }
81
- return data.readUInt16LE(peOffset + 4);
82
- }
83
-
84
- function readJson(filename) {
85
- return JSON.parse(fs.readFileSync(filename, "utf8"));
86
- }
87
-
88
- function assertEqual(actual, expected, label) {
89
- if (actual !== expected) throw new Error(`${label}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
90
- }
91
-
92
- /** Validate a prepared source package or an unpacked npm tarball. */
93
- export function validateManagedNiubashPackage(packageRoot, asset, { allowExtraRuntimeFiles = true } = {}) {
94
- const packageInfo = readJson(path.join(packageRoot, "package.json"));
95
- assertEqual(packageInfo.name, asset.packageName, "managed niubash package name");
96
- assertEqual(packageInfo.version, NIUBASH_DISTRIBUTION_VERSION, "managed niubash package version");
97
- if (!Array.isArray(packageInfo.os) || packageInfo.os.length !== 1 || packageInfo.os[0] !== "win32") {
98
- throw new Error(`${asset.packageName} must be restricted to os=win32`);
99
- }
100
- if (!Array.isArray(packageInfo.cpu) || packageInfo.cpu.length !== 1 || packageInfo.cpu[0] !== asset.arch) {
101
- throw new Error(`${asset.packageName} must be restricted to cpu=${asset.arch}`);
102
- }
103
-
104
- const runtime = path.join(packageRoot, "runtime");
105
- if (!fs.existsSync(path.join(runtime, "niu.exe"))) {
106
- throw new Error(`${asset.packageName} has no prepared runtime; run \`node scripts/prepare-niubash-packages.mjs\` before packing/publishing`);
107
- }
108
- const manifest = readJson(path.join(packageRoot, "runtime-manifest.json"));
109
- assertEqual(manifest.schema, 1, "managed niubash manifest schema");
110
- assertEqual(manifest.source?.repository, NIUBASH_REPOSITORY, "managed niubash source repository");
111
- assertEqual(manifest.source?.tag, NIUBASH_TAG, "managed niubash source tag");
112
- assertEqual(manifest.source?.asset, asset.asset, "managed niubash source asset");
113
- assertEqual(manifest.source?.sha256, asset.sha256, "managed niubash source SHA-256");
114
- if (!manifest.files || typeof manifest.files !== "object" || Array.isArray(manifest.files)) {
115
- throw new Error(`${asset.packageName} runtime manifest has no file map`);
116
- }
117
- const expectedFiles = Object.keys(manifest.files);
118
- if (expectedFiles.length < 300) throw new Error(`${asset.packageName} runtime is incomplete (${expectedFiles.length} files)`);
119
- for (const relative of expectedFiles) {
120
- if (!relative || relative.includes("\\") || path.posix.isAbsolute(relative) || relative.split("/").includes("..")) {
121
- throw new Error(`${asset.packageName} runtime manifest contains an unsafe path: ${relative}`);
122
- }
123
- const filename = path.join(runtime, ...relative.split("/"));
124
- const stat = fs.statSync(filename);
125
- if (!stat.isFile()) throw new Error(`${asset.packageName} runtime entry is not a file: ${relative}`);
126
- assertEqual(sha256File(filename), manifest.files[relative], `${asset.packageName} hash for ${relative}`);
127
- }
128
- const actualFiles = listFiles(runtime);
129
- const extra = actualFiles.filter((file) => !(file in manifest.files));
130
- if (!allowExtraRuntimeFiles && extra.length > 0) {
131
- throw new Error(
132
- `${asset.packageName} runtime has ${extra.length} files not in the signed manifest (niubash self-activates command links on first run). Re-run \`node scripts/prepare-niubash-packages.mjs\` before packing. First extra: ${extra[0]}`,
133
- );
134
- }
135
-
136
- for (const required of ["niu.exe", "winuxcmd/usr/bin/winuxcmd.exe", "bundles/oh-my-niu/bundle.toml"]) {
137
- if (!expectedFiles.includes(required)) throw new Error(`${asset.packageName} is missing ${required}`);
138
- }
139
- for (const executable of ["niu.exe", "winuxcmd/usr/bin/winuxcmd.exe"]) {
140
- assertEqual(readPeMachine(path.join(runtime, ...executable.split("/"))), asset.machine, `${asset.packageName} PE machine for ${executable}`);
141
- }
142
- for (const license of NIUBASH_LICENSES) {
143
- assertEqual(sha256File(path.join(packageRoot, "licenses", license.filename)), license.sha256, `${asset.packageName} license ${license.filename}`);
144
- }
145
- return { files: expectedFiles.length, runtime };
146
- }
@@ -1,182 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Build the Windows-only npm packages from pinned official niubash portable
4
- * archives. Nothing is downloaded on macOS/Linux user installs: this runs only
5
- * in PiWeb's release workflow (or explicitly by a maintainer).
6
- */
7
- import fs from "node:fs";
8
- import os from "node:os";
9
- import path from "node:path";
10
- import { spawnSync } from "node:child_process";
11
- import { fileURLToPath } from "node:url";
12
- import {
13
- MANAGED_NIUBASH_ASSETS,
14
- NIUBASH_LICENSES,
15
- NIUBASH_REPOSITORY,
16
- NIUBASH_TAG,
17
- listFiles,
18
- niubashAssetUrl,
19
- sha256,
20
- sha256File,
21
- validateManagedNiubashPackage,
22
- } from "./managed-niubash-assets.mjs";
23
- import { isMainModule } from "./entrypoint.mjs";
24
-
25
- const DEFAULT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
26
-
27
- async function download(url, expectedSha256) {
28
- let lastError;
29
- for (let attempt = 1; attempt <= 3; attempt += 1) {
30
- try {
31
- const response = await fetch(url, {
32
- headers: { "user-agent": "PiWeb managed-niubash packager" },
33
- signal: AbortSignal.timeout(120_000),
34
- });
35
- if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
36
- const data = Buffer.from(await response.arrayBuffer());
37
- const actual = sha256(data);
38
- if (actual !== expectedSha256) throw new Error(`SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
39
- return data;
40
- } catch (error) {
41
- lastError = error;
42
- if (attempt < 3) await new Promise((resolve) => setTimeout(resolve, attempt * 1000));
43
- }
44
- }
45
- throw new Error(`failed to download ${url}: ${lastError instanceof Error ? lastError.message : lastError}`);
46
- }
47
-
48
- function extractZip(archive, destination) {
49
- fs.mkdirSync(destination, { recursive: true });
50
- let result;
51
- if (process.platform === "win32") {
52
- result = spawnSync("powershell.exe", [
53
- "-NoProfile",
54
- "-NonInteractive",
55
- "-Command",
56
- "Expand-Archive -LiteralPath $env:PIWEB_NIUBASH_ARCHIVE -DestinationPath $env:PIWEB_NIUBASH_DESTINATION -Force",
57
- ], {
58
- encoding: "utf8",
59
- env: { ...process.env, PIWEB_NIUBASH_ARCHIVE: archive, PIWEB_NIUBASH_DESTINATION: destination },
60
- });
61
- } else {
62
- result = spawnSync("unzip", ["-q", "-o", archive, "-d", destination], { encoding: "utf8" });
63
- if (result.error?.code === "ENOENT") {
64
- result = spawnSync("python3", ["-m", "zipfile", "-e", archive, destination], { encoding: "utf8" });
65
- }
66
- }
67
- if (result.error) throw new Error(`cannot extract ${path.basename(archive)}: ${result.error.message}`);
68
- if (result.status !== 0) throw new Error(`cannot extract ${path.basename(archive)}: ${result.stderr || result.stdout}`);
69
- }
70
-
71
- async function replaceDirectory(source, destination) {
72
- fs.rmSync(destination, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
73
- for (let attempt = 1; ; attempt += 1) {
74
- try {
75
- fs.renameSync(source, destination);
76
- return;
77
- } catch (error) {
78
- // Windows:刚写入几百个文件时,Defender/索引器会短暂持有句柄,目录 rename 报 EPERM。
79
- // 先退避重试,仍不行就退化成复制(读通常是被允许的)。
80
- if (!["EPERM", "EBUSY", "EACCES"].includes(error?.code)) throw error;
81
- if (attempt >= 5) {
82
- fs.cpSync(source, destination, { recursive: true });
83
- fs.rmSync(source, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
84
- return;
85
- }
86
- await new Promise((resolve) => setTimeout(resolve, attempt * 250));
87
- }
88
- }
89
- }
90
-
91
- function extractedPortableRoot(destination) {
92
- const entries = fs.readdirSync(destination, { withFileTypes: true }).filter((entry) => entry.name !== "__MACOSX");
93
- if (entries.length !== 1 || !entries[0].isDirectory()) throw new Error("niubash archive must contain exactly one top-level directory");
94
- return path.join(destination, entries[0].name);
95
- }
96
-
97
- async function prepareLicenses(root, packageRoot) {
98
- const destination = path.join(packageRoot, "licenses");
99
- const staging = path.join(packageRoot, `.licenses-${process.pid}`);
100
- fs.rmSync(staging, { recursive: true, force: true });
101
- fs.mkdirSync(staging, { recursive: true });
102
- try {
103
- for (const license of NIUBASH_LICENSES) {
104
- const source = path.join(root, "third-party", "managed-niubash", "licenses", license.filename);
105
- if (sha256File(source) !== license.sha256) throw new Error(`license source failed integrity check: ${license.filename}`);
106
- fs.copyFileSync(source, path.join(staging, license.filename));
107
- }
108
- await replaceDirectory(staging, destination);
109
- } finally {
110
- fs.rmSync(staging, { recursive: true, force: true });
111
- }
112
- }
113
-
114
- async function prepareAsset(root, asset, log) {
115
- const packageRoot = path.join(root, "platform-packages", asset.directory);
116
- if (!fs.existsSync(path.join(packageRoot, "package.json"))) throw new Error(`missing platform package skeleton: ${packageRoot}`);
117
- const temporary = fs.mkdtempSync(path.join(os.tmpdir(), `piweb-niubash-${asset.arch}-`));
118
- const archive = path.join(temporary, asset.asset);
119
- const extracted = path.join(temporary, "extracted");
120
- const staging = path.join(packageRoot, `.runtime-${process.pid}`);
121
- try {
122
- log(`[piweb] Downloading ${asset.asset} ...`);
123
- fs.writeFileSync(archive, await download(niubashAssetUrl(asset), asset.sha256));
124
- extractZip(archive, extracted);
125
- const source = extractedPortableRoot(extracted);
126
- for (const required of ["niu.exe", "winuxcmd", "bundles"]) {
127
- if (!fs.existsSync(path.join(source, required))) throw new Error(`${asset.asset} is missing ${required}`);
128
- }
129
-
130
- fs.rmSync(staging, { recursive: true, force: true });
131
- fs.cpSync(source, staging, { recursive: true });
132
- await replaceDirectory(staging, path.join(packageRoot, "runtime"));
133
- const runtime = path.join(packageRoot, "runtime");
134
-
135
- const files = {};
136
- for (const relative of listFiles(runtime)) files[relative] = sha256File(path.join(runtime, ...relative.split("/")));
137
- fs.writeFileSync(path.join(packageRoot, "runtime-manifest.json"), `${JSON.stringify({
138
- schema: 1,
139
- source: { repository: NIUBASH_REPOSITORY, tag: NIUBASH_TAG, asset: asset.asset, sha256: asset.sha256 },
140
- files,
141
- }, null, 2)}\n`);
142
- await prepareLicenses(root, packageRoot);
143
- const checked = validateManagedNiubashPackage(packageRoot, asset, { allowExtraRuntimeFiles: false });
144
- log(`[piweb] Prepared ${asset.packageName}: ${checked.files} runtime files.`);
145
- return checked;
146
- } finally {
147
- fs.rmSync(staging, { recursive: true, force: true });
148
- fs.rmSync(temporary, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
149
- }
150
- }
151
-
152
- function selectAssets(args) {
153
- if (args.length === 0) return Object.values(MANAGED_NIUBASH_ASSETS);
154
- if (args.length === 1 && args[0] === "--current") {
155
- if (process.platform !== "win32" || !(process.arch in MANAGED_NIUBASH_ASSETS)) {
156
- throw new Error(`--current requires Windows x64/ARM64 (got ${process.platform}/${process.arch})`);
157
- }
158
- return [MANAGED_NIUBASH_ASSETS[process.arch]];
159
- }
160
- if (args.length === 1 && args[0].startsWith("--arch=")) {
161
- const arch = args[0].slice("--arch=".length);
162
- const asset = MANAGED_NIUBASH_ASSETS[arch];
163
- if (!asset) throw new Error(`unsupported niubash architecture: ${arch}`);
164
- return [asset];
165
- }
166
- throw new Error("Usage: node scripts/prepare-niubash-packages.mjs [--current|--arch=x64|--arch=arm64]");
167
- }
168
-
169
- export async function prepareManagedNiubashPackages(root = DEFAULT_ROOT, { args = [], log = console.log } = {}) {
170
- const results = [];
171
- for (const asset of selectAssets(args)) results.push(await prepareAsset(root, asset, log));
172
- return results;
173
- }
174
-
175
- if (isMainModule(import.meta.url)) {
176
- try {
177
- await prepareManagedNiubashPackages(DEFAULT_ROOT, { args: process.argv.slice(2) });
178
- } catch (error) {
179
- console.error(`[piweb] ${error instanceof Error ? error.message : error}`);
180
- process.exitCode = 1;
181
- }
182
- }
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env node
2
- /** Native smoke test. Run on both windows-latest (x64) and windows-11-arm. */
3
- import assert from "node:assert/strict";
4
- import path from "node:path";
5
- import { spawnSync } from "node:child_process";
6
- import { createRequire } from "node:module";
7
- import { fileURLToPath } from "node:url";
8
- import { createLocalBashOperations } from "@earendil-works/pi-coding-agent";
9
- import { MANAGED_NIUBASH_ASSETS, validateManagedNiubashPackage } from "./managed-niubash-assets.mjs";
10
-
11
- const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
12
- if (process.platform !== "win32" || !(process.arch in MANAGED_NIUBASH_ASSETS)) {
13
- throw new Error(`managed niubash smoke test requires Windows x64/ARM64 (got ${process.platform}/${process.arch})`);
14
- }
15
- const asset = MANAGED_NIUBASH_ASSETS[process.arch];
16
- const packageRoot = path.join(root, "platform-packages", asset.directory);
17
- validateManagedNiubashPackage(packageRoot, asset, { allowExtraRuntimeFiles: false });
18
- const require = createRequire(path.join(root, "package.json"));
19
- const runtime = require(path.join(packageRoot, "index.cjs"));
20
- assert.equal(runtime.packageName, asset.packageName);
21
- assert.equal(runtime.arch, process.arch);
22
-
23
- const version = spawnSync(runtime.shellPath, ["--version"], { encoding: "utf8", windowsHide: true });
24
- assert.equal(version.status, 0, version.stderr);
25
- assert.match(version.stdout, /Niubash 1\.1\.4/);
26
- assert.match(version.stdout, /rubash\s+git 8b81c7501646/);
27
- assert.match(version.stdout, /winuxcmd WinuxCmd 1\.0\.8/);
28
-
29
- const operations = createLocalBashOperations({ shellPath: runtime.shellPath });
30
- async function execute(command, options = {}) {
31
- const chunks = [];
32
- const result = await operations.exec(command, process.cwd(), {
33
- onData: (data) => chunks.push(data),
34
- ...options,
35
- });
36
- return { exitCode: result.exitCode, output: Buffer.concat(chunks).toString("utf8") };
37
- }
38
-
39
- const empty = await execute("");
40
- assert.equal(empty.exitCode, 0);
41
- assert.equal(empty.output, "");
42
-
43
- const pwd = await execute("pwd");
44
- assert.equal(pwd.exitCode, 0);
45
- assert.equal(path.resolve(pwd.output.trim().replaceAll("/", path.sep)).toLowerCase(), path.resolve(process.cwd()).toLowerCase());
46
-
47
- const quoted = await execute(`node -e "console.log(JSON.stringify(process.argv.slice(1)))" 'a b' '' 'c"d' 'e\\f'`);
48
- assert.equal(quoted.exitCode, 0, quoted.output);
49
- assert.deepEqual(JSON.parse(quoted.output.trim()), ["a b", "", "c\"d", "e\\f"]);
50
-
51
- const shellQuotedCwd = process.cwd().replaceAll("'", `'"'"'`);
52
- const windowsPath = await execute(`cd '${shellQuotedCwd}' && pwd`);
53
- assert.equal(windowsPath.exitCode, 0, windowsPath.output);
54
- assert.equal(path.resolve(windowsPath.output.trim().replaceAll("/", path.sep)).toLowerCase(), path.resolve(process.cwd()).toLowerCase());
55
-
56
- await assert.rejects(() => execute("sleep 5", { timeout: 0.1 }), /timeout/);
57
- const controller = new AbortController();
58
- const aborted = execute("sleep 5", { signal: controller.signal });
59
- setTimeout(() => controller.abort(), 100);
60
- await assert.rejects(() => aborted, /aborted/);
61
-
62
- console.log(`[piweb] Managed niubash ${process.arch} smoke test passed (${version.stdout.split(/\r?\n/, 1)[0]}).`);