@untitled-devs/wasla 0.1.2 → 1.0.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/README.md +34 -30
- package/dist/{utils → apps/cli/src}/cli-output.d.ts +4 -0
- package/dist/apps/cli/src/cli-output.js +98 -0
- package/dist/{cli → apps/cli/src}/commands/config.d.ts +1 -1
- package/dist/apps/cli/src/commands/config.js +62 -0
- package/dist/{cli → apps/cli/src}/commands/install.js +11 -6
- package/dist/{cli → apps/cli/src}/commands/register.js +8 -6
- package/dist/apps/cli/src/commands/status.d.ts +1 -0
- package/dist/apps/cli/src/commands/status.js +39 -0
- package/dist/{cli → apps/cli/src}/commands/sync-to.d.ts +0 -1
- package/dist/{cli → apps/cli/src}/commands/sync-to.js +7 -6
- package/dist/apps/cli/src/commands/sync.d.ts +5 -0
- package/dist/apps/cli/src/commands/sync.js +49 -0
- package/dist/apps/cli/src/commands/watch.d.ts +1 -0
- package/dist/{cli → apps/cli/src}/commands/watch.js +8 -7
- package/dist/{cli → apps/cli/src}/index.js +26 -16
- package/dist/{cli/commands/visualizer.d.ts → apps/cli/src/server/visualizer-server.d.ts} +0 -2
- package/dist/{cli/commands/visualizer.js → apps/cli/src/server/visualizer-server.js} +86 -31
- package/dist/{adapters → packages/adapters/src}/base.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/claude.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/claude.js +2 -2
- package/dist/{adapters → packages/adapters/src}/cursor.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/cursor.js +2 -2
- package/dist/{adapters → packages/adapters/src}/factory.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/gemini.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/gemini.js +2 -2
- package/dist/{adapters → packages/adapters/src}/github-copilot-cli.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/github-copilot-cli.js +2 -2
- package/dist/{adapters → packages/adapters/src}/github-copilot.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/github-copilot.js +2 -2
- package/dist/{adapters → packages/adapters/src}/openclaw.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/openclaw.js +2 -2
- package/dist/{adapters → packages/adapters/src}/opencode.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/opencode.js +2 -2
- package/dist/{core → packages/core/src}/registry.d.ts +1 -1
- package/dist/{core → packages/core/src}/registry.js +2 -2
- package/dist/{core → packages/core/src}/types.d.ts +0 -1
- package/dist/{core → packages/core/src}/visualizer-types.d.ts +8 -0
- package/dist/packages/shared/src/config.d.ts +6 -0
- package/dist/packages/shared/src/config.js +34 -0
- package/dist/{utils → packages/shared/src}/fs.js +13 -7
- package/dist/{syncer → packages/sync/src}/index.d.ts +3 -3
- package/dist/{syncer → packages/sync/src}/index.js +4 -4
- package/dist/{core → packages/sync/src}/scanner.d.ts +1 -1
- package/dist/{core → packages/sync/src}/scanner.js +11 -6
- package/dist/visualizer/assets/index-cU_xphSj.js +144 -0
- package/{src/visualizer/dist → dist/visualizer}/index.html +1 -1
- package/package.json +76 -62
- package/dist/cli/commands/config.js +0 -65
- package/dist/cli/commands/status.d.ts +0 -5
- package/dist/cli/commands/status.js +0 -36
- package/dist/cli/commands/sync.d.ts +0 -5
- package/dist/cli/commands/sync.js +0 -26
- package/dist/cli/commands/watch.d.ts +0 -5
- package/dist/utils/cli-output.js +0 -44
- package/src/visualizer/dist/assets/index-C6aJB2Yl.js +0 -144
- /package/dist/{cli → apps/cli/src}/commands/install.d.ts +0 -0
- /package/dist/{cli → apps/cli/src}/commands/register.d.ts +0 -0
- /package/dist/{cli → apps/cli/src}/index.d.ts +0 -0
- /package/dist/{adapters → packages/adapters/src}/base.js +0 -0
- /package/dist/{adapters → packages/adapters/src}/factory.js +0 -0
- /package/dist/{core → packages/core/src}/types.js +0 -0
- /package/dist/{core → packages/core/src}/visualizer-types.js +0 -0
- /package/dist/{utils → packages/shared/src}/fs.d.ts +0 -0
- /package/dist/{utils → packages/shared/src}/paths.d.ts +0 -0
- /package/dist/{utils → packages/shared/src}/paths.js +0 -0
- /package/{src/visualizer/dist → dist/visualizer}/logo.png +0 -0
|
@@ -5,19 +5,25 @@ import { readFile } from 'fs/promises';
|
|
|
5
5
|
import { existsSync } from 'fs';
|
|
6
6
|
import { exec } from 'child_process';
|
|
7
7
|
import { WebSocketServer } from 'ws';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { RegistryManager } from '#core/registry.js';
|
|
9
|
+
import { getAllAdapters, getInstalledAdapters } from '#adapters/factory.js';
|
|
10
|
+
import { error, highlight, info, section, spacer } from '../cli-output.js';
|
|
11
|
+
import { Syncer } from '#sync/index.js';
|
|
12
|
+
import { Scanner } from '#sync/scanner.js';
|
|
13
|
+
import { requireConfiguredScope } from '#shared/config.js';
|
|
13
14
|
export function resolveVisualizerDist(moduleUrl) {
|
|
14
|
-
return resolve(dirname(fileURLToPath(moduleUrl)), '
|
|
15
|
+
return resolve(dirname(fileURLToPath(moduleUrl)), '../../../../visualizer');
|
|
15
16
|
}
|
|
16
|
-
async function getEntityContent(scope, type, name) {
|
|
17
|
+
async function getEntityContent(scope, type, name, providerId) {
|
|
17
18
|
const scanner = new Scanner(scope);
|
|
18
19
|
await scanner.initialize();
|
|
19
|
-
const
|
|
20
|
-
const
|
|
20
|
+
const assetType = mapEntityType(type);
|
|
21
|
+
const discovered = providerId === 'waslagenie'
|
|
22
|
+
? await scanner.scanAllTools([assetType])
|
|
23
|
+
: await scanner.scanTool(providerId, [assetType]);
|
|
24
|
+
const target = discovered
|
|
25
|
+
.filter((item) => mapType(item.type) === type && item.name === name)
|
|
26
|
+
.sort((a, b) => b.modifiedAt - a.modifiedAt)[0];
|
|
21
27
|
if (!target)
|
|
22
28
|
return null;
|
|
23
29
|
if (target.content)
|
|
@@ -29,11 +35,23 @@ async function getEntityContent(scope, type, name) {
|
|
|
29
35
|
return null;
|
|
30
36
|
}
|
|
31
37
|
}
|
|
38
|
+
function isVisualizerEntityType(type) {
|
|
39
|
+
return type === 'instruction' || type === 'agent' || type === 'skill' || type === 'mcp';
|
|
40
|
+
}
|
|
32
41
|
function mapType(type) {
|
|
33
42
|
if (type === 'context')
|
|
34
43
|
return 'instruction';
|
|
35
44
|
return type;
|
|
36
45
|
}
|
|
46
|
+
function isAllowedOrigin(origin, port) {
|
|
47
|
+
if (!origin)
|
|
48
|
+
return true;
|
|
49
|
+
return origin === `http://127.0.0.1:${port}` || origin === `http://localhost:${port}`;
|
|
50
|
+
}
|
|
51
|
+
function isKnownProvider(scope, providerId) {
|
|
52
|
+
return (providerId === 'waslagenie' ||
|
|
53
|
+
getAllAdapters(scope).some((adapter) => adapter.name === providerId));
|
|
54
|
+
}
|
|
37
55
|
function mapEntityType(type) {
|
|
38
56
|
if (type === 'instruction')
|
|
39
57
|
return 'context';
|
|
@@ -84,12 +102,20 @@ async function buildConfig(scope) {
|
|
|
84
102
|
await scanner.initialize();
|
|
85
103
|
const discovered = await scanner.scanAllTools();
|
|
86
104
|
const installed = await getInstalledAdapters(scope);
|
|
105
|
+
const installedNames = new Set(installed.map((adapter) => adapter.name));
|
|
87
106
|
const providers = [
|
|
88
|
-
{
|
|
89
|
-
|
|
107
|
+
{
|
|
108
|
+
id: 'waslagenie',
|
|
109
|
+
title: 'WaslaGenie',
|
|
110
|
+
iconUrl: PROVIDER_ICONS.waslagenie,
|
|
111
|
+
isHub: true,
|
|
112
|
+
isInstalled: true,
|
|
113
|
+
},
|
|
114
|
+
...getAllAdapters(scope).map((adapter) => ({
|
|
90
115
|
id: adapter.name,
|
|
91
116
|
title: adapter.displayName,
|
|
92
117
|
iconUrl: PROVIDER_ICONS[adapter.name],
|
|
118
|
+
isInstalled: installedNames.has(adapter.name),
|
|
93
119
|
})),
|
|
94
120
|
];
|
|
95
121
|
const entityMap = new Map();
|
|
@@ -128,7 +154,6 @@ async function buildConfig(scope) {
|
|
|
128
154
|
providers,
|
|
129
155
|
entities: grouped,
|
|
130
156
|
connections: Array.from(connectionMap.values()),
|
|
131
|
-
websocketUrl: '',
|
|
132
157
|
};
|
|
133
158
|
}
|
|
134
159
|
function sendJson(res, statusCode, body) {
|
|
@@ -138,8 +163,8 @@ function sendJson(res, statusCode, body) {
|
|
|
138
163
|
}
|
|
139
164
|
export async function visualizerCommand(options) {
|
|
140
165
|
try {
|
|
141
|
-
const scope = (
|
|
142
|
-
const host =
|
|
166
|
+
const scope = await requireConfiguredScope();
|
|
167
|
+
const host = '127.0.0.1';
|
|
143
168
|
const port = Number(options.port || 4072);
|
|
144
169
|
const shouldOpen = options.noOpen !== true;
|
|
145
170
|
section('Starting visualizer...');
|
|
@@ -160,36 +185,43 @@ export async function visualizerCommand(options) {
|
|
|
160
185
|
};
|
|
161
186
|
const server = createServer(async (req, res) => {
|
|
162
187
|
const url = req.url || '/';
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
sendJson(res,
|
|
188
|
+
const parsed = new URL(`http://${host}:${port}${url}`);
|
|
189
|
+
const pathname = parsed.pathname;
|
|
190
|
+
if (!isAllowedOrigin(req.headers.origin, port)) {
|
|
191
|
+
sendJson(res, 403, { error: 'Forbidden origin' });
|
|
167
192
|
return;
|
|
168
193
|
}
|
|
169
|
-
if (req.method === 'GET' &&
|
|
170
|
-
|
|
194
|
+
if (req.method === 'GET' && pathname === '/api/config') {
|
|
195
|
+
sendJson(res, 200, await buildConfig(scope));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (req.method === 'GET' && pathname === '/api/entity-content') {
|
|
171
199
|
const type = parsed.searchParams.get('type');
|
|
172
200
|
const name = parsed.searchParams.get('name');
|
|
173
|
-
|
|
174
|
-
|
|
201
|
+
const providerId = parsed.searchParams.get('provider');
|
|
202
|
+
if (!isVisualizerEntityType(type) ||
|
|
203
|
+
!name ||
|
|
204
|
+
!providerId ||
|
|
205
|
+
!isKnownProvider(scope, providerId)) {
|
|
206
|
+
sendJson(res, 400, { error: 'valid type, name, and provider are required' });
|
|
175
207
|
return;
|
|
176
208
|
}
|
|
177
|
-
const content = await getEntityContent(scope, type, name);
|
|
209
|
+
const content = await getEntityContent(scope, type, name, providerId);
|
|
178
210
|
sendJson(res, 200, { content: content ?? '' });
|
|
179
211
|
return;
|
|
180
212
|
}
|
|
181
|
-
if (req.method === 'POST' &&
|
|
213
|
+
if (req.method === 'POST' && pathname === '/api/shutdown') {
|
|
182
214
|
sendJson(res, 200, { ok: true });
|
|
183
215
|
setTimeout(shutdown, 50);
|
|
184
216
|
return;
|
|
185
217
|
}
|
|
186
|
-
if (
|
|
218
|
+
if (pathname === '/' || pathname === '/index.html') {
|
|
187
219
|
const html = await readFile(join(visualizerDist, 'index.html'));
|
|
188
220
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
189
221
|
res.end(html);
|
|
190
222
|
return;
|
|
191
223
|
}
|
|
192
|
-
const staticPath = join(visualizerDist,
|
|
224
|
+
const staticPath = join(visualizerDist, pathname.replace(/^\//, ''));
|
|
193
225
|
if (!staticPath.startsWith(visualizerDist) || !existsSync(staticPath)) {
|
|
194
226
|
res.writeHead(404);
|
|
195
227
|
res.end('Not Found');
|
|
@@ -199,15 +231,24 @@ export async function visualizerCommand(options) {
|
|
|
199
231
|
res.writeHead(200, { 'Content-Type': mimeType(staticPath) });
|
|
200
232
|
res.end(payload);
|
|
201
233
|
});
|
|
202
|
-
const wss = new WebSocketServer({
|
|
234
|
+
const wss = new WebSocketServer({
|
|
235
|
+
server,
|
|
236
|
+
verifyClient: ({ origin }) => isAllowedOrigin(origin, port),
|
|
237
|
+
});
|
|
203
238
|
wss.on('connection', (socket) => {
|
|
204
239
|
socket.on('message', async (raw) => {
|
|
240
|
+
let requestId = '';
|
|
205
241
|
try {
|
|
206
242
|
const data = JSON.parse(raw.toString());
|
|
207
|
-
if (data.type !== 'CONNECTION_CHANGED' || !data.payload)
|
|
243
|
+
if (data.type !== 'CONNECTION_CHANGED' || !data.requestId || !data.payload)
|
|
208
244
|
return;
|
|
245
|
+
requestId = data.requestId;
|
|
209
246
|
const { sourceEntity, entityType, sourceProvider, targetProvider, action } = data.payload;
|
|
210
|
-
if (!sourceEntity ||
|
|
247
|
+
if (!sourceEntity ||
|
|
248
|
+
!isVisualizerEntityType(entityType) ||
|
|
249
|
+
!sourceProvider ||
|
|
250
|
+
!targetProvider ||
|
|
251
|
+
(action !== 'ATTACH' && action !== 'DETACH')) {
|
|
211
252
|
return;
|
|
212
253
|
}
|
|
213
254
|
info(`[Visualizer] ${action} ${entityType}:${sourceEntity} ${action === 'ATTACH' ? 'to' : 'from'} ${targetProvider}`);
|
|
@@ -217,9 +258,23 @@ export async function visualizerCommand(options) {
|
|
|
217
258
|
else {
|
|
218
259
|
await syncer.detachAssetFromTool(sourceEntity, mapEntityType(entityType), targetProvider);
|
|
219
260
|
}
|
|
261
|
+
const result = {
|
|
262
|
+
type: 'CONNECTION_CHANGED_RESULT',
|
|
263
|
+
requestId,
|
|
264
|
+
ok: true,
|
|
265
|
+
};
|
|
266
|
+
socket.send(JSON.stringify(result));
|
|
220
267
|
}
|
|
221
|
-
catch {
|
|
222
|
-
|
|
268
|
+
catch (err) {
|
|
269
|
+
if (!requestId)
|
|
270
|
+
return;
|
|
271
|
+
const result = {
|
|
272
|
+
type: 'CONNECTION_CHANGED_RESULT',
|
|
273
|
+
requestId,
|
|
274
|
+
ok: false,
|
|
275
|
+
error: err instanceof Error ? err.message : String(err),
|
|
276
|
+
};
|
|
277
|
+
socket.send(JSON.stringify(result));
|
|
223
278
|
}
|
|
224
279
|
});
|
|
225
280
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WaslaGenieAdapter, Asset, AssetFormat } from '
|
|
1
|
+
import { WaslaGenieAdapter, Asset, AssetFormat } from '#core/types.js';
|
|
2
2
|
export declare abstract class BaseAdapter implements WaslaGenieAdapter {
|
|
3
3
|
abstract name: string;
|
|
4
4
|
abstract displayName: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class ClaudeAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class CursorAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WaslaGenieAdapter } from '
|
|
1
|
+
import { WaslaGenieAdapter } from '#core/types.js';
|
|
2
2
|
export declare function getAdapter(toolName: string, scope?: 'user' | 'workspace'): WaslaGenieAdapter;
|
|
3
3
|
export declare function getInstalledAdapters(scope?: 'user' | 'workspace'): Promise<WaslaGenieAdapter[]>;
|
|
4
4
|
export declare function getAllAdapters(scope?: 'user' | 'workspace'): WaslaGenieAdapter[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class GeminiAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class GithubCopilotCliAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class GithubCopilotAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class OpenclawAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { fileExists, writeText, ensureDir } from '
|
|
2
|
+
import { fileExists, writeText, ensureDir } from '#shared/fs.js';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
-
import { getToolMarkers } from '
|
|
4
|
+
import { getToolMarkers } from '#shared/paths.js';
|
|
5
5
|
export class OpenCodeAdapter extends BaseAdapter {
|
|
6
6
|
constructor(scope = 'workspace') {
|
|
7
7
|
super();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readJSON, writeJSON, fileExists, ensureDir } from '
|
|
2
|
-
import { getRegistryPath, getRegistryDir } from '
|
|
1
|
+
import { readJSON, writeJSON, fileExists, ensureDir } from '#shared/fs.js';
|
|
2
|
+
import { getRegistryPath, getRegistryDir } from '#shared/paths.js';
|
|
3
3
|
import { randomUUID } from 'crypto';
|
|
4
4
|
export class RegistryManager {
|
|
5
5
|
constructor(scope = 'workspace') {
|
|
@@ -10,6 +10,7 @@ export interface VisualizerProvider {
|
|
|
10
10
|
title: string;
|
|
11
11
|
iconUrl?: string;
|
|
12
12
|
isHub?: boolean;
|
|
13
|
+
isInstalled?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export interface VisualizerConnection {
|
|
15
16
|
entityId: string;
|
|
@@ -28,6 +29,7 @@ export interface VisualizerConfiguration {
|
|
|
28
29
|
}
|
|
29
30
|
export interface ConnectionChangedMessage {
|
|
30
31
|
type: 'CONNECTION_CHANGED';
|
|
32
|
+
requestId: string;
|
|
31
33
|
payload: {
|
|
32
34
|
sourceEntity: string;
|
|
33
35
|
entityType: VisualizerEntityType;
|
|
@@ -36,3 +38,9 @@ export interface ConnectionChangedMessage {
|
|
|
36
38
|
action: 'ATTACH' | 'DETACH';
|
|
37
39
|
};
|
|
38
40
|
}
|
|
41
|
+
export interface ConnectionChangedResultMessage {
|
|
42
|
+
type: 'CONNECTION_CHANGED_RESULT';
|
|
43
|
+
requestId: string;
|
|
44
|
+
ok: boolean;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type WaslaScope = 'user' | 'workspace';
|
|
2
|
+
export declare function getConfigPath(): string;
|
|
3
|
+
export declare function readConfiguredScope(): Promise<WaslaScope | null>;
|
|
4
|
+
export declare function requireConfiguredScope(): Promise<WaslaScope>;
|
|
5
|
+
export declare function writeConfiguredScope(scope: WaslaScope): Promise<void>;
|
|
6
|
+
export declare function getConfiguredRegistryPath(scope: WaslaScope): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { dirname, join } from 'path';
|
|
2
|
+
import { ensureDir, fileExists, readJSON, writeJSON } from './fs.js';
|
|
3
|
+
import { getRegistryDir, getRegistryPath } from './paths.js';
|
|
4
|
+
export function getConfigPath() {
|
|
5
|
+
return join(getRegistryDir('user'), 'config.json');
|
|
6
|
+
}
|
|
7
|
+
export async function readConfiguredScope() {
|
|
8
|
+
const configPath = getConfigPath();
|
|
9
|
+
if (!(await fileExists(configPath)))
|
|
10
|
+
return null;
|
|
11
|
+
const config = await readJSON(configPath);
|
|
12
|
+
if (typeof config !== 'object' || config === null || typeof config.scope !== 'string') {
|
|
13
|
+
throw new Error(`Invalid scope in ${configPath}. Run: waslagenie config --scope <scope>`);
|
|
14
|
+
}
|
|
15
|
+
if (config.scope !== 'user' && config.scope !== 'workspace') {
|
|
16
|
+
throw new Error(`Invalid scope in ${configPath}. Run: waslagenie config --scope <scope>`);
|
|
17
|
+
}
|
|
18
|
+
return config.scope;
|
|
19
|
+
}
|
|
20
|
+
export async function requireConfiguredScope() {
|
|
21
|
+
const scope = await readConfiguredScope();
|
|
22
|
+
if (!scope) {
|
|
23
|
+
throw new Error('Scope is not configured. Run: waslagenie config --scope <user|workspace>');
|
|
24
|
+
}
|
|
25
|
+
return scope;
|
|
26
|
+
}
|
|
27
|
+
export async function writeConfiguredScope(scope) {
|
|
28
|
+
const configPath = getConfigPath();
|
|
29
|
+
await ensureDir(dirname(configPath));
|
|
30
|
+
await writeJSON(configPath, { scope });
|
|
31
|
+
}
|
|
32
|
+
export function getConfiguredRegistryPath(scope) {
|
|
33
|
+
return getRegistryPath(scope);
|
|
34
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { readdir, readFile, writeFile, stat, mkdir, rm } from 'fs/promises';
|
|
2
|
-
import { basename, extname } from 'path';
|
|
3
2
|
export async function fileExists(path) {
|
|
4
3
|
try {
|
|
5
4
|
await stat(path);
|
|
@@ -60,14 +59,21 @@ export async function listDirs(dir) {
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
export function getFileName(path) {
|
|
63
|
-
return
|
|
62
|
+
return path.split(/[/\\]/).filter(Boolean).pop() || '';
|
|
64
63
|
}
|
|
65
64
|
export function getFileNameWithoutExt(path) {
|
|
66
|
-
const fileName =
|
|
67
|
-
const
|
|
68
|
-
|
|
65
|
+
const fileName = getFileName(path);
|
|
66
|
+
const dotIndex = fileName.lastIndexOf('.');
|
|
67
|
+
if (dotIndex <= 0) {
|
|
68
|
+
return fileName;
|
|
69
|
+
}
|
|
70
|
+
return fileName.substring(0, dotIndex);
|
|
69
71
|
}
|
|
70
72
|
export function getFileExtension(path) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
+
const fileName = getFileName(path);
|
|
74
|
+
const dotIndex = fileName.lastIndexOf('.');
|
|
75
|
+
if (dotIndex <= 0) {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
return fileName.substring(dotIndex + 1);
|
|
73
79
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AssetType } from '
|
|
2
|
-
import { RegistryManager } from '
|
|
3
|
-
import { Scanner } from '
|
|
1
|
+
import { AssetType } from '#core/types.js';
|
|
2
|
+
import { RegistryManager } from '#core/registry.js';
|
|
3
|
+
import { Scanner } from './scanner.js';
|
|
4
4
|
export declare class Syncer {
|
|
5
5
|
private registry;
|
|
6
6
|
private scanner;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RegistryManager } from '
|
|
2
|
-
import { getAdapter } from '
|
|
1
|
+
import { RegistryManager } from '#core/registry.js';
|
|
2
|
+
import { getAdapter } from '#adapters/factory.js';
|
|
3
3
|
import { basename, dirname, join, sep } from 'path';
|
|
4
|
-
import { getRegistryDir } from '
|
|
5
|
-
import { readText, writeText, ensureDir, fileExists, readJSON, writeJSON, removePath, } from '
|
|
4
|
+
import { getRegistryDir } from '#shared/paths.js';
|
|
5
|
+
import { readText, writeText, ensureDir, fileExists, readJSON, writeJSON, removePath, } from '#shared/fs.js';
|
|
6
6
|
import { createHash } from 'crypto';
|
|
7
7
|
export class Syncer {
|
|
8
8
|
constructor(registry, scanner, scope = 'workspace') {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { fileExists, isDirectory, readJSON } from '
|
|
2
|
-
import { join, relative
|
|
3
|
-
import { getToolMarkers, getRegistryPath } from '
|
|
1
|
+
import { fileExists, isDirectory, readJSON } from '#shared/fs.js';
|
|
2
|
+
import { join, relative } from 'path';
|
|
3
|
+
import { getToolMarkers, getRegistryPath } from '#shared/paths.js';
|
|
4
4
|
import { stat, readdir } from 'fs/promises';
|
|
5
|
-
import { getAdapter } from '
|
|
5
|
+
import { getAdapter } from '#adapters/factory.js';
|
|
6
6
|
export class Scanner {
|
|
7
7
|
constructor(scope = 'workspace') {
|
|
8
8
|
this.stubPaths = new Set();
|
|
@@ -203,13 +203,18 @@ export class Scanner {
|
|
|
203
203
|
extractAssetName(relativePathOrFileName) {
|
|
204
204
|
// For nested paths: waslagenie/SKILL.md -> waslagenie
|
|
205
205
|
// For flat files: researcher.md -> researcher
|
|
206
|
-
const parts = relativePathOrFileName.split(
|
|
206
|
+
const parts = relativePathOrFileName.split(/[/\\]/);
|
|
207
207
|
if (parts.length > 1) {
|
|
208
208
|
// Nested: return first directory
|
|
209
209
|
return parts[0];
|
|
210
210
|
}
|
|
211
211
|
// Flat: remove extension
|
|
212
|
-
|
|
212
|
+
const fileName = parts[0];
|
|
213
|
+
const dotIndex = fileName.lastIndexOf('.');
|
|
214
|
+
if (dotIndex <= 0) {
|
|
215
|
+
return fileName;
|
|
216
|
+
}
|
|
217
|
+
return fileName.substring(0, dotIndex);
|
|
213
218
|
}
|
|
214
219
|
readNestedRecord(config, keyPath) {
|
|
215
220
|
let value = config;
|