@untitled-devs/wasla 0.1.3 → 1.0.1

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 (60) hide show
  1. package/README.md +23 -25
  2. package/dist/{utils → apps/cli/src}/cli-output.d.ts +1 -1
  3. package/dist/{cli → apps/cli/src}/commands/config.js +2 -2
  4. package/dist/{cli → apps/cli/src}/commands/install.js +2 -2
  5. package/dist/{cli → apps/cli/src}/commands/register.js +5 -5
  6. package/dist/{cli → apps/cli/src}/commands/status.js +6 -6
  7. package/dist/{cli → apps/cli/src}/commands/sync-to.js +5 -5
  8. package/dist/{cli → apps/cli/src}/commands/sync.js +5 -5
  9. package/dist/{cli → apps/cli/src}/commands/watch.js +6 -6
  10. package/dist/{cli → apps/cli/src}/index.js +19 -7
  11. package/dist/{cli/commands/visualizer.d.ts → apps/cli/src/server/visualizer-server.d.ts} +0 -1
  12. package/dist/{cli/commands/visualizer.js → apps/cli/src/server/visualizer-server.js} +85 -31
  13. package/dist/{adapters → packages/adapters/src}/base.d.ts +1 -1
  14. package/dist/{adapters → packages/adapters/src}/claude.d.ts +1 -1
  15. package/dist/{adapters → packages/adapters/src}/claude.js +7 -4
  16. package/dist/{adapters → packages/adapters/src}/cursor.d.ts +1 -1
  17. package/dist/{adapters → packages/adapters/src}/cursor.js +2 -2
  18. package/dist/{adapters → packages/adapters/src}/factory.d.ts +1 -1
  19. package/dist/{adapters → packages/adapters/src}/gemini.d.ts +1 -1
  20. package/dist/{adapters → packages/adapters/src}/gemini.js +2 -2
  21. package/dist/{adapters → packages/adapters/src}/github-copilot-cli.d.ts +1 -1
  22. package/dist/{adapters → packages/adapters/src}/github-copilot-cli.js +2 -2
  23. package/dist/{adapters → packages/adapters/src}/github-copilot.d.ts +1 -1
  24. package/dist/{adapters → packages/adapters/src}/github-copilot.js +2 -2
  25. package/dist/{adapters → packages/adapters/src}/openclaw.d.ts +1 -1
  26. package/dist/{adapters → packages/adapters/src}/openclaw.js +2 -2
  27. package/dist/{adapters → packages/adapters/src}/opencode.d.ts +1 -1
  28. package/dist/{adapters → packages/adapters/src}/opencode.js +2 -2
  29. package/dist/{core → packages/core/src}/registry.d.ts +1 -1
  30. package/dist/{core → packages/core/src}/registry.js +2 -2
  31. package/dist/{core → packages/core/src}/visualizer-types.d.ts +8 -0
  32. package/dist/{syncer → packages/sync/src}/index.d.ts +12 -3
  33. package/dist/{syncer → packages/sync/src}/index.js +58 -35
  34. package/dist/{core → packages/sync/src}/scanner.d.ts +1 -1
  35. package/dist/{core → packages/sync/src}/scanner.js +3 -3
  36. package/dist/visualizer/assets/index-cU_xphSj.js +144 -0
  37. package/{src/visualizer/dist → dist/visualizer}/index.html +1 -1
  38. package/package.json +77 -63
  39. package/src/visualizer/dist/assets/index-C6aJB2Yl.js +0 -144
  40. /package/dist/{utils → apps/cli/src}/cli-output.js +0 -0
  41. /package/dist/{cli → apps/cli/src}/commands/config.d.ts +0 -0
  42. /package/dist/{cli → apps/cli/src}/commands/install.d.ts +0 -0
  43. /package/dist/{cli → apps/cli/src}/commands/register.d.ts +0 -0
  44. /package/dist/{cli → apps/cli/src}/commands/status.d.ts +0 -0
  45. /package/dist/{cli → apps/cli/src}/commands/sync-to.d.ts +0 -0
  46. /package/dist/{cli → apps/cli/src}/commands/sync.d.ts +0 -0
  47. /package/dist/{cli → apps/cli/src}/commands/watch.d.ts +0 -0
  48. /package/dist/{cli → apps/cli/src}/index.d.ts +0 -0
  49. /package/dist/{adapters → packages/adapters/src}/base.js +0 -0
  50. /package/dist/{adapters → packages/adapters/src}/factory.js +0 -0
  51. /package/dist/{core → packages/core/src}/types.d.ts +0 -0
  52. /package/dist/{core → packages/core/src}/types.js +0 -0
  53. /package/dist/{core → packages/core/src}/visualizer-types.js +0 -0
  54. /package/dist/{utils → packages/shared/src}/config.d.ts +0 -0
  55. /package/dist/{utils → packages/shared/src}/config.js +0 -0
  56. /package/dist/{utils → packages/shared/src}/fs.d.ts +0 -0
  57. /package/dist/{utils → packages/shared/src}/fs.js +0 -0
  58. /package/dist/{utils → packages/shared/src}/paths.d.ts +0 -0
  59. /package/dist/{utils → packages/shared/src}/paths.js +0 -0
  60. /package/{src/visualizer/dist → dist/visualizer}/logo.png +0 -0
@@ -1,7 +1,7 @@
1
1
  import { BaseAdapter } from './base.js';
2
- import { fileExists, writeText, ensureDir } from '../utils/fs.js';
2
+ import { fileExists, writeText, ensureDir } from '#shared/fs.js';
3
3
  import { dirname, join } from 'path';
4
- import { getToolMarkers } from '../utils/paths.js';
4
+ import { getToolMarkers } from '#shared/paths.js';
5
5
  export class OpenCodeAdapter extends BaseAdapter {
6
6
  constructor(scope = 'workspace') {
7
7
  super();
@@ -1,4 +1,4 @@
1
- import { Registry, Asset, Conflict } from '../core/types.js';
1
+ import { Registry, Asset, Conflict } from './types.js';
2
2
  export declare class RegistryManager {
3
3
  private scope;
4
4
  private registry;
@@ -1,5 +1,5 @@
1
- import { readJSON, writeJSON, fileExists, ensureDir } from '../utils/fs.js';
2
- import { getRegistryPath, getRegistryDir } from '../utils/paths.js';
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
+ }
@@ -1,6 +1,6 @@
1
- import { AssetType } from '../core/types.js';
2
- import { RegistryManager } from '../core/registry.js';
3
- import { Scanner } from '../core/scanner.js';
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;
@@ -20,6 +20,15 @@ export declare class Syncer {
20
20
  attachAssetToTool(name: string, type: AssetType, sourceTool: string, targetTool: string): Promise<boolean>;
21
21
  detachAssetFromTool(name: string, type: AssetType, tool: string): Promise<boolean>;
22
22
  private calculateHash;
23
+ /**
24
+ * Filters discovered files, removing those that cannot be read (ENOENT).
25
+ * Treats read-time ENOENT as a deletion signal: if a file disappears during
26
+ * pruning, it is removed from sync and will be treated as a deletion in
27
+ * reconcileDeletedAssets. This prevents sync failures when files are deleted
28
+ * between scan and read phases.
29
+ */
30
+ private removeMissingFiles;
31
+ private isMissingFileError;
23
32
  private getTargetPath;
24
33
  private writeTarget;
25
34
  private writeMcpServer;
@@ -1,8 +1,8 @@
1
- import { RegistryManager } from '../core/registry.js';
2
- import { getAdapter } from '../adapters/factory.js';
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 '../utils/paths.js';
5
- import { readText, writeText, ensureDir, fileExists, readJSON, writeJSON, removePath, } from '../utils/fs.js';
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') {
@@ -27,6 +27,7 @@ export class Syncer {
27
27
  await ensureDir(join(registryDir, 'context'));
28
28
  const discovered = await this.scanner.scanAllTools();
29
29
  const grouped = this.groupByNameAndType(discovered);
30
+ await this.removeMissingFiles(grouped);
30
31
  const installedTools = await this.scanner.detectInstalledTools();
31
32
  let stubsWritten = 0;
32
33
  const stubsDeleted = await this.reconcileDeletedAssets(grouped, installedTools);
@@ -102,20 +103,7 @@ export class Syncer {
102
103
  continue;
103
104
  }
104
105
  // Update stub info in registry
105
- const existingStub = asset.stubs.find((s) => s.tool === tool);
106
- if (existingStub) {
107
- existingStub.path = primaryTargetPath;
108
- existingStub.written_at = new Date().toISOString();
109
- existingStub.hash = contentHash;
110
- }
111
- else {
112
- asset.stubs.push({
113
- tool,
114
- path: primaryTargetPath,
115
- written_at: new Date().toISOString(),
116
- hash: contentHash,
117
- });
118
- }
106
+ await this.upsertStub(asset, tool, primaryTargetPath, contentHash);
119
107
  }
120
108
  // Also save to canonical registry location
121
109
  const registrySubdir = type === 'agent'
@@ -166,11 +154,13 @@ export class Syncer {
166
154
  const assetTypes = ['agent', 'skill', 'mcp', 'context'];
167
155
  const discovered = await this.scanner.scanTool(sourceTool, assetTypes);
168
156
  const grouped = this.groupByNameAndType(discovered);
157
+ await this.removeMissingFiles(grouped);
169
158
  const deletionScan = [...discovered];
170
159
  for (const tool of targetTools.filter((tool) => tool !== sourceTool)) {
171
160
  deletionScan.push(...(await this.scanner.scanTool(tool, assetTypes)));
172
161
  }
173
162
  const deletionGrouped = this.groupByNameAndType(deletionScan);
163
+ await this.removeMissingFiles(deletionGrouped);
174
164
  let stubsWritten = 0;
175
165
  const stubsDeleted = await this.reconcileDeletedAssets(deletionGrouped, [...new Set([sourceTool, ...targetTools])], sourceTool);
176
166
  for (const key of Object.keys(grouped)) {
@@ -209,7 +199,7 @@ export class Syncer {
209
199
  last_synced_at: new Date().toISOString(),
210
200
  });
211
201
  }
212
- this.upsertStub(asset, sourceTool, source.path, contentHash);
202
+ await this.upsertStub(asset, sourceTool, source.path, contentHash);
213
203
  // Write only to target tools
214
204
  for (const tool of targetTools) {
215
205
  const adapter = getAdapter(tool, this.scope);
@@ -242,20 +232,7 @@ export class Syncer {
242
232
  continue;
243
233
  }
244
234
  // Update stub info in registry
245
- const existingStub = asset.stubs.find((s) => s.tool === tool);
246
- if (existingStub) {
247
- existingStub.path = primaryTargetPath;
248
- existingStub.written_at = new Date().toISOString();
249
- existingStub.hash = contentHash;
250
- }
251
- else {
252
- asset.stubs.push({
253
- tool,
254
- path: primaryTargetPath,
255
- written_at: new Date().toISOString(),
256
- hash: contentHash,
257
- });
258
- }
235
+ await this.upsertStub(asset, tool, primaryTargetPath, contentHash);
259
236
  }
260
237
  // Also save to canonical registry location
261
238
  const registrySubdir = type === 'agent'
@@ -311,7 +288,7 @@ export class Syncer {
311
288
  };
312
289
  this.registry.addAsset(asset);
313
290
  }
314
- this.upsertStub(asset, source.tool, source.path, contentHash);
291
+ await this.upsertStub(asset, source.tool, source.path, contentHash);
315
292
  const adapter = getAdapter(targetTool, this.scope);
316
293
  const formatsRecord = adapter.formats;
317
294
  if (!adapter.paths[type] || !formatsRecord[type]) {
@@ -374,6 +351,46 @@ export class Syncer {
374
351
  calculateHash(content) {
375
352
  return createHash('sha256').update(content).digest('hex');
376
353
  }
354
+ /**
355
+ * Filters discovered files, removing those that cannot be read (ENOENT).
356
+ * Treats read-time ENOENT as a deletion signal: if a file disappears during
357
+ * pruning, it is removed from sync and will be treated as a deletion in
358
+ * reconcileDeletedAssets. This prevents sync failures when files are deleted
359
+ * between scan and read phases.
360
+ */
361
+ async removeMissingFiles(grouped) {
362
+ for (const key of Object.keys(grouped)) {
363
+ const readable = [];
364
+ for (const item of grouped[key]) {
365
+ if (item.content !== undefined) {
366
+ readable.push(item);
367
+ continue;
368
+ }
369
+ try {
370
+ item.content = await readText(item.path);
371
+ readable.push(item);
372
+ }
373
+ catch (error) {
374
+ if (!this.isMissingFileError(error)) {
375
+ throw error;
376
+ }
377
+ // ENOENT: file was deleted after scanning, treat as deletion
378
+ }
379
+ }
380
+ if (readable.length > 0) {
381
+ grouped[key] = readable;
382
+ }
383
+ else {
384
+ delete grouped[key];
385
+ }
386
+ }
387
+ }
388
+ isMissingFileError(error) {
389
+ return (typeof error === 'object' &&
390
+ error !== null &&
391
+ 'code' in error &&
392
+ error.code === 'ENOENT');
393
+ }
377
394
  getTargetPath(adapter, type, name, relativePath) {
378
395
  const typeDir = adapter.paths[type];
379
396
  const format = adapter.formats[type];
@@ -436,9 +453,15 @@ export class Syncer {
436
453
  await writeJSON(targetPath, config);
437
454
  return true;
438
455
  }
439
- upsertStub(asset, tool, path, hash) {
456
+ async upsertStub(asset, tool, path, hash) {
440
457
  const existingStub = asset.stubs.find((stub) => stub.tool === tool);
441
458
  if (existingStub) {
459
+ if (asset.type === 'context' &&
460
+ existingStub.path !== path &&
461
+ (await fileExists(existingStub.path)) &&
462
+ this.calculateHash(await readText(existingStub.path)) === existingStub.hash) {
463
+ await removePath(existingStub.path);
464
+ }
442
465
  existingStub.path = path;
443
466
  existingStub.written_at = new Date().toISOString();
444
467
  existingStub.hash = hash;
@@ -1,4 +1,4 @@
1
- import { AssetType, DiscoveredFile, Conflict } from '../core/types.js';
1
+ import { AssetType, DiscoveredFile, Conflict } from '#core/types.js';
2
2
  export declare class Scanner {
3
3
  private scope;
4
4
  private stubPaths;
@@ -1,8 +1,8 @@
1
- import { fileExists, isDirectory, readJSON } from '../utils/fs.js';
1
+ import { fileExists, isDirectory, readJSON } from '#shared/fs.js';
2
2
  import { join, relative } from 'path';
3
- import { getToolMarkers, getRegistryPath } from '../utils/paths.js';
3
+ import { getToolMarkers, getRegistryPath } from '#shared/paths.js';
4
4
  import { stat, readdir } from 'fs/promises';
5
- import { getAdapter } from '../adapters/factory.js';
5
+ import { getAdapter } from '#adapters/factory.js';
6
6
  export class Scanner {
7
7
  constructor(scope = 'workspace') {
8
8
  this.stubPaths = new Set();