@resourcexjs/registry 1.6.0 → 2.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 CHANGED
@@ -10,36 +10,47 @@ bun add @resourcexjs/registry
10
10
 
11
11
  ## Overview
12
12
 
13
- The `@resourcexjs/registry` package provides a Maven-style registry for storing and resolving resources locally.
13
+ The `@resourcexjs/registry` package provides a Maven-style registry for storing and resolving resources (local or remote).
14
14
 
15
15
  ### Key Concepts
16
16
 
17
17
  - **Registry**: Interface for resource storage and retrieval
18
- - **ARPRegistry**: Implementation using ARP (Agent Resource Protocol) for I/O
19
- - **Local-first**: Resources cached locally at `~/.resourcex`
20
- - **Maven-style**: Organized by `domain/path/name.type@version`
18
+ - **LocalRegistry**: Filesystem-based implementation for local storage
19
+ - **RemoteRegistry**: HTTP API-based implementation for remote access
20
+ - **Well-known discovery**: Auto-discover registry endpoint via `/.well-known/resourcex`
21
+ - **Maven-style**: Organized by `domain/path/name.type/version`
21
22
 
22
23
  ## Usage
23
24
 
24
25
  ### Create Registry
25
26
 
26
27
  ```typescript
27
- import { createRegistry } from "@resourcexjs/registry";
28
+ import { createRegistry, discoverRegistry } from "@resourcexjs/registry";
28
29
 
29
- // Default configuration (~/.resourcex)
30
+ // Local registry (default)
30
31
  const registry = createRegistry();
31
32
 
32
- // Custom path
33
- const registry = createRegistry({
33
+ // Local registry with custom path
34
+ const registry2 = createRegistry({
34
35
  path: "./my-registry",
35
36
  });
36
37
 
37
- // With extension types
38
+ // Local registry with extension types
38
39
  import { promptType } from "@my-org/types";
39
40
 
40
- const registry = createRegistry({
41
+ const registry3 = createRegistry({
42
+ path: "~/.resourcex",
41
43
  types: [promptType],
42
44
  });
45
+
46
+ // Remote registry
47
+ const registry4 = createRegistry({
48
+ endpoint: "https://registry.deepractice.ai/v1",
49
+ });
50
+
51
+ // Remote registry with well-known discovery
52
+ const endpoint = await discoverRegistry("deepractice.ai");
53
+ const registry5 = createRegistry({ endpoint });
43
54
  ```
44
55
 
45
56
  ### Link Resource
@@ -201,30 +212,59 @@ const all = await registry.search();
201
212
 
202
213
  ## Storage Structure
203
214
 
204
- Resources are stored in Maven-style structure:
215
+ Resources are stored in two separate areas:
216
+
217
+ - **local/** - Development resources (organized by name.type/version)
218
+ - **cache/** - Remote cached resources (organized by domain/path/name.type/version)
205
219
 
206
220
  ```
207
221
  ~/.resourcex/
208
- └── {domain}/
209
- └── {path}/
210
- └── {name}.{type}@{version}/
211
- ├── manifest.json # RXM metadata
212
- └── content # Serialized content
222
+ ├── local/ # Development area
223
+ └── {name}.{type}/
224
+ └── {version}/
225
+ ├── manifest.json
226
+ └── content.tar.gz
227
+
228
+ └── cache/ # Remote cache area
229
+ └── {domain}/
230
+ └── {path}/
231
+ └── {name}.{type}/
232
+ └── {version}/
233
+ ├── manifest.json
234
+ └── content.tar.gz
213
235
  ```
214
236
 
215
237
  ### Example
216
238
 
217
- For resource `deepractice.ai/prompts/assistant.prompt@1.0.0`:
239
+ For a local development resource `my-prompt.text@1.0.0`:
240
+
241
+ ```
242
+ ~/.resourcex/
243
+ └── local/
244
+ └── my-prompt.text/
245
+ └── 1.0.0/
246
+ ├── manifest.json # domain can be "deepractice.ai" or "localhost"
247
+ └── content.tar.gz
248
+ ```
249
+
250
+ For a cached remote resource `deepractice.ai/prompts/assistant.text@1.0.0`:
218
251
 
219
252
  ```
220
253
  ~/.resourcex/
221
- └── deepractice.ai/
222
- └── prompts/
223
- └── assistant.prompt@1.0.0/
224
- ├── manifest.json
225
- └── content
254
+ └── cache/
255
+ └── deepractice.ai/
256
+ └── prompts/
257
+ └── assistant.text/
258
+ └── 1.0.0/
259
+ ├── manifest.json
260
+ └── content.tar.gz
226
261
  ```
227
262
 
263
+ ### Resolution Order
264
+
265
+ 1. **local/** is checked first (development resources)
266
+ 2. **cache/** is checked second (remote cached resources)
267
+
228
268
  **manifest.json:**
229
269
 
230
270
  ```json
@@ -237,7 +277,7 @@ For resource `deepractice.ai/prompts/assistant.prompt@1.0.0`:
237
277
  }
238
278
  ```
239
279
 
240
- **content:** (serialized by type's serializer)
280
+ **content.tar.gz:** Archive containing resource files (managed by TypeHandlerChain)
241
281
 
242
282
  ## Extension Types
243
283
 
@@ -373,31 +413,47 @@ await registry.link(agentResource);
373
413
 
374
414
  ## Resolution Strategy
375
415
 
376
- 1. **Check local registry** (`~/.resourcex` or custom path)
377
- 2. **If not found**: (TODO) Fetch from remote registry based on domain
378
- 3. **Cache locally** after fetching
379
- 4. **Return** resource
416
+ ### LocalRegistry
417
+
418
+ 1. **Check local filesystem** (`~/.resourcex` or custom path)
419
+ 2. **If not found**: Throw RegistryError
420
+ 3. **Return** RXR
421
+
422
+ ### RemoteRegistry
423
+
424
+ 1. **Fetch from HTTP API** (`GET /resource` + `GET /content`)
425
+ 2. **Return** RXR (no local caching)
426
+
427
+ ### Future: Hybrid Strategy (TODO)
380
428
 
381
- Currently only local resolution is implemented. Remote fetching is planned.
429
+ 1. Check local cache first
430
+ 2. If not found, fetch from remote based on domain
431
+ 3. Cache locally
432
+ 4. Return RXR
382
433
 
383
434
  ## Architecture
384
435
 
385
436
  ```
386
- ┌─────────────────────────────────────┐
387
- Registry Interface
388
- └──────────────┬──────────────────────┘
389
-
390
- ┌──────▼──────┐
391
- ARPRegistry
392
- │(implements) │
393
- └──────┬──────┘
394
-
395
- ┌─────────┴─────────┐
396
-
397
- ┌────▼────┐ ┌──────▼──────┐
398
- ARP TypeHandler
399
- (I/O) Chain │
400
- └─────────┘ └─────────────┘
437
+ ┌─────────────────────────────────────────┐
438
+ Registry Interface
439
+ └────────────┬────────────────────────────┘
440
+
441
+ ┌────────┴────────┐
442
+
443
+ ┌───▼──────────┐ ┌──▼──────────────┐
444
+ │LocalRegistry │ │RemoteRegistry │
445
+ (filesystem) │ │(HTTP API) │
446
+ └───┬──────────┘ └──┬──────────────┘
447
+
448
+ ┌───▼───────┐ ┌──▼──────┐
449
+ Node.js fetch
450
+ fs module │
451
+ └───────────┘ └─────────┘
452
+ │ │
453
+ ┌────▼────────────────▼─────┐
454
+ │ TypeHandlerChain │
455
+ │ (serialization logic) │
456
+ └───────────────────────────┘
401
457
  ```
402
458
 
403
459
  ## Type Safety
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { RXR, RXL } from "@resourcexjs/core";
2
2
  import { ResourceType, ResolvedResource } from "@resourcexjs/type";
3
3
  /**
4
- * Registry configuration options.
4
+ * Local registry configuration.
5
+ * Uses filesystem for storage.
5
6
  */
6
- interface RegistryConfig {
7
+ interface LocalRegistryConfig {
7
8
  /**
8
9
  * Local storage path. Defaults to ~/.resourcex
9
10
  */
@@ -15,6 +16,70 @@ interface RegistryConfig {
15
16
  types?: ResourceType[];
16
17
  }
17
18
  /**
19
+ * Remote registry configuration.
20
+ * Uses HTTP API for access.
21
+ */
22
+ interface RemoteRegistryConfig {
23
+ /**
24
+ * Remote registry API endpoint.
25
+ * Example: "https://registry.deepractice.ai/v1"
26
+ */
27
+ endpoint: string;
28
+ }
29
+ /**
30
+ * Git registry configuration.
31
+ * Uses git clone to fetch registry content.
32
+ */
33
+ interface GitRegistryConfig {
34
+ type: "git";
35
+ /** Git repository URL (SSH format: git@github.com:owner/repo.git) */
36
+ url: string;
37
+ /** Git ref (branch, tag, or commit). Default: "main" */
38
+ ref?: string;
39
+ /** Base path in repo for resources. Default: ".resourcex" */
40
+ basePath?: string;
41
+ /**
42
+ * Trusted domain for this registry.
43
+ * If set, only resources with this domain in manifest are allowed.
44
+ * Required for security - prevents untrusted registries from claiming any domain.
45
+ */
46
+ domain?: string;
47
+ }
48
+ /**
49
+ * Well-known discovery response format.
50
+ * Used by discoverRegistry() to find registry for a domain.
51
+ */
52
+ interface WellKnownResponse {
53
+ version: string;
54
+ /**
55
+ * List of registry URLs (git or http).
56
+ * First one is primary, rest are fallbacks (for future use).
57
+ */
58
+ registries: string[];
59
+ }
60
+ /**
61
+ * Result from discoverRegistry().
62
+ * Contains domain and its authorized registries.
63
+ */
64
+ interface DiscoveryResult {
65
+ /** The domain that was discovered */
66
+ domain: string;
67
+ /** Authorized registry URLs for this domain */
68
+ registries: string[];
69
+ }
70
+ /**
71
+ * Registry configuration - local, remote, or git.
72
+ */
73
+ type RegistryConfig = LocalRegistryConfig | RemoteRegistryConfig | GitRegistryConfig;
74
+ /**
75
+ * Type guard to check if config is for remote registry.
76
+ */
77
+ declare function isRemoteConfig(config?: RegistryConfig): config is RemoteRegistryConfig;
78
+ /**
79
+ * Type guard to check if config is for git registry.
80
+ */
81
+ declare function isGitConfig(config?: RegistryConfig): config is GitRegistryConfig;
82
+ /**
18
83
  * Search options for querying resources.
19
84
  */
20
85
  interface SearchOptions {
@@ -32,6 +97,36 @@ interface SearchOptions {
32
97
  offset?: number;
33
98
  }
34
99
  /**
100
+ * Options for pulling resources from remote registry.
101
+ */
102
+ interface PullOptions {
103
+ /**
104
+ * Pull from a specific registry instead of auto-discovering.
105
+ */
106
+ from?: Registry;
107
+ }
108
+ /**
109
+ * Target configuration for publishing resources.
110
+ */
111
+ interface PublishTarget {
112
+ type: "http" | "git";
113
+ /** HTTP endpoint (for type: "http") */
114
+ endpoint?: string;
115
+ /** Git repository URL (for type: "git") */
116
+ url?: string;
117
+ /** Git ref - branch/tag (for type: "git"). Default: "main" */
118
+ ref?: string;
119
+ }
120
+ /**
121
+ * Options for publishing resources to remote registry.
122
+ */
123
+ interface PublishOptions {
124
+ /**
125
+ * Target registry configuration.
126
+ */
127
+ to: PublishTarget;
128
+ }
129
+ /**
35
130
  * Registry interface for resource storage and retrieval.
36
131
  */
37
132
  interface Registry {
@@ -41,14 +136,30 @@ interface Registry {
41
136
  */
42
137
  supportType(type: ResourceType): void;
43
138
  /**
44
- * Publish resource to remote registry (based on domain).
139
+ * Pull resource from remote registry to local cache.
140
+ * Discovers remote registry via well-known and caches locally.
141
+ * @param locator - Resource locator (must include domain)
142
+ * @param options - Pull options
45
143
  */
46
- publish(resource: RXR): Promise<void>;
144
+ pull(locator: string, options?: PullOptions): Promise<void>;
145
+ /**
146
+ * Publish resource to remote registry.
147
+ * Resource must exist in local first.
148
+ * @param resource - Resource to publish
149
+ * @param options - Publish target configuration
150
+ */
151
+ publish(resource: RXR, options: PublishOptions): Promise<void>;
47
152
  /**
48
153
  * Link resource to local registry (for development/caching).
49
154
  */
50
155
  link(resource: RXR): Promise<void>;
51
156
  /**
157
+ * Get raw resource by locator string.
158
+ * Returns the RXR (locator + manifest + content) without resolving.
159
+ * Use this when you need access to raw resource content.
160
+ */
161
+ get(locator: string): Promise<RXR>;
162
+ /**
52
163
  * Resolve resource by locator string.
53
164
  * Returns a ResolvedResource with execute function and original resource.
54
165
  * Checks local first, then fetches from remote if not found.
@@ -82,23 +193,46 @@ declare class RegistryError extends ResourceXError {
82
193
  import { RXR as RXR2, RXL as RXL2 } from "@resourcexjs/core";
83
194
  import { ResourceType as ResourceType2, ResolvedResource as ResolvedResource2 } from "@resourcexjs/type";
84
195
  /**
85
- * ARP-based registry implementation.
86
- * Uses ARP protocol for atomic I/O operations.
87
- * Each instance has its own TypeHandlerChain for type handling.
196
+ * Local filesystem-based registry implementation.
197
+ * Uses Node.js fs module directly for storage operations.
88
198
  */
89
- declare class ARPRegistry implements Registry {
90
- private readonly arp;
199
+ declare class LocalRegistry implements Registry {
91
200
  private readonly basePath;
92
201
  private readonly typeHandler;
93
- constructor(config?: RegistryConfig);
202
+ constructor(config?: LocalRegistryConfig);
94
203
  supportType(type: ResourceType2): void;
95
204
  /**
96
- * Build ARP URL for a resource file.
97
- * Path structure: {basePath}/{domain}/{path}/{name}.{type}/{version}/{filename}
205
+ * Build filesystem path for a resource.
206
+ *
207
+ * Storage structure:
208
+ * - local: {basePath}/local/{name}.{type}/{version}
209
+ * - cache: {basePath}/cache/{domain}/{path}/{name}.{type}/{version}
210
+ *
211
+ * @param locator - Resource locator
212
+ * @param area - Storage area ("local" or "cache")
213
+ */
214
+ private buildPath;
215
+ /**
216
+ * Determine if a locator refers to a local-only resource (no domain or localhost).
217
+ */
218
+ private isLocalOnlyLocator;
219
+ /**
220
+ * Check if a resource exists at a specific path.
98
221
  */
99
- private buildUrl;
100
- publish(_resource: RXR2): Promise<void>;
222
+ private existsAt;
223
+ /**
224
+ * Find which area a resource exists in.
225
+ * Returns the area ("local" or "cache") or null if not found.
226
+ */
227
+ private findArea;
228
+ /**
229
+ * Load resource from a specific path.
230
+ */
231
+ private loadFrom;
232
+ pull(_locator: string, _options?: PullOptions): Promise<void>;
233
+ publish(_resource: RXR2, _options: PublishOptions): Promise<void>;
101
234
  link(resource: RXR2): Promise<void>;
235
+ get(locator: string): Promise<RXR2>;
102
236
  resolve<
103
237
  TArgs = void,
104
238
  TResult = unknown
@@ -107,14 +241,125 @@ declare class ARPRegistry implements Registry {
107
241
  delete(locator: string): Promise<void>;
108
242
  search(options?: SearchOptions): Promise<RXL2[]>;
109
243
  /**
110
- * Parse a file entry path to RXL.
244
+ * Recursively list all files in a directory.
245
+ */
246
+ private listRecursive;
247
+ /**
248
+ * Parse a local entry path to RXL.
249
+ * Entry format: {name}.{type}/{version}/manifest.json
250
+ */
251
+ private parseLocalEntry;
252
+ /**
253
+ * Parse a cache entry path to RXL.
111
254
  * Entry format: {domain}/{path}/{name}.{type}/{version}/manifest.json
112
255
  */
256
+ private parseCacheEntry;
257
+ /**
258
+ * Parse name and type from a combined string like "name.type" or "name".
259
+ */
260
+ private parseNameType;
261
+ }
262
+ import { RXR as RXR3, RXL as RXL3 } from "@resourcexjs/core";
263
+ import { ResourceType as ResourceType3, ResolvedResource as ResolvedResource3 } from "@resourcexjs/type";
264
+ /**
265
+ * Remote registry implementation.
266
+ * Uses HTTP API for resource access.
267
+ */
268
+ declare class RemoteRegistry implements Registry {
269
+ private readonly endpoint;
270
+ private readonly typeHandler;
271
+ constructor(config: RemoteRegistryConfig);
272
+ supportType(type: ResourceType3): void;
273
+ pull(_locator: string, _options?: PullOptions): Promise<void>;
274
+ publish(_resource: RXR3, _options: PublishOptions): Promise<void>;
275
+ link(_resource: RXR3): Promise<void>;
276
+ get(locator: string): Promise<RXR3>;
277
+ resolve<
278
+ TArgs = void,
279
+ TResult = unknown
280
+ >(locator: string): Promise<ResolvedResource3<TArgs, TResult>>;
281
+ exists(locator: string): Promise<boolean>;
282
+ delete(_locator: string): Promise<void>;
283
+ search(options?: SearchOptions): Promise<RXL3[]>;
284
+ }
285
+ /**
286
+ * Discover registry for a domain using well-known.
287
+ * @param domain - The domain to discover (e.g., "deepractice.ai")
288
+ * @returns Discovery result with domain and authorized registries
289
+ */
290
+ declare function discoverRegistry(domain: string): Promise<DiscoveryResult>;
291
+ import { RXR as RXR4, RXL as RXL4 } from "@resourcexjs/core";
292
+ import { ResourceType as ResourceType4, ResolvedResource as ResolvedResource4 } from "@resourcexjs/type";
293
+ /**
294
+ * Git-based registry implementation.
295
+ * Clones a git repository and reads resources from it.
296
+ */
297
+ declare class GitRegistry implements Registry {
298
+ private readonly url;
299
+ private readonly ref;
300
+ private readonly basePath;
301
+ private readonly cacheDir;
302
+ private readonly typeHandler;
303
+ private readonly trustedDomain?;
304
+ constructor(config: GitRegistryConfig);
305
+ /**
306
+ * Check if URL is a remote git URL (not local path).
307
+ */
308
+ private isRemoteUrl;
309
+ /**
310
+ * Build cache directory name from git URL.
311
+ * git@github.com:Deepractice/Registry.git → github.com-Deepractice-Registry
312
+ */
313
+ private buildCacheDir;
314
+ supportType(type: ResourceType4): void;
315
+ /**
316
+ * Ensure the repository is cloned and up to date.
317
+ */
318
+ private ensureCloned;
319
+ /**
320
+ * Execute git command in the cache directory.
321
+ */
322
+ private gitExec;
323
+ /**
324
+ * Build filesystem path for a resource in the cloned repo.
325
+ * Path structure: {cacheDir}/{basePath}/{domain}/{path}/{name}.{type}/{version}
326
+ */
327
+ private buildResourcePath;
328
+ get(locator: string): Promise<RXR4>;
329
+ resolve<
330
+ TArgs = void,
331
+ TResult = unknown
332
+ >(locator: string): Promise<ResolvedResource4<TArgs, TResult>>;
333
+ exists(locator: string): Promise<boolean>;
334
+ search(options?: SearchOptions): Promise<RXL4[]>;
335
+ private listRecursive;
113
336
  private parseEntryToRXL;
337
+ pull(_locator: string, _options?: PullOptions): Promise<void>;
338
+ publish(_resource: RXR4, _options: PublishOptions): Promise<void>;
339
+ link(_resource: RXR4): Promise<void>;
340
+ delete(_locator: string): Promise<void>;
114
341
  }
115
342
  /**
116
343
  * Create a registry instance.
117
- * Uses ARP protocol for storage operations.
344
+ *
345
+ * @param config - Registry configuration
346
+ * - No config or LocalRegistryConfig: Creates LocalRegistry (filesystem-based)
347
+ * - RemoteRegistryConfig: Creates RemoteRegistry (HTTP-based)
348
+ * - GitRegistryConfig: Creates GitRegistry (git clone-based)
349
+ *
350
+ * @example
351
+ * // Local registry (default)
352
+ * const registry = createRegistry();
353
+ * const registry2 = createRegistry({ path: "./custom-path" });
354
+ *
355
+ * // Remote registry
356
+ * const registry3 = createRegistry({ endpoint: "https://registry.deepractice.ai/v1" });
357
+ *
358
+ * // Git registry
359
+ * const registry4 = createRegistry({
360
+ * type: "git",
361
+ * url: "git@github.com:Deepractice/Registry.git",
362
+ * });
118
363
  */
119
364
  declare function createRegistry(config?: RegistryConfig): Registry;
120
- export { createRegistry, SearchOptions, RegistryError, RegistryConfig, Registry, ARPRegistry };
365
+ export { isRemoteConfig, isGitConfig, discoverRegistry, createRegistry, WellKnownResponse, SearchOptions, RemoteRegistryConfig, RemoteRegistry, RegistryError, RegistryConfig, Registry, PullOptions, PublishTarget, PublishOptions, LocalRegistryConfig, LocalRegistry, GitRegistryConfig, GitRegistry, DiscoveryResult };