@continuonai/rcan-ts 0.2.0 → 0.5.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 +90 -3
- package/bin/rcan-validate.mjs +86 -0
- package/dist/browser.d.mts +814 -5
- package/dist/browser.mjs +1217 -5
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +814 -5
- package/dist/index.d.ts +814 -5
- package/dist/index.js +1267 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1213 -5
- package/dist/index.mjs.map +1 -1
- package/dist/rcan-validate.js +47 -2
- package/dist/rcan.iife.js +3 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rcan-ts
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for the **RCAN v1.
|
|
3
|
+
Official TypeScript SDK for the **RCAN v1.5** Robot Communication and Addressing Network protocol.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@continuonai/rcan-ts)
|
|
6
6
|
[](https://github.com/continuonai/rcan-ts/actions)
|
|
@@ -9,6 +9,8 @@ Official TypeScript SDK for the **RCAN v1.2** Robot Communication and Accountabi
|
|
|
9
9
|
npm install @continuonai/rcan-ts
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
> **v0.2.0** — RCAN v1.2 spec compliance including §17 Distributed Registry Node Protocol
|
|
13
|
+
|
|
12
14
|
### CDN / Browser (no build step)
|
|
13
15
|
|
|
14
16
|
```html
|
|
@@ -139,6 +141,89 @@ result.warnings.forEach((w) => console.warn("⚠️", w));
|
|
|
139
141
|
| `validateURI` | Validate a Robot URI string |
|
|
140
142
|
| `validateMessage` | Validate a RCAN message object |
|
|
141
143
|
| `validateConfig` | L1/L2/L3 conformance check for a robot RCAN config |
|
|
144
|
+
| `NodeClient` | Resolve RRNs from federated registry nodes (§17) |
|
|
145
|
+
| `fetchCanonicalSchema` | Fetch the canonical JSON schema from rcan.dev |
|
|
146
|
+
| `validateConfigAgainstSchema` | Validate a config object against the live JSON schema |
|
|
147
|
+
| `validateNodeAgainstSchema` | Validate a node manifest against the node schema |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Distributed Registry Nodes (§17)
|
|
152
|
+
|
|
153
|
+
RCAN v1.2 §17 introduces a federated registry network. `NodeClient` resolves RRNs from any node — root or delegated authoritative.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { NodeClient } from '@continuonai/rcan-ts';
|
|
157
|
+
|
|
158
|
+
const client = new NodeClient();
|
|
159
|
+
|
|
160
|
+
// Resolve an RRN across the federation
|
|
161
|
+
const result = await client.resolve('RRN-BD-000000000001');
|
|
162
|
+
console.log(`Resolved by: ${result.resolved_by}`);
|
|
163
|
+
console.log(`Robot: ${result.record.name}`);
|
|
164
|
+
|
|
165
|
+
// Discover the authoritative node for a namespace
|
|
166
|
+
const node = await client.discover('RRN-BD-000000000001');
|
|
167
|
+
console.log(`Authoritative: ${node.operator} at ${node.api_base}`);
|
|
168
|
+
|
|
169
|
+
// List all known registry nodes
|
|
170
|
+
const nodes = await client.listNodes();
|
|
171
|
+
nodes.forEach(n => console.log(`${n.operator}: ${n.namespace_prefix}`));
|
|
172
|
+
|
|
173
|
+
// Verify a node manifest
|
|
174
|
+
const manifest = await client.getNodeManifest('https://registry.example.com');
|
|
175
|
+
const isValid = client.verifyNode(manifest);
|
|
176
|
+
|
|
177
|
+
// Error handling
|
|
178
|
+
import { RCANNodeNotFoundError, RCANNodeTrustError } from '@continuonai/rcan-ts';
|
|
179
|
+
try {
|
|
180
|
+
const result = await client.resolve('RRN-UNKNOWN-000000000001');
|
|
181
|
+
} catch (e) {
|
|
182
|
+
if (e instanceof RCANNodeNotFoundError) {
|
|
183
|
+
console.log(`Not found: ${e.rrn}`);
|
|
184
|
+
} else if (e instanceof RCANNodeTrustError) {
|
|
185
|
+
console.log(`Trust failure: ${e.reason}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### RRN Format
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
Root namespace: RRN-000000000001 (12-digit recommended, 8-digit still valid)
|
|
194
|
+
Delegated: RRN-BD-000000000001 (prefix 2-8 alphanumeric chars)
|
|
195
|
+
Legacy (valid): RRN-00000001 (8-digit, backward compatible)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Schema Validation
|
|
199
|
+
|
|
200
|
+
Validate configs against the canonical JSON schema published at rcan.dev:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { validateConfigAgainstSchema, validateNodeAgainstSchema } from '@continuonai/rcan-ts';
|
|
204
|
+
|
|
205
|
+
// Validate a RCAN config against the canonical schema from rcan.dev
|
|
206
|
+
const result = await validateConfigAgainstSchema(myConfig);
|
|
207
|
+
if (!result.valid) {
|
|
208
|
+
console.error('Config invalid:', result.errors);
|
|
209
|
+
} else if (result.skipped) {
|
|
210
|
+
console.warn('Schema validation skipped (rcan.dev unreachable)');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Validate a node manifest
|
|
214
|
+
const nodeResult = await validateNodeAgainstSchema(manifest);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### CDN / Browser Usage
|
|
218
|
+
|
|
219
|
+
```html
|
|
220
|
+
<script src="https://unpkg.com/@continuonai/rcan-ts/dist/rcan.iife.js"></script>
|
|
221
|
+
<script>
|
|
222
|
+
const { validateConfig, NodeClient } = window.RCAN;
|
|
223
|
+
const client = new NodeClient();
|
|
224
|
+
client.resolve('RRN-000000000001').then(r => console.log(r));
|
|
225
|
+
</script>
|
|
226
|
+
```
|
|
142
227
|
|
|
143
228
|
---
|
|
144
229
|
|
|
@@ -155,10 +240,12 @@ The Python and TypeScript SDKs share an identical API surface — `RobotURI`, `R
|
|
|
155
240
|
## Links
|
|
156
241
|
|
|
157
242
|
- ⚡ [Quickstart](https://rcan.dev/quickstart) — from zero to first message in 5 min
|
|
158
|
-
- 📖 [RCAN Spec](https://rcan.dev/spec) — full protocol specification
|
|
243
|
+
- 📖 [RCAN Spec v1.5](https://rcan.dev/spec) — full protocol specification
|
|
159
244
|
- 🌐 [rcan.dev](https://rcan.dev) — robot registry and documentation
|
|
160
245
|
- 🐍 [rcan-py](https://github.com/continuonai/rcan-py) — Python SDK
|
|
161
|
-
- 🤖 [OpenCastor](https://github.com/craigm26/OpenCastor) — robot runtime
|
|
246
|
+
- 🤖 [OpenCastor](https://github.com/craigm26/OpenCastor) — Python robot runtime (RCAN reference implementation)
|
|
247
|
+
- 🖥️ [OpenCastor Fleet UI](https://app.opencastor.com) — Flutter web app for remote fleet management (uses rcan-ts for message construction)
|
|
248
|
+
- 🏛️ [Robot Registry Foundation](https://robotregistryfoundation.org) — global robot identity registry
|
|
162
249
|
|
|
163
250
|
---
|
|
164
251
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { NodeClient, validateConfig, VERSION, SPEC_VERSION } from '../dist/index.js';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { parseArgs } from 'util';
|
|
5
|
+
|
|
6
|
+
const { values, positionals } = parseArgs({
|
|
7
|
+
args: process.argv.slice(2),
|
|
8
|
+
options: {
|
|
9
|
+
version: { type: 'boolean', short: 'v' },
|
|
10
|
+
file: { type: 'string', short: 'f' },
|
|
11
|
+
},
|
|
12
|
+
allowPositionals: true,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (values.version) {
|
|
16
|
+
console.log(`rcan-validate (ts) ${VERSION} (RCAN spec ${SPEC_VERSION})`);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const [subcommand, target] = positionals;
|
|
21
|
+
|
|
22
|
+
if (subcommand === 'node') {
|
|
23
|
+
if (!target) {
|
|
24
|
+
console.error('Usage: rcan-validate node <url>');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const client = new NodeClient();
|
|
29
|
+
console.log(`Validating node manifest: ${target}`);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const manifest = await client.getNodeManifest(target);
|
|
33
|
+
const valid = client.verifyNode(manifest);
|
|
34
|
+
|
|
35
|
+
const checks = [
|
|
36
|
+
['node_type', manifest.node_type, ['root','authoritative','resolver','cache'].includes(manifest.node_type)],
|
|
37
|
+
['operator', manifest.operator, !!manifest.operator],
|
|
38
|
+
['namespace_prefix', manifest.namespace_prefix, !!manifest.namespace_prefix],
|
|
39
|
+
['public_key', manifest.public_key?.substring(0, 20) + '...', manifest.public_key?.startsWith('ed25519:')],
|
|
40
|
+
['api_base', manifest.api_base, manifest.api_base?.startsWith('https://')],
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
for (const [name, value, pass] of checks) {
|
|
44
|
+
console.log(` ${pass ? '✓' : '✗'} ${name}: ${value}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const passed = checks.filter(([,,p]) => p).length;
|
|
48
|
+
console.log(`\n${valid ? 'PASS' : 'FAIL'} (${passed}/${checks.length} checks)`);
|
|
49
|
+
process.exit(valid ? 0 : 1);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error(`❌ Error: ${e.message}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (subcommand === 'config') {
|
|
57
|
+
const filePath = target || values.file;
|
|
58
|
+
if (!filePath) {
|
|
59
|
+
console.error('Usage: rcan-validate config <file>');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
65
|
+
const config = JSON.parse(raw);
|
|
66
|
+
const result = validateConfig(config);
|
|
67
|
+
if (result.ok) {
|
|
68
|
+
console.log('✓ Config valid');
|
|
69
|
+
if (result.warnings?.length) {
|
|
70
|
+
result.warnings.forEach(w => console.warn(` ⚠️ ${w}`));
|
|
71
|
+
}
|
|
72
|
+
process.exit(0);
|
|
73
|
+
} else {
|
|
74
|
+
console.error('✗ Config invalid:');
|
|
75
|
+
result.issues?.forEach(i => console.error(` ❌ ${i}`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.error(`❌ Error: ${e.message}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
85
|
+
console.error('Usage: rcan-validate node <url> | rcan-validate config <file> | rcan-validate --version');
|
|
86
|
+
process.exit(1);
|