@arkvenai/cli 0.5.1 → 0.6.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 (139) hide show
  1. package/README.md +80 -242
  2. package/dist/befehle.js +138 -77
  3. package/dist/index.js +63 -334
  4. package/dist/projekt.js +121 -0
  5. package/dist/ruf.js +45 -0
  6. package/dist/uebertragen.js +185 -0
  7. package/dist/zugang.js +114 -0
  8. package/package.json +7 -39
  9. package/CHANGELOG.md +0 -105
  10. package/dist/agents.d.ts +0 -11
  11. package/dist/agents.d.ts.map +0 -1
  12. package/dist/agents.js +0 -76
  13. package/dist/agents.js.map +0 -1
  14. package/dist/auftrag.d.ts +0 -3
  15. package/dist/auftrag.d.ts.map +0 -1
  16. package/dist/auftrag.js +0 -45
  17. package/dist/auftrag.js.map +0 -1
  18. package/dist/bauplan.d.ts +0 -15
  19. package/dist/bauplan.d.ts.map +0 -1
  20. package/dist/bauplan.js +0 -219
  21. package/dist/bauplan.js.map +0 -1
  22. package/dist/befehle.d.ts +0 -12
  23. package/dist/befehle.d.ts.map +0 -1
  24. package/dist/befehle.js.map +0 -1
  25. package/dist/befund.d.ts +0 -21
  26. package/dist/befund.d.ts.map +0 -1
  27. package/dist/befund.js +0 -107
  28. package/dist/befund.js.map +0 -1
  29. package/dist/codeAblage.d.ts +0 -31
  30. package/dist/codeAblage.d.ts.map +0 -1
  31. package/dist/codeAblage.js +0 -80
  32. package/dist/codeAblage.js.map +0 -1
  33. package/dist/deklaration.d.ts +0 -3
  34. package/dist/deklaration.d.ts.map +0 -1
  35. package/dist/deklaration.js +0 -14
  36. package/dist/deklaration.js.map +0 -1
  37. package/dist/diff.d.ts +0 -10
  38. package/dist/diff.d.ts.map +0 -1
  39. package/dist/diff.js +0 -72
  40. package/dist/diff.js.map +0 -1
  41. package/dist/erkennung.d.ts +0 -26
  42. package/dist/erkennung.d.ts.map +0 -1
  43. package/dist/erkennung.js +0 -174
  44. package/dist/erkennung.js.map +0 -1
  45. package/dist/fix.d.ts +0 -26
  46. package/dist/fix.d.ts.map +0 -1
  47. package/dist/fix.js +0 -101
  48. package/dist/fix.js.map +0 -1
  49. package/dist/geteilt/bauplan/form.d.ts +0 -100
  50. package/dist/geteilt/bauplan/form.d.ts.map +0 -1
  51. package/dist/geteilt/bauplan/form.js +0 -21
  52. package/dist/geteilt/bauplan/form.js.map +0 -1
  53. package/dist/geteilt/bauplan/index.d.ts +0 -4
  54. package/dist/geteilt/bauplan/index.d.ts.map +0 -1
  55. package/dist/geteilt/bauplan/index.js +0 -4
  56. package/dist/geteilt/bauplan/index.js.map +0 -1
  57. package/dist/geteilt/bauplan/lesen.d.ts +0 -6
  58. package/dist/geteilt/bauplan/lesen.d.ts.map +0 -1
  59. package/dist/geteilt/bauplan/lesen.js +0 -344
  60. package/dist/geteilt/bauplan/lesen.js.map +0 -1
  61. package/dist/geteilt/bauplan/schreiben.d.ts +0 -3
  62. package/dist/geteilt/bauplan/schreiben.d.ts.map +0 -1
  63. package/dist/geteilt/bauplan/schreiben.js +0 -80
  64. package/dist/geteilt/bauplan/schreiben.js.map +0 -1
  65. package/dist/index.d.ts +0 -3
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/init.d.ts +0 -28
  69. package/dist/init.d.ts.map +0 -1
  70. package/dist/init.js +0 -324
  71. package/dist/init.js.map +0 -1
  72. package/dist/login.d.ts +0 -45
  73. package/dist/login.d.ts.map +0 -1
  74. package/dist/login.js +0 -162
  75. package/dist/login.js.map +0 -1
  76. package/dist/mcp.d.ts +0 -18
  77. package/dist/mcp.d.ts.map +0 -1
  78. package/dist/mcp.js +0 -115
  79. package/dist/mcp.js.map +0 -1
  80. package/dist/neu.d.ts +0 -17
  81. package/dist/neu.d.ts.map +0 -1
  82. package/dist/neu.js +0 -118
  83. package/dist/neu.js.map +0 -1
  84. package/dist/npmrc.d.ts +0 -9
  85. package/dist/npmrc.d.ts.map +0 -1
  86. package/dist/npmrc.js +0 -49
  87. package/dist/npmrc.js.map +0 -1
  88. package/dist/projekte.d.ts +0 -23
  89. package/dist/projekte.d.ts.map +0 -1
  90. package/dist/projekte.js +0 -82
  91. package/dist/projekte.js.map +0 -1
  92. package/dist/pull.d.ts +0 -15
  93. package/dist/pull.d.ts.map +0 -1
  94. package/dist/pull.js +0 -301
  95. package/dist/pull.js.map +0 -1
  96. package/dist/push/bericht.d.ts +0 -8
  97. package/dist/push/bericht.d.ts.map +0 -1
  98. package/dist/push/bericht.js +0 -85
  99. package/dist/push/bericht.js.map +0 -1
  100. package/dist/push/form.d.ts +0 -67
  101. package/dist/push/form.d.ts.map +0 -1
  102. package/dist/push/form.js +0 -6
  103. package/dist/push/form.js.map +0 -1
  104. package/dist/push/medien.d.ts +0 -7
  105. package/dist/push/medien.d.ts.map +0 -1
  106. package/dist/push/medien.js +0 -154
  107. package/dist/push/medien.js.map +0 -1
  108. package/dist/push/sammeln.d.ts +0 -34
  109. package/dist/push/sammeln.d.ts.map +0 -1
  110. package/dist/push/sammeln.js +0 -112
  111. package/dist/push/sammeln.js.map +0 -1
  112. package/dist/push/verzeichnisse.d.ts +0 -40
  113. package/dist/push/verzeichnisse.d.ts.map +0 -1
  114. package/dist/push/verzeichnisse.js +0 -158
  115. package/dist/push/verzeichnisse.js.map +0 -1
  116. package/dist/push.d.ts +0 -8
  117. package/dist/push.d.ts.map +0 -1
  118. package/dist/push.js +0 -306
  119. package/dist/push.js.map +0 -1
  120. package/dist/starter.d.ts +0 -4
  121. package/dist/starter.d.ts.map +0 -1
  122. package/dist/starter.js +0 -587
  123. package/dist/starter.js.map +0 -1
  124. package/dist/tiefpruefung.d.ts +0 -39
  125. package/dist/tiefpruefung.d.ts.map +0 -1
  126. package/dist/tiefpruefung.js +0 -56
  127. package/dist/tiefpruefung.js.map +0 -1
  128. package/dist/vertrag.d.ts +0 -6
  129. package/dist/vertrag.d.ts.map +0 -1
  130. package/dist/vertrag.js +0 -267
  131. package/dist/vertrag.js.map +0 -1
  132. package/dist/vertragSpaet.d.ts +0 -4
  133. package/dist/vertragSpaet.d.ts.map +0 -1
  134. package/dist/vertragSpaet.js +0 -158
  135. package/dist/vertragSpaet.js.map +0 -1
  136. package/dist/vorlagen.d.ts +0 -7
  137. package/dist/vorlagen.d.ts.map +0 -1
  138. package/dist/vorlagen.js +0 -83
  139. package/dist/vorlagen.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,352 +1,81 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import readline from 'node:readline/promises';
5
- import { alsJson, alsSarif, alsText, exitCode } from './befund.js';
6
- import { baueAuftrag } from './auftrag.js';
7
- import { fahreFix } from './fix.js';
8
- import { fahreInit } from './init.js';
9
- import { fahreLogin, nimmEinfuegeWert, tageBis, zugangsDatei } from './login.js';
10
- import { fahreNeu } from './neu.js';
11
- import { richteBezugEin } from './npmrc.js';
12
- import { fahrePush, leseArkvenDatei, sammleTeile } from './push.js';
13
- import { alsBefunde, tiefPruefen } from './tiefpruefung.js';
14
- const DEKLARATION = 'arkven.json';
15
- import { fahrePull } from './pull.js';
16
- import { deklarationsBefunde } from './deklaration.js';
17
- import { pruefeAnker, pruefeMailArten } from './bauplan.js';
18
- import { pruefeVertrag } from './vertrag.js';
19
- import { BEFEHLE } from './befehle.js';
20
- const VERSION = '0.5.1';
21
- function gib(e, flags) {
22
- if (flags.has('--sarif'))
23
- return alsSarif(e, VERSION);
24
- if (flags.has('--json'))
25
- return alsJson(e);
26
- return alsText(e);
27
- }
28
- function umbrich(einzug, text, breite = 98) {
29
- const folgeEinzug = ' '.repeat(einzug.length);
30
- const zeilen = [];
31
- let laufend = einzug;
32
- for (const wort of text.split(' ')) {
33
- if (laufend.trimEnd().length > einzug.length && laufend.length + 1 + wort.length > breite) {
34
- zeilen.push(laufend);
35
- laufend = folgeEinzug + wort;
36
- }
37
- else {
38
- laufend = laufend.trimEnd().length === einzug.trimEnd().length && laufend.endsWith(' ')
39
- ? laufend + wort
40
- : laufend + ' ' + wort;
41
- }
42
- }
43
- zeilen.push(laufend);
44
- return zeilen;
45
- }
46
- function befehlsZeilen(namen) {
47
- const zeilen = [];
48
- for (const name of namen) {
49
- const b = BEFEHLE.find((x) => x.name === name);
50
- if (!b)
51
- continue;
52
- const kopf = ` arkven ${b.name}${b.stelle ? ' ' + b.stelle : ''}`;
53
- zeilen.push(...umbrich(kopf.padEnd(30), b.wirkung));
54
- for (const s of b.schalter)
55
- zeilen.push(...umbrich(` ${s.flagge.padEnd(26)}`, s.wirkung));
56
- }
57
- return zeilen.join(String.fromCharCode(10));
58
- }
59
- const HILFE = `arkven ${VERSION} - eigene Projekte auf Arkven
2
+ import { login, status, umgebung } from './befehle.js';
3
+ import { live, pull, push } from './uebertragen.js';
4
+ /**
5
+ * Die Befehlsverteilung. Sie tut selbst nichts, ausser Argumente zu lesen und den
6
+ * richtigen Befehl zu rufen: was hier an Logik stuende, waere an einem Ort, an dem sie
7
+ * niemand vermutet.
8
+ */
9
+ const HILFE = `arkven -- dein Arkven auf diesem Rechner
60
10
 
61
- So geht der Rundlauf. Eine App, die es in Arkven schon gibt:
62
- 1. arkven login einmal pro Rechner
63
- 2. arkven pull zeigt deine Projekte, du wählst die Nummer
64
- 3. Änderungen machen
65
- 4. arkven push Vorschau ansehen und bestätigen
66
- 5. arkven push --deploy online stellen
11
+ arkven login [--server=<adresse>] [--umgebung=<name>]
12
+ Diesen Rechner anmelden. Du bestaetigst im Browser.
67
13
 
68
- Ein Projekt, das auf deinem Rechner entstanden ist:
69
- 1. arkven login
70
- 2. arkven init schreibt arkven.json
71
- 3. arkven push --name="Mein Tool" legt das Projekt in Arkven an
72
- 4. arkven push --deploy online stellen
14
+ arkven status [--umgebung=<name>]
15
+ Wer du bist, welche Umgebung gilt, welche Apps es gibt.
73
16
 
74
- ${befehlsZeilen(['new', 'init', 'check', 'auftrag', 'fix', 'login', 'pull'])}
17
+ arkven umgebung [<name>] [--weg=<name>]
18
+ Ohne Namen: alle angemeldeten Umgebungen, die aktive mit einem Stern.
19
+ Mit Namen: diese wird aktiv.
75
20
 
76
- Der Code wird nach verzeichnisse abgelegt, derselben Angabe, nach der push sammelt. pull löscht
77
- nie etwas: Dateien, die nur bei dir liegen, bleiben unberührt.
78
- ${befehlsZeilen(['push'])}
21
+ arkven pull [--app=<kennung>] [--ja]
22
+ Den Quelltext einer App in diesen Ordner holen. Ohne --ja nur die Vorschau.
79
23
 
80
- Liegen Server und Frontend in getrennten Ordnern, sagt das arkven.json:
81
- "verzeichnisse": { "server": "server", "frontend": "web" }
82
- Dann sammelt push beide Teile und schickt sie getrennt: das Frontend wird die Vorschau, der Server
83
- geht in den Container. Ohne diese Angabe bleibt alles wie bisher.
84
- ${befehlsZeilen(['version', 'help'])}
24
+ arkven push [--betreff="..."] [--ja]
25
+ Den Quelltext von hier nach Arkven. Ohne --ja nur die Vorschau.
85
26
 
86
- Rückgabewerte von check: 0 alles gehalten - 1 mindestens ein blockierender Punkt - 2 nichts messbar.
87
- Rückgabewerte von init: 0 alles geklärt - 1 etwas bleibt zu entscheiden - 2 nichts messbar.
88
- Rückgabewerte von new: 0 angelegt und nachgemessen - 1 abgebrochen, nichts angelegt.
89
- Rückgabewerte von push: 0 übernommen - 1 etwas blockiert (Befund oder Konflikt) - 2 nichts messbar.
27
+ arkven live [--ja]
28
+ Die App live schalten. Ohne --ja nur der Hinweis, was passieren wuerde.
90
29
 
91
- Bei einem Konflikt bricht push ab und nennt die Dateien. Es gibt bewusst kein --force: eine
92
- automatische Wahl würde eine der beiden Fassungen still verlieren.
93
-
94
- ${befehlsZeilen(['mcp'])}
30
+ Ueberall moeglich: --umgebung=<name>, wenn nicht die aktive gemeint ist.
95
31
  `;
96
- async function frageImTerminal(text, vorschlag) {
97
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
98
- try {
99
- const antwort = (await rl.question(`\n${text}\n[${vorschlag ? 'J/n' : 'j/N'}] `)).trim().toLowerCase();
100
- if (antwort === '')
101
- return vorschlag;
102
- return antwort === 'j' || antwort === 'ja' || antwort === 'y' || antwort === 'yes';
103
- }
104
- finally {
105
- rl.close();
106
- }
107
- }
108
- async function frageTextImTerminal(text) {
109
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
110
- try {
111
- return (await rl.question(`\n${text}\n> `)).trim();
112
- }
113
- finally {
114
- rl.close();
115
- }
32
+ function flagge(argv, name) {
33
+ const mit = argv.find((a) => a.startsWith(`--${name}=`));
34
+ if (mit)
35
+ return mit.slice(name.length + 3);
36
+ return undefined;
116
37
  }
117
- async function alleZusatzBefunde(wurzel) {
118
- const befunde = deklarationsBefunde(wurzel);
119
- const datei = leseArkvenDatei(wurzel);
120
- const v = datei['verzeichnisse'];
121
- const verz = v && typeof v === 'object' && !Array.isArray(v) ? v : {};
122
- const teile = sammleTeile(wurzel, {
123
- ...(typeof verz.server === 'string' ? { server: verz.server } : {}),
124
- ...(typeof verz.frontend === 'string' ? { frontend: verz.frontend } : {}),
125
- });
126
- const tief = await tiefPruefen(teile.app.sammlung.dateien, (teile.server ?? teile.app).sammlung.dateien);
127
- if (!tief.erreichbar)
128
- return befunde;
129
- befunde.push(...alsBefunde(tief.ergebnis));
130
- befunde.push(...pruefeMailArten(wurzel, tief.ergebnis.mailArten ?? []));
131
- befunde.push(...pruefeAnker(teile.app.sammlung.dateien, tief.ergebnis.screens ?? []));
132
- return befunde;
133
- }
134
- async function main(argv) {
135
- const [befehl, ...rest] = argv;
136
- const flags = new Set(rest.filter((a) => a.startsWith('--')));
137
- const stellen = rest.filter((a) => !a.startsWith('--'));
38
+ const hat = (argv, name) => argv.includes(`--${name}`);
39
+ export async function fahre(argv, sage) {
40
+ const befehl = argv.find((a) => !a.startsWith('-')) ?? '';
41
+ const gemeinsam = { umgebung: flagge(argv, 'umgebung'), sage };
42
+ const bestaetigt = hat(argv, 'ja');
43
+ const wurzel = process.cwd();
138
44
  switch (befehl) {
139
- case 'new': {
140
- const name = stellen[0];
141
- if (!name) {
142
- console.error('`arkven new` braucht einen Namen für das neue Projekt.');
143
- console.error('So kommst du weiter: `arkven new meine-app` legt ./meine-app an.');
144
- return 1;
145
- }
146
- const e = await fahreNeu(name, { sage: (z) => console.log(z) });
147
- return e.code;
148
- }
149
- case 'init': {
150
- const wurzel = path.resolve(stellen[0] ?? process.cwd());
151
- const e = await fahreInit(wurzel, {
152
- sage: (z) => console.log(z),
153
- frage: process.stdin.isTTY ? frageImTerminal : undefined,
154
- uebernehmen: flags.has('--apply'),
155
- });
156
- return e.code;
157
- }
158
- case 'check': {
159
- const wurzel = path.resolve(stellen[0] ?? process.cwd());
160
- const ergebnis = pruefeVertrag(wurzel);
161
- ergebnis.befunde.push(...deklarationsBefunde(wurzel));
162
- if (flags.has('--tief')) {
163
- const datei = leseArkvenDatei(wurzel);
164
- const v = datei['verzeichnisse'];
165
- const verz = v && typeof v === 'object' && !Array.isArray(v) ? v : {};
166
- const teile = sammleTeile(wurzel, {
167
- ...(typeof verz.server === 'string' ? { server: verz.server } : {}),
168
- ...(typeof verz.frontend === 'string' ? { frontend: verz.frontend } : {}),
169
- });
170
- const serverBaum = (teile.server ?? teile.app).sammlung.dateien;
171
- const appBaum = teile.app.sammlung.dateien;
172
- if (!appBaum.some((d) => d.pfad === DEKLARATION)) {
173
- let ordner = wurzel;
174
- for (let i = 0; i < 3; i++) {
175
- try {
176
- const inhalt = fs.readFileSync(path.join(ordner, DEKLARATION), 'utf8');
177
- appBaum.push({ pfad: DEKLARATION, inhalt });
178
- if (ordner !== wurzel)
179
- console.log(`Deklaration aus ${ordner} gelesen (sie beschreibt das ganze Projekt).`);
180
- break;
181
- }
182
- catch {
183
- const oben = path.dirname(ordner);
184
- if (oben === ordner)
185
- break;
186
- ordner = oben;
187
- }
188
- }
189
- }
190
- console.log('Die Gates der Plattform laufen mit (dieselbe Prüfung wie beim Ausrollen) …');
191
- const tief = await tiefPruefen(appBaum, serverBaum);
192
- if (tief.erreichbar) {
193
- ergebnis.befunde.push(...alsBefunde(tief.ergebnis));
194
- ergebnis.befunde.push(...pruefeMailArten(wurzel, tief.ergebnis.mailArten ?? []));
195
- ergebnis.befunde.push(...pruefeAnker(appBaum, tief.ergebnis.screens ?? []));
196
- for (const c of tief.ergebnis.checks)
197
- if (c.ok)
198
- ergebnis.gehalten.push(c.regel ?? c.name);
199
- console.log(gib(ergebnis, flags));
200
- for (const u of tief.ergebnis.ungeprueft)
201
- console.log(`? nicht geprüft: ${u}`);
202
- return exitCode(ergebnis);
203
- }
204
- ergebnis.befunde.push({
205
- regel: 'arkven/plattform-gates',
206
- schwere: 'ungeprueft',
207
- titel: 'Die Gates der Plattform sind in diesem Lauf nicht mitgelaufen.',
208
- ausweg: tief.grund,
209
- });
210
- }
211
- console.log(gib(ergebnis, flags));
212
- return exitCode(ergebnis);
213
- }
214
- case 'auftrag': {
215
- const wurzel = path.resolve(stellen[0] ?? process.cwd());
216
- const mitBauplan = pruefeVertrag(wurzel);
217
- mitBauplan.befunde.push(...(await alleZusatzBefunde(wurzel)));
218
- const auftrag = baueAuftrag(mitBauplan);
219
- if (!auftrag) {
220
- console.log('Es blockiert nichts, deshalb gibt es keinen Auftrag.');
221
- return 0;
222
- }
223
- console.log(auftrag);
224
- return 0;
225
- }
226
- case 'fix': {
227
- const wurzel = path.resolve(stellen[0] ?? process.cwd());
228
- const rundenArg = rest.find((a) => a.startsWith('--runden'));
229
- const runden = Number(rundenArg?.split('=')[1] ?? rest[rest.indexOf('--runden') + 1] ?? 3);
230
- if (!Number.isFinite(runden) || runden < 1 || runden > 10) {
231
- console.error('`--runden` braucht eine Zahl zwischen 1 und 10.');
232
- return 1;
233
- }
234
- console.log(`Claude Code bearbeitet gleich Dateien in ${wurzel}.`);
235
- console.log('Datei-Änderungen werden dabei ohne Rückfrage übernommen - dafür hast du diesen Befehl aufgerufen.');
236
- console.log('Alles andere bleibt hinter Claudes eigener Rückfrage. Es wird nichts committet und nichts gepusht.');
237
- console.log('Sieh dir hinterher `git diff` an. Abbrechen mit Strg-C.\n');
238
- const e = await fahreFix(wurzel, {
239
- runden,
240
- sage: (z) => console.log(z),
241
- zusatzBefunde: () => alleZusatzBefunde(wurzel),
242
- });
243
- console.log('');
244
- if (e.behoben.length > 0)
245
- console.log(`Behoben und nachgemessen (${e.behoben.length}): ${e.behoben.join(', ')}`);
246
- if (e.offen.length > 0)
247
- console.log(`Weiterhin offen (${e.offen.length}): ${e.offen.join(', ')}`);
248
- if (e.grund)
249
- console.log(e.grund);
250
- console.log(e.offen.length > 0
251
- ? '\nSieh dir die Änderungen an (`git diff`) und rufe danach `arkven check` erneut auf.'
252
- : '\nSieh dir die Änderungen an (`git diff`). Nächster Schritt: `arkven push --deploy`.');
253
- return e.offen.length > 0 ? 1 : 0;
254
- }
255
- case 'version':
256
- case '--version':
257
- case '-v':
258
- console.log(VERSION);
259
- return 0;
260
- case undefined:
45
+ case 'login':
46
+ return (await login({ server: flagge(argv, 'server'), umgebung: flagge(argv, 'umgebung'), sage })).code;
47
+ case 'status':
48
+ return (await status(gemeinsam)).code;
49
+ case 'umgebung':
50
+ return umgebung({
51
+ name: argv.filter((a) => !a.startsWith('-'))[1],
52
+ weg: flagge(argv, 'weg'),
53
+ sage,
54
+ }).code;
55
+ case 'pull':
56
+ return (await pull(wurzel, { ...gemeinsam, app: flagge(argv, 'app'), bestaetigt })).code;
57
+ case 'push':
58
+ return (await push(wurzel, { ...gemeinsam, app: flagge(argv, 'app'), betreff: flagge(argv, 'betreff'), bestaetigt })).code;
59
+ case 'live':
60
+ return (await live(wurzel, { ...gemeinsam, app: flagge(argv, 'app'), bestaetigt })).code;
61
+ case '':
261
62
  case 'help':
262
63
  case '--help':
263
- case '-h':
264
- console.log(HILFE);
265
- return 0;
266
- case 'login': {
267
- const serverArg = rest.find((a) => a.startsWith('--server='))?.split('=')[1];
268
- let e;
269
- if (flags.has('--einfuegen')) {
270
- if (!process.stdin.isTTY) {
271
- console.error('`--einfuegen` braucht ein Terminal, in das du den Wert einfügen kannst.');
272
- console.error('So kommst du weiter: den Befehl direkt in einer Eingabeaufforderung ausführen.');
273
- return 1;
274
- }
275
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
276
- const wert = await rl.question('\nWert von der Bestätigungsseite einfügen (beginnt mit arkv1_):\n> ');
277
- rl.close();
278
- e = nimmEinfuegeWert(wert);
279
- }
280
- else {
281
- e = await fahreLogin({ server: serverArg, sage: (z) => console.log(z) });
282
- }
283
- if (e.code !== 0 || !e.zugang) {
284
- console.error('');
285
- console.error(e.grund ?? 'Die Anmeldung ist fehlgeschlagen.');
286
- return e.code;
287
- }
288
- const bezug = richteBezugEin(e.zugang.server, e.zugang.registryToken, path.resolve(stellen[0] ?? process.cwd()));
289
- const teams = e.zugang.teams ?? [];
290
- console.log('');
291
- console.log(` Angemeldet. Der Zugang gilt für ${teams.length} Team(s)${teams.length ? `: ${teams.map((t) => t.name).join(', ')}` : ''}.`);
292
- console.log(` Er läuft in ${tageBis(e.zugang.laeuftAbAm.registry)} Tagen ab und ist jederzeit in den Konto-Einstellungen entziehbar.`);
293
- console.log(` Gespeichert in ${zugangsDatei()}`);
294
- console.log(` Paketbezug eingerichtet: ${bezug.projektDatei} (Zuordnung) und ${bezug.benutzerDatei} (Token)`);
295
- console.log('');
296
- console.log(' `npm install @arkvenai/base` funktioniert jetzt in diesem Projekt.');
297
- console.log(' Nächster Schritt: `arkven check` prüft dein Projekt.');
64
+ sage(HILFE);
298
65
  return 0;
299
- }
300
- case 'pull': {
301
- const wurzel = path.resolve(stellen[0] ?? process.cwd());
302
- const e = await fahrePull(wurzel, {
303
- sage: (z) => console.log(z),
304
- frage: process.stdin.isTTY ? (text) => frageImTerminal(text, false) : undefined,
305
- ja: flags.has('--yes') || flags.has('--ja'),
306
- server: rest.find((a) => a.startsWith('--server='))?.split('=')[1],
307
- nurDeklaration: flags.has('--nur-deklaration'),
308
- projektId: rest.find((a) => a.startsWith('--projekt='))?.split('=')[1],
309
- frageText: process.stdin.isTTY ? (text) => frageTextImTerminal(text) : undefined,
310
- });
311
- if (e.grund) {
312
- console.log('');
313
- if (e.code === 0)
314
- console.log(e.grund);
315
- else
316
- console.error(e.grund);
317
- }
318
- return e.code;
319
- }
320
- case 'push': {
321
- const wurzel = path.resolve(stellen[0] ?? process.cwd());
322
- const e = await fahrePush(wurzel, {
323
- sage: (z) => console.log(z),
324
- frage: process.stdin.isTTY ? (text) => frageImTerminal(text, false) : undefined,
325
- frageText: process.stdin.isTTY ? (text) => frageTextImTerminal(text) : undefined,
326
- ja: flags.has('--yes') || flags.has('--ja'),
327
- deploy: flags.has('--deploy'),
328
- server: rest.find((a) => a.startsWith('--server='))?.split('=')[1],
329
- name: rest.find((a) => a.startsWith('--name='))?.split('=')[1],
330
- team: rest.find((a) => a.startsWith('--team='))?.split('=')[1],
331
- });
332
- if (e.grund) {
333
- console.log('');
334
- if (e.code === 0)
335
- console.log(e.grund);
336
- else
337
- console.error(e.grund);
338
- }
339
- return e.code;
340
- }
341
- case 'mcp': {
342
- const { fahreMcp } = await import('./mcp.js');
343
- return await fahreMcp();
344
- }
345
66
  default:
346
- console.error(`Unbekannter Befehl: ${befehl}`);
347
- console.error('So kommst du weiter: `arkven help` zeigt, was es gibt.');
67
+ sage(`"${befehl}" kennt arkven nicht.`);
68
+ sage('');
69
+ sage(HILFE);
348
70
  return 1;
349
71
  }
350
72
  }
351
- void main(process.argv.slice(2)).then((code) => process.exit(code));
352
- //# sourceMappingURL=index.js.map
73
+ // Nur ausfuehren, wenn dieses Modul der Einstieg ist -- die Probe importiert es.
74
+ if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, '/').split('/').pop() ?? '')) {
75
+ fahre(process.argv.slice(2), (z) => console.log(z))
76
+ .then((code) => process.exit(code))
77
+ .catch((f) => {
78
+ console.error(f instanceof Error ? f.message : String(f));
79
+ process.exit(1);
80
+ });
81
+ }
@@ -0,0 +1,121 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export const DATEI = 'arkven.json';
4
+ export function lesen(wurzel) {
5
+ try {
6
+ const j = JSON.parse(fs.readFileSync(path.join(wurzel, DATEI), 'utf8'));
7
+ return j.app ? { app: j.app, umgebung: j.umgebung, name: j.name } : null;
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ export function schreiben(wurzel, v) {
14
+ fs.writeFileSync(path.join(wurzel, DATEI), JSON.stringify(v, null, 2) + '\n', 'utf8');
15
+ }
16
+ /**
17
+ * Was NICHT mitgeschickt wird.
18
+ *
19
+ * Auf dem Server entscheidet das git; hier gibt es kein git, auf das man sich verlassen
20
+ * koennte -- ein frisch gezogener Ordner hat keins. Diese Liste ist deshalb bewusst
21
+ * kurz und nennt nur, was ueberall dasselbe bedeutet. Alles andere geht mit: eine
22
+ * Datei, die jemand angelegt hat, gehoert ihm, und wir raten nicht, ob er sie meint.
23
+ */
24
+ const NICHT_MIT = new Set([
25
+ 'node_modules',
26
+ '.git',
27
+ 'dist',
28
+ 'build',
29
+ '.next',
30
+ '.turbo',
31
+ '.cache',
32
+ 'coverage',
33
+ '.arkven',
34
+ ]);
35
+ /**
36
+ * Dateien ohne Endung, die trotzdem Quelltext sind. Sie stehen in EINER Liste mit denen,
37
+ * die den Versteckt-Filter passieren duerfen: zwei Listen fuer dieselbe Frage waren der
38
+ * Fehler, den die Gegenprobe gefunden hat. Die .gitignore kam durch den einen Filter
39
+ * und blieb am anderen haengen, weil sie keine Endung hat.
40
+ */
41
+ const BEKANNT_ALS_TEXT = new Set(['.gitignore', '.env.example', '.npmrc', 'Dockerfile', 'Procfile']);
42
+ /**
43
+ * Was sonst als Text lesbar ist. Bilder, Schriften und Videos gehen diesen Weg nicht:
44
+ * sie wuerden als kaputter Text ankommen, und das waere schlimmer als sie wegzulassen.
45
+ */
46
+ const TEXT = /\.(tsx?|jsx?|mjs|cjs|css|scss|json|md|sql|html?|ya?ml|txt|svg)$/i;
47
+ function istText(name) {
48
+ return BEKANNT_ALS_TEXT.has(name) || TEXT.test(name);
49
+ }
50
+ /** Wieviel eine einzelne Datei hoechstens wiegt. Darueber ist es kein Quelltext mehr. */
51
+ const ZEICHEN_JE_DATEI = 400_000;
52
+ export function sammeln(wurzel) {
53
+ const dateien = [];
54
+ const uebersprungen = [];
55
+ const gehen = (ordner, praefix) => {
56
+ let eintraege;
57
+ try {
58
+ eintraege = fs.readdirSync(ordner, { withFileTypes: true });
59
+ }
60
+ catch {
61
+ return;
62
+ }
63
+ for (const e of eintraege) {
64
+ if (NICHT_MIT.has(e.name))
65
+ continue;
66
+ // Versteckte Dateien bleiben draussen, bis auf die, die zum Projekt gehoeren -
67
+ // dieselbe Liste, die auch "ist das Text" beantwortet. Eine .env steht bewusst
68
+ // NICHT darin: dort liegen Geheimnisse, und die gehen keinen Push mit.
69
+ if (e.name.startsWith('.') && !BEKANNT_ALS_TEXT.has(e.name))
70
+ continue;
71
+ const relativ = praefix ? `${praefix}/${e.name}` : e.name;
72
+ const voll = path.join(ordner, e.name);
73
+ if (e.isDirectory()) {
74
+ gehen(voll, relativ);
75
+ continue;
76
+ }
77
+ if (!e.isFile())
78
+ continue;
79
+ if (relativ === DATEI)
80
+ continue;
81
+ if (!istText(e.name)) {
82
+ uebersprungen.push(relativ);
83
+ continue;
84
+ }
85
+ let inhalt;
86
+ try {
87
+ inhalt = fs.readFileSync(voll, 'utf8');
88
+ }
89
+ catch {
90
+ uebersprungen.push(relativ);
91
+ continue;
92
+ }
93
+ if (inhalt.length > ZEICHEN_JE_DATEI) {
94
+ uebersprungen.push(relativ);
95
+ continue;
96
+ }
97
+ dateien.push({ pfad: relativ, inhalt });
98
+ }
99
+ };
100
+ gehen(wurzel, '');
101
+ return { dateien: dateien.sort((a, b) => a.pfad.localeCompare(b.pfad)), uebersprungen };
102
+ }
103
+ /** Die Grenze zwischen Oberflaeche und Server, dieselbe wie auf dem Server. */
104
+ export function istServerDatei(pfad) {
105
+ return pfad === 'server' || pfad.startsWith('server/');
106
+ }
107
+ export function schreibenAufPlatte(wurzel, dateien) {
108
+ let n = 0;
109
+ for (const d of dateien) {
110
+ // Derselbe Riegel wie auf dem Server, und aus demselben Grund: nachrechnen statt
111
+ // ein Muster gegen ".." legen. Was von aussen kommt, darf nicht ausserhalb des
112
+ // Ordners landen -- auch dann nicht, wenn es vom eigenen Server kommt.
113
+ const ziel = path.resolve(wurzel, d.pfad);
114
+ if (ziel !== wurzel && !ziel.startsWith(wurzel + path.sep))
115
+ continue;
116
+ fs.mkdirSync(path.dirname(ziel), { recursive: true });
117
+ fs.writeFileSync(ziel, d.inhalt, 'utf8');
118
+ n++;
119
+ }
120
+ return n;
121
+ }
package/dist/ruf.js ADDED
@@ -0,0 +1,45 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ export async function ruf(server, weg, opts = {}) {
3
+ const adresse = `${server.replace(/\/+$/, '')}${weg}`;
4
+ try {
5
+ const res = await fetch(adresse, {
6
+ method: opts.methode ?? (opts.koerper ? 'POST' : 'GET'),
7
+ headers: {
8
+ ...(opts.koerper ? { 'content-type': 'application/json' } : {}),
9
+ ...(opts.token ? { authorization: `Bearer ${opts.token}` } : {}),
10
+ },
11
+ body: opts.koerper ? JSON.stringify(opts.koerper) : undefined,
12
+ });
13
+ const daten = (await res.json().catch(() => ({})));
14
+ if (!res.ok)
15
+ return { ok: false, status: res.status, daten };
16
+ return { ok: true, status: res.status, daten: daten };
17
+ }
18
+ catch (e) {
19
+ // Kein Netz ist kein HTTP-Fehler: Status 0 sagt dem Aufrufer, dass die Anfrage nie
20
+ // angekommen ist. Der Unterschied zaehlt - bei einem Push heisst das "es wurde
21
+ // nichts geaendert", bei einem 500 weiss man das nicht.
22
+ return { ok: false, status: 0, daten: {}, netz: String(e?.message ?? e) };
23
+ }
24
+ }
25
+ /**
26
+ * Der Satz, den ein Mensch aus einer Absage liest.
27
+ *
28
+ * Der Server schickt "fehler" und oft "ausweg". Beide gehoeren zusammen: ein Grund
29
+ * ohne Weg hinaus ist eine Sackgasse, und wer in einer Sackgasse steht, probiert
30
+ * dasselbe noch einmal.
31
+ */
32
+ export function absage(a) {
33
+ if (a.ok)
34
+ return '';
35
+ if (a.netz)
36
+ return `Arkven war nicht erreichbar: ${a.netz}`;
37
+ const grund = String(a.daten.fehler ?? a.daten.error ?? `Arkven antwortete mit ${a.status}.`);
38
+ const ausweg = a.daten.ausweg ? `\n${String(a.daten.ausweg)}` : '';
39
+ return grund + ausweg;
40
+ }
41
+ export function neuesGeheimnis() {
42
+ const verifier = randomBytes(32).toString('base64url');
43
+ return { verifier, challenge: createHash('sha256').update(verifier).digest('base64url') };
44
+ }
45
+ export const schlafe = (ms) => new Promise((f) => setTimeout(f, ms));