heyio 3.0.2 → 3.0.3
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/dist/api/server.js +1 -1
- package/dist/api/server.js.map +1 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +13 -1
- package/dist/logging/logger.js.map +1 -1
- package/node_modules/@io/shared/package.json +1 -1
- package/package.json +7 -2
- package/public/assets/index-2RY89H3W.js +336 -0
- package/public/assets/index-2RY89H3W.js.map +1 -0
- package/public/assets/index-D3cGfBsj.css +1 -0
- package/public/index.html +14 -0
- package/src/api/middleware/auth.ts +0 -76
- package/src/api/notifications.ts +0 -122
- package/src/api/routes/activity.ts +0 -29
- package/src/api/routes/attachments.ts +0 -93
- package/src/api/routes/config.ts +0 -115
- package/src/api/routes/conversations.ts +0 -87
- package/src/api/routes/health.ts +0 -18
- package/src/api/routes/inbox.ts +0 -98
- package/src/api/routes/schedules.ts +0 -121
- package/src/api/routes/skills.ts +0 -105
- package/src/api/routes/squads.ts +0 -145
- package/src/api/routes/usage.ts +0 -57
- package/src/api/routes/wiki.ts +0 -49
- package/src/api/server.ts +0 -186
- package/src/config.ts +0 -3
- package/src/copilot/client.ts +0 -42
- package/src/copilot/health-monitor.ts +0 -85
- package/src/copilot/orchestrator.ts +0 -222
- package/src/copilot/tools.ts +0 -707
- package/src/index.ts +0 -113
- package/src/logging/logger.ts +0 -26
- package/src/models/index.ts +0 -11
- package/src/models/pricing.ts +0 -121
- package/src/models/registry.ts +0 -131
- package/src/models/token-tracker.ts +0 -151
- package/src/scheduler/engine.ts +0 -146
- package/src/skills/index.ts +0 -13
- package/src/skills/store.ts +0 -188
- package/src/squad/agent.ts +0 -326
- package/src/squad/autonomy.ts +0 -78
- package/src/squad/event-bus.ts +0 -71
- package/src/squad/execution/index.ts +0 -17
- package/src/squad/execution/instance.ts +0 -186
- package/src/squad/execution/meeting.ts +0 -191
- package/src/squad/execution/pr.ts +0 -127
- package/src/squad/execution/runner.ts +0 -97
- package/src/squad/execution/tasks.ts +0 -111
- package/src/squad/execution/worktree.ts +0 -138
- package/src/squad/hiring.ts +0 -222
- package/src/squad/index.ts +0 -17
- package/src/squad/manager.ts +0 -337
- package/src/squad/name-generator.ts +0 -135
- package/src/squad/roles/templates.ts +0 -104
- package/src/squad/skill-parser.ts +0 -120
- package/src/squad/source-resolver.ts +0 -57
- package/src/store/activity.ts +0 -176
- package/src/store/db.ts +0 -237
- package/src/store/inbox.ts +0 -199
- package/src/store/schedules.ts +0 -199
- package/src/wiki/index.ts +0 -12
- package/src/wiki/store.ts +0 -139
- package/tsconfig.json +0 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:"Inter", system-ui, -apple-system, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", monospace;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.5rem;--radius-lg:.75rem;--radius-2xl:1rem;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-background:#1a1a1a;--color-foreground:#f0ece8;--color-muted:#6b7280;--color-muted-foreground:#9ca3af;--color-border:#333;--color-input:#2a2a2a;--color-accent:#e43a9c;--color-destructive:#ef4444;--color-success:#22c55e;--color-warning:#f59e0b;--color-info:#3b82f6}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.relative{position:relative}.z-10{z-index:10}.col-span-full{grid-column:1/-1}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-auto{margin-left:auto}.line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.h-2{height:calc(var(--spacing) * 2)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-14{height:calc(var(--spacing) * 14)}.h-\[180px\]{height:180px}.h-\[250px\]{height:250px}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing) * 32)}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing) * 2)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-64{width:calc(var(--spacing) * 64)}.w-80{width:calc(var(--spacing) * 80)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-\[70\%\]{max-width:70%}.max-w-\[80\%\]{max-width:80%}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.cursor-not-allowed{cursor:not-allowed}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing) * 1)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-br-md{border-bottom-right-radius:var(--radius-md)}.rounded-bl-md{border-bottom-left-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-\[var\(--color-accent\)\]\/20{border-color:#e43a9c33}@supports (color:color-mix(in lab,red,red)){.border-\[var\(--color-accent\)\]\/20{border-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.border-\[var\(--color-border\)\]{border-color:var(--color-border)}.bg-\[var\(--color-accent\)\]{background-color:var(--color-accent)}.bg-\[var\(--color-accent\)\]\/15{background-color:#e43a9c26}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-accent\)\]\/15{background-color:color-mix(in oklab,var(--color-accent) 15%,transparent)}}.bg-\[var\(--color-background\)\]\/80{background-color:#1a1a1acc}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-background\)\]\/80{background-color:color-mix(in oklab,var(--color-background) 80%,transparent)}}.bg-\[var\(--color-destructive\)\]{background-color:var(--color-destructive)}.bg-\[var\(--color-destructive\)\]\/10{background-color:#ef44441a}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-destructive\)\]\/10{background-color:color-mix(in oklab,var(--color-destructive) 10%,transparent)}}.bg-\[var\(--color-info\)\]\/10{background-color:#3b82f61a}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-info\)\]\/10{background-color:color-mix(in oklab,var(--color-info) 10%,transparent)}}.bg-\[var\(--color-input\)\]{background-color:var(--color-input)}.bg-\[var\(--color-success\)\]{background-color:var(--color-success)}.bg-\[var\(--color-success\)\]\/10{background-color:#22c55e1a}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-success\)\]\/10{background-color:color-mix(in oklab,var(--color-success) 10%,transparent)}}.bg-\[var\(--color-warning\)\]\/10{background-color:#f59e0b1a}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--color-warning\)\]\/10{background-color:color-mix(in oklab,var(--color-warning) 10%,transparent)}}.bg-transparent{background-color:#0000}.bg-white\/3{background-color:#ffffff08}@supports (color:color-mix(in lab,red,red)){.bg-white\/3{background-color:color-mix(in oklab,var(--color-white) 3%,transparent)}}.bg-white\/5{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/5{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.bg-white\/10{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.bg-white\/10{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-12{padding-block:calc(var(--spacing) * 12)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[var\(--color-accent\)\]{color:var(--color-accent)}.text-\[var\(--color-destructive\)\]{color:var(--color-destructive)}.text-\[var\(--color-foreground\)\]{color:var(--color-foreground)}.text-\[var\(--color-info\)\]{color:var(--color-info)}.text-\[var\(--color-muted\)\]{color:var(--color-muted)}.text-\[var\(--color-muted-foreground\)\]{color:var(--color-muted-foreground)}.text-\[var\(--color-success\)\]{color:var(--color-success)}.text-\[var\(--color-warning\)\]{color:var(--color-warning)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.opacity-30{opacity:.3}.opacity-60{opacity:.6}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-\[var\(--color-muted\)\]::placeholder{color:var(--color-muted)}@media(hover:hover){.hover\:border-\[var\(--color-accent\)\]\/30:hover{border-color:#e43a9c4d}@supports (color:color-mix(in lab,red,red)){.hover\:border-\[var\(--color-accent\)\]\/30:hover{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.hover\:bg-\[var\(--color-destructive\)\]\/10:hover{background-color:#ef44441a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--color-destructive\)\]\/10:hover{background-color:color-mix(in oklab,var(--color-destructive) 10%,transparent)}}.hover\:bg-\[var\(--color-success\)\]\/10:hover{background-color:#22c55e1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-\[var\(--color-success\)\]\/10:hover{background-color:color-mix(in oklab,var(--color-success) 10%,transparent)}}.hover\:bg-white\/3:hover{background-color:#ffffff08}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/3:hover{background-color:color-mix(in oklab,var(--color-white) 3%,transparent)}}.hover\:bg-white\/5:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/5:hover{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.hover\:text-\[var\(--color-destructive\)\]:hover{color:var(--color-destructive)}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-\[var\(--color-accent\)\]:focus{border-color:var(--color-accent)}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:80rem){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}body{background-color:var(--color-background);color:var(--color-foreground);font-family:var(--font-sans);min-height:100vh;overflow:hidden}#root{min-height:100vh}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#444;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#555}.glass-card{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-radius:var(--radius-lg);background:#222222b3;border:1px solid #ffffff0f}.gradient-text{-webkit-text-fill-color:transparent;background:linear-gradient(135deg,#d83333,#e43a9c,#f041ff);-webkit-background-clip:text;background-clip:text}.orb{filter:blur(80px);opacity:.15;pointer-events:none;z-index:0;border-radius:50%;position:fixed}.orb-1{background:#e43a9c;width:400px;height:400px;animation:20s ease-in-out infinite orb-float-1;top:-100px;right:-100px}.orb-2{background:#d83333;width:300px;height:300px;animation:25s ease-in-out infinite orb-float-2;bottom:-50px;left:-50px}.orb-3{background:#f041ff;width:250px;height:250px;animation:18s ease-in-out infinite orb-float-3;top:40%;left:30%}@keyframes orb-float-1{0%,to{transform:translate(0)scale(1)}33%{transform:translate(-30px,20px)scale(1.05)}66%{transform:translate(20px,-10px)scale(.95)}}@keyframes orb-float-2{0%,to{transform:translate(0)scale(1)}33%{transform:translate(20px,-30px)scale(1.1)}66%{transform:translate(-15px,15px)scale(.9)}}@keyframes orb-float-3{0%,to{transform:translate(0)scale(1)}50%{transform:translate(-25px,25px)scale(1.05)}}.prose-io h1,.prose-io h2,.prose-io h3,.prose-io h4{-webkit-text-fill-color:transparent;background:linear-gradient(135deg,#d83333,#e43a9c,#f041ff);-webkit-background-clip:text;background-clip:text;margin:1.1em 0 .4em;font-weight:600}.prose-io h1{font-size:1.35em}.prose-io h2{font-size:1.15em}.prose-io h3{font-size:1em}.prose-io p{margin:.5em 0}.prose-io ul,.prose-io ol{margin:.5em 0;padding-left:1.5em}.prose-io li{margin:.25em 0}.prose-io code{font-family:var(--font-mono);background:#ffffff0f;border-radius:4px;padding:.15em .4em;font-size:.85em}.prose-io pre{border-radius:var(--radius-md);background:#0006;margin:.75em 0;padding:1em;overflow-x:auto}.prose-io pre code{background:0 0;padding:0}.prose-io a{color:var(--color-accent);text-decoration:underline}.prose-io table{border-collapse:collapse;width:100%;margin:.75em 0}.prose-io th,.prose-io td{border:1px solid var(--color-border);text-align:left;padding:.4em .75em}.prose-io th{background:#ffffff08;font-weight:600}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>IO</title>
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-2RY89H3W.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-D3cGfBsj.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
-
import jwt from 'jsonwebtoken';
|
|
3
|
-
import type { IOConfig } from '../../config.js';
|
|
4
|
-
import { createChildLogger } from '../../logging/logger.js';
|
|
5
|
-
|
|
6
|
-
const logger = () => createChildLogger('auth');
|
|
7
|
-
|
|
8
|
-
// Routes that don't require authentication
|
|
9
|
-
const EXEMPT_ROUTES: Array<{ method: string; path: string }> = [
|
|
10
|
-
{ method: 'GET', path: '/api/health' },
|
|
11
|
-
{ method: 'GET', path: '/api/config' },
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
function isExempt(method: string, path: string): boolean {
|
|
15
|
-
return EXEMPT_ROUTES.some((r) => r.method === method.toUpperCase() && path.startsWith(r.path));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Express middleware that verifies Supabase JWT tokens.
|
|
20
|
-
* If Supabase is not configured (no jwtSecret), all requests pass through.
|
|
21
|
-
* Exempt routes always pass through regardless of config.
|
|
22
|
-
*/
|
|
23
|
-
export function authMiddleware(config: IOConfig) {
|
|
24
|
-
return (req: Request, res: Response, next: NextFunction): void => {
|
|
25
|
-
// If no Supabase JWT secret configured, skip auth entirely (local-only mode)
|
|
26
|
-
if (!config.supabase.jwtSecret) {
|
|
27
|
-
next();
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Exempt routes don't require auth
|
|
32
|
-
if (isExempt(req.method, req.path)) {
|
|
33
|
-
next();
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const authHeader = req.headers.authorization;
|
|
38
|
-
if (!authHeader?.startsWith('Bearer ')) {
|
|
39
|
-
res.status(401).json({ error: 'Missing or invalid Authorization header' });
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const token = authHeader.slice(7);
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
jwt.verify(token, config.supabase.jwtSecret, {
|
|
47
|
-
algorithms: ['HS256'],
|
|
48
|
-
});
|
|
49
|
-
next();
|
|
50
|
-
} catch (err) {
|
|
51
|
-
logger().debug({ err }, 'JWT verification failed');
|
|
52
|
-
res.status(401).json({ error: 'Invalid or expired token' });
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Verify a JWT token for WebSocket connections.
|
|
59
|
-
* Returns true if valid or if auth is not configured.
|
|
60
|
-
*/
|
|
61
|
-
export function verifyWsToken(config: IOConfig, token: string | null): boolean {
|
|
62
|
-
if (!config.supabase.jwtSecret) {
|
|
63
|
-
return true; // No auth configured — allow
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!token) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
jwt.verify(token, config.supabase.jwtSecret, { algorithms: ['HS256'] });
|
|
72
|
-
return true;
|
|
73
|
-
} catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
}
|
package/src/api/notifications.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import type { IOEvent } from '@io/shared';
|
|
2
|
-
import type { WebSocket } from 'ws';
|
|
3
|
-
import { createChildLogger } from '../logging/logger.js';
|
|
4
|
-
import { getEventBus } from '../squad/event-bus.js';
|
|
5
|
-
|
|
6
|
-
const logger = () => createChildLogger('notifications');
|
|
7
|
-
|
|
8
|
-
// Connected clients that want event notifications
|
|
9
|
-
const subscribers = new Map<string, WebSocket>();
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Register a WebSocket client for event notifications.
|
|
13
|
-
*/
|
|
14
|
-
export function subscribeClient(connectionId: string, ws: WebSocket): void {
|
|
15
|
-
subscribers.set(connectionId, ws);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Unregister a WebSocket client.
|
|
20
|
-
*/
|
|
21
|
-
export function unsubscribeClient(connectionId: string): void {
|
|
22
|
-
subscribers.delete(connectionId);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Render an event to a human-readable notification string.
|
|
27
|
-
*/
|
|
28
|
-
function renderNotification(event: IOEvent): string {
|
|
29
|
-
switch (event.type) {
|
|
30
|
-
case 'squad:created':
|
|
31
|
-
return `🆕 Squad "${event.squadName}" has been hired`;
|
|
32
|
-
case 'squad:disbanded':
|
|
33
|
-
return `🗑️ Squad "${event.squadName}" has been disbanded`;
|
|
34
|
-
case 'squad:member_added':
|
|
35
|
-
return `👋 New member added to "${event.squadName}"`;
|
|
36
|
-
case 'squad:member_retired':
|
|
37
|
-
return `👤 Member retired from "${event.squadName}"`;
|
|
38
|
-
case 'instance:created':
|
|
39
|
-
return '🚀 New work instance started';
|
|
40
|
-
case 'instance:meeting_started':
|
|
41
|
-
return '🤝 Round-table meeting in progress';
|
|
42
|
-
case 'instance:meeting_complete':
|
|
43
|
-
return '✅ Meeting complete — consensus reached';
|
|
44
|
-
case 'instance:work_started':
|
|
45
|
-
return '⚡ Squad is working on tasks';
|
|
46
|
-
case 'instance:pr_created':
|
|
47
|
-
return `📬 PR created: ${(event.data as { prUrl?: string })?.prUrl ?? ''}`;
|
|
48
|
-
case 'instance:complete':
|
|
49
|
-
return '🎉 Work instance completed successfully';
|
|
50
|
-
case 'instance:failed':
|
|
51
|
-
return '❌ Work instance failed';
|
|
52
|
-
case 'agent:task_started':
|
|
53
|
-
return `🔧 ${event.agentRole} started a task`;
|
|
54
|
-
case 'agent:task_completed':
|
|
55
|
-
return `✔️ ${event.agentRole} completed a task`;
|
|
56
|
-
case 'agent:error':
|
|
57
|
-
return `⚠️ ${event.agentRole} encountered an error`;
|
|
58
|
-
case 'agent:permission_denied':
|
|
59
|
-
return `🚫 ${event.agentRole} was denied permission`;
|
|
60
|
-
case 'agent:tool_call':
|
|
61
|
-
return `🛠️ ${event.agentRole} used a tool`;
|
|
62
|
-
case 'meeting:contribution':
|
|
63
|
-
return `💬 ${event.agentRole}: "${event.content.slice(0, 80)}"`;
|
|
64
|
-
case 'meeting:consensus_reached':
|
|
65
|
-
return '🤝 Consensus reached in meeting';
|
|
66
|
-
case 'meeting:veto':
|
|
67
|
-
return `🛑 ${event.agentRole} vetoed the proposal`;
|
|
68
|
-
case 'inbox:new':
|
|
69
|
-
return event.kind === 'question'
|
|
70
|
-
? `❓ Squad has a question: "${event.title}"`
|
|
71
|
-
: `📋 Squad delivered: "${event.title}"`;
|
|
72
|
-
case 'inbox:resolved':
|
|
73
|
-
return `✅ Inbox item resolved: "${event.title}"`;
|
|
74
|
-
case 'schedule:fired':
|
|
75
|
-
return `⏰ Schedule fired: "${(event.data as { name?: string })?.name ?? 'unknown'}"`;
|
|
76
|
-
case 'schedule:completed':
|
|
77
|
-
return `✅ Schedule completed: "${(event.data as { name?: string })?.name ?? 'unknown'}"`;
|
|
78
|
-
case 'schedule:failed':
|
|
79
|
-
return `❌ Schedule failed: "${(event.data as { name?: string })?.name ?? 'unknown'}"`;
|
|
80
|
-
default:
|
|
81
|
-
return `📣 Event: ${(event as { type: string }).type}`;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Initialize the notification system — subscribes to event bus and broadcasts to clients.
|
|
87
|
-
*/
|
|
88
|
-
export function initNotifications(): void {
|
|
89
|
-
const log = logger();
|
|
90
|
-
|
|
91
|
-
getEventBus().onAny((event: IOEvent) => {
|
|
92
|
-
const notification = renderNotification(event);
|
|
93
|
-
const payload = JSON.stringify({
|
|
94
|
-
type: 'event',
|
|
95
|
-
notification,
|
|
96
|
-
event: {
|
|
97
|
-
id: event.id,
|
|
98
|
-
type: event.type,
|
|
99
|
-
timestamp: event.timestamp.toISOString(),
|
|
100
|
-
squadId: event.squadId,
|
|
101
|
-
instanceId: event.instanceId,
|
|
102
|
-
data: 'data' in event ? event.data : undefined,
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
let delivered = 0;
|
|
107
|
-
for (const [connId, ws] of subscribers) {
|
|
108
|
-
if (ws.readyState === ws.OPEN) {
|
|
109
|
-
ws.send(payload);
|
|
110
|
-
delivered++;
|
|
111
|
-
} else {
|
|
112
|
-
subscribers.delete(connId);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (delivered > 0) {
|
|
117
|
-
log.debug({ eventType: event.type, delivered }, 'Event broadcast');
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
log.info('Notification system initialized');
|
|
122
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import { type ActivityType, queryActivity } from '../../store/activity.js';
|
|
3
|
-
|
|
4
|
-
export function activityRouter(): Router {
|
|
5
|
-
const router = Router();
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* GET /api/activity
|
|
9
|
-
* Query agent activity log with optional filters.
|
|
10
|
-
* Query params: squad, instance, agent, type, limit, offset
|
|
11
|
-
*/
|
|
12
|
-
router.get('/activity', async (req, res) => {
|
|
13
|
-
try {
|
|
14
|
-
const entries = await queryActivity({
|
|
15
|
-
squadId: req.query.squad as string | undefined,
|
|
16
|
-
instanceId: req.query.instance as string | undefined,
|
|
17
|
-
agentRole: req.query.agent as string | undefined,
|
|
18
|
-
activityType: req.query.type as ActivityType | undefined,
|
|
19
|
-
limit: req.query.limit ? Number.parseInt(req.query.limit as string, 10) : undefined,
|
|
20
|
-
offset: req.query.offset ? Number.parseInt(req.query.offset as string, 10) : undefined,
|
|
21
|
-
});
|
|
22
|
-
res.json({ entries });
|
|
23
|
-
} catch (err) {
|
|
24
|
-
res.status(500).json({ error: 'Failed to query activity' });
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return router;
|
|
29
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { createReadStream, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import { writeFile } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { Router } from 'express';
|
|
5
|
-
import multer from 'multer';
|
|
6
|
-
import { getDatabase } from '../../store/db.js';
|
|
7
|
-
|
|
8
|
-
export function attachmentsRouter(dataDir: string): Router {
|
|
9
|
-
const router = Router();
|
|
10
|
-
const attachmentsDir = join(dataDir, 'attachments');
|
|
11
|
-
mkdirSync(attachmentsDir, { recursive: true });
|
|
12
|
-
|
|
13
|
-
// Use memory storage — we'll write to disk ourselves with proper naming
|
|
14
|
-
const upload = multer({
|
|
15
|
-
storage: multer.memoryStorage(),
|
|
16
|
-
limits: { fileSize: 50 * 1024 * 1024 },
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* POST /api/attachments
|
|
21
|
-
* Upload a file attachment. Returns the attachment metadata.
|
|
22
|
-
*/
|
|
23
|
-
router.post('/attachments', upload.single('file'), async (req, res) => {
|
|
24
|
-
try {
|
|
25
|
-
const file = req.file;
|
|
26
|
-
if (!file) {
|
|
27
|
-
res.status(400).json({ error: 'No file uploaded' });
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const id = crypto.randomUUID();
|
|
32
|
-
const messageId = (req.body?.messageId as string) ?? null;
|
|
33
|
-
const fileDir = join(attachmentsDir, id);
|
|
34
|
-
mkdirSync(fileDir, { recursive: true });
|
|
35
|
-
const diskPath = join(fileDir, file.originalname);
|
|
36
|
-
|
|
37
|
-
await writeFile(diskPath, file.buffer);
|
|
38
|
-
|
|
39
|
-
const db = getDatabase();
|
|
40
|
-
await db.execute({
|
|
41
|
-
sql: `INSERT INTO attachments (id, message_id, filename, mime_type, size_bytes, disk_path)
|
|
42
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
43
|
-
args: [id, messageId, file.originalname, file.mimetype, file.size, diskPath],
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
res.status(201).json({
|
|
47
|
-
id,
|
|
48
|
-
filename: file.originalname,
|
|
49
|
-
mimeType: file.mimetype,
|
|
50
|
-
sizeBytes: file.size,
|
|
51
|
-
});
|
|
52
|
-
} catch (err) {
|
|
53
|
-
res.status(500).json({ error: 'Failed to upload attachment' });
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* GET /api/attachments/:id
|
|
59
|
-
* Download an attachment by ID.
|
|
60
|
-
*/
|
|
61
|
-
router.get('/attachments/:id', async (req, res) => {
|
|
62
|
-
try {
|
|
63
|
-
const db = getDatabase();
|
|
64
|
-
const result = await db.execute({
|
|
65
|
-
sql: 'SELECT filename, mime_type, disk_path FROM attachments WHERE id = ?',
|
|
66
|
-
args: [req.params.id],
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
if (result.rows.length === 0) {
|
|
70
|
-
res.status(404).json({ error: 'Attachment not found' });
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const row = result.rows[0];
|
|
75
|
-
const diskPath = row.disk_path as string;
|
|
76
|
-
const filename = row.filename as string;
|
|
77
|
-
const mimeType = (row.mime_type as string) ?? 'application/octet-stream';
|
|
78
|
-
|
|
79
|
-
if (!existsSync(diskPath)) {
|
|
80
|
-
res.status(404).json({ error: 'Attachment file missing from disk' });
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
res.setHeader('Content-Type', mimeType);
|
|
85
|
-
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
86
|
-
createReadStream(diskPath).pipe(res);
|
|
87
|
-
} catch (err) {
|
|
88
|
-
res.status(500).json({ error: 'Failed to retrieve attachment' });
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
return router;
|
|
93
|
-
}
|
package/src/api/routes/config.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { Router } from 'express';
|
|
5
|
-
import { loadConfig } from '../../config.js';
|
|
6
|
-
|
|
7
|
-
export function configRouter(): Router {
|
|
8
|
-
const router = Router();
|
|
9
|
-
|
|
10
|
-
function getConfigPath(): string {
|
|
11
|
-
const dataDir = process.env.IO_DATA_DIR ?? join(homedir(), '.io');
|
|
12
|
-
const resolved = dataDir.startsWith('~') ? join(homedir(), dataDir.slice(1)) : dataDir;
|
|
13
|
-
return join(resolved, 'config.json');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* GET /api/config
|
|
18
|
-
* Return current config with sensitive fields redacted.
|
|
19
|
-
*/
|
|
20
|
-
router.get('/config', (_req, res) => {
|
|
21
|
-
try {
|
|
22
|
-
const config = loadConfig();
|
|
23
|
-
// Redact sensitive fields
|
|
24
|
-
const redacted = {
|
|
25
|
-
apiPort: config.apiPort,
|
|
26
|
-
logLevel: config.logLevel,
|
|
27
|
-
defaultModel: config.defaultModel,
|
|
28
|
-
maxInstancesPerSquad: config.maxInstancesPerSquad,
|
|
29
|
-
dataDir: config.dataDir,
|
|
30
|
-
pricing: config.pricing,
|
|
31
|
-
telegram: {
|
|
32
|
-
botToken: config.telegram.botToken ? '••••••••' : null,
|
|
33
|
-
allowedChatIds: config.telegram.allowedChatIds,
|
|
34
|
-
},
|
|
35
|
-
supabase: {
|
|
36
|
-
projectUrl: config.supabase.projectUrl,
|
|
37
|
-
anonKey: config.supabase.anonKey,
|
|
38
|
-
jwtSecret: config.supabase.jwtSecret ? '••••••••' : null,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
res.json({ config: redacted });
|
|
42
|
-
} catch (err) {
|
|
43
|
-
res.status(500).json({ error: 'Failed to load config' });
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* PATCH /api/config
|
|
49
|
-
* Merge partial config into config.json on disk.
|
|
50
|
-
* Does NOT accept dataDir changes (immutable).
|
|
51
|
-
* Body: partial config object
|
|
52
|
-
*/
|
|
53
|
-
router.patch('/config', (req, res) => {
|
|
54
|
-
try {
|
|
55
|
-
const updates = req.body as Record<string, any>;
|
|
56
|
-
|
|
57
|
-
if (!updates || typeof updates !== 'object') {
|
|
58
|
-
res.status(400).json({ error: 'Body must be a JSON object' });
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Disallow changing dataDir (would break running daemon)
|
|
63
|
-
delete updates.dataDir;
|
|
64
|
-
|
|
65
|
-
const configPath = getConfigPath();
|
|
66
|
-
let existing: Record<string, any> = {};
|
|
67
|
-
if (existsSync(configPath)) {
|
|
68
|
-
existing = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Deep merge one level (telegram, pricing)
|
|
72
|
-
const merged = { ...existing };
|
|
73
|
-
for (const [key, value] of Object.entries(updates)) {
|
|
74
|
-
if (
|
|
75
|
-
value !== null &&
|
|
76
|
-
typeof value === 'object' &&
|
|
77
|
-
!Array.isArray(value) &&
|
|
78
|
-
typeof merged[key] === 'object'
|
|
79
|
-
) {
|
|
80
|
-
merged[key] = { ...merged[key], ...value };
|
|
81
|
-
} else {
|
|
82
|
-
merged[key] = value;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
writeFileSync(configPath, JSON.stringify(merged, null, 2), 'utf-8');
|
|
87
|
-
|
|
88
|
-
// Reload and return redacted
|
|
89
|
-
const config = loadConfig();
|
|
90
|
-
res.json({
|
|
91
|
-
config: {
|
|
92
|
-
apiPort: config.apiPort,
|
|
93
|
-
logLevel: config.logLevel,
|
|
94
|
-
defaultModel: config.defaultModel,
|
|
95
|
-
maxInstancesPerSquad: config.maxInstancesPerSquad,
|
|
96
|
-
dataDir: config.dataDir,
|
|
97
|
-
pricing: config.pricing,
|
|
98
|
-
telegram: {
|
|
99
|
-
botToken: config.telegram.botToken ? '••••••••' : null,
|
|
100
|
-
allowedChatIds: config.telegram.allowedChatIds,
|
|
101
|
-
},
|
|
102
|
-
supabase: {
|
|
103
|
-
projectUrl: config.supabase.projectUrl,
|
|
104
|
-
anonKey: config.supabase.anonKey,
|
|
105
|
-
jwtSecret: config.supabase.jwtSecret ? '••••••••' : null,
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
} catch (err) {
|
|
110
|
-
res.status(500).json({ error: 'Failed to update config' });
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
return router;
|
|
115
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import { getDatabase } from '../../store/db.js';
|
|
3
|
-
|
|
4
|
-
export function conversationsRouter(): Router {
|
|
5
|
-
const router = Router();
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* GET /api/conversations
|
|
9
|
-
* Load chat history with cursor-based pagination.
|
|
10
|
-
* Query params: limit (default 50), before (message id for pagination)
|
|
11
|
-
*/
|
|
12
|
-
router.get('/conversations', async (req, res) => {
|
|
13
|
-
try {
|
|
14
|
-
const limit = Math.min(Number.parseInt(req.query.limit as string, 10) || 50, 200);
|
|
15
|
-
const before = req.query.before as string | undefined;
|
|
16
|
-
|
|
17
|
-
const db = getDatabase();
|
|
18
|
-
|
|
19
|
-
let rows: Array<{
|
|
20
|
-
id: string;
|
|
21
|
-
role: string;
|
|
22
|
-
content: string;
|
|
23
|
-
source: string | null;
|
|
24
|
-
attachments: string | null;
|
|
25
|
-
created_at: string;
|
|
26
|
-
}>;
|
|
27
|
-
|
|
28
|
-
if (before) {
|
|
29
|
-
// Get the timestamp of the cursor message
|
|
30
|
-
const cursorResult = await db.execute({
|
|
31
|
-
sql: 'SELECT created_at FROM conversations WHERE id = ?',
|
|
32
|
-
args: [before],
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if (cursorResult.rows.length === 0) {
|
|
36
|
-
res.status(400).json({ error: 'Invalid cursor: message not found' });
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const cursorTime = cursorResult.rows[0].created_at as string;
|
|
41
|
-
|
|
42
|
-
const result = await db.execute({
|
|
43
|
-
sql: `SELECT id, role, content, source, attachments, created_at
|
|
44
|
-
FROM conversations
|
|
45
|
-
WHERE created_at < ? OR (created_at = ? AND id < ?)
|
|
46
|
-
ORDER BY created_at DESC, id DESC
|
|
47
|
-
LIMIT ?`,
|
|
48
|
-
args: [cursorTime, cursorTime, before, limit],
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
rows = result.rows as any;
|
|
52
|
-
} else {
|
|
53
|
-
const result = await db.execute({
|
|
54
|
-
sql: `SELECT id, role, content, source, attachments, created_at
|
|
55
|
-
FROM conversations
|
|
56
|
-
ORDER BY created_at DESC, id DESC
|
|
57
|
-
LIMIT ?`,
|
|
58
|
-
args: [limit],
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
rows = result.rows as any;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Reverse so messages are in chronological order
|
|
65
|
-
const messages = rows.reverse().map((row) => ({
|
|
66
|
-
id: row.id,
|
|
67
|
-
role: row.role,
|
|
68
|
-
content: row.content,
|
|
69
|
-
source: row.source,
|
|
70
|
-
attachments: row.attachments ? JSON.parse(row.attachments) : null,
|
|
71
|
-
timestamp: row.created_at,
|
|
72
|
-
}));
|
|
73
|
-
|
|
74
|
-
const cursor = rows.length > 0 ? rows[0].id : null;
|
|
75
|
-
|
|
76
|
-
res.json({
|
|
77
|
-
messages,
|
|
78
|
-
cursor,
|
|
79
|
-
hasMore: rows.length === limit,
|
|
80
|
-
});
|
|
81
|
-
} catch (err) {
|
|
82
|
-
res.status(500).json({ error: 'Failed to load conversations' });
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return router;
|
|
87
|
-
}
|
package/src/api/routes/health.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import { getHealthStatus } from '../../copilot/health-monitor.js';
|
|
3
|
-
|
|
4
|
-
export function healthRouter(): Router {
|
|
5
|
-
const router = Router();
|
|
6
|
-
|
|
7
|
-
router.get('/health', (_req, res) => {
|
|
8
|
-
const health = getHealthStatus();
|
|
9
|
-
res.json({
|
|
10
|
-
status: health.status,
|
|
11
|
-
uptime: health.uptime,
|
|
12
|
-
copilotConnected: health.copilotConnected,
|
|
13
|
-
lastCheck: health.lastCheck.toISOString(),
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return router;
|
|
18
|
-
}
|