@kl-c/matrixos 0.1.22 → 0.1.24

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.
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ export declare const DashboardConfigSchema: z.ZodObject<{
3
+ enabled: z.ZodOptional<z.ZodBoolean>;
4
+ port: z.ZodOptional<z.ZodNumber>;
5
+ gateway_passphrase: z.ZodOptional<z.ZodString>;
6
+ }, z.core.$strip>;
7
+ export type DashboardConfig = z.infer<typeof DashboardConfigSchema>;
@@ -2446,6 +2446,11 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
2446
2446
  auditLogPath: z.ZodOptional<z.ZodString>;
2447
2447
  enabled: z.ZodDefault<z.ZodBoolean>;
2448
2448
  }, z.core.$strict>>;
2449
+ dashboard: z.ZodOptional<z.ZodObject<{
2450
+ enabled: z.ZodOptional<z.ZodBoolean>;
2451
+ port: z.ZodOptional<z.ZodNumber>;
2452
+ gateway_passphrase: z.ZodOptional<z.ZodString>;
2453
+ }, z.core.$strip>>;
2449
2454
  watchdog: z.ZodOptional<z.ZodObject<{
2450
2455
  enabled: z.ZodDefault<z.ZodBoolean>;
2451
2456
  doomLoopThreshold: z.ZodDefault<z.ZodNumber>;
@@ -9,6 +9,7 @@ export * from "./schema/codegraph";
9
9
  export * from "./schema/comment-checker";
10
10
  export * from "./schema/commands";
11
11
  export * from "./schema/cron";
12
+ export * from "./schema/dashboard";
12
13
  export * from "./schema/default-mode";
13
14
  export * from "./schema/dynamic-context-pruning";
14
15
  export * from "./schema/egress";
@@ -207,5 +207,17 @@ body{font-family:var(--font-body);font-size:var(--text-base);color:var(--text-pr
207
207
  .config-editor{width:100%;min-height:400px;background:var(--bg-base);color:var(--text-primary);font-family:var(--font-mono);font-size:var(--text-sm);border:none;padding:var(--space-4);resize:vertical;tab-size:2;line-height:1.5}
208
208
  .config-editor:focus{outline:1px solid var(--accent-primary)}
209
209
 
210
+ /* FORM CONTROLS (gateway + future forms) */
211
+ .label{display:block;font-size:var(--text-xs);color:var(--text-secondary);font-weight:500;margin-bottom:var(--space-1);text-transform:uppercase;letter-spacing:.05em}
212
+ .input,.select{width:100%;background:var(--bg-base);color:var(--text-primary);border:1px solid var(--border-default);border-radius:var(--radius-lg);padding:var(--space-2) var(--space-3);font-size:var(--text-sm);font-family:var(--font-body);transition:border-color .15s}
213
+ .input:focus,.select:focus{outline:none;border-color:var(--accent-primary)}
214
+ .input::placeholder{color:var(--text-tertiary)}
215
+ .select{cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%238B8D97' stroke-width='1.5'><path d='M3 5l3 3 3-3'/></svg>");background-repeat:no-repeat;background-position:right var(--space-3) center;padding-right:var(--space-6)}
216
+ .gateway-form{display:flex;flex-direction:column;gap:var(--space-4);max-width:480px}
217
+ .gateway-help{font-size:var(--text-xs);color:var(--text-secondary);line-height:1.5;margin-bottom:var(--space-1)}
218
+ .form-row{display:flex;flex-direction:column}
219
+ .form-actions{display:flex;align-items:center;gap:var(--space-3);margin-top:var(--space-2)}
220
+ .gateway-status{font-size:var(--text-sm);color:var(--text-tertiary)}
221
+
210
222
  @media(max-width:1024px){.kpi-grid,.grid-2{grid-template-columns:repeat(2,1fr)}.kanban-board{grid-template-columns:repeat(2,1fr)}.gauge-grid{grid-template-columns:repeat(2,1fr)}}
211
223
  @media(max-width:768px){.kpi-grid,.grid-2{grid-template-columns:1fr}.kanban-board{grid-template-columns:1fr}.agent-grid{grid-template-columns:1fr}}
@@ -166,6 +166,7 @@
166
166
  </div>
167
167
  </div>
168
168
  <div class="card"><div class="card-header"><span class="card-title">matrixos.jsonc</span><span id="configStatus" style="font-size:var(--text-xs);color:var(--text-tertiary)"></span></div><div class="card-body"><textarea id="configEditor" class="config-editor" spellcheck="false"></textarea></div></div>
169
+ <div class="card"><div class="card-header"><span class="card-title">Connecter un canal de messagerie</span><span class="badge badge-purple">Gateway</span></div><div class="card-body" id="gatewayBody"></div></div>
169
170
  </div>
170
171
 
171
172
  <div id="tab-schedule" class="tab-panel">
@@ -1,2 +1,2 @@
1
- const API={health:()=>fetchJSON('/health'),agents:()=>fetchJSON('/agents'),sessions:(l)=>fetchJSON(`/sessions?limit=${l}`),cost:()=>fetchJSON('/cost'),kanban:()=>fetchJSON('/kanban'),incidents:(l)=>fetchJSON(`/incidents?limit=${l}`),metrics:()=>fetchJSON('/metrics'),config:()=>fetchJSON('/config'),memory:()=>fetchJSON('/memory'),skills:()=>fetchJSON('/skills')}
2
- async function fetchJSON(e){const r=await fetch(`/api${e}`);if(!r.ok)throw new Error(`API ${e}: ${r.status}`);return r.json()}
1
+ const API={health:()=>fetchJSON('/health'),agents:()=>fetchJSON('/agents'),sessions:(l)=>fetchJSON(`/sessions?limit=${l}`),cost:()=>fetchJSON('/cost'),kanban:()=>fetchJSON('/kanban'),incidents:(l)=>fetchJSON(`/incidents?limit=${l}`),metrics:()=>fetchJSON('/metrics'),config:()=>fetchJSON('/config'),memory:()=>fetchJSON('/memory'),skills:()=>fetchJSON('/skills'),gatewayToken:(type,token,passphrase)=>fetch('/api/gateway/token',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({type,token,passphrase})}).then(r=>r.json())}
2
+ async function fetchJSON(e){const r=await fetch(`/api${e}`);if(!r.ok)throw new Error(`API ${e}: ${r.status}`);return r.json()}
@@ -266,6 +266,66 @@ async function loadConfig(){
266
266
  }catch(e){status.textContent=`Invalid JSON: ${e.message}`;status.style.color='var(--danger)'}
267
267
  }
268
268
  document.getElementById('configRefreshBtn').onclick=loadConfig
269
+ loadGateway()
270
+ }
271
+
272
+ // ===== GATEWAY (Connecter un canal) =====
273
+ function loadGateway(){
274
+ const body=document.getElementById('gatewayBody')
275
+ if(!body)return
276
+ body.innerHTML=`
277
+ <div class="gateway-form">
278
+ <p class="gateway-help">Renseignez le token du bot et la passphrase pour connecter un canal de messagerie. Le token est envoyé au backend et n'est jamais stocké côté navigateur.</p>
279
+ <div class="form-row">
280
+ <label class="label" for="gwType">Type</label>
281
+ <select class="select" id="gwType">
282
+ <option value="telegram">Telegram</option>
283
+ <option value="discord">Discord</option>
284
+ </select>
285
+ </div>
286
+ <div class="form-row">
287
+ <label class="label" for="gwToken">Token du bot</label>
288
+ <input class="input" type="password" id="gwToken" autocomplete="off" placeholder="123456:ABC-DEF...">
289
+ </div>
290
+ <div class="form-row">
291
+ <label class="label" for="gwPass">Passphrase</label>
292
+ <input class="input" type="password" id="gwPass" autocomplete="off" placeholder="Passphrase configurée côté serveur">
293
+ </div>
294
+ <div class="form-actions">
295
+ <button class="btn btn-primary" id="gwSubmit">Enregistrer</button>
296
+ <span class="gateway-status" id="gwStatus"></span>
297
+ </div>
298
+ </div>`
299
+ const submit=document.getElementById('gwSubmit')
300
+ const status=document.getElementById('gwStatus')
301
+ submit.onclick=async()=>{
302
+ const type=document.getElementById('gwType').value
303
+ const token=document.getElementById('gwToken').value
304
+ const passphrase=document.getElementById('gwPass').value
305
+ if(!token){status.textContent='Token requis';status.style.color='var(--danger)';return}
306
+ if(!passphrase){status.textContent='Passphrase requise';status.style.color='var(--danger)';return}
307
+ submit.disabled=true
308
+ status.textContent='Envoi en cours...'
309
+ status.style.color='var(--text-tertiary)'
310
+ try{
311
+ const res=await API.gatewayToken(type,token,passphrase)
312
+ if(res&&res.ok){
313
+ status.textContent='Token enregistré ✓'
314
+ status.style.color='var(--success)'
315
+ document.getElementById('gwToken').value=''
316
+ document.getElementById('gwPass').value=''
317
+ }else{
318
+ const msg=(res&&res.error)||(Array.isArray(res)&&res[0]&&res[0].error)||'Erreur inconnue'
319
+ status.textContent='Erreur : '+msg
320
+ status.style.color='var(--danger)'
321
+ }
322
+ }catch(e){
323
+ status.textContent='Erreur : '+e.message
324
+ status.style.color='var(--danger)'
325
+ }finally{
326
+ submit.disabled=false
327
+ }
328
+ }
269
329
  }
270
330
 
271
331
  // ===== MEMORY =====
@@ -0,0 +1,10 @@
1
+ export interface GatewayTokenBody {
2
+ type: unknown;
3
+ token: unknown;
4
+ passphrase: unknown;
5
+ }
6
+ export interface GatewayTokenResult {
7
+ status: number;
8
+ body: Record<string, unknown>;
9
+ }
10
+ export declare function handleGatewayTokenRequest(body: GatewayTokenBody, configPassphrase: string | undefined): GatewayTokenResult;
@@ -3,6 +3,8 @@ export interface DashboardServerConfig {
3
3
  port: number;
4
4
  host: string;
5
5
  frontendDir: string;
6
+ /** Passphrase required to submit gateway tokens via POST /api/gateway/token */
7
+ gatewayPassphrase?: string;
6
8
  }
7
9
  export declare function createDashboardServer(dataProvider: DataProvider, config: DashboardServerConfig): {
8
10
  start(): void;