@zerwiz/ymir 0.1.34 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/apps/README.md +4 -0
- package/apps/hlidskjalf/README.md +68 -0
- package/apps/hlidskjalf/android/app/build.gradle +73 -0
- package/apps/hlidskjalf/android/app/capacitor.build.gradle +19 -0
- package/apps/hlidskjalf/android/app/proguard-rules.pro +21 -0
- package/apps/hlidskjalf/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
- package/apps/hlidskjalf/android/app/src/main/AndroidManifest.xml +41 -0
- package/apps/hlidskjalf/android/app/src/main/java/org/zerwiz/ymir/MainActivity.java +5 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
- package/apps/hlidskjalf/android/app/src/main/res/layout/activity_main.xml +12 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
- package/apps/hlidskjalf/android/app/src/main/res/values/strings.xml +7 -0
- package/apps/hlidskjalf/android/app/src/main/res/values/styles.xml +22 -0
- package/apps/hlidskjalf/android/app/src/main/res/xml/file_paths.xml +5 -0
- package/apps/hlidskjalf/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
- package/apps/hlidskjalf/android/build.gradle +29 -0
- package/apps/hlidskjalf/android/capacitor.settings.gradle +3 -0
- package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/apps/hlidskjalf/android/gradle.properties +22 -0
- package/apps/hlidskjalf/android/gradlew +248 -0
- package/apps/hlidskjalf/android/gradlew.bat +92 -0
- package/apps/hlidskjalf/android/settings.gradle +5 -0
- package/apps/hlidskjalf/android/variables.gradle +16 -0
- package/apps/hlidskjalf/capacitor.config.ts +26 -0
- package/apps/hlidskjalf/dist/apple-touch-icon.png +0 -0
- package/apps/hlidskjalf/dist/assets/index-BWmNIyaG.css +1 -0
- package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js +88 -0
- package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js.map +1 -0
- package/apps/hlidskjalf/dist/icon.svg +7 -0
- package/apps/hlidskjalf/dist/index.html +64 -0
- package/apps/hlidskjalf/dist/manifest.webmanifest +31 -0
- package/apps/hlidskjalf/dist/og.jpg +0 -0
- package/apps/hlidskjalf/dist/og.png +0 -0
- package/apps/hlidskjalf/dist/og.svg +64 -0
- package/apps/hlidskjalf/dist/sw.js +39 -0
- package/apps/hlidskjalf/dist/ymir-icon.png +0 -0
- package/apps/hlidskjalf/dist/ymir-mark.svg +62 -0
- package/apps/hlidskjalf/electron/icon.png +0 -0
- package/apps/hlidskjalf/electron/main.cjs +236 -0
- package/apps/hlidskjalf/electron/preload.cjs +8 -0
- package/apps/hlidskjalf/electron/smidja-icon.png +0 -0
- package/apps/hlidskjalf/electron/ymir-hlidskjalf.desktop.in +12 -0
- package/apps/hlidskjalf/electron/ymir-smidja.desktop.in +12 -0
- package/apps/hlidskjalf/index.html +63 -0
- package/apps/hlidskjalf/package-lock.json +5398 -0
- package/apps/hlidskjalf/package.json +55 -0
- package/apps/hlidskjalf/public/apple-touch-icon.png +0 -0
- package/apps/hlidskjalf/public/icon.svg +7 -0
- package/apps/hlidskjalf/public/manifest.webmanifest +31 -0
- package/apps/hlidskjalf/public/og.jpg +0 -0
- package/apps/hlidskjalf/public/og.png +0 -0
- package/apps/hlidskjalf/public/og.svg +64 -0
- package/apps/hlidskjalf/public/sw.js +39 -0
- package/apps/hlidskjalf/public/ymir-icon.png +0 -0
- package/apps/hlidskjalf/public/ymir-mark.svg +62 -0
- package/apps/hlidskjalf/server/accounts.ts +159 -0
- package/apps/hlidskjalf/server/index.ts +2045 -0
- package/apps/hlidskjalf/src/app/App.tsx +116 -0
- package/apps/hlidskjalf/src/app/BottomStream.tsx +128 -0
- package/apps/hlidskjalf/src/app/MobileNav.tsx +87 -0
- package/apps/hlidskjalf/src/app/Rail.tsx +106 -0
- package/apps/hlidskjalf/src/app/Shell.tsx +135 -0
- package/apps/hlidskjalf/src/app/Topbar.tsx +130 -0
- package/apps/hlidskjalf/src/components/AccentPicker.tsx +207 -0
- package/apps/hlidskjalf/src/components/AccountMenu.tsx +115 -0
- package/apps/hlidskjalf/src/components/AgentCard.tsx +75 -0
- package/apps/hlidskjalf/src/components/EmberBackground.tsx +36 -0
- package/apps/hlidskjalf/src/components/Emblem.tsx +48 -0
- package/apps/hlidskjalf/src/components/Halls.tsx +155 -0
- package/apps/hlidskjalf/src/components/LoginModal.tsx +329 -0
- package/apps/hlidskjalf/src/components/Markdown.tsx +10 -0
- package/apps/hlidskjalf/src/components/MetricTile.tsx +40 -0
- package/apps/hlidskjalf/src/components/ModelPicker.tsx +100 -0
- package/apps/hlidskjalf/src/components/Overlay.tsx +134 -0
- package/apps/hlidskjalf/src/components/PRCard.tsx +154 -0
- package/apps/hlidskjalf/src/components/RecallPanel.tsx +116 -0
- package/apps/hlidskjalf/src/components/RuneTag.tsx +23 -0
- package/apps/hlidskjalf/src/components/Status.tsx +35 -0
- package/apps/hlidskjalf/src/components/TaskChip.tsx +23 -0
- package/apps/hlidskjalf/src/components/TraceRow.tsx +28 -0
- package/apps/hlidskjalf/src/data/lore.ts +127 -0
- package/apps/hlidskjalf/src/data/metadata.ts +147 -0
- package/apps/hlidskjalf/src/data/mythology.ts +95 -0
- package/apps/hlidskjalf/src/data/realms.ts +167 -0
- package/apps/hlidskjalf/src/gates/Cron.tsx +77 -0
- package/apps/hlidskjalf/src/gates/Decisions.tsx +67 -0
- package/apps/hlidskjalf/src/gates/Files.tsx +138 -0
- package/apps/hlidskjalf/src/gates/Fleet.tsx +184 -0
- package/apps/hlidskjalf/src/gates/Forge.tsx +486 -0
- package/apps/hlidskjalf/src/gates/OmniChat.tsx +217 -0
- package/apps/hlidskjalf/src/gates/Processes.tsx +162 -0
- package/apps/hlidskjalf/src/gates/Profile.tsx +225 -0
- package/apps/hlidskjalf/src/gates/Reviews.tsx +33 -0
- package/apps/hlidskjalf/src/gates/Runes.tsx +116 -0
- package/apps/hlidskjalf/src/gates/Runtime.tsx +106 -0
- package/apps/hlidskjalf/src/gates/Sessions.tsx +109 -0
- package/apps/hlidskjalf/src/gates/Stats.tsx +268 -0
- package/apps/hlidskjalf/src/gates/Tasks.tsx +173 -0
- package/apps/hlidskjalf/src/gates/Trace.tsx +155 -0
- package/apps/hlidskjalf/src/gates/Well.tsx +154 -0
- package/apps/hlidskjalf/src/gates/Worktrees.tsx +48 -0
- package/apps/hlidskjalf/src/hooks/useTenantDef.ts +13 -0
- package/apps/hlidskjalf/src/main.tsx +31 -0
- package/apps/hlidskjalf/src/services/api.ts +404 -0
- package/apps/hlidskjalf/src/services/auth.ts +107 -0
- package/apps/hlidskjalf/src/services/stream.ts +35 -0
- package/apps/hlidskjalf/src/state/store.ts +648 -0
- package/apps/hlidskjalf/src/state/ui.ts +62 -0
- package/apps/hlidskjalf/src/styles/auth.css +485 -0
- package/apps/hlidskjalf/src/styles/components.css +1259 -0
- package/apps/hlidskjalf/src/styles/forge.css +115 -0
- package/apps/hlidskjalf/src/styles/global.css +167 -0
- package/apps/hlidskjalf/src/styles/menus.css +175 -0
- package/apps/hlidskjalf/src/styles/mobile.css +298 -0
- package/apps/hlidskjalf/src/styles/overlays.css +205 -0
- package/apps/hlidskjalf/src/styles/shell.css +641 -0
- package/apps/hlidskjalf/src/types.ts +251 -0
- package/apps/hlidskjalf/src/vite-env.d.ts +9 -0
- package/apps/hlidskjalf/static/landing.md +6 -0
- package/apps/hlidskjalf/tsconfig.json +25 -0
- package/apps/hlidskjalf/vite.config.ts +49 -0
- package/apps/hlidskjalf-mobile/App.tsx +93 -0
- package/apps/hlidskjalf-mobile/app.json +13 -0
- package/apps/hlidskjalf-mobile/index.ts +4 -0
- package/apps/hlidskjalf-mobile/package.json +35 -0
- package/apps/hlidskjalf-mobile/public/icon.svg +7 -0
- package/apps/hlidskjalf-mobile/tsconfig.json +4 -0
- package/apps/odrerir/.astro/content.d.ts +159 -0
- package/apps/odrerir/.astro/dev.json +24 -0
- package/apps/odrerir/.astro/settings.json +5 -0
- package/apps/odrerir/.astro/types.d.ts +2 -0
- package/apps/odrerir/astro.config.mjs +79 -0
- package/apps/odrerir/dist/apple-touch-icon.png +0 -0
- package/apps/odrerir/dist/favicon-192.png +0 -0
- package/apps/odrerir/dist/favicon-32.png +0 -0
- package/apps/odrerir/dist/favicon-512.png +0 -0
- package/apps/odrerir/dist/favicon.svg +53 -0
- package/apps/odrerir/dist/icon.svg +7 -0
- package/apps/odrerir/dist/index.html +837 -0
- package/apps/odrerir/dist/og-square.png +0 -0
- package/apps/odrerir/dist/og-story.png +0 -0
- package/apps/odrerir/dist/og.png +0 -0
- package/apps/odrerir/dist/safari-pinned-tab.svg +7 -0
- package/apps/odrerir/dist/site.webmanifest +16 -0
- package/apps/odrerir/dist/styles/cloth.css +112 -0
- package/apps/odrerir/dist/styles/livehall.css +278 -0
- package/apps/odrerir/electron/icon.png +0 -0
- package/apps/odrerir/electron/main.cjs +183 -0
- package/apps/odrerir/electron/ymir-odrerir.desktop.in +12 -0
- package/apps/odrerir/package-lock.json +4195 -0
- package/apps/odrerir/package.json +30 -0
- package/apps/odrerir/public/apple-touch-icon.png +0 -0
- package/apps/odrerir/public/favicon-192.png +0 -0
- package/apps/odrerir/public/favicon-32.png +0 -0
- package/apps/odrerir/public/favicon-512.png +0 -0
- package/apps/odrerir/public/favicon.svg +53 -0
- package/apps/odrerir/public/icon.svg +7 -0
- package/apps/odrerir/public/og-square.png +0 -0
- package/apps/odrerir/public/og-story.png +0 -0
- package/apps/odrerir/public/og.png +0 -0
- package/apps/odrerir/public/safari-pinned-tab.svg +7 -0
- package/apps/odrerir/public/site.webmanifest +16 -0
- package/apps/odrerir/scripts/extract-cloth.mjs +101 -0
- package/apps/odrerir/src/index.html +837 -0
- package/apps/odrerir/src/styles/cloth.css +112 -0
- package/apps/odrerir/src/styles/livehall.css +278 -0
- package/apps/sessrumnir/.github/ISSUE_TEMPLATE/bug_report.yml +99 -0
- package/apps/sessrumnir/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/apps/sessrumnir/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
- package/apps/sessrumnir/.github/PULL_REQUEST_TEMPLATE.md +56 -0
- package/apps/sessrumnir/.github/SUPPORT.md +18 -0
- package/apps/sessrumnir/.github/workflows/build.yml +133 -0
- package/apps/sessrumnir/AGENTS.md +540 -0
- package/apps/sessrumnir/CLA.md +63 -0
- package/apps/sessrumnir/CONTRIBUTING.md +233 -0
- package/apps/sessrumnir/LICENSE +200 -0
- package/apps/sessrumnir/NOTICE +33 -0
- package/apps/sessrumnir/README.md +329 -0
- package/apps/sessrumnir/bin/sessrumnir-web.sh +117 -0
- package/apps/sessrumnir/bin/sessrumnir.js +169 -0
- package/apps/sessrumnir/docs/CNAME +1 -0
- package/apps/sessrumnir/docs/favicon.ico +0 -0
- package/apps/sessrumnir/docs/icon-512.png +0 -0
- package/apps/sessrumnir/docs/index.html +441 -0
- package/apps/sessrumnir/docs/robots.txt +4 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_181929.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182005.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182020.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182039.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182117.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182225.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182248.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182322.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182340.png +0 -0
- package/apps/sessrumnir/docs/sitemap.xml +9 -0
- package/apps/sessrumnir/electron.vite.config.ts +49 -0
- package/apps/sessrumnir/eslint.config.mjs +75 -0
- package/apps/sessrumnir/i18next.config.ts +33 -0
- package/apps/sessrumnir/install.sh +144 -0
- package/apps/sessrumnir/out/main/index.js +10881 -0
- package/apps/sessrumnir/out/preload/index.js +722 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-C8QS47vb.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-D3EsxgFc.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-B7dJQtg-.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-BLlg2W5x.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-BcIsDXzC.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-CBymlcNF.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-Bgrpe4p1.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-BlcaxZtM.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-CdQuyvtc.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-pZw22qtS.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-Dr-wLyOm.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-dLR2-a_v.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-BGH8Vunh.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-C3_-2Ua-.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-Dj3SQ6fR.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-EBdSCOD3.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-B254Xisw.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-DUW-TI76.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuQ88yz3.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuXFa1Dr.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-AH9qog1s.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-DAuUCO41.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-CD-dlDpD.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-D3T6BBXG.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-C-RiYxEf.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-DmUuA7Y2.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-DsPuwQHi.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-tGBW_mI7.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-Dar_cCT6.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-GIs8CF2x.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-Ba-HN6uq.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-ByB2hQUF.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-B8O3uHbr.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-BbtBtHoB.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BPU54Tw3.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BRS-C4KA.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-BEtQ2wm2.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-CQbCaAQ4.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-B6fSpYYV.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-CpdbWO_2.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/index-BxqsRq2x.css +4858 -0
- package/apps/sessrumnir/out/renderer/assets/index-TaD69rFk.js +99435 -0
- package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-italic-DFS986qL.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-italic-Bxi8ein9.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-italic-xAqemagA.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/openmoji-color-9qo_2DLG.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/pi-logo-B6SVnhuS.svg +7 -0
- package/apps/sessrumnir/out/renderer/index.html +19 -0
- package/apps/sessrumnir/out/renderer/theme-boot.js +30 -0
- package/apps/sessrumnir/package-lock.json +10636 -0
- package/apps/sessrumnir/package.json +183 -0
- package/apps/sessrumnir/public/apple-touch-icon.png +0 -0
- package/apps/sessrumnir/public/icon.svg +7 -0
- package/apps/sessrumnir/resources/icons/generate_icons.py +38 -0
- package/apps/sessrumnir/resources/icons/icon-128.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-16.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-256.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-32.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-48.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-512.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-64.png +0 -0
- package/apps/sessrumnir/resources/icons/icon.icns +0 -0
- package/apps/sessrumnir/resources/icons/icon.ico +0 -0
- package/apps/sessrumnir/resources/icons/icon.png +0 -0
- package/apps/sessrumnir/resources/icons/icon.svg +7 -0
- package/apps/sessrumnir/resources/linux/after-install.sh +23 -0
- package/apps/sessrumnir/resources/linux/after-remove.sh +18 -0
- package/apps/sessrumnir/resources/locales/en/translation.json +1735 -0
- package/apps/sessrumnir/resources/permission-prompt-text.test.ts +111 -0
- package/apps/sessrumnir/resources/permission-prompt-text.ts +84 -0
- package/apps/sessrumnir/resources/permission-rules.test.ts +351 -0
- package/apps/sessrumnir/resources/permission-rules.ts +293 -0
- package/apps/sessrumnir/resources/pi-desktop-permissions.ts +62 -0
- package/apps/sessrumnir/resources/ymir-sessrumnir.desktop.in +12 -0
- package/apps/sessrumnir/scripts/check-semantic-colors.mjs +57 -0
- package/apps/sessrumnir/src/main/activity-stats.test.ts +240 -0
- package/apps/sessrumnir/src/main/activity-stats.ts +596 -0
- package/apps/sessrumnir/src/main/agent-detection.test.ts +25 -0
- package/apps/sessrumnir/src/main/agent-detection.ts +114 -0
- package/apps/sessrumnir/src/main/app-data-paths.test.ts +90 -0
- package/apps/sessrumnir/src/main/app-data-paths.ts +95 -0
- package/apps/sessrumnir/src/main/app-log.test.ts +114 -0
- package/apps/sessrumnir/src/main/app-log.ts +210 -0
- package/apps/sessrumnir/src/main/archived-sessions.test.ts +50 -0
- package/apps/sessrumnir/src/main/archived-sessions.ts +95 -0
- package/apps/sessrumnir/src/main/attachment-reader.test.ts +56 -0
- package/apps/sessrumnir/src/main/attachment-reader.ts +52 -0
- package/apps/sessrumnir/src/main/auto-tag.test.ts +38 -0
- package/apps/sessrumnir/src/main/auto-tag.ts +75 -0
- package/apps/sessrumnir/src/main/autostart-linux.test.ts +56 -0
- package/apps/sessrumnir/src/main/autostart-linux.ts +58 -0
- package/apps/sessrumnir/src/main/cmd-escape.test.ts +424 -0
- package/apps/sessrumnir/src/main/cmd-escape.ts +186 -0
- package/apps/sessrumnir/src/main/council-manager.test.ts +167 -0
- package/apps/sessrumnir/src/main/council-manager.ts +265 -0
- package/apps/sessrumnir/src/main/diagnostics-report.test.ts +140 -0
- package/apps/sessrumnir/src/main/diagnostics-report.ts +91 -0
- package/apps/sessrumnir/src/main/diagnostics.ts +111 -0
- package/apps/sessrumnir/src/main/editor-guard.test.ts +59 -0
- package/apps/sessrumnir/src/main/editor-guard.ts +50 -0
- package/apps/sessrumnir/src/main/extension-ui-ipc.test.ts +299 -0
- package/apps/sessrumnir/src/main/extension-ui-ipc.ts +117 -0
- package/apps/sessrumnir/src/main/file-service.test.ts +279 -0
- package/apps/sessrumnir/src/main/file-service.ts +652 -0
- package/apps/sessrumnir/src/main/fs-errors.ts +42 -0
- package/apps/sessrumnir/src/main/get-messages-trim.test.ts +77 -0
- package/apps/sessrumnir/src/main/get-messages-trim.ts +97 -0
- package/apps/sessrumnir/src/main/git-conveyor.test.ts +305 -0
- package/apps/sessrumnir/src/main/git-conveyor.ts +338 -0
- package/apps/sessrumnir/src/main/git-worktree.test.ts +101 -0
- package/apps/sessrumnir/src/main/git-worktree.ts +201 -0
- package/apps/sessrumnir/src/main/i18n.ts +31 -0
- package/apps/sessrumnir/src/main/index.ts +619 -0
- package/apps/sessrumnir/src/main/ipc/active-engine.ts +15 -0
- package/apps/sessrumnir/src/main/ipc/context.ts +58 -0
- package/apps/sessrumnir/src/main/ipc/council-handlers.ts +126 -0
- package/apps/sessrumnir/src/main/ipc/diagnostics-handlers.ts +8 -0
- package/apps/sessrumnir/src/main/ipc/file-handlers.ts +96 -0
- package/apps/sessrumnir/src/main/ipc/git-conveyor-handlers.ts +55 -0
- package/apps/sessrumnir/src/main/ipc/model-handlers.ts +33 -0
- package/apps/sessrumnir/src/main/ipc/models-config-handlers.ts +80 -0
- package/apps/sessrumnir/src/main/ipc/notes-handlers.ts +65 -0
- package/apps/sessrumnir/src/main/ipc/package-handlers.ts +330 -0
- package/apps/sessrumnir/src/main/ipc/permission-rules-handlers.ts +223 -0
- package/apps/sessrumnir/src/main/ipc/pi-handlers.ts +136 -0
- package/apps/sessrumnir/src/main/ipc/pi-start-options.ts +150 -0
- package/apps/sessrumnir/src/main/ipc/run-pi-cli.ts +53 -0
- package/apps/sessrumnir/src/main/ipc/session-handlers.ts +491 -0
- package/apps/sessrumnir/src/main/ipc/settings.ts +120 -0
- package/apps/sessrumnir/src/main/ipc/skills-mcp-handlers.ts +192 -0
- package/apps/sessrumnir/src/main/ipc/system-handlers.ts +131 -0
- package/apps/sessrumnir/src/main/ipc/tag-handlers.ts +69 -0
- package/apps/sessrumnir/src/main/ipc/terminal-handlers.ts +41 -0
- package/apps/sessrumnir/src/main/ipc/theme-handlers.ts +131 -0
- package/apps/sessrumnir/src/main/ipc/update-handlers.ts +61 -0
- package/apps/sessrumnir/src/main/ipc/validation.ts +45 -0
- package/apps/sessrumnir/src/main/ipc/workflow-handlers.ts +108 -0
- package/apps/sessrumnir/src/main/ipc/workspace-activity-wiring.ts +179 -0
- package/apps/sessrumnir/src/main/ipc/workspace-handlers.ts +133 -0
- package/apps/sessrumnir/src/main/ipc-handlers.ts +98 -0
- package/apps/sessrumnir/src/main/map-concurrent.test.ts +80 -0
- package/apps/sessrumnir/src/main/map-concurrent.ts +30 -0
- package/apps/sessrumnir/src/main/models-file.test.ts +114 -0
- package/apps/sessrumnir/src/main/models-file.ts +89 -0
- package/apps/sessrumnir/src/main/notes-manager.test.ts +64 -0
- package/apps/sessrumnir/src/main/notes-manager.ts +183 -0
- package/apps/sessrumnir/src/main/notify-decision.test.ts +39 -0
- package/apps/sessrumnir/src/main/notify-decision.ts +25 -0
- package/apps/sessrumnir/src/main/omp-fork-points.test.ts +119 -0
- package/apps/sessrumnir/src/main/omp-fork-points.ts +105 -0
- package/apps/sessrumnir/src/main/omp-plugin-list.test.ts +94 -0
- package/apps/sessrumnir/src/main/omp-plugin-list.ts +103 -0
- package/apps/sessrumnir/src/main/package-catalog.test.ts +157 -0
- package/apps/sessrumnir/src/main/package-catalog.ts +166 -0
- package/apps/sessrumnir/src/main/package-updates.test.ts +149 -0
- package/apps/sessrumnir/src/main/package-updates.ts +185 -0
- package/apps/sessrumnir/src/main/path-authorization.test.ts +64 -0
- package/apps/sessrumnir/src/main/path-authorization.ts +31 -0
- package/apps/sessrumnir/src/main/pi-binary-resolution.test.ts +582 -0
- package/apps/sessrumnir/src/main/pi-binary-resolution.ts +649 -0
- package/apps/sessrumnir/src/main/pi-event-router.test.ts +388 -0
- package/apps/sessrumnir/src/main/pi-event-router.ts +290 -0
- package/apps/sessrumnir/src/main/pi-paths.test.ts +134 -0
- package/apps/sessrumnir/src/main/pi-paths.ts +74 -0
- package/apps/sessrumnir/src/main/pi-rpc-manager.test.ts +467 -0
- package/apps/sessrumnir/src/main/pi-rpc-manager.ts +1319 -0
- package/apps/sessrumnir/src/main/process-tree.test.ts +94 -0
- package/apps/sessrumnir/src/main/renderer-origin.test.ts +33 -0
- package/apps/sessrumnir/src/main/renderer-origin.ts +35 -0
- package/apps/sessrumnir/src/main/session-lineage-reader.test.ts +317 -0
- package/apps/sessrumnir/src/main/session-lineage-reader.ts +111 -0
- package/apps/sessrumnir/src/main/session-metadata.test.ts +508 -0
- package/apps/sessrumnir/src/main/session-metadata.ts +526 -0
- package/apps/sessrumnir/src/main/session-name.test.ts +71 -0
- package/apps/sessrumnir/src/main/session-name.ts +69 -0
- package/apps/sessrumnir/src/main/session-paths.test.ts +110 -0
- package/apps/sessrumnir/src/main/session-paths.ts +88 -0
- package/apps/sessrumnir/src/main/session-tags.test.ts +61 -0
- package/apps/sessrumnir/src/main/session-tags.ts +253 -0
- package/apps/sessrumnir/src/main/session-trash.test.ts +66 -0
- package/apps/sessrumnir/src/main/session-trash.ts +65 -0
- package/apps/sessrumnir/src/main/settings-logic.ts +66 -0
- package/apps/sessrumnir/src/main/skills-discovery.test.ts +149 -0
- package/apps/sessrumnir/src/main/skills-discovery.ts +179 -0
- package/apps/sessrumnir/src/main/startup-launch.ts +63 -0
- package/apps/sessrumnir/src/main/terminal-service.ts +96 -0
- package/apps/sessrumnir/src/main/theme-store.test.ts +440 -0
- package/apps/sessrumnir/src/main/theme-store.ts +495 -0
- package/apps/sessrumnir/src/main/tray-decision.test.ts +46 -0
- package/apps/sessrumnir/src/main/tray-decision.ts +49 -0
- package/apps/sessrumnir/src/main/tray-manager.ts +220 -0
- package/apps/sessrumnir/src/main/tsconfig.json +20 -0
- package/apps/sessrumnir/src/main/window-background.test.ts +29 -0
- package/apps/sessrumnir/src/main/window-background.ts +16 -0
- package/apps/sessrumnir/src/main/workflow-discovery.test.ts +252 -0
- package/apps/sessrumnir/src/main/workflow-monitor.test.ts +371 -0
- package/apps/sessrumnir/src/main/workflow-monitor.ts +1020 -0
- package/apps/sessrumnir/src/main/workspace-activity.test.ts +227 -0
- package/apps/sessrumnir/src/main/workspace-activity.ts +215 -0
- package/apps/sessrumnir/src/main/workspace-manager.test.ts +680 -0
- package/apps/sessrumnir/src/main/workspace-manager.ts +1199 -0
- package/apps/sessrumnir/src/main/workspace-trust.test.ts +78 -0
- package/apps/sessrumnir/src/main/workspace-trust.ts +84 -0
- package/apps/sessrumnir/src/preload/index.ts +358 -0
- package/apps/sessrumnir/src/preload/tsconfig.json +20 -0
- package/apps/sessrumnir/src/renderer/index.html +18 -0
- package/apps/sessrumnir/src/renderer/public/theme-boot.js +30 -0
- package/apps/sessrumnir/src/renderer/src/app.tsx +194 -0
- package/apps/sessrumnir/src/renderer/src/assets/openmoji-color.woff2 +0 -0
- package/apps/sessrumnir/src/renderer/src/assets/pi-logo.svg +7 -0
- package/apps/sessrumnir/src/renderer/src/assets/ymir-mark.svg +55 -0
- package/apps/sessrumnir/src/renderer/src/assets.d.ts +7 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-code-highlight.ts +239 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-file-link.ts +140 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-input.tsx +729 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.test.ts +175 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.ts +100 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-panel.tsx +523 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-project-picker.tsx +193 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-search.tsx +251 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor-highlight.ts +85 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor-language.test.ts +19 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor-language.ts +88 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor.tsx +128 -0
- package/apps/sessrumnir/src/renderer/src/components/command-palette.tsx +315 -0
- package/apps/sessrumnir/src/renderer/src/components/command-results.tsx +80 -0
- package/apps/sessrumnir/src/renderer/src/components/composer-permission-menu.tsx +91 -0
- package/apps/sessrumnir/src/renderer/src/components/context-menu.tsx +457 -0
- package/apps/sessrumnir/src/renderer/src/components/copy-button.tsx +36 -0
- package/apps/sessrumnir/src/renderer/src/components/council-panels.tsx +193 -0
- package/apps/sessrumnir/src/renderer/src/components/custom-models-editor.tsx +330 -0
- package/apps/sessrumnir/src/renderer/src/components/diagnostics-panel.tsx +380 -0
- package/apps/sessrumnir/src/renderer/src/components/diff-viewer.tsx +318 -0
- package/apps/sessrumnir/src/renderer/src/components/ember-background.tsx +113 -0
- package/apps/sessrumnir/src/renderer/src/components/error-boundary.tsx +38 -0
- package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.test.ts +43 -0
- package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.ts +51 -0
- package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog.tsx +486 -0
- package/apps/sessrumnir/src/renderer/src/components/file-tree.tsx +784 -0
- package/apps/sessrumnir/src/renderer/src/components/git-conveyor-actions.tsx +246 -0
- package/apps/sessrumnir/src/renderer/src/components/home-screen.tsx +396 -0
- package/apps/sessrumnir/src/renderer/src/components/image-viewer.tsx +100 -0
- package/apps/sessrumnir/src/renderer/src/components/line-numbered-code.tsx +58 -0
- package/apps/sessrumnir/src/renderer/src/components/markdown-renderer.tsx +237 -0
- package/apps/sessrumnir/src/renderer/src/components/message-bubble.tsx +1006 -0
- package/apps/sessrumnir/src/renderer/src/components/mission-control.tsx +341 -0
- package/apps/sessrumnir/src/renderer/src/components/model-selector.tsx +219 -0
- package/apps/sessrumnir/src/renderer/src/components/note-picker.tsx +155 -0
- package/apps/sessrumnir/src/renderer/src/components/notes-panel.tsx +437 -0
- package/apps/sessrumnir/src/renderer/src/components/package-browser.tsx +604 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-mode.test.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-mode.ts +49 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.test.ts +44 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.ts +35 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor.tsx +226 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-selector.tsx +104 -0
- package/apps/sessrumnir/src/renderer/src/components/resize-handle.tsx +51 -0
- package/apps/sessrumnir/src/renderer/src/components/review-rail.tsx +194 -0
- package/apps/sessrumnir/src/renderer/src/components/session-menu-position.test.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/components/session-menu-position.ts +34 -0
- package/apps/sessrumnir/src/renderer/src/components/session-panel.tsx +711 -0
- package/apps/sessrumnir/src/renderer/src/components/session-runtime-indicator.tsx +43 -0
- package/apps/sessrumnir/src/renderer/src/components/settings-panel.tsx +1360 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.test.ts +47 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.ts +76 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.test.ts +73 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.ts +47 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar.tsx +1045 -0
- package/apps/sessrumnir/src/renderer/src/components/skills-panel.tsx +159 -0
- package/apps/sessrumnir/src/renderer/src/components/stats-panel.tsx +368 -0
- package/apps/sessrumnir/src/renderer/src/components/status-bar.tsx +274 -0
- package/apps/sessrumnir/src/renderer/src/components/status-popover.tsx +481 -0
- package/apps/sessrumnir/src/renderer/src/components/streaming-bubble.tsx +128 -0
- package/apps/sessrumnir/src/renderer/src/components/subagent-progress.tsx +254 -0
- package/apps/sessrumnir/src/renderer/src/components/task-launcher.tsx +168 -0
- package/apps/sessrumnir/src/renderer/src/components/terminal.tsx +203 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.test.ts +35 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.ts +35 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-editor.tsx +440 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-gallery.tsx +373 -0
- package/apps/sessrumnir/src/renderer/src/components/thinking-level-selector.tsx +85 -0
- package/apps/sessrumnir/src/renderer/src/components/timeline.tsx +289 -0
- package/apps/sessrumnir/src/renderer/src/components/tool-call-icon.ts +32 -0
- package/apps/sessrumnir/src/renderer/src/components/workflow-navigator.tsx +901 -0
- package/apps/sessrumnir/src/renderer/src/components/workspace-tabs.tsx +241 -0
- package/apps/sessrumnir/src/renderer/src/global.d.ts +8 -0
- package/apps/sessrumnir/src/renderer/src/hooks/use-folder-drop.ts +101 -0
- package/apps/sessrumnir/src/renderer/src/hooks.test.ts +88 -0
- package/apps/sessrumnir/src/renderer/src/hooks.ts +574 -0
- package/apps/sessrumnir/src/renderer/src/i18n.test.ts +54 -0
- package/apps/sessrumnir/src/renderer/src/i18n.ts +89 -0
- package/apps/sessrumnir/src/renderer/src/index.css +425 -0
- package/apps/sessrumnir/src/renderer/src/main.tsx +42 -0
- package/apps/sessrumnir/src/renderer/src/message-grouping.test.ts +422 -0
- package/apps/sessrumnir/src/renderer/src/message-grouping.ts +410 -0
- package/apps/sessrumnir/src/renderer/src/message-parsing.test.ts +86 -0
- package/apps/sessrumnir/src/renderer/src/message-parsing.ts +166 -0
- package/apps/sessrumnir/src/renderer/src/store-external-prompts.test.ts +161 -0
- package/apps/sessrumnir/src/renderer/src/store-session-transitions.test.ts +2090 -0
- package/apps/sessrumnir/src/renderer/src/store-timeline.test.ts +52 -0
- package/apps/sessrumnir/src/renderer/src/store-turn-error.test.ts +136 -0
- package/apps/sessrumnir/src/renderer/src/store.ts +3726 -0
- package/apps/sessrumnir/src/renderer/src/subagent-detection.test.ts +55 -0
- package/apps/sessrumnir/src/renderer/src/theme/engine.test.ts +29 -0
- package/apps/sessrumnir/src/renderer/src/theme/engine.ts +20 -0
- package/apps/sessrumnir/src/renderer/src/themes/breeze-claudius.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/breeze-dark.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/breeze-light.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/builtin-themes.test.ts +95 -0
- package/apps/sessrumnir/src/renderer/src/themes/dark.json +46 -0
- package/apps/sessrumnir/src/renderer/src/themes/fensalir.json +82 -0
- package/apps/sessrumnir/src/renderer/src/themes/gruvbox.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/index.ts +60 -0
- package/apps/sessrumnir/src/renderer/src/themes/light.json +46 -0
- package/apps/sessrumnir/src/renderer/src/themes/nord.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/sessrumnir.json +82 -0
- package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.test.ts +81 -0
- package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.ts +57 -0
- package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.test.ts +62 -0
- package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.ts +28 -0
- package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.test.ts +57 -0
- package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.ts +41 -0
- package/apps/sessrumnir/src/renderer/src/utils/ipc-error.test.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/utils/ipc-error.ts +12 -0
- package/apps/sessrumnir/src/renderer/src/utils/model-search.test.ts +63 -0
- package/apps/sessrumnir/src/renderer/src/utils/model-search.ts +20 -0
- package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.test.ts +31 -0
- package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.ts +27 -0
- package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.test.ts +15 -0
- package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/utils/process-status-label.test.ts +13 -0
- package/apps/sessrumnir/src/renderer/src/utils/process-status-label.ts +19 -0
- package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.test.ts +68 -0
- package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.ts +52 -0
- package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.test.ts +46 -0
- package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.ts +23 -0
- package/apps/sessrumnir/src/renderer/src/utils/relative-time.tsx +25 -0
- package/apps/sessrumnir/src/renderer/src/utils/session-title.test.ts +98 -0
- package/apps/sessrumnir/src/renderer/src/utils/session-title.ts +58 -0
- package/apps/sessrumnir/src/renderer/src/utils/stale-guard.test.ts +29 -0
- package/apps/sessrumnir/src/renderer/src/utils/stale-guard.ts +20 -0
- package/apps/sessrumnir/src/renderer/src/utils/theme.test.ts +174 -0
- package/apps/sessrumnir/src/renderer/src/utils/theme.ts +159 -0
- package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.test.ts +130 -0
- package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.ts +84 -0
- package/apps/sessrumnir/src/renderer/tsconfig.json +24 -0
- package/apps/sessrumnir/src/shared/agent-engine-label.test.ts +45 -0
- package/apps/sessrumnir/src/shared/agent-engine-label.ts +53 -0
- package/apps/sessrumnir/src/shared/app-settings.test.ts +25 -0
- package/apps/sessrumnir/src/shared/app-settings.ts +23 -0
- package/apps/sessrumnir/src/shared/bridge-http.ts +341 -0
- package/apps/sessrumnir/src/shared/bridge.ts +337 -0
- package/apps/sessrumnir/src/shared/council-config.test.ts +300 -0
- package/apps/sessrumnir/src/shared/council-config.ts +356 -0
- package/apps/sessrumnir/src/shared/default-settings.ts +39 -0
- package/apps/sessrumnir/src/shared/folder-drop.test.ts +118 -0
- package/apps/sessrumnir/src/shared/folder-drop.ts +74 -0
- package/apps/sessrumnir/src/shared/fork-point.test.ts +30 -0
- package/apps/sessrumnir/src/shared/fork-point.ts +24 -0
- package/apps/sessrumnir/src/shared/i18n/i18next.d.ts +10 -0
- package/apps/sessrumnir/src/shared/i18n/index.test.ts +51 -0
- package/apps/sessrumnir/src/shared/i18n/index.ts +50 -0
- package/apps/sessrumnir/src/shared/i18n/languages.ts +12 -0
- package/apps/sessrumnir/src/shared/i18n/locale-checks.ts +98 -0
- package/apps/sessrumnir/src/shared/i18n/locales.test.ts +92 -0
- package/apps/sessrumnir/src/shared/i18n/pseudo.test.ts +39 -0
- package/apps/sessrumnir/src/shared/i18n/pseudo.ts +44 -0
- package/apps/sessrumnir/src/shared/i18n/resolve.test.ts +43 -0
- package/apps/sessrumnir/src/shared/i18n/resolve.ts +41 -0
- package/apps/sessrumnir/src/shared/i18n/resources.ts +11 -0
- package/apps/sessrumnir/src/shared/ipc-contracts.ts +1539 -0
- package/apps/sessrumnir/src/shared/models-config.test.ts +124 -0
- package/apps/sessrumnir/src/shared/models-config.ts +167 -0
- package/apps/sessrumnir/src/shared/package-filter.ts +19 -0
- package/apps/sessrumnir/src/shared/package-spec.test.ts +51 -0
- package/apps/sessrumnir/src/shared/package-spec.ts +25 -0
- package/apps/sessrumnir/src/shared/path-compare.test.ts +38 -0
- package/apps/sessrumnir/src/shared/path-compare.ts +76 -0
- package/apps/sessrumnir/src/shared/permission-mode.ts +8 -0
- package/apps/sessrumnir/src/shared/pi-command.test.ts +165 -0
- package/apps/sessrumnir/src/shared/pi-command.ts +139 -0
- package/apps/sessrumnir/src/shared/product-name.ts +7 -0
- package/apps/sessrumnir/src/shared/session-lineage.test.ts +103 -0
- package/apps/sessrumnir/src/shared/session-lineage.ts +50 -0
- package/apps/sessrumnir/src/shared/session-preview.test.ts +93 -0
- package/apps/sessrumnir/src/shared/session-preview.ts +56 -0
- package/apps/sessrumnir/src/shared/sidebar-width.test.ts +58 -0
- package/apps/sessrumnir/src/shared/sidebar-width.ts +37 -0
- package/apps/sessrumnir/src/shared/theme/builtin-ids.ts +16 -0
- package/apps/sessrumnir/src/shared/theme/resolve.test.ts +66 -0
- package/apps/sessrumnir/src/shared/theme/resolve.ts +29 -0
- package/apps/sessrumnir/src/shared/theme/syntax-defaults.ts +24 -0
- package/apps/sessrumnir/src/shared/theme/theme-file.test.ts +88 -0
- package/apps/sessrumnir/src/shared/theme/theme-file.ts +132 -0
- package/apps/sessrumnir/src/shared/theme/tokens.test.ts +35 -0
- package/apps/sessrumnir/src/shared/theme/tokens.ts +86 -0
- package/apps/sessrumnir/src/shared/tsconfig.json +17 -0
- package/apps/sessrumnir/src/shared/untrusted-data.test.ts +32 -0
- package/apps/sessrumnir/src/shared/untrusted-data.ts +22 -0
- package/apps/sessrumnir/src/shared/version-compare.test.ts +33 -0
- package/apps/sessrumnir/src/shared/version-compare.ts +50 -0
- package/apps/sessrumnir/src/shared/workflow-control.ts +33 -0
- package/apps/sessrumnir/src/web-server.ts +315 -0
- package/apps/sessrumnir/tsconfig.json +26 -0
- package/apps/smidja/smidja_build.py +45 -0
- package/apps/smidja/smidja_build_review.py +76 -0
- package/apps/smidja/smidja_build_test.py +79 -0
- package/apps/smidja/smidja_document.py +76 -0
- package/apps/smidja/smidja_modules/agent_cc.py +15 -0
- package/apps/smidja/smidja_modules/agent_opencode.py +194 -0
- package/apps/smidja/smidja_modules/agent_pi.py +392 -0
- package/apps/smidja/smidja_modules/agents.py +993 -0
- package/apps/smidja/smidja_modules/changes.py +103 -0
- package/apps/smidja/smidja_modules/console.py +131 -0
- package/apps/smidja/smidja_modules/data_types.py +447 -0
- package/apps/smidja/smidja_modules/gates.py +108 -0
- package/apps/smidja/smidja_modules/git_helper.py +120 -0
- package/apps/smidja/smidja_modules/lm_local.py +144 -0
- package/apps/smidja/smidja_modules/permissions.py +194 -0
- package/apps/smidja/smidja_modules/prompts.py +21 -0
- package/apps/smidja/smidja_modules/quality.py +240 -0
- package/apps/smidja/smidja_modules/runner.py +155 -0
- package/apps/smidja/smidja_modules/session.py +50 -0
- package/apps/smidja/smidja_modules/stall_watch.py +149 -0
- package/apps/smidja/smidja_modules/tracer.py +271 -0
- package/apps/smidja/smidja_modules/utils.py +77 -0
- package/apps/smidja/smidja_orchestrate.py +162 -0
- package/apps/smidja/smidja_plan.py +45 -0
- package/apps/smidja/smidja_plan_build.py +55 -0
- package/apps/smidja/smidja_plan_build_test.py +86 -0
- package/apps/smidja/smidja_plan_build_test_quality.py +98 -0
- package/apps/smidja/smidja_prompt.py +44 -0
- package/apps/smidja/smidja_quality.py +49 -0
- package/apps/smidja/smidja_recon_iv.py +202 -0
- package/apps/smidja/smidja_scout.py +45 -0
- package/apps/smidja/smidja_simple_sdlc.py +183 -0
- package/apps/smidja/smidja_smidja_config/smidja.config.yaml +143 -0
- package/apps/smidja-factory/AGENTS.md +165 -0
- package/apps/smidja-factory/README.md +3 -0
- package/apps/smidja-factory/SKILL.md +18 -0
- package/apps/smidja-factory/apps/visualizer/.oxlintrc.json +18 -0
- package/apps/smidja-factory/apps/visualizer/bun.lock +269 -0
- package/apps/smidja-factory/apps/visualizer/desktop/icon.png +0 -0
- package/apps/smidja-factory/apps/visualizer/desktop/main.js +140 -0
- package/apps/smidja-factory/apps/visualizer/desktop/package-lock.json +871 -0
- package/apps/smidja-factory/apps/visualizer/desktop/package.json +18 -0
- package/apps/smidja-factory/apps/visualizer/desktop/preload.js +19 -0
- package/apps/smidja-factory/apps/visualizer/index.html +28 -0
- package/apps/smidja-factory/apps/visualizer/package.json +31 -0
- package/apps/smidja-factory/apps/visualizer/public/icon.svg +7 -0
- package/apps/smidja-factory/apps/visualizer/public/models/claude.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/gemini.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/kimi.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/openai.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/zai.png +0 -0
- package/apps/smidja-factory/apps/visualizer/server/chat.ts +425 -0
- package/apps/smidja-factory/apps/visualizer/server/db.ts +1050 -0
- package/apps/smidja-factory/apps/visualizer/server/index.ts +648 -0
- package/apps/smidja-factory/apps/visualizer/server/model-catalog.ts +90 -0
- package/apps/smidja-factory/apps/visualizer/server/roster-api.ts +75 -0
- package/apps/smidja-factory/apps/visualizer/server/roster_resolve.py +170 -0
- package/apps/smidja-factory/apps/visualizer/server/settings.ts +106 -0
- package/apps/smidja-factory/apps/visualizer/shared/types.ts +630 -0
- package/apps/smidja-factory/apps/visualizer/src/App.vue +379 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ChatInput.vue +155 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ChatThread.vue +219 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ChatView.vue +335 -0
- package/apps/smidja-factory/apps/visualizer/src/components/DecisionsView.vue +177 -0
- package/apps/smidja-factory/apps/visualizer/src/components/DetailSection.vue +76 -0
- package/apps/smidja-factory/apps/visualizer/src/components/MemoryView.vue +397 -0
- package/apps/smidja-factory/apps/visualizer/src/components/PhaseDetail.vue +1354 -0
- package/apps/smidja-factory/apps/visualizer/src/components/PhaseDots.vue +55 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionCard.vue +583 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionMiniCard.vue +58 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionPanel.vue +272 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionStartForm.vue +152 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionTrace.vue +1157 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionsList.vue +96 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SettingsView.vue +152 -0
- package/apps/smidja-factory/apps/visualizer/src/components/StatChip.vue +98 -0
- package/apps/smidja-factory/apps/visualizer/src/components/StatsView.vue +382 -0
- package/apps/smidja-factory/apps/visualizer/src/components/StatusChip.vue +73 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ToolCallCard.vue +156 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/api.ts +177 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/chat-api.ts +95 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/chat-store.ts +351 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/events.ts +131 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/format.ts +92 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/highlight.ts +54 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/markdown.ts +120 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/models.ts +42 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/router.ts +67 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/types.ts +61 -0
- package/apps/smidja-factory/apps/visualizer/src/main.ts +13 -0
- package/apps/smidja-factory/apps/visualizer/src/style.css +592 -0
- package/apps/smidja-factory/apps/visualizer/tsconfig.json +30 -0
- package/apps/smidja-factory/apps/visualizer/vite.config.js +32 -0
- package/apps/smidja-factory/apps/visualizer/vite.config.ts +31 -0
- package/apps/smidja-factory/cookbooks/create_config.md +61 -0
- package/apps/smidja-factory/cookbooks/create_smidja.md +120 -0
- package/apps/smidja-factory/cookbooks/how_to_prompt_for_the_eng.md +110 -0
- package/apps/smidja-factory/cookbooks/install.md +56 -0
- package/apps/smidja-factory/cookbooks/run_smidja.md +122 -0
- package/apps/smidja-factory/cookbooks/smidja_overview.md +88 -0
- package/apps/smidja-factory/cookbooks/update_config.md +103 -0
- package/apps/smidja-factory/cookbooks/update_modules.md +100 -0
- package/apps/smidja-factory/cookbooks/update_smidja.md +90 -0
- package/apps/smidja-factory/docs/enhancement-plan.md +420 -0
- package/apps/smidja-factory/references/config.md +202 -0
- package/apps/smidja-factory/references/handoff.md +161 -0
- package/apps/smidja-factory/references/observability.md +166 -0
- package/apps/smidja-factory/scripts/install.py +113 -0
- package/apps/smidja-factory/scripts/make_config.py +35 -0
- package/apps/smidja-factory/scripts/make_smidja.py +119 -0
- package/apps/smidja-factory/skills/add-or-edit-ai-models/SKILL.md +151 -0
- package/apps/smidja-factory/skills/create-new-agent/SKILL.md +125 -0
- package/apps/smidja-factory/skills/create-new-teams/SKILL.md +151 -0
- package/apps/smidja-factory/skills/git-ops/README.md +25 -0
- package/apps/smidja-factory/skills/git-ops/SKILL.md +18 -0
- package/apps/smidja-factory/skills/git-ops/create_branch.sh +79 -0
- package/apps/smidja-factory/skills/git-ops/safe_commit.sh +82 -0
- package/apps/smidja-factory/skills/git-ops/sync_upstream.sh +94 -0
- package/apps/smidja-factory/skills/how-to-run-agent-teams/SKILL.md +117 -0
- package/apps/smidja-factory/skills/smidja-instructions/SKILL.md +168 -0
- package/apps/smidja-factory/skills/smidja-launcher/SKILL.md +155 -0
- package/apps/smidja-factory/skills/smidja-start/SKILL.md +266 -0
- package/apps/smidja-factory/skills/volundr/SKILL.md +232 -0
- package/apps/smidja-factory/skills/volundr/prompt/dispatch-prompt.md +91 -0
- package/apps/smidja-factory/skills/volundr/prompt/volundr-system.md +63 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-observe.py +52 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-recall.py +54 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-status.py +51 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-teach.py +47 -0
- package/apps/smidja-factory/templates/env.sample +28 -0
- package/apps/smidja-factory/templates/harness_engineering/subagents.ts +706 -0
- package/apps/smidja-factory/templates/harness_engineering/themeMap.ts +145 -0
- package/apps/smidja-factory/templates/justfile +89 -0
- package/apps/smidja-factory/templates/prompt_engineering/builder/system.md +13 -0
- package/apps/smidja-factory/templates/prompt_engineering/builder/user.md +34 -0
- package/apps/smidja-factory/templates/prompt_engineering/documenter/system.md +17 -0
- package/apps/smidja-factory/templates/prompt_engineering/documenter/user.md +48 -0
- package/apps/smidja-factory/templates/prompt_engineering/orchestrator/system.md +55 -0
- package/apps/smidja-factory/templates/prompt_engineering/orchestrator/user.md +39 -0
- package/apps/smidja-factory/templates/prompt_engineering/planner/system.md +21 -0
- package/apps/smidja-factory/templates/prompt_engineering/planner/user.md +45 -0
- package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/system.md +51 -0
- package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/user.md +26 -0
- package/apps/smidja-factory/templates/prompt_engineering/reviewer/system.md +16 -0
- package/apps/smidja-factory/templates/prompt_engineering/reviewer/user.md +44 -0
- package/apps/smidja-factory/templates/prompt_engineering/scout/system.md +32 -0
- package/apps/smidja-factory/templates/prompt_engineering/scout/user.md +34 -0
- package/apps/smidja-factory/templates/smidja/smidja_build.py +45 -0
- package/apps/smidja-factory/templates/smidja/smidja_build_review.py +76 -0
- package/apps/smidja-factory/templates/smidja/smidja_build_test.py +79 -0
- package/apps/smidja-factory/templates/smidja/smidja_document.py +76 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agent_cc.py +15 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agent_opencode.py +194 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agent_pi.py +392 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agents.py +993 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/changes.py +103 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/console.py +131 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/data_types.py +447 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/gates.py +108 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/git_helper.py +120 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/lm_local.py +144 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/permissions.py +194 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/prompts.py +21 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/quality.py +240 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/runner.py +155 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/session.py +50 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/stall_watch.py +149 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/tracer.py +271 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/utils.py +77 -0
- package/apps/smidja-factory/templates/smidja/smidja_orchestrate.py +162 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan.py +45 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan_build.py +55 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan_build_test.py +86 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan_build_test_quality.py +98 -0
- package/apps/smidja-factory/templates/smidja/smidja_prompt.py +44 -0
- package/apps/smidja-factory/templates/smidja/smidja_quality.py +49 -0
- package/apps/smidja-factory/templates/smidja/smidja_recon_iv.py +202 -0
- package/apps/smidja-factory/templates/smidja/smidja_scout.py +45 -0
- package/apps/smidja-factory/templates/smidja/smidja_simple_sdlc.py +183 -0
- package/apps/smidja-factory/templates/smidja.config.yaml +143 -0
- package/bin/app-build.sh +36 -0
- package/bin/electron-lib.sh +25 -0
- package/bin/sessrumnir.sh +1 -1
- package/bin/ymir-install.sh +7 -0
- package/package.json +15 -7
- package/scripts/electron.sh +6 -2
- package/.agents/bus/messages.json +0 -1
- package/.agents/config/agents.machine.example.yaml +0 -16
- package/.pi/mcp.json +0 -16
- package/assets/reference/state/.branch-eligible-owner +0 -4
- package/assets/reference/state/.branch-mirror-cursor +0 -1
- package/assets/reference/state/.branch-outcomes-cursor +0 -1
- package/assets/reference/state/.branch-session +0 -1
- package/assets/reference/state/.count-default_wF_p5 +0 -1
- package/assets/reference/state/.count-default_wF_p6 +0 -1
- package/assets/reference/state/.count-default_wR_p2 +0 -1
- package/assets/reference/state/.count-default_wS_p2 +0 -1
- package/assets/reference/state/.count-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.hash-default_wF_p5 +0 -1
- package/assets/reference/state/.hash-default_wF_p6 +0 -1
- package/assets/reference/state/.hash-default_wR_p2 +0 -1
- package/assets/reference/state/.hash-default_wS_p2 +0 -1
- package/assets/reference/state/.hash-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.heartbeat-streak +0 -1
- package/assets/reference/state/.inactive-outcome-reconcile +0 -2
- package/assets/reference/state/.lock +0 -1
- package/assets/reference/state/.pi-branch-extension-loaded +0 -1
- package/assets/reference/state/.pi-turnend-extension-loaded +0 -2
- package/assets/reference/state/.pi-watch-extension-loaded +0 -2
- package/assets/reference/state/.seen-server-knowledge_turn-ended +0 -1
- package/assets/reference/state/.session-start-agents-baseline +0 -2
- package/assets/reference/state/.session-start-complete +0 -1
- package/assets/reference/state/.stale-default_wF_p5 +0 -1
- package/assets/reference/state/.stale-default_wR_p2 +0 -1
- package/assets/reference/state/.stale-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.stale-since-default_wF_p5 +0 -1
- package/assets/reference/state/.stale-since-default_wR_p2 +0 -1
- package/assets/reference/state/.stale-since-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.startup-network.delivered +0 -1
- package/assets/reference/state/.startup-network.report +0 -1
- package/assets/reference/state/.startup-network.status +0 -10
- package/assets/reference/state/.startup-network.timings +0 -6
- package/assets/reference/state/.trace-context-effective +0 -1
- package/assets/reference/state/.wake-queue +0 -0
- package/assets/reference/state/.wake-queue.seq +0 -1
- package/assets/reference/state/.wake-rows.consume.BcqXSq +0 -0
- package/assets/reference/state/.wake-rows.consume.NDZZei +0 -0
- package/assets/reference/state/.wake-rows.consume.VGkhYm +0 -0
- package/assets/reference/state/.wake-rows.consume.tR2rTy +0 -0
- package/assets/reference/state/.watch-arm-output.3581p6 +0 -0
- package/assets/reference/state/.watch-arm-output.3ciDNP +0 -0
- package/assets/reference/state/.watch-arm-output.SksR6k +0 -0
- package/assets/reference/state/.watch-cycle-exits.log +0 -22
- package/assets/reference/state/.watch-deliveries.log +0 -19
- package/assets/reference/state/.watch-triage.log +0 -38
- package/assets/reference/state/.watch.lock/fm-home +0 -1
- package/assets/reference/state/.watch.lock/pid +0 -1
- package/assets/reference/state/.watch.lock/pid-identity +0 -1
- package/assets/reference/state/.watch.lock/watcher-path +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/fm-home +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/pid +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/pid-identity +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/watcher-path +0 -1
- package/assets/reference/state/.watcher-down +0 -1
- package/assets/reference/state/branch-outcomes.jsonl +0 -1
- package/assets/reference/state/branch-session/2026-09-04T16-49-38-671Z_01a06d53-6b6f-7619-930d-e6dfd9a4a29e.jsonl +0 -211
- package/assets/reference/state/home-summary.json +0 -42
- package/assets/reference/state/server-knowledge.busy-gen +0 -1
- package/assets/reference/state/server-knowledge.busy-state +0 -1
- package/assets/reference/state/server-knowledge.herdr-presentation +0 -12
- package/assets/reference/state/server-knowledge.meta +0 -18
- package/assets/reference/state/server-knowledge.pi-ext.ts +0 -26
- package/assets/reference/state/server-knowledge.turn-ended +0 -0
- /package/{assets/reference/state/.last-check → apps/hlidskjalf/src/app/.gitkeep} +0 -0
- /package/{assets/reference/state/.last-heartbeat → apps/hlidskjalf/src/components/.gitkeep} +0 -0
- /package/{assets/reference/state/.last-watcher-beat → apps/hlidskjalf/src/services/.gitkeep} +0 -0
- /package/{assets/reference/state/.main-eligible-rows.tmp.p2HMKo → apps/smidja/smidja_modules/__init__.py} +0 -0
- /package/{assets/reference/state/.main-eligible-rows.tmp.Exxm82 → apps/smidja-factory/templates/smidja/smidja_modules/__init__.py} +0 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, writeFile, unlink } from 'node:fs/promises'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { isIPv6 } from 'node:net'
|
|
4
|
+
import {
|
|
5
|
+
validateThemeFile, themeIdFromName, MAX_THEME_FILE_BYTES,
|
|
6
|
+
MAX_THEME_AUTHOR_LENGTH, MAX_THEME_DESCRIPTION_LENGTH, type ThemeFile,
|
|
7
|
+
} from '../shared/theme/theme-file'
|
|
8
|
+
import { BUILTIN_THEME_IDS } from '../shared/theme/builtin-ids'
|
|
9
|
+
import type { GalleryTheme } from '../shared/ipc-contracts'
|
|
10
|
+
import { t, tEnglish } from '../shared/i18n'
|
|
11
|
+
|
|
12
|
+
const THEME_FILE_EXT = '.json'
|
|
13
|
+
const VALID_THEME_ID = /^[a-z0-9-]+$/
|
|
14
|
+
|
|
15
|
+
export interface UserThemeList {
|
|
16
|
+
themes: Array<{ id: string; file: ThemeFile }>
|
|
17
|
+
warnings: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Theme file names in `dir`, or a warning when the directory cannot be read. */
|
|
21
|
+
async function readThemeEntries(dir: string): Promise<{ entries: string[]; warning: string | null }> {
|
|
22
|
+
try {
|
|
23
|
+
await mkdir(dir, { recursive: true })
|
|
24
|
+
return { entries: (await readdir(dir)).filter((f) => f.endsWith(THEME_FILE_EXT)).sort(), warning: null }
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { entries: [], warning: `${dir}: ${error instanceof Error ? error.message : String(error)}` }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function listUserThemes(dir: string): Promise<UserThemeList> {
|
|
31
|
+
const themes: UserThemeList['themes'] = []
|
|
32
|
+
const { entries, warning } = await readThemeEntries(dir)
|
|
33
|
+
const warnings: string[] = warning ? [warning] : []
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
const id = entry.slice(0, -THEME_FILE_EXT.length)
|
|
36
|
+
try {
|
|
37
|
+
// These warnings only ever reach console.warn (renderer store.ts), never
|
|
38
|
+
// the UI, so validation errors are rendered in fixed English (ruling R15).
|
|
39
|
+
const file = validateThemeFile(JSON.parse(await readFile(join(dir, entry), 'utf8')), tEnglish)
|
|
40
|
+
// Theme files are untrusted input (imported from disk or installed
|
|
41
|
+
// from arbitrary URLs). saveUserTheme refuses to *create* a file whose
|
|
42
|
+
// id collides with a built-in, but a colliding file can still land in
|
|
43
|
+
// this directory by other means (predates that fix, external write,
|
|
44
|
+
// future bug). If loaded, it would silently replace the real built-in
|
|
45
|
+
// in the renderer's theme registry (Map.set) on every launch, so any
|
|
46
|
+
// such file must be excluded here regardless of how it got there.
|
|
47
|
+
if ((BUILTIN_THEME_IDS as readonly string[]).includes(id)) {
|
|
48
|
+
warnings.push(`${entry}: id '${id}' collides with a built-in theme and was ignored`)
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
themes.push({ id, file })
|
|
52
|
+
} catch (error) {
|
|
53
|
+
warnings.push(`${entry}: ${error instanceof Error ? error.message : String(error)}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { themes, warnings }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const IDENTITY_SEPARATOR = ' '
|
|
60
|
+
|
|
61
|
+
function themeIdentity(file: ThemeFile): string {
|
|
62
|
+
return `${file.name}${IDENTITY_SEPARATOR}${file.kind}`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// A real theme's identity is always `${name}${IDENTITY_SEPARATOR}${kind}`
|
|
66
|
+
// with kind restricted to 'dark' | 'light', so it can never equal this
|
|
67
|
+
// sentinel. Seeding the taken-id map with it for every built-in id forces
|
|
68
|
+
// the numeric-suffix loop below to run whenever a fresh save's base id
|
|
69
|
+
// collides with a built-in, without disturbing the legitimate "resave of an
|
|
70
|
+
// existing user theme file" identity-match path (a built-in is never itself
|
|
71
|
+
// a file in the user themes directory, so it can never be the thing a
|
|
72
|
+
// resave is legitimately updating).
|
|
73
|
+
const BUILTIN_IDENTITY_SENTINEL = '\0builtin'
|
|
74
|
+
|
|
75
|
+
const SUFFIX_START = 2
|
|
76
|
+
|
|
77
|
+
// Windows treats these as device names even with a file extension, so a slug
|
|
78
|
+
// matching one cannot be written as "<id>.json" on Windows (fs writes fail or
|
|
79
|
+
// hit the device). Blocking them in the id search makes the suffix loop yield
|
|
80
|
+
// a safe id (e.g. "con" -> "con-2", which is not reserved), keeping user-theme
|
|
81
|
+
// files portable across macOS, Windows, and Linux. Appending "-N" always
|
|
82
|
+
// de-reserves, so this can never loop forever.
|
|
83
|
+
const WINDOWS_RESERVED_ID = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/i
|
|
84
|
+
|
|
85
|
+
// Finds the first id starting at `base` that is neither reserved nor rejected
|
|
86
|
+
// by `isBlocked`, appending `-2`, `-3`, ... until one is free. Shared by both
|
|
87
|
+
// save paths below; only the caller's definition of "blocked" differs.
|
|
88
|
+
function nextAvailableId(base: string, isBlocked: (id: string) => boolean): string {
|
|
89
|
+
let id = base
|
|
90
|
+
for (let n = SUFFIX_START; WINDOWS_RESERVED_ID.test(id) || isBlocked(id); n += 1) id = `${base}-${n}`
|
|
91
|
+
return id
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// `existingId` is set only when the theme editor is re-saving a theme it is
|
|
95
|
+
// already editing (isUserTheme === true). It must NOT be derived from
|
|
96
|
+
// name+kind identity like the fresh-save path below: two different user
|
|
97
|
+
// themes can share a name+kind (e.g. after one of them gets renamed to
|
|
98
|
+
// match the other), and identity-matching on the *new* name would let the
|
|
99
|
+
// save silently overwrite the OTHER theme's file while the editor's
|
|
100
|
+
// rename-cleanup then deletes the theme actually being edited — destroying
|
|
101
|
+
// both. Restricting overwrite to the exact id under edit closes that path:
|
|
102
|
+
// any collision with any other id, built-in or user, is suffixed instead.
|
|
103
|
+
export async function saveUserTheme(
|
|
104
|
+
dir: string, file: ThemeFile, existingId?: string,
|
|
105
|
+
): Promise<{ id: string }> {
|
|
106
|
+
const theme = validateThemeFile(file)
|
|
107
|
+
if (existingId !== undefined) {
|
|
108
|
+
if (!VALID_THEME_ID.test(existingId)) throw new Error(`invalid theme id: ${existingId}`)
|
|
109
|
+
if ((BUILTIN_THEME_IDS as readonly string[]).includes(existingId)) {
|
|
110
|
+
throw new Error(`cannot overwrite built-in theme id: ${existingId}`)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
await mkdir(dir, { recursive: true })
|
|
114
|
+
const base = themeIdFromName(theme.name) || 'theme'
|
|
115
|
+
const { themes } = await listUserThemes(dir)
|
|
116
|
+
|
|
117
|
+
let id: string
|
|
118
|
+
if (existingId !== undefined) {
|
|
119
|
+
const takenIds = new Set<string>(BUILTIN_THEME_IDS)
|
|
120
|
+
for (const userTheme of themes) takenIds.add(userTheme.id)
|
|
121
|
+
id = nextAvailableId(base, (candidate) => takenIds.has(candidate) && candidate !== existingId)
|
|
122
|
+
} else {
|
|
123
|
+
// Fresh create, file import, or URL install: dedupe by identity
|
|
124
|
+
// (name+kind), so re-importing/re-installing the same theme keeps
|
|
125
|
+
// updating the same file instead of piling up numbered duplicates. This
|
|
126
|
+
// is intentionally different from the existingId path above — here
|
|
127
|
+
// there is no "theme under edit" to protect, so identity is a safe and
|
|
128
|
+
// desirable match key.
|
|
129
|
+
const taken = new Map<string, string>(
|
|
130
|
+
BUILTIN_THEME_IDS.map((builtinId) => [builtinId, BUILTIN_IDENTITY_SENTINEL]),
|
|
131
|
+
)
|
|
132
|
+
for (const userTheme of themes) taken.set(userTheme.id, themeIdentity(userTheme.file))
|
|
133
|
+
const identity = themeIdentity(theme)
|
|
134
|
+
id = nextAvailableId(base, (candidate) => taken.has(candidate) && taken.get(candidate) !== identity)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await writeFile(join(dir, `${id}${THEME_FILE_EXT}`), JSON.stringify(theme, null, 2))
|
|
138
|
+
return { id }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function deleteUserTheme(dir: string, id: string): Promise<void> {
|
|
142
|
+
if (!VALID_THEME_ID.test(id)) throw new Error(`invalid theme id: ${id}`)
|
|
143
|
+
await unlink(join(dir, `${id}${THEME_FILE_EXT}`))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Reads the response body incrementally, aborting as soon as the byte
|
|
147
|
+
// count exceeds limitBytes, so an oversized or unbounded response is never
|
|
148
|
+
// fully buffered into memory before the size check applies.
|
|
149
|
+
async function readCappedText(response: Response, limitBytes: number): Promise<string> {
|
|
150
|
+
const reader = response.body?.getReader()
|
|
151
|
+
if (!reader) return response.text()
|
|
152
|
+
const decoder = new TextDecoder()
|
|
153
|
+
let text = ''
|
|
154
|
+
let totalBytes = 0
|
|
155
|
+
for (;;) {
|
|
156
|
+
const { done, value } = await reader.read()
|
|
157
|
+
if (done) break
|
|
158
|
+
totalBytes += value.byteLength
|
|
159
|
+
if (totalBytes > limitBytes) {
|
|
160
|
+
await reader.cancel()
|
|
161
|
+
throw new Error(t('errors.theme.fileTooLarge', { limit: limitBytes }))
|
|
162
|
+
}
|
|
163
|
+
text += decoder.decode(value, { stream: true })
|
|
164
|
+
}
|
|
165
|
+
text += decoder.decode()
|
|
166
|
+
return text
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// --- SSRF guard for installThemeFromUrl -----------------------------------
|
|
170
|
+
//
|
|
171
|
+
// installThemeFromUrl fetches an arbitrary attacker-influenced URL from the
|
|
172
|
+
// Electron MAIN process, outside the renderer's CSP connect-src. Without
|
|
173
|
+
// host classification this is a blind SSRF primitive: pointing it (or a
|
|
174
|
+
// redirect target) at a private/loopback/link-local/cloud-metadata host
|
|
175
|
+
// makes the main process issue a real GET against internal infrastructure
|
|
176
|
+
// with no per-URL consent. Today the URL is user-typed; a planned in-app
|
|
177
|
+
// theme gallery would install from URLs listed in a fetched index.json, at
|
|
178
|
+
// which point a single poisoned gallery entry could target an internal
|
|
179
|
+
// host with no user interaction at all. This guard, plus manual redirect
|
|
180
|
+
// validation below, is the mitigation for both.
|
|
181
|
+
|
|
182
|
+
const THEME_FETCH_TIMEOUT_MS = 10_000
|
|
183
|
+
const MAX_THEME_REDIRECTS = 5
|
|
184
|
+
const REDIRECT_STATUS_CODES = new Set([301, 302, 303, 307, 308])
|
|
185
|
+
|
|
186
|
+
// CGNAT range 100.64.0.0/10: octet 2 spans 64-127.
|
|
187
|
+
const CGNAT_SECOND_OCTET_MIN = 64
|
|
188
|
+
const CGNAT_SECOND_OCTET_MAX = 127
|
|
189
|
+
// 172.16.0.0/12: octet 2 spans 16-31.
|
|
190
|
+
const RANGE_172_SECOND_OCTET_MIN = 16
|
|
191
|
+
const RANGE_172_SECOND_OCTET_MAX = 31
|
|
192
|
+
const IPV4_OCTET_MAX = 255
|
|
193
|
+
|
|
194
|
+
type IPv4Octets = [number, number, number, number]
|
|
195
|
+
|
|
196
|
+
function parseIPv4(hostname: string): IPv4Octets | null {
|
|
197
|
+
const parts = hostname.split('.')
|
|
198
|
+
if (parts.length !== 4) return null
|
|
199
|
+
const octets: number[] = []
|
|
200
|
+
for (const part of parts) {
|
|
201
|
+
if (!/^\d{1,3}$/.test(part)) return null
|
|
202
|
+
const value = Number(part)
|
|
203
|
+
if (value > IPV4_OCTET_MAX) return null
|
|
204
|
+
octets.push(value)
|
|
205
|
+
}
|
|
206
|
+
return octets as IPv4Octets
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Ranges per RFC 1918 (private), RFC 6598 (CGNAT), RFC 5735/3927 (loopback,
|
|
210
|
+
// link-local incl. 169.254.169.254 cloud metadata), and RFC 1122 (0.0.0.0/8).
|
|
211
|
+
function isBlockedIPv4([a, b]: IPv4Octets): boolean {
|
|
212
|
+
if (a === 0) return true // 0.0.0.0/8 "this network"
|
|
213
|
+
if (a === 10) return true // 10.0.0.0/8
|
|
214
|
+
if (a === 100 && b >= CGNAT_SECOND_OCTET_MIN && b <= CGNAT_SECOND_OCTET_MAX) return true // 100.64.0.0/10
|
|
215
|
+
if (a === 127) return true // 127.0.0.0/8 loopback
|
|
216
|
+
if (a === 169 && b === 254) return true // 169.254.0.0/16 link-local, incl. cloud metadata
|
|
217
|
+
if (a === 172 && b >= RANGE_172_SECOND_OCTET_MIN && b <= RANGE_172_SECOND_OCTET_MAX) return true // 172.16.0.0/12
|
|
218
|
+
if (a === 192 && b === 168) return true // 192.168.0.0/16
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const IPV4_MAPPED_PREFIX = '::ffff:'
|
|
223
|
+
// fc00::/7 (ULA): the first hextet's top 7 bits are 1111110, i.e. the
|
|
224
|
+
// hextet's leading byte is 0xfc or 0xfd.
|
|
225
|
+
const ULA_HEXTET_PREFIXES = ['fc', 'fd']
|
|
226
|
+
// fe80::/10 (link-local): the first hextet ranges 0xfe80-0xfebf.
|
|
227
|
+
const LINK_LOCAL_HEXTET_MIN = 0xfe80
|
|
228
|
+
const LINK_LOCAL_HEXTET_MAX = 0xfebf
|
|
229
|
+
|
|
230
|
+
// Extracts an embedded IPv4 address from an IPv4-mapped IPv6 literal, e.g.
|
|
231
|
+
// "::ffff:10.0.0.1" (dotted) or "::ffff:0a00:0001" (hex groups). Returns
|
|
232
|
+
// null if `lowerHostname` has no such form.
|
|
233
|
+
function extractIPv4MappedAddress(lowerHostname: string): string | null {
|
|
234
|
+
const idx = lowerHostname.lastIndexOf(IPV4_MAPPED_PREFIX)
|
|
235
|
+
if (idx === -1) return null
|
|
236
|
+
const tail = lowerHostname.slice(idx + IPV4_MAPPED_PREFIX.length)
|
|
237
|
+
if (parseIPv4(tail)) return tail
|
|
238
|
+
const hexGroups = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(tail)
|
|
239
|
+
if (!hexGroups) return null
|
|
240
|
+
const high = Number.parseInt(hexGroups[1], 16)
|
|
241
|
+
const low = Number.parseInt(hexGroups[2], 16)
|
|
242
|
+
return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// hostname is a raw IPv6 literal (brackets already stripped by URL parsing,
|
|
246
|
+
// e.g. "::1", "fc00::1"). Full IPv6 canonicalization is out of scope; this
|
|
247
|
+
// pragmatically covers the loopback/unspecified exact forms, the ULA and
|
|
248
|
+
// link-local prefixes via the first hextet, and IPv4-mapped addresses by
|
|
249
|
+
// delegating to the IPv4 rules above. Any other IPv6 form (e.g. a
|
|
250
|
+
// non-mapped global unicast address, or unusual zero-compression shapes
|
|
251
|
+
// this parsing does not normalize) is NOT further inspected and is allowed
|
|
252
|
+
// through if it isn't caught by one of these specific checks.
|
|
253
|
+
function isBlockedIPv6(hostname: string): boolean {
|
|
254
|
+
const lower = hostname.toLowerCase()
|
|
255
|
+
if (lower === '::1' || lower === '::') return true // loopback / unspecified
|
|
256
|
+
|
|
257
|
+
const mapped = extractIPv4MappedAddress(lower)
|
|
258
|
+
if (mapped) {
|
|
259
|
+
const octets = parseIPv4(mapped)
|
|
260
|
+
return octets !== null && isBlockedIPv4(octets)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const colonIndex = lower.indexOf(':')
|
|
264
|
+
const firstHextet = (colonIndex <= 0 ? '' : lower.slice(0, colonIndex)).padStart(4, '0')
|
|
265
|
+
if (ULA_HEXTET_PREFIXES.some((prefix) => firstHextet.startsWith(prefix))) return true
|
|
266
|
+
const hextetValue = Number.parseInt(firstHextet, 16)
|
|
267
|
+
return !Number.isNaN(hextetValue)
|
|
268
|
+
&& hextetValue >= LINK_LOCAL_HEXTET_MIN && hextetValue <= LINK_LOCAL_HEXTET_MAX
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Rejects any URL that would send the main process's fetch at an internal
|
|
272
|
+
// or well-known-local host. Must be called on the initial URL AND on every
|
|
273
|
+
// redirect hop's target before that hop is fetched (see installThemeFromUrl)
|
|
274
|
+
// — validating only the first URL is not sufficient because fetch resolves
|
|
275
|
+
// and connects to whatever the Location header says next.
|
|
276
|
+
function assertSafeThemeUrl(parsed: URL): void {
|
|
277
|
+
if (parsed.protocol !== 'https:') {
|
|
278
|
+
throw new Error(t('errors.theme.mustUseHttps', { protocol: parsed.protocol }))
|
|
279
|
+
}
|
|
280
|
+
// WHATWG URL.hostname keeps IPv6 literals bracketed (e.g. "[::1]"), unlike
|
|
281
|
+
// the "brackets already stripped" shape node:net's isIP()/isIPv6() and our
|
|
282
|
+
// own IPv6 parsing below expect, so unwrap them once up front.
|
|
283
|
+
const rawHostname = parsed.hostname.toLowerCase()
|
|
284
|
+
const hostname = rawHostname.startsWith('[') && rawHostname.endsWith(']')
|
|
285
|
+
? rawHostname.slice(1, -1)
|
|
286
|
+
: rawHostname
|
|
287
|
+
if (hostname === 'localhost' || hostname.endsWith('.localhost') || hostname.endsWith('.local')) {
|
|
288
|
+
throw new Error(t('errors.theme.hostBlockedLocal', { host: hostname }))
|
|
289
|
+
}
|
|
290
|
+
const ipv4 = parseIPv4(hostname)
|
|
291
|
+
if (ipv4 && isBlockedIPv4(ipv4)) {
|
|
292
|
+
throw new Error(t('errors.theme.hostBlockedIPv4', { host: hostname }))
|
|
293
|
+
}
|
|
294
|
+
if (isIPv6(hostname) && isBlockedIPv6(hostname)) {
|
|
295
|
+
throw new Error(t('errors.theme.hostBlockedIPv6', { host: hostname }))
|
|
296
|
+
}
|
|
297
|
+
// Residual limitation, deliberately not solved here: a *hostname* that is
|
|
298
|
+
// not an IP literal (e.g. an attacker-registered "public" domain) can
|
|
299
|
+
// still resolve via DNS to a private/loopback address at request time
|
|
300
|
+
// (DNS rebinding). String classification of the URL cannot see what
|
|
301
|
+
// address DNS will hand back, and Node's fetch resolves + connects
|
|
302
|
+
// atomically with no hook to re-validate the resolved address first.
|
|
303
|
+
// Closing that gap needs a custom dispatcher that pins and validates the
|
|
304
|
+
// resolved IP per connection (and re-validates on every redirect), which
|
|
305
|
+
// is out of scope for this fix. This guard covers IP-literal SSRF and
|
|
306
|
+
// well-known local hostnames only, not DNS-rebinding SSRF.
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Fetches `url` following redirects manually, re-running the SSRF guard on
|
|
310
|
+
// every hop's target before it is fetched, with a per-request timeout and a
|
|
311
|
+
// redirect cap. Returns the first non-redirect Response (the caller checks
|
|
312
|
+
// `.ok` and reads the body under a size cap). Shared by installThemeFromUrl
|
|
313
|
+
// and fetchGalleryThemes so both get identical redirect/SSRF protection.
|
|
314
|
+
async function guardedFetch(url: string, fetchFn: typeof fetch): Promise<Response> {
|
|
315
|
+
let current = new URL(url)
|
|
316
|
+
assertSafeThemeUrl(current)
|
|
317
|
+
|
|
318
|
+
let response: Response
|
|
319
|
+
let redirects = 0
|
|
320
|
+
for (;;) {
|
|
321
|
+
// Each hop's target depends on the previous hop's response, so this
|
|
322
|
+
// await must stay sequential rather than being parallelized.
|
|
323
|
+
response = await fetchFn(current.toString(), {
|
|
324
|
+
redirect: 'manual',
|
|
325
|
+
signal: AbortSignal.timeout(THEME_FETCH_TIMEOUT_MS),
|
|
326
|
+
})
|
|
327
|
+
if (!REDIRECT_STATUS_CODES.has(response.status)) break
|
|
328
|
+
redirects += 1
|
|
329
|
+
if (redirects > MAX_THEME_REDIRECTS) {
|
|
330
|
+
throw new Error(t('errors.theme.tooManyRedirects', { max: MAX_THEME_REDIRECTS }))
|
|
331
|
+
}
|
|
332
|
+
const location = response.headers.get('location')
|
|
333
|
+
if (!location) throw new Error(t('errors.theme.redirectMissingLocation', { status: response.status }))
|
|
334
|
+
// Resolve relative to the current hop, then re-validate before the next
|
|
335
|
+
// hop is fetched: this is what stops a public URL from bouncing the
|
|
336
|
+
// GET onto an internal host via a redirect the guard never saw.
|
|
337
|
+
current = new URL(location, current)
|
|
338
|
+
assertSafeThemeUrl(current)
|
|
339
|
+
}
|
|
340
|
+
return response
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export async function installThemeFromUrl(
|
|
344
|
+
dir: string, url: string, fetchFn: typeof fetch = fetch,
|
|
345
|
+
): Promise<{ id: string; file: ThemeFile }> {
|
|
346
|
+
const response = await guardedFetch(url, fetchFn)
|
|
347
|
+
if (!response.ok) throw new Error(t('errors.theme.downloadFailed', { status: response.status }))
|
|
348
|
+
const body = await readCappedText(response, MAX_THEME_FILE_BYTES)
|
|
349
|
+
const file = validateThemeFile(JSON.parse(body))
|
|
350
|
+
const { id } = await saveUserTheme(dir, file)
|
|
351
|
+
return { id, file }
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// --- Community gallery ------------------------------------------------------
|
|
355
|
+
//
|
|
356
|
+
// The gallery is a first-party GitHub repo whose index.json lists themes as
|
|
357
|
+
// { name, kind, file } where `file` is a repo-relative path. The install URL
|
|
358
|
+
// is built here as GALLERY_RAW_BASE + '/' + file, NOT taken from the entry as
|
|
359
|
+
// a full URL: this pins every gallery install to the gallery's own raw host
|
|
360
|
+
// and path, so even a compromised index can only reference files inside the
|
|
361
|
+
// (PR-reviewed, CI-validated) gallery repo — it can never point an install at
|
|
362
|
+
// an arbitrary or internal host. Each `file` is still validated against a
|
|
363
|
+
// strict pattern to block path traversal, and installs go through
|
|
364
|
+
// installThemeFromUrl, which re-validates content and re-runs the SSRF guard.
|
|
365
|
+
|
|
366
|
+
const GALLERY_RAW_BASE =
|
|
367
|
+
'https://raw.githubusercontent.com/FaqFirebase/pi-desktop-themes/main'
|
|
368
|
+
const GALLERY_INDEX_URL = `${GALLERY_RAW_BASE}/index.json`
|
|
369
|
+
// The index embeds each theme's full content for gallery preview cards, so
|
|
370
|
+
// its cap is a multiple of the single-file cap rather than equal to it.
|
|
371
|
+
const MAX_GALLERY_INDEX_BYTES = 1048576
|
|
372
|
+
// Accepts both gallery layouts: the current per-theme folder form
|
|
373
|
+
// (themes/<slug>/theme.json) and the original flat form (themes/<slug>.json),
|
|
374
|
+
// so the app keeps working across the repo's layout migration. The character
|
|
375
|
+
// class has no '.', '/' or '\', so neither form can smuggle traversal or an
|
|
376
|
+
// absolute/other-host URL into the pinned base join.
|
|
377
|
+
const GALLERY_FILE_PATH = /^themes\/[a-z0-9-]+(?:\/theme)?\.json$/
|
|
378
|
+
// An optional author screenshot lives beside the theme file. Only these exact
|
|
379
|
+
// shapes are ever fetched, so a poisoned index can at most reference an image
|
|
380
|
+
// already committed under themes/<slug>/ in the pinned repo.
|
|
381
|
+
const GALLERY_SCREENSHOT_PATH = /^themes\/[a-z0-9-]+\/screenshot\.(?:png|jpe?g|webp)$/
|
|
382
|
+
const MAX_GALLERY_IMAGE_BYTES = 2097152
|
|
383
|
+
// Content types allowed for a fetched screenshot. An allowlist, not an
|
|
384
|
+
// `image/` prefix: it keeps out image/svg+xml (SVG can carry script) and any
|
|
385
|
+
// other exotic image type even if the pinned path check were ever loosened.
|
|
386
|
+
const GALLERY_IMAGE_CONTENT_TYPES = ['image/png', 'image/jpeg', 'image/webp']
|
|
387
|
+
|
|
388
|
+
// Untrusted display string from the gallery index: usable only when it is a
|
|
389
|
+
// non-empty string within the cap; anything else is treated as absent.
|
|
390
|
+
function displayText(value: unknown, maxLength: number): string | undefined {
|
|
391
|
+
if (typeof value !== 'string') return undefined
|
|
392
|
+
const trimmed = value.trim()
|
|
393
|
+
if (trimmed.length === 0 || trimmed.length > maxLength) return undefined
|
|
394
|
+
return trimmed
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export async function fetchGalleryThemes(fetchFn: typeof fetch = fetch): Promise<GalleryTheme[]> {
|
|
398
|
+
const response = await guardedFetch(GALLERY_INDEX_URL, fetchFn)
|
|
399
|
+
if (!response.ok) throw new Error(t('errors.theme.galleryIndexDownloadFailed', { status: response.status }))
|
|
400
|
+
const body = await readCappedText(response, MAX_GALLERY_INDEX_BYTES)
|
|
401
|
+
const parsed: unknown = JSON.parse(body)
|
|
402
|
+
if (!Array.isArray(parsed)) throw new Error(t('errors.theme.galleryIndexNotArray'))
|
|
403
|
+
|
|
404
|
+
const themes: GalleryTheme[] = []
|
|
405
|
+
for (const entry of parsed) {
|
|
406
|
+
if (typeof entry !== 'object' || entry === null) continue
|
|
407
|
+
const { name, kind, file, author, description, theme } = entry as Record<string, unknown>
|
|
408
|
+
if (typeof name !== 'string' || name.trim().length === 0) continue
|
|
409
|
+
if (kind !== 'dark' && kind !== 'light') continue
|
|
410
|
+
if (typeof file !== 'string' || !GALLERY_FILE_PATH.test(file)) continue
|
|
411
|
+
|
|
412
|
+
// The embedded theme content and metadata come from the same untrusted
|
|
413
|
+
// index bytes as everything else, so they get the full theme validator
|
|
414
|
+
// (which also caps author/description). An entry whose embedded theme
|
|
415
|
+
// fails validation is kept, but without a preview — installing it still
|
|
416
|
+
// fetches and validates the canonical file, which is the real gate.
|
|
417
|
+
let embedded: ThemeFile | undefined
|
|
418
|
+
try {
|
|
419
|
+
embedded = theme === undefined ? undefined : validateThemeFile(theme)
|
|
420
|
+
} catch {
|
|
421
|
+
embedded = undefined
|
|
422
|
+
}
|
|
423
|
+
const galleryTheme: GalleryTheme = { name, kind, url: `${GALLERY_RAW_BASE}/${file}` }
|
|
424
|
+
if (embedded) galleryTheme.theme = embedded
|
|
425
|
+
// Metadata: the embedded theme's own fields win (the file is the source
|
|
426
|
+
// of truth); entry-level fields fill the gaps. Entry-level strings do not
|
|
427
|
+
// pass through the theme validator, so cap them here the same way.
|
|
428
|
+
galleryTheme.author = embedded?.author
|
|
429
|
+
?? displayText(author, MAX_THEME_AUTHOR_LENGTH)
|
|
430
|
+
galleryTheme.description = embedded?.description
|
|
431
|
+
?? displayText(description, MAX_THEME_DESCRIPTION_LENGTH)
|
|
432
|
+
const { screenshot } = entry as Record<string, unknown>
|
|
433
|
+
if (typeof screenshot === 'string' && GALLERY_SCREENSHOT_PATH.test(screenshot)) {
|
|
434
|
+
galleryTheme.screenshotUrl = `${GALLERY_RAW_BASE}/${screenshot}`
|
|
435
|
+
}
|
|
436
|
+
themes.push(galleryTheme)
|
|
437
|
+
}
|
|
438
|
+
return themes
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Reads a response body as bytes, aborting once the cap is exceeded — the
|
|
442
|
+
// binary counterpart of readCappedText, for screenshots.
|
|
443
|
+
async function readCappedBytes(response: Response, limitBytes: number): Promise<Uint8Array> {
|
|
444
|
+
const reader = response.body?.getReader()
|
|
445
|
+
if (!reader) {
|
|
446
|
+
const buffer = new Uint8Array(await response.arrayBuffer())
|
|
447
|
+
if (buffer.byteLength > limitBytes) {
|
|
448
|
+
throw new Error(t('errors.theme.screenshotTooLarge', { limit: limitBytes }))
|
|
449
|
+
}
|
|
450
|
+
return buffer
|
|
451
|
+
}
|
|
452
|
+
const chunks: Uint8Array[] = []
|
|
453
|
+
let total = 0
|
|
454
|
+
for (;;) {
|
|
455
|
+
const { done, value } = await reader.read()
|
|
456
|
+
if (done) break
|
|
457
|
+
total += value.byteLength
|
|
458
|
+
if (total > limitBytes) {
|
|
459
|
+
await reader.cancel()
|
|
460
|
+
throw new Error(t('errors.theme.screenshotTooLarge', { limit: limitBytes }))
|
|
461
|
+
}
|
|
462
|
+
chunks.push(value)
|
|
463
|
+
}
|
|
464
|
+
const out = new Uint8Array(total)
|
|
465
|
+
let offset = 0
|
|
466
|
+
for (const chunk of chunks) {
|
|
467
|
+
out.set(chunk, offset)
|
|
468
|
+
offset += chunk.byteLength
|
|
469
|
+
}
|
|
470
|
+
return out
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Fetches an author screenshot and returns it as a data: URI (the renderer's
|
|
474
|
+
// CSP allows data: images but not remote hosts, so the fetch must happen here).
|
|
475
|
+
// The URL is re-pinned to the gallery base + screenshot path even though it
|
|
476
|
+
// was built here, and the response must actually be an image — the renderer
|
|
477
|
+
// hands the URL back over IPC, so it is treated as untrusted on the way in.
|
|
478
|
+
export async function fetchGalleryImage(
|
|
479
|
+
url: string, fetchFn: typeof fetch = fetch,
|
|
480
|
+
): Promise<{ dataUri: string }> {
|
|
481
|
+
const prefix = `${GALLERY_RAW_BASE}/`
|
|
482
|
+
if (!url.startsWith(prefix) || !GALLERY_SCREENSHOT_PATH.test(url.slice(prefix.length))) {
|
|
483
|
+
throw new Error(t('errors.theme.screenshotPathNotAllowed'))
|
|
484
|
+
}
|
|
485
|
+
const response = await guardedFetch(url, fetchFn)
|
|
486
|
+
if (!response.ok) throw new Error(t('errors.theme.screenshotDownloadFailed', { status: response.status }))
|
|
487
|
+
const contentType = (response.headers.get('content-type') ?? '').split(';')[0].trim().toLowerCase()
|
|
488
|
+
if (!GALLERY_IMAGE_CONTENT_TYPES.includes(contentType)) {
|
|
489
|
+
throw new Error(contentType
|
|
490
|
+
? t('errors.theme.screenshotTypeNotAllowed', { contentType })
|
|
491
|
+
: t('errors.theme.screenshotTypeMissing'))
|
|
492
|
+
}
|
|
493
|
+
const bytes = await readCappedBytes(response, MAX_GALLERY_IMAGE_BYTES)
|
|
494
|
+
return { dataUri: `data:${contentType};base64,${Buffer.from(bytes).toString('base64')}` }
|
|
495
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { shouldHideToTray, trayIsSupported, parseDbusBoolean } from './tray-decision'
|
|
4
|
+
|
|
5
|
+
test('parseDbusBoolean reads NameHasOwner and property replies', () => {
|
|
6
|
+
assert.equal(parseDbusBoolean(' boolean true\n'), true)
|
|
7
|
+
assert.equal(parseDbusBoolean(' boolean false\n'), false)
|
|
8
|
+
// Property Get wraps the value in a variant.
|
|
9
|
+
assert.equal(parseDbusBoolean(' variant boolean true\n'), true)
|
|
10
|
+
assert.equal(parseDbusBoolean(' variant boolean false\n'), false)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('parseDbusBoolean returns null when no boolean is present', () => {
|
|
14
|
+
assert.equal(parseDbusBoolean(''), null)
|
|
15
|
+
assert.equal(parseDbusBoolean('Error org.freedesktop.DBus.Error.UnknownObject'), null)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('trayIsSupported is true on Windows and Linux, false on macOS', () => {
|
|
19
|
+
assert.equal(trayIsSupported('win32'), true)
|
|
20
|
+
assert.equal(trayIsSupported('linux'), true)
|
|
21
|
+
assert.equal(trayIsSupported('darwin'), false)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('shouldHideToTray hides when enabled, not quitting, tray available, on a supported platform', () => {
|
|
25
|
+
assert.equal(shouldHideToTray({ isQuitting: false, enabled: true, platform: 'win32', trayAvailable: true }), true)
|
|
26
|
+
assert.equal(shouldHideToTray({ isQuitting: false, enabled: true, platform: 'linux', trayAvailable: true }), true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('shouldHideToTray never hides when the tray is not actually available', () => {
|
|
30
|
+
// e.g. GNOME without the AppIndicator extension, minimal WM, or no session bus.
|
|
31
|
+
assert.equal(shouldHideToTray({ isQuitting: false, enabled: true, platform: 'linux', trayAvailable: false }), false)
|
|
32
|
+
assert.equal(shouldHideToTray({ isQuitting: false, enabled: true, platform: 'win32', trayAvailable: false }), false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('shouldHideToTray never hides during a real quit', () => {
|
|
36
|
+
assert.equal(shouldHideToTray({ isQuitting: true, enabled: true, platform: 'win32', trayAvailable: true }), false)
|
|
37
|
+
assert.equal(shouldHideToTray({ isQuitting: true, enabled: true, platform: 'linux', trayAvailable: true }), false)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('shouldHideToTray does nothing when the setting is off', () => {
|
|
41
|
+
assert.equal(shouldHideToTray({ isQuitting: false, enabled: false, platform: 'win32', trayAvailable: true }), false)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('shouldHideToTray never hides on macOS even when enabled and available', () => {
|
|
45
|
+
assert.equal(shouldHideToTray({ isQuitting: false, enabled: true, platform: 'darwin', trayAvailable: true }), false)
|
|
46
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Pure decision logic for "minimize to system tray on close". Kept free of any
|
|
2
|
+
// `electron` import so it can be unit-tested under a plain Node runtime. The
|
|
3
|
+
// electron-facing Tray lifecycle lives in tray-manager.ts.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse a `dbus-send --print-reply` boolean reply body. Handles both a bare
|
|
7
|
+
* `boolean <v>` (NameHasOwner) and a `variant boolean <v>` (a property Get).
|
|
8
|
+
* Returns null when no boolean is present (e.g. an error reply or empty output),
|
|
9
|
+
* so callers can distinguish "definitely false" from "couldn't determine".
|
|
10
|
+
*/
|
|
11
|
+
export function parseDbusBoolean(stdout: string): boolean | null {
|
|
12
|
+
if (/\bboolean\s+true\b/.test(stdout)) return true
|
|
13
|
+
if (/\bboolean\s+false\b/.test(stdout)) return false
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Platforms where the app supports a system-tray / minimize-to-tray idiom. */
|
|
18
|
+
export function trayIsSupported(platform: NodeJS.Platform): boolean {
|
|
19
|
+
// macOS is intentionally excluded: closing the window there already keeps the
|
|
20
|
+
// app alive in the Dock, which is the native equivalent. Adding a menu-bar
|
|
21
|
+
// item would be a different convention (see the feature design decision).
|
|
22
|
+
return platform === 'win32' || platform === 'linux'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Whether a window `close` should be intercepted and turned into "hide to tray"
|
|
27
|
+
* instead of a real close.
|
|
28
|
+
*
|
|
29
|
+
* `trayAvailable` is the critical guard for cross-platform robustness: on a
|
|
30
|
+
* system with no working system tray (e.g. GNOME without the AppIndicator
|
|
31
|
+
* extension, a minimal WM, or no session bus), hiding the window would strand
|
|
32
|
+
* it behind a non-existent icon. When the tray isn't available we fall through
|
|
33
|
+
* to a normal close so the app quits as expected instead of vanishing.
|
|
34
|
+
*
|
|
35
|
+
* @param isQuitting true once a real quit is underway (set in `before-quit`),
|
|
36
|
+
* so tray Quit / Cmd-Ctrl+Q / menu Quit still exit.
|
|
37
|
+
* @param enabled the user's `minimizeToTrayOnClose` setting.
|
|
38
|
+
* @param platform `process.platform`.
|
|
39
|
+
* @param trayAvailable whether a usable tray icon actually exists this session.
|
|
40
|
+
*/
|
|
41
|
+
export function shouldHideToTray(params: {
|
|
42
|
+
isQuitting: boolean
|
|
43
|
+
enabled: boolean
|
|
44
|
+
platform: NodeJS.Platform
|
|
45
|
+
trayAvailable: boolean
|
|
46
|
+
}): boolean {
|
|
47
|
+
const { isQuitting, enabled, platform, trayAvailable } = params
|
|
48
|
+
return !isQuitting && enabled && trayIsSupported(platform) && trayAvailable
|
|
49
|
+
}
|