@danieltmn/tamnora 0.1.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 (123) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/SECURITY.md +31 -0
  5. package/harness/packages/cli/bin/tamnora.mjs +11 -0
  6. package/harness/packages/cli/package.json +10 -0
  7. package/harness/packages/cli/src/cli.js +90 -0
  8. package/harness/packages/cli/src/commands/autostart.js +121 -0
  9. package/harness/packages/cli/src/commands/bridge.js +274 -0
  10. package/harness/packages/cli/src/commands/doctor.js +73 -0
  11. package/harness/packages/cli/src/commands/eval.js +183 -0
  12. package/harness/packages/cli/src/commands/init.js +31 -0
  13. package/harness/packages/cli/src/commands/mcp.js +101 -0
  14. package/harness/packages/cli/src/commands/repl.js +172 -0
  15. package/harness/packages/cli/src/commands/run.js +165 -0
  16. package/harness/packages/cli/src/commands/serve.js +1 -0
  17. package/harness/packages/cli/src/commands/server.js +345 -0
  18. package/harness/packages/cli/src/commands/tui.js +110 -0
  19. package/harness/packages/cli/src/commands/update.js +76 -0
  20. package/harness/packages/cli/src/commands/user.js +135 -0
  21. package/harness/packages/cli/src/lib/core.js +2 -0
  22. package/harness/packages/cli/src/lib/daemon.js +153 -0
  23. package/harness/packages/cli/src/lib/qr.js +203 -0
  24. package/harness/packages/cli/src/lib/runtime.js +110 -0
  25. package/harness/packages/cli/src/lib/tunnel.js +113 -0
  26. package/harness/packages/cli/src/lib/util.js +24 -0
  27. package/harness/packages/cli/src/tui/app.js +302 -0
  28. package/harness/packages/cli/src/tui/keys.js +78 -0
  29. package/harness/packages/cli/src/tui/layout.js +130 -0
  30. package/harness/packages/cli/src/tui/screen.js +13 -0
  31. package/harness/packages/cli/src/ui/color.js +15 -0
  32. package/harness/packages/cli/test/release.test.js +34 -0
  33. package/harness/packages/cli/test/tui.test.js +99 -0
  34. package/harness/packages/cli/test/tunnel.test.js +20 -0
  35. package/harness/packages/core/package.json +14 -0
  36. package/harness/packages/core/src/agent/context.js +66 -0
  37. package/harness/packages/core/src/agent/modes.js +26 -0
  38. package/harness/packages/core/src/agent/runAgent.js +257 -0
  39. package/harness/packages/core/src/config/app.js +49 -0
  40. package/harness/packages/core/src/config/home.js +98 -0
  41. package/harness/packages/core/src/config/identity.js +41 -0
  42. package/harness/packages/core/src/config/index.js +69 -0
  43. package/harness/packages/core/src/config/providers.js +24 -0
  44. package/harness/packages/core/src/env.js +19 -0
  45. package/harness/packages/core/src/eval/dataset.js +47 -0
  46. package/harness/packages/core/src/eval/judge.js +39 -0
  47. package/harness/packages/core/src/eval/report.js +48 -0
  48. package/harness/packages/core/src/eval/runTask.js +71 -0
  49. package/harness/packages/core/src/eval/runner.js +150 -0
  50. package/harness/packages/core/src/eval/scorers.js +217 -0
  51. package/harness/packages/core/src/eval/task.js +59 -0
  52. package/harness/packages/core/src/index.js +72 -0
  53. package/harness/packages/core/src/mcp/common.js +82 -0
  54. package/harness/packages/core/src/mcp/http.js +149 -0
  55. package/harness/packages/core/src/mcp/index.js +167 -0
  56. package/harness/packages/core/src/mcp/stdio.js +218 -0
  57. package/harness/packages/core/src/providers/index.js +119 -0
  58. package/harness/packages/core/src/providers/ollama.js +76 -0
  59. package/harness/packages/core/src/providers/openaiCompatible.js +57 -0
  60. package/harness/packages/core/src/providers/openrouter.js +57 -0
  61. package/harness/packages/core/src/providers/presets.js +19 -0
  62. package/harness/packages/core/src/providers/resilience.js +74 -0
  63. package/harness/packages/core/src/providers/stream.js +200 -0
  64. package/harness/packages/core/src/providers/utils.js +66 -0
  65. package/harness/packages/core/src/storage/sqlite.js +453 -0
  66. package/harness/packages/core/src/tools/diff.js +90 -0
  67. package/harness/packages/core/src/tools/executors.js +277 -0
  68. package/harness/packages/core/src/tools/glob.js +67 -0
  69. package/harness/packages/core/src/tools/index.js +28 -0
  70. package/harness/packages/core/src/tools/paths.js +29 -0
  71. package/harness/packages/core/src/tools/permissions.js +69 -0
  72. package/harness/packages/core/src/tools/registry.js +44 -0
  73. package/harness/packages/core/src/tools/schema.js +177 -0
  74. package/harness/packages/core/test/agent.test.js +237 -0
  75. package/harness/packages/core/test/config.test.js +33 -0
  76. package/harness/packages/core/test/context.test.js +44 -0
  77. package/harness/packages/core/test/diff.test.js +20 -0
  78. package/harness/packages/core/test/eval.test.js +144 -0
  79. package/harness/packages/core/test/git-tools.test.js +44 -0
  80. package/harness/packages/core/test/home.test.js +62 -0
  81. package/harness/packages/core/test/mcp.test.js +158 -0
  82. package/harness/packages/core/test/providers.test.js +80 -0
  83. package/harness/packages/core/test/resilience.test.js +61 -0
  84. package/harness/packages/core/test/storage.test.js +55 -0
  85. package/harness/packages/core/test/stream.test.js +91 -0
  86. package/harness/packages/core/test/tools.test.js +96 -0
  87. package/harness/packages/server/bin/tamnora-server.mjs +11 -0
  88. package/harness/packages/server/package.json +10 -0
  89. package/harness/packages/server/public/app.js +2188 -0
  90. package/harness/packages/server/public/img/fondo-agentes.webp +0 -0
  91. package/harness/packages/server/public/index.html +368 -0
  92. package/harness/packages/server/public/landing/index.html +523 -0
  93. package/harness/packages/server/public/landing/landing.js +89 -0
  94. package/harness/packages/server/public/markdown.js +174 -0
  95. package/harness/packages/server/public/styles.css +923 -0
  96. package/harness/packages/server/src/auth.js +146 -0
  97. package/harness/packages/server/src/cli.js +61 -0
  98. package/harness/packages/server/src/index.js +1 -0
  99. package/harness/packages/server/src/lib/approvals.js +51 -0
  100. package/harness/packages/server/src/lib/core.js +2 -0
  101. package/harness/packages/server/src/lib/http.js +74 -0
  102. package/harness/packages/server/src/lib/modelCache.js +32 -0
  103. package/harness/packages/server/src/lib/prompt.js +48 -0
  104. package/harness/packages/server/src/lib/workspace.js +74 -0
  105. package/harness/packages/server/src/routes/auth.js +56 -0
  106. package/harness/packages/server/src/routes/chat.js +222 -0
  107. package/harness/packages/server/src/routes/eval.js +100 -0
  108. package/harness/packages/server/src/routes/files.js +117 -0
  109. package/harness/packages/server/src/routes/folders.js +80 -0
  110. package/harness/packages/server/src/routes/mcp.js +50 -0
  111. package/harness/packages/server/src/routes/models.js +26 -0
  112. package/harness/packages/server/src/routes/providers.js +135 -0
  113. package/harness/packages/server/src/routes/sessions.js +157 -0
  114. package/harness/packages/server/src/server.js +249 -0
  115. package/harness/packages/server/src/static.js +67 -0
  116. package/harness/packages/server/test/markdown.test.js +36 -0
  117. package/harness/packages/server/test/modelCache.test.js +72 -0
  118. package/harness/packages/server/test/server.test.js +809 -0
  119. package/harness/scripts/example-dataset.jsonl +4 -0
  120. package/harness/scripts/mcp-echo-server.mjs +108 -0
  121. package/harness/scripts/mock-provider.mjs +134 -0
  122. package/harness/tamnora.example.json +18 -0
  123. package/package.json +70 -0
@@ -0,0 +1,174 @@
1
+ // Dependency-free Markdown -> HTML for the console chat, mirroring the SPA's
2
+ // react-markdown + rehype-highlight behaviour. Every raw text is HTML-escaped
3
+ // before any tag is inserted, so model output cannot inject markup.
4
+
5
+ const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
6
+
7
+ export function escapeHtml(value) {
8
+ return String(value ?? '').replace(/[&<>"']/g, (c) => HTML_ESCAPES[c]);
9
+ }
10
+
11
+ const KEYWORDS = {
12
+ js: 'const let var function return if else for while do break continue new class extends super import export from default async await try catch finally throw typeof instanceof delete void yield switch case this null undefined true false of in',
13
+ jsx: 'const let var function return if else for while new class import export default async await try catch finally throw typeof this null undefined true false',
14
+ ts: 'const let var function return if else for while do break continue new class extends implements interface type enum import export from default async await try catch finally throw public private protected readonly as switch case this null undefined true false of in',
15
+ json: 'true false null',
16
+ bash: 'if then else elif fi for while do done case esac function in echo cd ls cat grep sed awk export source return exit sudo rm mkdir touch pwd curl wget git npm pnpm node',
17
+ sh: 'if then else elif fi for while do done case esac function in echo cd ls cat grep sed awk export source return exit sudo rm mkdir touch pwd',
18
+ php: 'function return if else elseif endif foreach as echo print class extends implements public private protected static new use namespace try catch finally throw match fn true false null isset unset array',
19
+ python: 'def return if elif else for while import from as class try except finally raise with lambda yield pass break continue in is not and or None True False self',
20
+ };
21
+
22
+ function normalizeLang(lang) {
23
+ const value = String(lang ?? '').toLowerCase();
24
+ if (value === 'tsx' || value === 'typescript') return 'ts';
25
+ if (value === 'javascript' || value === 'mjs' || value === 'cjs') return 'js';
26
+ if (value === 'shell' || value === 'zsh' || value === 'console') return 'bash';
27
+ if (value === 'yml' || value === 'yaml') return 'yaml';
28
+ return value;
29
+ }
30
+
31
+ function wrap(cls, text) {
32
+ return `<span class="${cls}">${escapeHtml(text)}</span>`;
33
+ }
34
+
35
+ /** Minimal tokenizer: comments, strings, numbers and keywords. Best effort. */
36
+ export function highlight(code, lang) {
37
+ const language = normalizeLang(lang);
38
+ const keywords = new Set((KEYWORDS[language] ?? '').split(/\s+/).filter(Boolean));
39
+ const pattern =
40
+ /(\/\/[^\n]*|#[^\n]*|\/\*[\s\S]*?\*\/)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|(\b\d[\d._]*\b)|([A-Za-z_$][\w$]*)/g;
41
+
42
+ let out = '';
43
+ let last = 0;
44
+ let match;
45
+ while ((match = pattern.exec(code))) {
46
+ out += escapeHtml(code.slice(last, match.index));
47
+ const [full, comment, str, num, word] = match;
48
+ if (comment) out += wrap('tok-comment', full);
49
+ else if (str) out += wrap('tok-string', full);
50
+ else if (num) out += wrap('tok-number', full);
51
+ else if (word && keywords.has(word)) out += wrap('tok-keyword', full);
52
+ else out += escapeHtml(full);
53
+ last = match.index + full.length;
54
+ }
55
+ return out + escapeHtml(code.slice(last));
56
+ }
57
+
58
+ function inline(text) {
59
+ let out = escapeHtml(text);
60
+ const codes = [];
61
+ out = out.replace(/`([^`]+)`/g, (_, code) => {
62
+ codes.push(code);
63
+ return `\uE000${codes.length - 1}\uE000`;
64
+ });
65
+ out = out
66
+ .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
67
+ .replace(/__([^_]+)__/g, '<strong>$1</strong>')
68
+ .replace(/(^|[^*\w])\*([^*\n]+)\*/g, '$1<em>$2</em>')
69
+ .replace(
70
+ /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,
71
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
72
+ );
73
+ return out.replace(/\uE000(\d+)\uE000/g, (_, idx) => `<code>${codes[Number(idx)]}</code>`);
74
+ }
75
+
76
+ /** Render a Markdown string to safe HTML. */
77
+ export function renderMarkdown(text) {
78
+ const lines = String(text ?? '').replace(/\r\n?/g, '\n').split('\n');
79
+ const out = [];
80
+ let paragraph = [];
81
+ let i = 0;
82
+
83
+ const flush = () => {
84
+ if (paragraph.length) {
85
+ out.push(`<p>${inline(paragraph.join(' '))}</p>`);
86
+ paragraph = [];
87
+ }
88
+ };
89
+
90
+ while (i < lines.length) {
91
+ const line = lines[i];
92
+
93
+ const fence = line.match(/^\s*```\s*([\w+#.-]*)\s*$/);
94
+ if (fence) {
95
+ flush();
96
+ const lang = fence[1] || '';
97
+ const body = [];
98
+ i += 1;
99
+ while (i < lines.length && !/^\s*```\s*$/.test(lines[i])) {
100
+ body.push(lines[i]);
101
+ i += 1;
102
+ }
103
+ i += 1;
104
+ const code = body.join('\n');
105
+ out.push(
106
+ `<div class="code-block"><div class="code-bar"><span class="code-lang">${escapeHtml(
107
+ lang || 'texto'
108
+ )}</span><button type="button" class="code-copy">copiar</button></div>` +
109
+ `<pre class="code"><code>${highlight(code, lang)}</code></pre></div>`
110
+ );
111
+ continue;
112
+ }
113
+
114
+ if (/^\s*$/.test(line)) {
115
+ flush();
116
+ i += 1;
117
+ continue;
118
+ }
119
+
120
+ const heading = line.match(/^(#{1,6})\s+(.*)$/);
121
+ if (heading) {
122
+ flush();
123
+ const level = heading[1].length;
124
+ out.push(`<h${level}>${inline(heading[2])}</h${level}>`);
125
+ i += 1;
126
+ continue;
127
+ }
128
+
129
+ if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(line)) {
130
+ flush();
131
+ out.push('<hr />');
132
+ i += 1;
133
+ continue;
134
+ }
135
+
136
+ if (/^\s*>/.test(line)) {
137
+ flush();
138
+ const quote = [];
139
+ while (i < lines.length && /^\s*>/.test(lines[i])) {
140
+ quote.push(lines[i].replace(/^\s*>\s?/, ''));
141
+ i += 1;
142
+ }
143
+ out.push(`<blockquote>${renderMarkdown(quote.join('\n'))}</blockquote>`);
144
+ continue;
145
+ }
146
+
147
+ if (/^\s*([-*+])\s+/.test(line)) {
148
+ flush();
149
+ const items = [];
150
+ while (i < lines.length && /^\s*([-*+])\s+/.test(lines[i])) {
151
+ items.push(lines[i].replace(/^\s*([-*+])\s+/, ''));
152
+ i += 1;
153
+ }
154
+ out.push(`<ul>${items.map((item) => `<li>${inline(item)}</li>`).join('')}</ul>`);
155
+ continue;
156
+ }
157
+
158
+ if (/^\s*\d+[.)]\s+/.test(line)) {
159
+ flush();
160
+ const items = [];
161
+ while (i < lines.length && /^\s*\d+[.)]\s+/.test(lines[i])) {
162
+ items.push(lines[i].replace(/^\s*\d+[.)]\s+/, ''));
163
+ i += 1;
164
+ }
165
+ out.push(`<ol>${items.map((item) => `<li>${inline(item)}</li>`).join('')}</ol>`);
166
+ continue;
167
+ }
168
+
169
+ paragraph.push(line.trim());
170
+ i += 1;
171
+ }
172
+ flush();
173
+ return out.join('\n');
174
+ }