onlymcp 0.9.0 → 0.10.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.
- package/dist/apiClient.d.ts +99 -7
- package/dist/apiClient.js +58 -2
- package/dist/apiClient.js.map +1 -1
- package/dist/auth/deviceLogin.js +5 -1
- package/dist/auth/deviceLogin.js.map +1 -1
- package/dist/bin/cli.js +204 -24
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/mcp.js +16 -2
- package/dist/bin/mcp.js.map +1 -1
- package/dist/export/unpack.d.ts +33 -0
- package/dist/export/unpack.js +54 -0
- package/dist/export/unpack.js.map +1 -0
- package/dist/identity.d.ts +25 -0
- package/dist/identity.js +106 -0
- package/dist/identity.js.map +1 -0
- package/dist/import/collect.d.ts +14 -1
- package/dist/import/collect.js +25 -3
- package/dist/import/collect.js.map +1 -1
- package/dist/import/frontmatter.d.ts +31 -5
- package/dist/import/frontmatter.js +64 -22
- package/dist/import/frontmatter.js.map +1 -1
- package/dist/import/plan.d.ts +22 -1
- package/dist/import/plan.js +66 -19
- package/dist/import/plan.js.map +1 -1
- package/dist/import/prune.d.ts +46 -0
- package/dist/import/prune.js +61 -0
- package/dist/import/prune.js.map +1 -0
- package/dist/import/upload.js +10 -1
- package/dist/import/upload.js.map +1 -1
- package/dist/install.d.ts +7 -0
- package/dist/install.js +6 -2
- package/dist/install.js.map +1 -1
- package/dist/instructions.d.ts +3 -1
- package/dist/instructions.js +19 -2
- package/dist/instructions.js.map +1 -1
- package/dist/memorySync.d.ts +28 -0
- package/dist/memorySync.js +56 -0
- package/dist/memorySync.js.map +1 -0
- package/dist/paths.d.ts +12 -0
- package/dist/paths.js +34 -8
- package/dist/paths.js.map +1 -1
- package/dist/stubs.d.ts +1 -0
- package/dist/stubs.js +83 -11
- package/dist/stubs.js.map +1 -1
- package/dist/tools/memoryGraph.d.ts +69 -0
- package/dist/tools/memoryGraph.js +123 -0
- package/dist/tools/memoryGraph.js.map +1 -0
- package/package.json +3 -2
package/dist/apiClient.d.ts
CHANGED
|
@@ -4,11 +4,40 @@ export declare class ApiError extends Error {
|
|
|
4
4
|
readonly status: number;
|
|
5
5
|
constructor(code: string, params?: Record<string, unknown>, status?: number);
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Herkunft und Bearbeitbarkeit, wie jede Liste des Servers sie mitschickt
|
|
9
|
+
* (teams/scope.ts). Der Client braucht sie fuer `--prune`: geloescht wird nur,
|
|
10
|
+
* was diesem Konto allein gehoert.
|
|
11
|
+
*/
|
|
12
|
+
export type Scoped = {
|
|
13
|
+
scope: 'personal' | 'team';
|
|
14
|
+
teamName: string | null;
|
|
15
|
+
isOwn: boolean;
|
|
16
|
+
canEdit: boolean;
|
|
17
|
+
archivedAt: string | null;
|
|
18
|
+
};
|
|
19
|
+
/** Die Sammlung, aus der ein Skill stammt (Paket 03 Aufgabe 11). */
|
|
20
|
+
export type SkillCollection = {
|
|
21
|
+
key: string;
|
|
22
|
+
kind: string;
|
|
23
|
+
label: string;
|
|
24
|
+
};
|
|
25
|
+
export type SkillProvenance = {
|
|
26
|
+
description?: string;
|
|
27
|
+
sourcePath?: string;
|
|
28
|
+
collection?: {
|
|
29
|
+
key: string;
|
|
30
|
+
kind: string;
|
|
31
|
+
label?: string;
|
|
32
|
+
} | null;
|
|
33
|
+
};
|
|
34
|
+
export type SkillListItem = Scoped & {
|
|
8
35
|
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
collection: SkillCollection | null;
|
|
9
38
|
updatedAt: string;
|
|
10
39
|
};
|
|
11
|
-
export type AgentListItem = {
|
|
40
|
+
export type AgentListItem = Scoped & {
|
|
12
41
|
name: string;
|
|
13
42
|
description: string;
|
|
14
43
|
model: string | null;
|
|
@@ -29,17 +58,36 @@ export type MemoryFile = {
|
|
|
29
58
|
path: string;
|
|
30
59
|
content: string;
|
|
31
60
|
frontmatter: Record<string, unknown>;
|
|
32
|
-
links:
|
|
61
|
+
links: {
|
|
62
|
+
resolved: string[];
|
|
63
|
+
unresolved: string[];
|
|
64
|
+
};
|
|
33
65
|
updatedAt: string;
|
|
34
66
|
updatedByDevice: string | null;
|
|
35
67
|
};
|
|
36
|
-
export type
|
|
68
|
+
export type SearchHit = {
|
|
69
|
+
kind: 'memory' | 'skill' | 'agent';
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
path: string | null;
|
|
73
|
+
snippet: string;
|
|
74
|
+
scope: 'personal' | 'team';
|
|
75
|
+
teamId: string | null;
|
|
76
|
+
};
|
|
77
|
+
export type TreeEntry = {
|
|
78
|
+
kind: 'folder' | 'file';
|
|
37
79
|
path: string;
|
|
38
|
-
|
|
80
|
+
name: string;
|
|
81
|
+
type: string | null;
|
|
82
|
+
updatedAt: string | null;
|
|
39
83
|
scope: 'personal' | 'team';
|
|
40
84
|
teamName: string | null;
|
|
41
85
|
canEdit: boolean;
|
|
42
86
|
};
|
|
87
|
+
export type MemoryListItem = Scoped & {
|
|
88
|
+
path: string;
|
|
89
|
+
updatedAt: string;
|
|
90
|
+
};
|
|
43
91
|
export type Connection = {
|
|
44
92
|
id: string;
|
|
45
93
|
name: string;
|
|
@@ -97,6 +145,8 @@ export declare class ApiClient {
|
|
|
97
145
|
plan: {
|
|
98
146
|
name: string;
|
|
99
147
|
};
|
|
148
|
+
/** Optional, weil eine aeltere API sie nicht schickt. Fehlt sie, gilt an. */
|
|
149
|
+
brainvaultMode?: boolean;
|
|
100
150
|
}>;
|
|
101
151
|
listSkills(): Promise<{
|
|
102
152
|
skills: SkillListItem[];
|
|
@@ -104,10 +154,19 @@ export declare class ApiClient {
|
|
|
104
154
|
getSkill(name: string): Promise<{
|
|
105
155
|
name: string;
|
|
106
156
|
body: string;
|
|
157
|
+
description: string;
|
|
158
|
+
collection: SkillCollection | null;
|
|
159
|
+
sourcePath: string | null;
|
|
107
160
|
updatedAt: string;
|
|
108
161
|
files: SkillFileMeta[];
|
|
109
162
|
}>;
|
|
110
|
-
|
|
163
|
+
/**
|
|
164
|
+
* Die Herkunft ist optional und wird WEGGELASSEN, nicht auf leer gesetzt,
|
|
165
|
+
* wenn es keine gibt: ein fehlendes Feld heisst serverseitig "nicht
|
|
166
|
+
* anfassen" (Spec 5, Regel 1). Ein `description: ''` mitzuschicken loeschte
|
|
167
|
+
* die Beschreibung, die ein frueherer Import geschrieben hat.
|
|
168
|
+
*/
|
|
169
|
+
putSkill(name: string, body: string, provenance?: SkillProvenance): Promise<{
|
|
111
170
|
name: string;
|
|
112
171
|
updatedAt: string;
|
|
113
172
|
}>;
|
|
@@ -130,7 +189,13 @@ export declare class ApiClient {
|
|
|
130
189
|
name: string;
|
|
131
190
|
updatedAt: string;
|
|
132
191
|
}>;
|
|
133
|
-
getAgent(name: string): Promise<
|
|
192
|
+
getAgent(name: string): Promise<Scoped & {
|
|
193
|
+
name: string;
|
|
194
|
+
description: string;
|
|
195
|
+
model: string | null;
|
|
196
|
+
tools: string[];
|
|
197
|
+
updatedAt: string;
|
|
198
|
+
} & {
|
|
134
199
|
body: string;
|
|
135
200
|
}>;
|
|
136
201
|
readMemory(path: string): Promise<MemoryFile>;
|
|
@@ -139,6 +204,33 @@ export declare class ApiClient {
|
|
|
139
204
|
listMemory(): Promise<{
|
|
140
205
|
files: MemoryListItem[];
|
|
141
206
|
}>;
|
|
207
|
+
searchContent(q: string, opts?: {
|
|
208
|
+
types?: string[];
|
|
209
|
+
limit?: number;
|
|
210
|
+
}): Promise<{
|
|
211
|
+
hits: SearchHit[];
|
|
212
|
+
}>;
|
|
213
|
+
memoryTree(scope?: string): Promise<{
|
|
214
|
+
nodes: TreeEntry[];
|
|
215
|
+
}>;
|
|
216
|
+
moveMemory(from: string, to: string, updateLinks?: boolean): Promise<{
|
|
217
|
+
from: string;
|
|
218
|
+
to: string;
|
|
219
|
+
rewritten: number;
|
|
220
|
+
skipped: number;
|
|
221
|
+
}>;
|
|
222
|
+
/**
|
|
223
|
+
* Das Export-Archiv, als ROHE Antwort.
|
|
224
|
+
*
|
|
225
|
+
* Geht bewusst nicht durch `call`: das dort ist ein JSON-Client, und ein
|
|
226
|
+
* Archiv von zwei Gigabyte durch `res.json()` zu schicken waere genau der
|
|
227
|
+
* Fehler, den die Route auf ihrer Seite vermeidet. Der Aufrufer bekommt den
|
|
228
|
+
* Strom und entscheidet, ob er ihn in eine Datei giesst oder auspackt.
|
|
229
|
+
*/
|
|
230
|
+
exportArchive(opts?: {
|
|
231
|
+
scope?: string;
|
|
232
|
+
types?: string[];
|
|
233
|
+
}): Promise<Response>;
|
|
142
234
|
deleteMemory(path: string): Promise<void>;
|
|
143
235
|
deleteSkill(name: string): Promise<void>;
|
|
144
236
|
deleteSkillFile(name: string, path: string): Promise<void>;
|
package/dist/apiClient.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { identityHeaders } from './identity.js';
|
|
1
2
|
export class ApiError extends Error {
|
|
2
3
|
code;
|
|
3
4
|
params;
|
|
@@ -21,9 +22,12 @@ export class ApiClient {
|
|
|
21
22
|
const tok = this.token();
|
|
22
23
|
if (!tok)
|
|
23
24
|
throw new ApiError('errors.NOT_LOGGED_IN');
|
|
25
|
+
// Wer anruft, steht in den Koepfen - sonst schickt Node 'node' und das
|
|
26
|
+
// Geraet heisst in der Liste genau so. Siehe identity.ts.
|
|
24
27
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
25
28
|
...init,
|
|
26
29
|
headers: {
|
|
30
|
+
...(await identityHeaders()),
|
|
27
31
|
...(init.headers ?? {}),
|
|
28
32
|
authorization: `Bearer ${tok}`,
|
|
29
33
|
'content-type': 'application/json',
|
|
@@ -46,8 +50,14 @@ export class ApiClient {
|
|
|
46
50
|
getSkill(name) {
|
|
47
51
|
return this.call(`/api/skills/${encodeURIComponent(name)}`);
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Die Herkunft ist optional und wird WEGGELASSEN, nicht auf leer gesetzt,
|
|
55
|
+
* wenn es keine gibt: ein fehlendes Feld heisst serverseitig "nicht
|
|
56
|
+
* anfassen" (Spec 5, Regel 1). Ein `description: ''` mitzuschicken loeschte
|
|
57
|
+
* die Beschreibung, die ein frueherer Import geschrieben hat.
|
|
58
|
+
*/
|
|
59
|
+
putSkill(name, body, provenance = {}) {
|
|
60
|
+
return this.call(`/api/skills/${encodeURIComponent(name)}`, { method: 'PUT', body: JSON.stringify({ body, ...provenance }) });
|
|
51
61
|
}
|
|
52
62
|
// Der Pfad wird Segment fuer Segment kodiert, genau wie bei readMemory:
|
|
53
63
|
// encodeURIComponent ueber die ganze Zeichenkette machte aus jedem
|
|
@@ -89,6 +99,52 @@ export class ApiClient {
|
|
|
89
99
|
listMemory() {
|
|
90
100
|
return this.call('/api/memory');
|
|
91
101
|
}
|
|
102
|
+
// --- Suchen, Baum und Verschieben (Paket 03 Aufgabe 14) -----------------
|
|
103
|
+
searchContent(q, opts = {}) {
|
|
104
|
+
const query = new URLSearchParams({ q });
|
|
105
|
+
if (opts.types?.length)
|
|
106
|
+
query.set('types', opts.types.join(','));
|
|
107
|
+
if (opts.limit)
|
|
108
|
+
query.set('limit', String(opts.limit));
|
|
109
|
+
return this.call(`/api/search?${query.toString()}`);
|
|
110
|
+
}
|
|
111
|
+
memoryTree(scope = 'all') {
|
|
112
|
+
return this.call(`/api/memory/tree?scope=${encodeURIComponent(scope)}`);
|
|
113
|
+
}
|
|
114
|
+
moveMemory(from, to, updateLinks = true) {
|
|
115
|
+
return this.call('/api/memory/move', {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
body: JSON.stringify({ from, to, updateLinks }),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// --- Der Weg nach draussen ---------------------------------------------
|
|
121
|
+
/**
|
|
122
|
+
* Das Export-Archiv, als ROHE Antwort.
|
|
123
|
+
*
|
|
124
|
+
* Geht bewusst nicht durch `call`: das dort ist ein JSON-Client, und ein
|
|
125
|
+
* Archiv von zwei Gigabyte durch `res.json()` zu schicken waere genau der
|
|
126
|
+
* Fehler, den die Route auf ihrer Seite vermeidet. Der Aufrufer bekommt den
|
|
127
|
+
* Strom und entscheidet, ob er ihn in eine Datei giesst oder auspackt.
|
|
128
|
+
*/
|
|
129
|
+
async exportArchive(opts = {}) {
|
|
130
|
+
const tok = this.token();
|
|
131
|
+
if (!tok)
|
|
132
|
+
throw new ApiError('errors.NOT_LOGGED_IN');
|
|
133
|
+
const query = new URLSearchParams();
|
|
134
|
+
if (opts.scope)
|
|
135
|
+
query.set('scope', opts.scope);
|
|
136
|
+
if (opts.types?.length)
|
|
137
|
+
query.set('types', opts.types.join(','));
|
|
138
|
+
const tail = query.size > 0 ? `?${query.toString()}` : '';
|
|
139
|
+
const res = await fetch(`${this.baseUrl}/api/export${tail}`, {
|
|
140
|
+
headers: { ...(await identityHeaders()), authorization: `Bearer ${tok}` },
|
|
141
|
+
});
|
|
142
|
+
if (!res.ok) {
|
|
143
|
+
const body = (await res.json().catch(() => ({})));
|
|
144
|
+
throw new ApiError(body.code ?? 'errors.UNKNOWN', body.params ?? {}, res.status);
|
|
145
|
+
}
|
|
146
|
+
return res;
|
|
147
|
+
}
|
|
92
148
|
deleteMemory(path) {
|
|
93
149
|
return this.call(this.memoryUrl(path), { method: 'DELETE' });
|
|
94
150
|
}
|
package/dist/apiClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEtB;IACA;IACA;IAHX,YACW,IAAY,EACZ,SAAkC,EAAE,EACpC,SAAS,CAAC;QAEnB,KAAK,CAAC,IAAI,CAAC,CAAC;QAJH,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAA8B;QACpC,WAAM,GAAN,MAAM,CAAI;QAGnB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/deviceLogin.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiError } from '../apiClient.js';
|
|
2
|
+
import { identityHeaders } from '../identity.js';
|
|
2
3
|
const defaultDeps = {
|
|
3
4
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
4
5
|
};
|
|
@@ -6,9 +7,12 @@ export async function pollForToken(baseUrl, deviceCode, intervalSeconds, deps =
|
|
|
6
7
|
let interval = intervalSeconds;
|
|
7
8
|
for (;;) {
|
|
8
9
|
await deps.sleep(interval * 1000);
|
|
10
|
+
// Diese Anfrage ist die, die die Sitzung erzeugt - hier entscheidet sich,
|
|
11
|
+
// wie das Geraet in der Liste heissen wird. Ohne die Koepfe schickt Node
|
|
12
|
+
// 'node', und genau das stand vorher dort.
|
|
9
13
|
const res = await fetch(`${baseUrl}/api/auth/device/token`, {
|
|
10
14
|
method: 'POST',
|
|
11
|
-
headers: { 'content-type': 'application/json' },
|
|
15
|
+
headers: { ...(await identityHeaders()), 'content-type': 'application/json' },
|
|
12
16
|
body: JSON.stringify({
|
|
13
17
|
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
14
18
|
device_code: deviceCode,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceLogin.js","sourceRoot":"","sources":["../../src/auth/deviceLogin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"deviceLogin.js","sourceRoot":"","sources":["../../src/auth/deviceLogin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIjD,MAAM,WAAW,GAAS;IACxB,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,UAAkB,EAClB,eAAuB,EACvB,OAAa,WAAW;IAExB,IAAI,QAAQ,GAAG,eAAe,CAAC;IAC/B,SAAS,CAAC;QACR,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;QAClC,0EAA0E;QAC1E,yEAAyE;QACzE,2CAA2C;QAC3C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,wBAAwB,EAAE;YAC1D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,GAAG,CAAC,MAAM,eAAe,EAAE,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC7E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,UAAU,EAAE,8CAA8C;gBAC1D,WAAW,EAAE,UAAU;gBACvB,SAAS,EAAE,aAAa;aACzB,CAAC;SACH,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAsB,CAAC;QAC1D,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,uBAAuB;gBAC1B,MAAM;YACR,KAAK,WAAW;gBACd,QAAQ,IAAI,CAAC,CAAC;gBACd,MAAM;YACR,KAAK,eAAe;gBAClB,MAAM,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAAC;YACpD,KAAK,eAAe;gBAClB,MAAM,IAAI,QAAQ,CAAC,4BAA4B,CAAC,CAAC;YACnD;gBACE,MAAM,IAAI,QAAQ,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/bin/cli.js
CHANGED
|
@@ -5,7 +5,9 @@ import open from 'open';
|
|
|
5
5
|
import QRCode from 'qrcode';
|
|
6
6
|
import { createRequire } from 'node:module';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { appendFileSync, createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { pipeline } from 'node:stream/promises';
|
|
10
|
+
import { Readable } from 'node:stream';
|
|
9
11
|
import { dirname, join } from 'node:path';
|
|
10
12
|
import { ApiClient } from '../apiClient.js';
|
|
11
13
|
import { Keyring } from '../auth/keyring.js';
|
|
@@ -17,6 +19,10 @@ import { writeStubs } from '../stubs.js';
|
|
|
17
19
|
import { collect } from '../import/collect.js';
|
|
18
20
|
import { buildImportPlan } from '../import/plan.js';
|
|
19
21
|
import { countPlan, runUpload } from '../import/upload.js';
|
|
22
|
+
import { countTargets, isEmpty, planPrune, runPrune } from '../import/prune.js';
|
|
23
|
+
import { syncMemoryToDisk } from '../memorySync.js';
|
|
24
|
+
import { unpackArchive } from '../export/unpack.js';
|
|
25
|
+
import { deviceLabel } from '../identity.js';
|
|
20
26
|
const baseUrl = process.env.ONLYMCP_API_URL ?? 'https://only-mcp.com';
|
|
21
27
|
// Die Version kommt aus der package.json und steht nicht ein zweites Mal hier
|
|
22
28
|
// als Literal: sonst meldet `onlymcp --version` nach der ersten
|
|
@@ -65,6 +71,11 @@ program
|
|
|
65
71
|
.description('Dieses Geraet mit OnlyMCP verbinden')
|
|
66
72
|
.action(async () => {
|
|
67
73
|
intro('OnlyMCP');
|
|
74
|
+
// Einmal ehrlich sagen, was mitgeht, BEVOR es mitgeht. Die beiden Werte
|
|
75
|
+
// stehen nachher in der Geraeteliste dieses Kontos, sonst nirgends - aber
|
|
76
|
+
// wer sie nicht schicken will, soll das erfahren, solange er noch abbrechen
|
|
77
|
+
// kann.
|
|
78
|
+
log.info(`Dieses Geraet meldet sich als: ${await deviceLabel()}`);
|
|
68
79
|
const res = await fetch(`${baseUrl}/api/auth/device/code`, {
|
|
69
80
|
method: 'POST',
|
|
70
81
|
headers: { 'content-type': 'application/json' },
|
|
@@ -86,11 +97,28 @@ program
|
|
|
86
97
|
Keyring.save(token);
|
|
87
98
|
s.stop('Verbunden');
|
|
88
99
|
const api = new ApiClient(baseUrl, () => Keyring.read());
|
|
89
|
-
|
|
90
|
-
for (const dir of stubDirs())
|
|
91
|
-
counts = await writeStubs(api, dir);
|
|
100
|
+
const counts = await writeStubsEverywhere(api);
|
|
92
101
|
outro(`${counts.skills} Skills, ${counts.agents} Agents, ${counts.files} Dateien bereit`);
|
|
93
102
|
});
|
|
103
|
+
/**
|
|
104
|
+
* Stubs in jeden Ordner, den ein Client auf diesem Geraet liest.
|
|
105
|
+
*
|
|
106
|
+
* Gezaehlt wird ueber alle Ordner zusammen, nicht der letzte: bis Aufgabe 12
|
|
107
|
+
* ueberschrieb die Schleife ihr Ergebnis bei jedem Durchlauf und meldete
|
|
108
|
+
* deshalb die Zahlen des zuletzt beschriebenen Ordners als Gesamtzahl.
|
|
109
|
+
*/
|
|
110
|
+
async function writeStubsEverywhere(api) {
|
|
111
|
+
const total = { skills: 0, agents: 0, files: 0, kept: [] };
|
|
112
|
+
for (const dir of stubDirs()) {
|
|
113
|
+
const c = await writeStubs(api, dir);
|
|
114
|
+
total.skills += c.skills;
|
|
115
|
+
total.agents += c.agents;
|
|
116
|
+
total.files += c.files;
|
|
117
|
+
for (const k of c.kept)
|
|
118
|
+
total.kept.push(`${dir}/${k}`);
|
|
119
|
+
}
|
|
120
|
+
return total;
|
|
121
|
+
}
|
|
94
122
|
program
|
|
95
123
|
.command('logout')
|
|
96
124
|
.description('Anmeldung und Cache auf diesem Geraet loeschen')
|
|
@@ -108,12 +136,37 @@ program
|
|
|
108
136
|
program
|
|
109
137
|
.command('sync')
|
|
110
138
|
.description('Stub-Dateien neu schreiben')
|
|
111
|
-
.
|
|
139
|
+
.option('--memory <dir>', 'Memory zusaetzlich als Dateien in diesen Ordner schreiben')
|
|
140
|
+
.action(async (opts) => {
|
|
112
141
|
const api = new ApiClient(baseUrl, () => Keyring.read());
|
|
113
|
-
|
|
114
|
-
for (const dir of stubDirs())
|
|
115
|
-
counts = await writeStubs(api, dir);
|
|
142
|
+
const counts = await writeStubsEverywhere(api);
|
|
116
143
|
log.success(`${counts.skills} Skills, ${counts.agents} Agents, ${counts.files} Dateien`);
|
|
144
|
+
if (counts.kept.length > 0) {
|
|
145
|
+
// Stubs liegen in denselben Ordnern, aus denen importiert wird. Was
|
|
146
|
+
// dort schon stand und kein Stub war, bleibt stehen - aber nicht
|
|
147
|
+
// stillschweigend, sonst sucht jemand einen Stub, der nie geschrieben
|
|
148
|
+
// wurde. Gekuerzt, weil ein Geraet mit 270 eigenen Agent-Dateien sonst
|
|
149
|
+
// 270 Zeilen bekaeme und die Erfolgsmeldung aus dem Bild schoebe.
|
|
150
|
+
log.warn([
|
|
151
|
+
`Unberuehrt gelassen, weil dort schon etwas Eigenes liegt (${counts.kept.length})`,
|
|
152
|
+
...counts.kept.slice(0, 10).map((k) => ` ${k}`),
|
|
153
|
+
...(counts.kept.length > 10 ? [` ... und ${counts.kept.length - 10} weitere`] : []),
|
|
154
|
+
].join('\n'));
|
|
155
|
+
}
|
|
156
|
+
if (!opts.memory)
|
|
157
|
+
return;
|
|
158
|
+
const m = await syncMemoryToDisk(api, opts.memory);
|
|
159
|
+
log.success(`${m.written} Memory-Dateien nach ${opts.memory}`);
|
|
160
|
+
if (m.collisions.length > 0) {
|
|
161
|
+
log.warn(['Team-Dateien mit einem Pfad, den eine eigene Datei schon hat', ...m.collisions.map((p) => ` ${p}`)].join('\n'));
|
|
162
|
+
}
|
|
163
|
+
if (m.refused.length > 0) {
|
|
164
|
+
log.warn(['Pfade abgelehnt', ...m.refused.map((p) => ` ${p}`)].join('\n'));
|
|
165
|
+
}
|
|
166
|
+
if (m.failures.length > 0) {
|
|
167
|
+
log.error([`${m.failures.length} fehlgeschlagen`, ...m.failures.map((f) => ` ${f.what} ${f.code}`)].join('\n'));
|
|
168
|
+
process.exitCode = 1;
|
|
169
|
+
}
|
|
117
170
|
});
|
|
118
171
|
program
|
|
119
172
|
.command('install')
|
|
@@ -207,6 +260,12 @@ function printPlan(plan, roots) {
|
|
|
207
260
|
byReason.set(s.reason, (byReason.get(s.reason) ?? 0) + 1);
|
|
208
261
|
log.message(['Uebersprungen', ...[...byReason].map(([reason, n]) => ` ${String(n).padStart(4)} ${reason}`)].join('\n'));
|
|
209
262
|
}
|
|
263
|
+
if (plan.noDescription.length > 0) {
|
|
264
|
+
// Gehen trotzdem hoch. Gemeldet, weil die Liste im Dashboard und die
|
|
265
|
+
// Stubs auf jedem Geraet von der Beschreibung leben und ein Skill ohne
|
|
266
|
+
// sie dort als blosser Name steht.
|
|
267
|
+
log.warn(['Ohne description im Kopf der SKILL.md', ...plan.noDescription.map((n) => ` ${n}`)].join('\n'));
|
|
268
|
+
}
|
|
210
269
|
if (plan.flagged.length > 0) {
|
|
211
270
|
log.warn([
|
|
212
271
|
'Zurueckgehalten wegen moeglicher Zugangsdaten (nichts davon geht raus)',
|
|
@@ -220,7 +279,7 @@ function printPlan(plan, roots) {
|
|
|
220
279
|
program
|
|
221
280
|
.command('import')
|
|
222
281
|
.description('Bestehende lokale Skills, Agents und Memory nach OnlyMCP holen')
|
|
223
|
-
.option('--skills <dir>', 'Ordner mit Skill-Unterordnern')
|
|
282
|
+
.option('--skills <dir>', 'Ordner mit Skill-Unterordnern oder ein Plugin-Cache')
|
|
224
283
|
.option('--agents <dir>', 'Ordner mit Agent-Dateien')
|
|
225
284
|
// --memory hat mit Absicht keine Vorgabe. Skills und Agents liegen an einem
|
|
226
285
|
// bekannten Pfad, an dem nur Skills und Agents liegen. Ein Memory-Ordner
|
|
@@ -229,6 +288,10 @@ program
|
|
|
229
288
|
.option('--memory <dir>', 'Ordner mit Memory-Dateien (keine Vorgabe)')
|
|
230
289
|
.option('--dry-run', 'Nur zeigen, was hochginge, und beenden')
|
|
231
290
|
.option('--yes', 'Ohne Rueckfrage hochladen')
|
|
291
|
+
// --prune loescht im Konto. Es hat deshalb bewusst KEINE Abkuerzung ueber
|
|
292
|
+
// --yes: die Liste wird immer gezeigt und immer einzeln bestaetigt, auch
|
|
293
|
+
// wenn der Upload selbst unbeaufsichtigt laeuft.
|
|
294
|
+
.option('--prune', 'Was im Konto steht und in der Quelle fehlt, nach Rueckfrage loeschen')
|
|
232
295
|
.action(async (opts) => {
|
|
233
296
|
// Dieselbe Testschleuse wie in install.ts und paths.ts.
|
|
234
297
|
const home = process.env.ONLYMCP_HOME ?? homedir();
|
|
@@ -247,7 +310,7 @@ program
|
|
|
247
310
|
existsSync(roots.agents) ? collect(roots.agents) : Promise.resolve([]),
|
|
248
311
|
roots.memory && existsSync(roots.memory) ? collect(roots.memory) : Promise.resolve([]),
|
|
249
312
|
]);
|
|
250
|
-
plan = buildImportPlan({ skills, agents, memory });
|
|
313
|
+
plan = buildImportPlan({ skills, agents, memory }, { skillsRoot: roots.skills });
|
|
251
314
|
}
|
|
252
315
|
catch (e) {
|
|
253
316
|
s.stop('Abgebrochen');
|
|
@@ -262,11 +325,13 @@ program
|
|
|
262
325
|
memory: roots.memory && existsSync(roots.memory) ? roots.memory : undefined,
|
|
263
326
|
});
|
|
264
327
|
const total = countPlan(plan);
|
|
265
|
-
|
|
328
|
+
// Mit --prune gibt es auch dann etwas zu tun, wenn nichts hochgeht: genau
|
|
329
|
+
// ein leerer Quellordner ist der Fall, in dem am meisten geloescht wuerde.
|
|
330
|
+
if (total === 0 && !opts.prune) {
|
|
266
331
|
outro('Nichts zu tun.');
|
|
267
332
|
return;
|
|
268
333
|
}
|
|
269
|
-
if (opts.dryRun) {
|
|
334
|
+
if (opts.dryRun && !opts.prune) {
|
|
270
335
|
outro('Nichts hochgeladen (--dry-run).');
|
|
271
336
|
return;
|
|
272
337
|
}
|
|
@@ -283,7 +348,7 @@ program
|
|
|
283
348
|
process.exitCode = 1;
|
|
284
349
|
return;
|
|
285
350
|
}
|
|
286
|
-
if (!opts.yes) {
|
|
351
|
+
if (total > 0 && !opts.yes && !opts.dryRun) {
|
|
287
352
|
if (!process.stdin.isTTY) {
|
|
288
353
|
// Ohne Terminal gibt es keine Rueckfrage, also auch keinen Upload.
|
|
289
354
|
// Wer das automatisieren will, sagt --yes und hat den Plan vorher mit
|
|
@@ -323,23 +388,138 @@ program
|
|
|
323
388
|
skills: clear(skills),
|
|
324
389
|
agents: clear(agents),
|
|
325
390
|
memory: clear(memory),
|
|
391
|
+
}, { skillsRoot: roots.skills });
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (total > 0 && !opts.dryRun) {
|
|
396
|
+
const up = spinner();
|
|
397
|
+
up.start('Lade hoch');
|
|
398
|
+
const result = await runUpload(api, plan, (done, all, label) => {
|
|
399
|
+
up.message(`${done}/${all} ${label}`);
|
|
400
|
+
});
|
|
401
|
+
up.stop('Fertig');
|
|
402
|
+
log.success(`${result.skills} Skills, ${result.skillFiles} Skill-Dateien, ` +
|
|
403
|
+
`${result.agents} Agents, ${result.memory} Memory-Dateien`);
|
|
404
|
+
if (result.failures.length > 0) {
|
|
405
|
+
log.error([`${result.failures.length} fehlgeschlagen`, ...result.failures.map((f) => ` ${f.what} ${f.code}`)].join('\n'));
|
|
406
|
+
process.exitCode = 1;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (opts.prune) {
|
|
410
|
+
// Verglichen wird nur, was auch gelesen wurde. Ohne --memory sagt ein
|
|
411
|
+
// Import gar nichts ueber Memory aus, und "steht im Konto, fehlt in der
|
|
412
|
+
// Quelle" waere dort keine Aussage, sondern ein Unfall.
|
|
413
|
+
const covered = {
|
|
414
|
+
skills: existsSync(roots.skills),
|
|
415
|
+
agents: existsSync(roots.agents),
|
|
416
|
+
memory: Boolean(roots.memory && existsSync(roots.memory)),
|
|
417
|
+
};
|
|
418
|
+
const targets = await planPrune(api, plan, covered);
|
|
419
|
+
if (isEmpty(targets)) {
|
|
420
|
+
log.info('Nichts zu loeschen: das Konto haelt nichts, was die Quelle nicht kennt.');
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
// Vollstaendig aufgezaehlt, nicht gezaehlt. Eine Zahl zu bestaetigen
|
|
424
|
+
// heisst, etwas zu bestaetigen, das man nicht gesehen hat.
|
|
425
|
+
log.warn([
|
|
426
|
+
'Wuerde geloescht (nur Eigenes, nichts aus einem Team, nichts Archiviertes)',
|
|
427
|
+
...targets.skills.map((n) => ` Skill ${n}`),
|
|
428
|
+
...targets.agents.map((n) => ` Agent ${n}`),
|
|
429
|
+
...targets.memory.map((p) => ` Memory ${p}`),
|
|
430
|
+
].join('\n'));
|
|
431
|
+
if (opts.dryRun) {
|
|
432
|
+
log.info('Nichts geloescht (--dry-run).');
|
|
433
|
+
}
|
|
434
|
+
else if (!process.stdin.isTTY) {
|
|
435
|
+
// Kein --yes-Durchgriff: geloescht wird nur von Hand.
|
|
436
|
+
log.error('Kein Terminal fuer die Rueckfrage. Nichts geloescht.');
|
|
437
|
+
process.exitCode = 1;
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
const go = await confirm({
|
|
441
|
+
message: `${countTargets(targets)} Eintraege im Konto loeschen?`,
|
|
442
|
+
initialValue: false,
|
|
326
443
|
});
|
|
444
|
+
if (isCancel(go) || !go) {
|
|
445
|
+
log.info('Nichts geloescht.');
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
const pruned = await runPrune(api, targets);
|
|
449
|
+
log.success(`${pruned.deleted} geloescht`);
|
|
450
|
+
if (pruned.failures.length > 0) {
|
|
451
|
+
log.error([
|
|
452
|
+
`${pruned.failures.length} fehlgeschlagen`,
|
|
453
|
+
...pruned.failures.map((f) => ` ${f.what} ${f.code}`),
|
|
454
|
+
].join('\n'));
|
|
455
|
+
process.exitCode = 1;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
327
458
|
}
|
|
328
459
|
}
|
|
329
460
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
461
|
+
outro(opts.dryRun ? 'Nichts geaendert (--dry-run).' : 'Danach: onlymcp sync auf jedem Geraet.');
|
|
462
|
+
});
|
|
463
|
+
program
|
|
464
|
+
.command('export')
|
|
465
|
+
.description('Alles aus dem Konto holen: Memory, Skills, Agents, Verbindungen')
|
|
466
|
+
.option('--out <dir>', 'Archiv in diesen Ordner entpacken')
|
|
467
|
+
.option('--zip <file>', 'Archiv als Datei behalten')
|
|
468
|
+
.option('--scope <scope>', 'personal, all oder team:<id>', 'all')
|
|
469
|
+
.option('--types <list>', 'memory,skills,agents,connections')
|
|
470
|
+
.action(async (opts) => {
|
|
471
|
+
// Genau eines von beiden. Beides hiesse zweimal herunterladen, keines
|
|
472
|
+
// hiesse, die teuerste Leseoperation des Produkts ins Nichts zu schicken.
|
|
473
|
+
if ((opts.out ? 1 : 0) + (opts.zip ? 1 : 0) !== 1) {
|
|
474
|
+
log.error('Entweder --out <ordner> oder --zip <datei>, nicht beides und nicht keines.');
|
|
475
|
+
process.exitCode = 1;
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
intro('OnlyMCP Export');
|
|
479
|
+
const api = new ApiClient(baseUrl, () => Keyring.read());
|
|
480
|
+
const s = spinner();
|
|
481
|
+
s.start('Hole das Archiv');
|
|
482
|
+
let res;
|
|
483
|
+
try {
|
|
484
|
+
res = await api.exportArchive({
|
|
485
|
+
scope: opts.scope,
|
|
486
|
+
types: opts.types?.split(',').map((t) => t.trim()).filter((t) => t !== ''),
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
catch (e) {
|
|
490
|
+
s.stop('Abgebrochen');
|
|
491
|
+
log.error(e instanceof Error ? e.message : String(e));
|
|
492
|
+
log.info('Nicht angemeldet? Zuerst: onlymcp login');
|
|
493
|
+
process.exitCode = 1;
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (opts.zip) {
|
|
497
|
+
// In die Datei geschrieben, waehrend es ankommt. Ein Konto mit zwei
|
|
498
|
+
// Gigabyte soll nicht erst zwei Gigabyte Arbeitsspeicher kosten.
|
|
499
|
+
mkdirSync(dirname(opts.zip), { recursive: true });
|
|
500
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(opts.zip));
|
|
501
|
+
s.stop('Fertig');
|
|
502
|
+
outro(`Archiv: ${opts.zip}`);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
506
|
+
const result = await unpackArchive(bytes, opts.out);
|
|
507
|
+
s.stop('Fertig');
|
|
508
|
+
log.success(`${result.written} Dateien in ${opts.out}`);
|
|
509
|
+
if (result.refused.length > 0) {
|
|
510
|
+
log.warn(['Nicht geschrieben, unzulaessiger Pfad im Archiv', ...result.refused.map((p) => ` ${p}`)].join('\n'));
|
|
511
|
+
}
|
|
512
|
+
if (result.failed.length > 0) {
|
|
513
|
+
// Auf Windows der Normalfall fuer einen Skill mit Namensraum: der
|
|
514
|
+
// Doppelpunkt ist dort kein zulaessiges Zeichen in einem Ordnernamen.
|
|
515
|
+
log.warn([
|
|
516
|
+
'Nicht geschrieben, der Dateiname geht auf diesem System nicht',
|
|
517
|
+
...result.failed.map((f) => ` ${f.path} ${f.reason}`),
|
|
518
|
+
'Mit --zip <datei> bleibt das Archiv unveraendert erhalten.',
|
|
519
|
+
].join('\n'));
|
|
340
520
|
process.exitCode = 1;
|
|
341
521
|
}
|
|
342
|
-
outro(
|
|
522
|
+
outro(`Zurueckspielen: onlymcp import --skills ${opts.out}/skills --agents ${opts.out}/agents --memory ${opts.out}/memory`);
|
|
343
523
|
});
|
|
344
524
|
await program.parseAsync();
|
|
345
525
|
//# sourceMappingURL=cli.js.map
|