mythos-code 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/mythos.js +105 -21
  2. package/package.json +1 -1
package/bin/mythos.js CHANGED
@@ -4,7 +4,7 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import os from "node:os";
6
6
  import readline from "node:readline";
7
- import { execSync } from "node:child_process";
7
+ import { exec } from "node:child_process";
8
8
 
9
9
  const API = process.env.MYTHOS_API_URL || "https://rdxaqiacoitzeowehdts.supabase.co/functions/v1/v1-messages";
10
10
  const CFG = path.join(os.homedir(), ".mythos-code.json");
@@ -24,33 +24,112 @@ Du kannst Werkzeuge nutzen. Um ein Werkzeug aufzurufen, antworte mit GENAU EINEM
24
24
  <tool>{"name":"ls","path":"."}</tool> -> Ordner auflisten
25
25
  Nach jedem Werkzeug bekommst du das Ergebnis. Arbeite Schritt für Schritt, bis die Aufgabe erledigt ist, dann antworte normal ohne <tool>. Sei präzise.`;
26
26
 
27
- async function login() {
28
- console.log(C.c + "Hol dir einen kostenlosen API-Key im Mythos Dashboard (sk-ant-mythos-...)." + C.x);
29
- const k = (await ask("API-Key: ")).trim();
27
+ const SITE = process.env.MYTHOS_SITE_URL || "https://ai-mythos.lovable.app";
28
+ const FN = process.env.MYTHOS_FN_URL || API.replace(/\/v1-messages$/, "");
29
+ const authPost = async (body) => (await fetch(FN + "/cli-auth", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })).json();
30
+
31
+ function openBrowser(url) {
32
+ const cmd = process.platform === "win32" ? `start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`;
33
+ exec(cmd, () => {});
34
+ }
35
+
36
+ async function loginWithKey() {
37
+ const k = (await ask("API-Key (sk-ant-mythos-...): ")).trim();
30
38
  if (!k.startsWith("sk-ant-mythos-")) { console.log(C.r + "Ungültiger Key." + C.x); process.exit(1); }
31
- cfg.key = k; save(cfg); console.log(C.g + "✓ Gespeichert in " + CFG + C.x);
39
+ cfg.key = k; save(cfg); console.log(C.g + "✓ Angemeldet" + C.x);
32
40
  }
33
41
 
34
- async function call(messages) {
35
- const r = await fetch(API, {
36
- method: "POST",
37
- headers: { "content-type": "application/json", "x-api-key": cfg.key, "anthropic-version": "2023-06-01" },
38
- body: JSON.stringify({ model: cfg.model || "mythos-code", max_tokens: 8000, system: SYSTEM, messages }),
39
- });
40
- const j = await r.json().catch(() => ({}));
41
- if (!r.ok) throw new Error(j?.error?.message || ("HTTP " + r.status));
42
- return (j.content || []).map((p) => p.text || "").join("");
42
+ // Anmeldung über die Website: Browser öffnet sich, dort auf "Authentifizieren" klicken.
43
+ async function login() {
44
+ if (process.argv.includes("--key")) return loginWithKey();
45
+ let s;
46
+ try { s = await authPost({ action: "start", client: "cli" }); } catch { s = null; }
47
+ if (!s?.code) { console.log(C.r + "Anmeldung gerade nicht möglich – versuch es gleich nochmal." + C.x); process.exit(1); }
48
+ const url = SITE + "/cli-auth?code=" + s.code;
49
+ console.log(C.c + "\n Anmeldung mit MythosAI" + C.x);
50
+ console.log(" Dein Browser öffnet sich. Klicke dort auf " + C.b + "Authentifizieren" + C.x + ".");
51
+ console.log(C.d + " Code: " + s.code + " · Falls sich nichts öffnet: " + url + C.x + "\n");
52
+ openBrowser(url);
53
+ const until = Date.now() + 15 * 60e3;
54
+ process.stdout.write(C.d + " Warte auf Bestätigung…" + C.x);
55
+ while (Date.now() < until) {
56
+ await new Promise((r) => setTimeout(r, 2000));
57
+ let p; try { p = await authPost({ action: "poll", code: s.code, poll_secret: s.poll_secret }); } catch { continue; }
58
+ if (p.status === "ok") { cfg.key = p.api_key; cfg.name = p.name; save(cfg); clearLine(); console.log(C.g + " ✓ Angemeldet als " + p.name + C.x + "\n"); return; }
59
+ if (p.status === "expired") break;
60
+ }
61
+ clearLine(); console.log(C.r + " Anmeldung abgelaufen – starte nochmal mit: mythos login" + C.x); process.exit(1);
62
+ }
63
+
64
+ const fmt = (ms) => { const s = Math.floor(ms / 1000); return (s >= 60 ? Math.floor(s / 60) + "m " : "") + (s % 60) + "s"; };
65
+
66
+ // Live-Timer: läuft während der ganzen Aufgabe (auch während Befehle laufen), pausiert nur für Rückfragen.
67
+ let T0 = 0, tick = null;
68
+ const clearLine = () => process.stdout.write("\r\x1b[K");
69
+ const startTick = () => { if (tick) return; tick = setInterval(() => process.stdout.write("\r" + C.d + "⏳ Mythos arbeitet… " + fmt(Date.now() - T0) + C.x + " "), 250); };
70
+ const stopTick = () => { if (tick) { clearInterval(tick); tick = null; } clearLine(); };
71
+ const say = (s) => { const was = !!tick; stopTick(); console.log(s); if (was) startTick(); };
72
+
73
+ // Verlauf kürzen, damit lange Aufgaben nicht an zu großem Kontext scheitern. level 0 = mild, 2 = stark.
74
+ const shorten = (s, max) => s.length <= max ? s : s.slice(0, Math.floor(max * 0.7)) + "\n…[gekürzt]…\n" + s.slice(s.length - Math.floor(max * 0.3));
75
+ function compact(history, level) {
76
+ const keep = [6, 8, 4][level], recentMax = [20000, 3000, 1500][level], oldMax = [1500, 800, 400][level];
77
+ const cut = Math.max(0, history.length - keep);
78
+ const older = level === 0 ? history.slice(0, cut) : history.slice(0, Math.min(1, cut));
79
+ return older.map((m) => ({ role: m.role, content: shorten(m.content, oldMax) }))
80
+ .concat(history.slice(cut).map((m) => ({ role: m.role, content: shorten(m.content, recentMax) })));
81
+ }
82
+
83
+ async function once(messages) {
84
+ const ctl = new AbortController(); const to = setTimeout(() => ctl.abort(), 170000);
85
+ try {
86
+ const r = await fetch(API, {
87
+ method: "POST", signal: ctl.signal,
88
+ headers: { "content-type": "application/json", "x-api-key": cfg.key, "anthropic-version": "2023-06-01" },
89
+ body: JSON.stringify({ model: cfg.model || "mythos-code", max_tokens: 8000, system: SYSTEM, messages, stream: true }),
90
+ });
91
+ if (!r.ok) { const j = await r.json().catch(() => ({})); const e = new Error(j?.error?.message || ("HTTP " + r.status)); e.status = r.status; throw e; }
92
+ const reader = r.body.getReader(); const dec = new TextDecoder(); let buf = "", out = "";
93
+ while (true) {
94
+ const { done, value } = await reader.read(); if (done) break;
95
+ buf += dec.decode(value, { stream: true });
96
+ let i; while ((i = buf.indexOf("\n")) !== -1) {
97
+ const line = buf.slice(0, i).trim(); buf = buf.slice(i + 1);
98
+ if (!line.startsWith("data:")) continue;
99
+ let p; try { p = JSON.parse(line.slice(5)); } catch { continue; }
100
+ if (p.type === "error") { const e = new Error(p.error?.message || "Überlastet"); e.status = 529; throw e; }
101
+ if (p.delta?.text) out += p.delta.text;
102
+ }
103
+ }
104
+ if (!out.trim()) throw new Error("Leere Antwort");
105
+ return out;
106
+ } finally { clearTimeout(to); }
107
+ }
108
+
109
+ async function call(history) {
110
+ for (let a = 0; a < 6; a++) {
111
+ try { return await once(compact(history, Math.min(2, Math.floor(a / 2)))); }
112
+ catch (e) {
113
+ if (e.status === 401) throw new Error("Anmeldung ungültig – bitte neu anmelden: mythos login");
114
+ if (/Daily limit/i.test(e.message)) throw new Error("Tageslimit erreicht – mit Pro unbegrenzt.");
115
+ await new Promise((r) => setTimeout(r, Math.min(8000, 1500 * (a + 1))));
116
+ }
117
+ }
118
+ throw new Error("Mythos ist gerade nicht erreichbar – bitte gleich nochmal versuchen.");
43
119
  }
44
120
 
45
121
  async function runTool(t) {
46
122
  const show = t.name === "write" ? `write ${t.path} (${(t.content || "").length} Zeichen)` : `${t.name} ${t.cmd || t.path || ""}`;
47
- console.log(C.y + "⚙ " + show + C.x);
123
+ say(C.y + "⚙ " + show + C.x);
48
124
  if ((t.name === "run" || t.name === "write") && !autoYes) {
125
+ stopTick();
49
126
  const a = (await ask(C.d + " Erlauben? [j/N/a=immer] " + C.x)).trim().toLowerCase();
127
+ startTick();
50
128
  if (a === "a") autoYes = true; else if (a !== "j" && a !== "y") return "Vom Nutzer abgelehnt.";
51
129
  }
52
130
  try {
53
- if (t.name === "run") return execSync(t.cmd, { encoding: "utf8", stdio: "pipe", timeout: 120000, maxBuffer: 1e7 }).slice(-8000) || "(keine Ausgabe)";
131
+ if (t.name === "run") return await new Promise((res) => exec(t.cmd, { encoding: "utf8", timeout: 180000, maxBuffer: 1e7 },
132
+ (err, so, se) => res(((so || "") + (se || "") + (err ? "\nExit: " + (err.code ?? err.message) : "")).slice(-8000) || "(keine Ausgabe)")));
54
133
  if (t.name === "read") return fs.readFileSync(t.path, "utf8").slice(0, 20000);
55
134
  if (t.name === "write") { fs.mkdirSync(path.dirname(path.resolve(t.path)), { recursive: true }); fs.writeFileSync(t.path, t.content ?? ""); return "OK geschrieben: " + t.path; }
56
135
  if (t.name === "ls") return fs.readdirSync(t.path || ".", { withFileTypes: true }).map((e) => (e.isDirectory() ? "📁 " : " ") + e.name).join("\n");
@@ -59,14 +138,19 @@ async function runTool(t) {
59
138
  }
60
139
 
61
140
  async function turn(history, input) {
141
+ T0 = Date.now(); startTick();
142
+ try { await turnInner(history, input); }
143
+ catch (e) { say(C.r + "⚠ " + e.message + C.x); }
144
+ finally { stopTick(); console.log(C.g + "✓ Mythos hat " + fmt(Date.now() - T0) + " gearbeitet" + C.x + "\n"); }
145
+ }
146
+ async function turnInner(history, input) {
62
147
  history.push({ role: "user", content: input });
63
- for (let i = 0; i < 25; i++) {
64
- process.stdout.write(C.d + "… denkt" + C.x + "\r");
148
+ for (let i = 0; i < 40; i++) {
65
149
  const out = await call(history);
66
150
  history.push({ role: "assistant", content: out });
67
151
  const m = out.match(/<tool>([\s\S]*?)<\/tool>/);
68
152
  const text = out.replace(/<tool>[\s\S]*?<\/tool>/g, "").trim();
69
- if (text) console.log(C.b + "Mythos: " + C.x + text + "\n");
153
+ if (text) say(C.b + "Mythos: " + C.x + text + "\n");
70
154
  if (!m) return;
71
155
  let t; try { t = JSON.parse(m[1]); } catch { history.push({ role: "user", content: "Tool-JSON ungültig." }); continue; }
72
156
  const res = await runTool(t);
@@ -75,7 +159,7 @@ async function turn(history, input) {
75
159
  }
76
160
 
77
161
  async function main() {
78
- const args = process.argv.slice(2).filter((a) => a !== "--yes");
162
+ const args = process.argv.slice(2).filter((a) => a !== "--yes" && a !== "--key");
79
163
  if (args[0] === "login" || !cfg.key) await login();
80
164
  if (args[0] === "login") process.exit(0);
81
165
  if (args[0] === "logout") { save({}); console.log("Abgemeldet."); process.exit(0); }
@@ -87,7 +171,7 @@ async function main() {
87
171
  if (!q) continue;
88
172
  if (q === "/exit") break;
89
173
  if (q === "/clear") { history.length = 0; console.log("Verlauf geleert."); continue; }
90
- try { await turn(history, q); } catch (e) { console.log(C.r + "Fehler: " + e.message + C.x); }
174
+ await turn(history, q);
91
175
  }
92
176
  rl.close();
93
177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythos-code",
3
- "version": "1.0.0",
3
+ "version": "1.3.0",
4
4
  "description": "Mythos Code – KI-Coding-Agent im Terminal (by Mythoscraft)",
5
5
  "bin": {
6
6
  "mythos": "bin/mythos.js",