@continuonai/rcan-ts 0.1.1 → 0.1.2

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
@@ -9,6 +9,21 @@ Official TypeScript SDK for the **RCAN v1.2** Robot Communication and Accountabi
9
9
  npm install @continuonai/rcan-ts
10
10
  ```
11
11
 
12
+ ### CDN / Browser (no build step)
13
+
14
+ ```html
15
+ <script src="https://unpkg.com/@continuonai/rcan-ts/dist/rcan.iife.js"></script>
16
+ <script>
17
+ const uri = new RCAN.RobotURI.parse('rcan://registry.rcan.dev/acme/arm/v1/unit-001');
18
+ console.log(uri.manufacturer); // acme
19
+ </script>
20
+ ```
21
+
22
+ Also available via jsDelivr:
23
+ ```html
24
+ <script src="https://cdn.jsdelivr.net/npm/@continuonai/rcan-ts/dist/rcan.iife.js"></script>
25
+ ```
26
+
12
27
  ---
13
28
 
14
29
  ## Quick Start
@@ -0,0 +1,4 @@
1
+ "use strict";var RCAN=(()=>{var G=Object.defineProperty;var gt=Object.getOwnPropertyDescriptor;var ut=Object.getOwnPropertyNames;var mt=Object.prototype.hasOwnProperty;var lt=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});var ft=(n,t)=>{for(var e in t)G(n,e,{get:t[e],enumerable:!0})},ht=(n,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of ut(t))!mt.call(n,s)&&s!==e&&G(n,s,{get:()=>t[s],enumerable:!(r=gt(t,s))||r.enumerable});return n};var pt=n=>ht(G({},"__esModule",{value:!0}),n);var It={};ft(It,{AuditChain:()=>P,AuditError:()=>H,CommitmentRecord:()=>O,ConfidenceGate:()=>J,GateError:()=>x,HiTLGate:()=>E,RCANAddressError:()=>D,RCANError:()=>b,RCANGateError:()=>M,RCANMessage:()=>U,RCANMessageError:()=>f,RCANRegistryError:()=>_,RCANSignatureError:()=>B,RCANValidationError:()=>q,RCAN_VERSION:()=>wt,RegistryClient:()=>V,RobotURI:()=>I,RobotURIError:()=>w,VERSION:()=>vt,validateConfig:()=>st,validateMessage:()=>nt,validateURI:()=>rt});var w=class extends Error{constructor(t){super(t),this.name="RobotURIError"}},I=class n{registry;manufacturer;model;version;deviceId;constructor(t){this.registry=t.registry,this.manufacturer=t.manufacturer,this.model=t.model,this.version=t.version,this.deviceId=t.deviceId}static parse(t){if(!t.startsWith("rcan://"))throw new w(`URI must start with 'rcan://' \u2014 got: ${t}`);let r=t.slice(7).split("/");if(r.length!==5)throw new w(`URI must have exactly 5 path segments (registry/manufacturer/model/version/device-id) \u2014 got ${r.length} in: ${t}`);let[s,i,o,c,g]=r;for(let[d,h]of[["registry",s],["manufacturer",i],["model",o],["version",c],["device-id",g]])if(!h||h.trim()==="")throw new w(`URI segment '${d}' must not be empty`);return new n({registry:s,manufacturer:i,model:o,version:c,deviceId:g})}static build(t){let e=t.registry??"registry.rcan.dev",{manufacturer:r,model:s,version:i,deviceId:o}=t;for(let[c,g]of[["manufacturer",r],["model",s],["version",i],["deviceId",o]])if(!g||g.trim()==="")throw new w(`'${c}' must not be empty`);return new n({registry:e,manufacturer:r,model:s,version:i,deviceId:o})}toString(){return`rcan://${this.registry}/${this.manufacturer}/${this.model}/${this.version}/${this.deviceId}`}get namespace(){return`${this.manufacturer}/${this.model}`}get registryUrl(){return`https://${this.registry}/registry/${this.manufacturer}/${this.model}/${this.version}/${this.deviceId}`}equals(t){return this.toString()===t.toString()}toJSON(){return{uri:this.toString(),registry:this.registry,manufacturer:this.manufacturer,model:this.model,version:this.version,deviceId:this.deviceId}}};var f=class extends Error{constructor(t){super(t),this.name="RCANMessageError"}},U=class n{rcan;cmd;target;params;confidence;modelIdentity;signature;timestamp;constructor(t){if(!t.cmd||t.cmd.trim()==="")throw new f("'cmd' is required");if(!t.target)throw new f("'target' is required");if(this.rcan=t.rcan??"1.2",this.cmd=t.cmd,this.target=t.target instanceof I?t.target.toString():String(t.target),this.params=t.params??{},this.confidence=t.confidence,this.modelIdentity=t.modelIdentity??t.model_identity,this.signature=t.signature,this.timestamp=t.timestamp??new Date().toISOString(),this.confidence!==void 0&&(this.confidence<0||this.confidence>1))throw new f(`confidence must be in [0.0, 1.0] \u2014 got ${this.confidence}`)}get isSigned(){return this.signature!==void 0&&this.signature.sig!==""}get isAiDriven(){return this.confidence!==void 0}toJSON(){let t={rcan:this.rcan,cmd:this.cmd,target:this.target,timestamp:this.timestamp};return Object.keys(this.params).length>0&&(t.params=this.params),this.confidence!==void 0&&(t.confidence=this.confidence),this.modelIdentity&&(t.model_identity=this.modelIdentity),this.signature&&(t.signature=this.signature),t}toJSONString(t){return JSON.stringify(this.toJSON(),null,t)}static fromJSON(t){let e;if(typeof t=="string")try{e=JSON.parse(t)}catch{throw new f("Invalid JSON string")}else e=t;if(!e.cmd)throw new f("Missing required field: 'cmd'");if(!e.target)throw new f("Missing required field: 'target'");if(!e.rcan)throw new f("Missing required field: 'rcan'");return new n({rcan:e.rcan,cmd:e.cmd,target:e.target,params:e.params??{},confidence:e.confidence,modelIdentity:e.model_identity??e.modelIdentity,signature:e.signature,timestamp:e.timestamp})}};function j(){if(typeof globalThis.crypto<"u"&&typeof globalThis.crypto.randomUUID=="function")return globalThis.crypto.randomUUID();try{let{randomUUID:n}=lt("crypto");return n()}catch{return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let t=Math.random()*16|0;return(n==="x"?t:t&3|8).toString(16)})}}function Q(n,t){return typeof process<"u",xt(n,t)}function z(n){let t=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],e=1779033703,r=3144134277,s=1013904242,i=2773480762,o=1359893119,c=2600822924,g=528734635,d=1541459225,it=n.length*8,y=[...n];for(y.push(128);y.length%64!==56;)y.push(0);for(let l=7;l>=0;l--)y.push(it/Math.pow(2,l*8)&255);for(let l=0;l<y.length;l+=64){let u=[];for(let a=0;a<16;a++)u[a]=y[l+a*4]<<24|y[l+a*4+1]<<16|y[l+a*4+2]<<8|y[l+a*4+3];for(let a=16;a<64;a++){let W=p(u[a-15],7)^p(u[a-15],18)^u[a-15]>>>3,F=p(u[a-2],17)^p(u[a-2],19)^u[a-2]>>>10;u[a]=u[a-16]+W+u[a-7]+F>>>0}let[R,k,N,K,v,$,L,T]=[e,r,s,i,o,c,g,d];for(let a=0;a<64;a++){let W=p(v,6)^p(v,11)^p(v,25),F=v&$^~v&L,Z=T+W+F+t[a]+u[a]>>>0,at=p(R,2)^p(R,13)^p(R,22),ct=R&k^R&N^k&N,dt=at+ct>>>0;[T,L,$,v,K,N,k,R]=[L,$,v,K+Z>>>0,N,k,R,Z+dt>>>0]}e=e+R>>>0,r=r+k>>>0,s=s+N>>>0,i=i+K>>>0,o=o+v>>>0,c=c+$>>>0,g=g+L>>>0,d=d+T>>>0}let Y=new Uint8Array(32),ot=new DataView(Y.buffer);return[e,r,s,i,o,c,g,d].forEach((l,u)=>ot.setUint32(u*4,l)),Y}function p(n,t){return n>>>t|n<<32-t}function tt(n){if(typeof TextEncoder<"u")return new TextEncoder().encode(n);let t=new Uint8Array(n.length);for(let e=0;e<n.length;e++)t[e]=n.charCodeAt(e)&255;return t}function yt(n){return Array.from(n).map(t=>t.toString(16).padStart(2,"0")).join("")}function xt(n,t){let r=tt(n);r.length>64&&(r=z(r));let s=new Uint8Array(64),i=new Uint8Array(64);for(let h=0;h<64;h++)s[h]=(r[h]??0)^54,i[h]=(r[h]??0)^92;let o=tt(t),c=new Uint8Array(64+o.length);c.set(s),c.set(o,64);let g=z(c),d=new Uint8Array(96);return d.set(i),d.set(g,64),yt(z(d))}var x=class extends Error{constructor(t){super(t),this.name="GateError"}},J=class{threshold;constructor(t=.8){if(t<0||t>1)throw new x(`threshold must be in [0.0, 1.0] \u2014 got ${t}`);this.threshold=t}allows(t){return t>=this.threshold}margin(t){return t-this.threshold}assert(t,e){if(!this.allows(t)){let r=e?` for action '${e}'`:"";throw new x(`Confidence ${t}${r} is below threshold ${this.threshold}`)}}},E=class{_pending=new Map;request(t,e={}){let r=j();return this._pending.set(r,{token:r,action:t,context:e,createdAt:new Date().toISOString(),status:"pending"}),r}approve(t){let e=this._pending.get(t);if(!e)throw new x(`Unknown token: ${t}`);e.status="approved"}deny(t,e){let r=this._pending.get(t);if(!r)throw new x(`Unknown token: ${t}`);r.status="denied",e&&(r.reason=e)}check(t){let e=this._pending.get(t);if(!e)throw new x(`Unknown token: ${t}`);return e.status}get pendingApprovals(){return Array.from(this._pending.values()).filter(t=>t.status==="pending")}getApproval(t){return this._pending.get(t)}clearResolved(){for(let[t,e]of this._pending.entries())e.status!=="pending"&&this._pending.delete(t)}};var H=class extends Error{constructor(t){super(t),this.name="AuditError"}};function bt(n,t,e,r,s){let i=JSON.stringify({recordId:n,action:t,robotUri:e,timestamp:r,params:s},Object.keys({recordId:n,action:t,robotUri:e,timestamp:r,params:s}).sort());return Q("rcan-content-hash",i)}function et(n,t){let{hmac:e,...r}=t,s=JSON.stringify(r,Object.keys(r).sort());return Q(n,s)}var O=class n{recordId;action;robotUri;confidence;modelIdentity;params;safetyApproved;timestamp;contentHash;previousHash;hmac;constructor(t){this.recordId=t.recordId,this.action=t.action,this.robotUri=t.robotUri,this.confidence=t.confidence,this.modelIdentity=t.modelIdentity,this.params=t.params,this.safetyApproved=t.safetyApproved,this.timestamp=t.timestamp,this.contentHash=t.contentHash,this.previousHash=t.previousHash,this.hmac=t.hmac}static create(t,e,r=null){let s=j(),i=new Date().toISOString(),o=t.params??{},c=t.robotUri??"",g=bt(s,t.action,c,i,o),d={recordId:s,action:t.action,robotUri:c,confidence:t.confidence,modelIdentity:t.modelIdentity,params:o,safetyApproved:t.safetyApproved??!0,timestamp:i,contentHash:g,previousHash:r,hmac:""};return d.hmac=et(e,d),new n(d)}verify(t){return et(t,this.toJSON())===this.hmac}toJSON(){return{recordId:this.recordId,action:this.action,robotUri:this.robotUri,confidence:this.confidence,modelIdentity:this.modelIdentity,params:this.params,safetyApproved:this.safetyApproved,timestamp:this.timestamp,contentHash:this.contentHash,previousHash:this.previousHash,hmac:this.hmac}}static fromJSON(t){return new n(t)}},P=class n{_records=[];_secret;constructor(t){this._secret=t}get records(){return this._records}append(t){let r=this._records[this._records.length-1]?.contentHash??null,s=O.create(t,this._secret,r);return this._records.push(s),s}verifyAll(){let t=[],e=null;for(let r of this._records)r.verify(this._secret)||t.push(`HMAC invalid for record ${r.recordId.slice(0,8)}`),e!==null&&r.previousHash!==e&&t.push(`Chain broken at ${r.recordId.slice(0,8)}: expected prev=${e.slice(0,12)}`),e=r.contentHash;return{valid:t.length===0,count:this._records.length,errors:t}}toJSONL(){return this._records.map(t=>JSON.stringify(t.toJSON())).join(`
2
+ `)+`
3
+ `}static fromJSONL(t,e){let r=new n(e),s=t.trim().split(`
4
+ `).filter(i=>i.trim()!=="");for(let i of s){let o=JSON.parse(i);r._records.push(O.fromJSON(o))}return r}};function X(){return{ok:!0,issues:[],warnings:[],info:[]}}function C(n,t){n.ok=!1,n.issues.push(t)}function A(n,t){n.warnings.push(t)}function m(n,t){n.info.push(t)}function rt(n){let t=X();try{let e=I.parse(n);m(t,"\u2705 Valid RCAN URI"),m(t,` Registry: ${e.registry}`),m(t,` Manufacturer: ${e.manufacturer}`),m(t,` Model: ${e.model}`),m(t,` Version: ${e.version}`),m(t,` Device ID: ${e.deviceId}`)}catch(e){C(t,`Invalid RCAN URI: ${e instanceof Error?e.message:e}`)}return t}function nt(n){let t=X(),e;if(typeof n=="string")try{e=JSON.parse(n)}catch{return C(t,"Invalid JSON string"),t}else if(typeof n=="object"&&n!==null)e=n;else return C(t,"Expected object or JSON string"),t;for(let r of["rcan","cmd","target"])(!(r in e)||!e[r])&&C(t,`Missing required field: '${r}'`);if(!t.ok)return t;try{let r=U.fromJSON(e);m(t,`\u2705 RCAN message valid (v${r.rcan})`),m(t,` cmd: ${r.cmd}`),m(t,` target: ${r.target}`),r.confidence!==void 0?m(t,` confidence: ${r.confidence}`):A(t,"No confidence score \u2014 add for RCAN \xA716 AI accountability"),r.isSigned?m(t,` signature: alg=${r.signature?.alg}, kid=${r.signature?.kid}`):A(t,"Message is unsigned (recommended for production)")}catch(r){C(t,`Message validation failed: ${r instanceof Error?r.message:r}`)}return t}function st(n){let t=X(),e=n.metadata??{},r=n.agent??{},s=n.rcan_protocol??{};if(e.manufacturer||C(t,"L1: metadata.manufacturer is required (\xA72)"),e.model||C(t,"L1: metadata.model is required (\xA72)"),n.rcan_version||A(t,"L1: rcan_version not declared (recommended)"),s.jwt_auth?.enabled||A(t,"L2: jwt_auth not enabled (required for L2 conformance, \xA78)"),(!r.confidence_gates||r.confidence_gates.length===0)&&A(t,"L2: confidence_gates not configured (\xA716)"),(!r.hitl_gates||r.hitl_gates.length===0)&&A(t,"L3: hitl_gates not configured (\xA716)"),r.commitment_chain?.enabled||A(t,"L3: commitment_chain not enabled (\xA716)"),e.rrn?m(t,`\u2705 RRN registered: ${e.rrn}`):A(t,"Robot not registered \u2014 visit rcan.dev/registry/register"),t.ok&&t.issues.length===0){let i=!t.warnings.some(d=>d.startsWith("L1")),o=i&&!t.warnings.some(d=>d.startsWith("L2")),g=o&&!t.warnings.some(d=>d.startsWith("L3"))?"L3":o?"L2":i?"L1":"FAIL";m(t,`\u2705 Config valid \u2014 conformance level: ${g}`)}return t}var b=class extends Error{constructor(t){super(t),this.name="RCANError",Object.setPrototypeOf(this,new.target.prototype)}},D=class extends b{constructor(t){super(t),this.name="RCANAddressError",Object.setPrototypeOf(this,new.target.prototype)}},q=class extends b{constructor(t){super(t),this.name="RCANValidationError",Object.setPrototypeOf(this,new.target.prototype)}},M=class extends b{constructor(e,r,s,i){super(e);this.gateType=r;this.value=s;this.threshold=i;this.name="RCANGateError",Object.setPrototypeOf(this,new.target.prototype)}},B=class extends b{constructor(t){super(t),this.name="RCANSignatureError",Object.setPrototypeOf(this,new.target.prototype)}},_=class extends b{constructor(t){super(t),this.name="RCANRegistryError",Object.setPrototypeOf(this,new.target.prototype)}};var Rt="https://rcan-spec.pages.dev",V=class{baseUrl;apiKey;timeout;constructor(t){this.baseUrl=(t?.baseUrl??Rt).replace(/\/$/,""),this.apiKey=t?.apiKey,this.timeout=t?.timeout??1e4}async _fetch(t,e={}){let r=`${this.baseUrl}${t}`,s=new AbortController,i=setTimeout(()=>s.abort(),this.timeout);try{return await fetch(r,{...e,signal:s.signal,headers:{"Content-Type":"application/json",...e.headers??{}}})}finally{clearTimeout(i)}}_authHeaders(){if(!this.apiKey)throw new _("API key required for write operations. Pass apiKey to RegistryClient.");return{Authorization:`Bearer ${this.apiKey}`}}async _checkResponse(t){if(!t.ok){let e=`Registry API error: ${t.status}`;try{let r=await t.json();r?.error&&(e=r.error)}catch{}throw new _(e)}return await t.json()}async register(t){let e=await this._fetch("/api/v1/robots",{method:"POST",body:JSON.stringify(t)});return this._checkResponse(e)}async get(t){let e=await this._fetch(`/api/v1/robots/${encodeURIComponent(t)}`);return this._checkResponse(e)}async list(t){let e=new URLSearchParams;t?.limit!==void 0&&e.set("limit",String(t.limit)),t?.offset!==void 0&&e.set("offset",String(t.offset)),t?.tier&&e.set("tier",t.tier);let r=e.toString()?`?${e}`:"",s=await this._fetch(`/api/v1/robots${r}`);return this._checkResponse(s)}async patch(t,e){let r=await this._fetch(`/api/v1/robots/${encodeURIComponent(t)}`,{method:"PATCH",headers:this._authHeaders(),body:JSON.stringify(e)});return this._checkResponse(r)}async delete(t){let e=await this._fetch(`/api/v1/robots/${encodeURIComponent(t)}`,{method:"DELETE",headers:this._authHeaders()});e.ok||await this._checkResponse(e)}async search(t){let e=new URLSearchParams;t.q&&e.set("q",t.q),t.manufacturer&&e.set("manufacturer",t.manufacturer),t.model&&e.set("model",t.model),t.tier&&e.set("tier",t.tier);let r=e.toString()?`?${e}`:"",s=await this._fetch(`/api/v1/robots/search${r}`);if(!s.ok){let o=await this._fetch(`/api/v1/robots${r}`),c=await this._checkResponse(o);return"robots"in c?c.robots:"results"in c&&c.results?c.results:[]}let i=await s.json();return Array.isArray(i)?i:"results"in i&&i.results?i.results:"robots"in i?i.robots:[]}};var vt="0.1.0",wt="1.2";return pt(It);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuonai/rcan-ts",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Official TypeScript SDK for the RCAN v1.2 robot communication protocol",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -51,6 +51,9 @@
51
51
  }
52
52
  },
53
53
  "browser": "dist/browser.mjs",
54
+ "cdn": "./dist/rcan.iife.js",
55
+ "unpkg": "./dist/rcan.iife.js",
56
+ "jsdelivr": "./dist/rcan.iife.js",
54
57
  "bin": {
55
58
  "rcan-validate": "./dist/rcan-validate.js"
56
59
  },