thebird 1.2.78 → 1.2.80
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/.github/workflows/publish.yml +9 -1
- package/CHANGELOG.md +226 -0
- package/CLAUDE.md +16 -0
- package/docs/agent-chat.js +7 -4
- package/docs/app.js +14 -11
- package/docs/defaults.json +1 -1
- package/docs/index.html +23 -6
- package/docs/kilo-http-stream.js +24 -0
- package/docs/node-builtins.js +194 -0
- package/docs/preview/index.html +32 -0
- package/docs/preview-sw-client.js +37 -6
- package/docs/preview-sw.js +55 -51
- package/docs/shell-awk.js +113 -0
- package/docs/shell-builtins-extra.js +121 -0
- package/docs/shell-builtins-text.js +109 -0
- package/docs/shell-builtins-util.js +112 -0
- package/docs/shell-builtins.js +183 -0
- package/docs/shell-bun.js +45 -0
- package/docs/shell-control.js +132 -0
- package/docs/shell-deno.js +54 -0
- package/docs/shell-exec.js +85 -0
- package/docs/shell-expand.js +164 -0
- package/docs/shell-fd.js +86 -0
- package/docs/shell-jobs.js +86 -0
- package/docs/shell-node-advanced.js +86 -0
- package/docs/shell-node-brotli.js +22 -0
- package/docs/shell-node-busnet.js +90 -0
- package/docs/shell-node-cipher.js +61 -0
- package/docs/shell-node-cluster.js +33 -0
- package/docs/shell-node-coreutils.js +36 -0
- package/docs/shell-node-crypto.js +137 -0
- package/docs/shell-node-dns.js +41 -0
- package/docs/shell-node-extras.js +148 -0
- package/docs/shell-node-firefox.js +95 -0
- package/docs/shell-node-git.js +60 -0
- package/docs/shell-node-inspector.js +39 -0
- package/docs/shell-node-io.js +131 -0
- package/docs/shell-node-ipc.js +15 -0
- package/docs/shell-node-keyobject.js +60 -0
- package/docs/shell-node-modules.js +157 -0
- package/docs/shell-node-native.js +31 -0
- package/docs/shell-node-net.js +71 -0
- package/docs/shell-node-observe.js +80 -0
- package/docs/shell-node-opfs.js +54 -0
- package/docs/shell-node-procfs.js +42 -0
- package/docs/shell-node-profiler.js +50 -0
- package/docs/shell-node-registry.js +24 -0
- package/docs/shell-node-resolve.js +147 -0
- package/docs/shell-node-runtime.js +83 -0
- package/docs/shell-node-srcmap.js +52 -0
- package/docs/shell-node-stdlib.js +103 -0
- package/docs/shell-node-streams.js +66 -0
- package/docs/shell-node-tar.js +47 -0
- package/docs/shell-node-testrunner.js +35 -0
- package/docs/shell-node-util-extras.js +66 -0
- package/docs/shell-node.js +188 -97
- package/docs/shell-npm.js +173 -0
- package/docs/shell-parser.js +122 -0
- package/docs/shell-pm-layout.js +62 -0
- package/docs/shell-pm.js +39 -0
- package/docs/shell-posix.js +70 -0
- package/docs/shell-procsub.js +65 -0
- package/docs/shell-readline.js +59 -4
- package/docs/shell-runtime.js +37 -0
- package/docs/shell-sed.js +83 -0
- package/docs/shell-signals.js +54 -0
- package/docs/shell-sw-jobs.js +76 -0
- package/docs/shell-ts.js +30 -0
- package/docs/shell.js +159 -167
- package/docs/terminal.js +9 -11
- package/docs/todo.html +211 -0
- package/package.json +1 -1
- package/server.js +43 -4
- package/start-kilo.js +17 -0
- package/test.js +199 -0
- package/.codeinsight +0 -73
- package/docs/acp-stream.js +0 -102
- package/docs/coi-serviceworker.js +0 -2
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export function makeStreamingZlib(streamMod,Buf,fflate){
|
|
2
|
+
const Transform=streamMod.Transform;
|
|
3
|
+
const mkT=(Klass,errMsg)=>()=>{let inst=null;const chunks=[];const out=new Transform({transform(c,e,cb){try{if(!inst)inst=new fflate[Klass]((chunk,fin)=>{out.push(Buf.from(chunk));});inst.push(c instanceof Uint8Array?c:new TextEncoder().encode(String(c)),false);cb();}catch(e){cb(e);}},flush(cb){try{if(inst)inst.push(new Uint8Array(0),true);cb();}catch(e){cb(e);}}});return out;};
|
|
4
|
+
return{
|
|
5
|
+
createGzip:mkT('Gzip'),
|
|
6
|
+
createGunzip:mkT('Gunzip'),
|
|
7
|
+
createDeflate:mkT('Deflate'),
|
|
8
|
+
createInflate:mkT('Inflate'),
|
|
9
|
+
createDeflateRaw:mkT('Deflate'),
|
|
10
|
+
createInflateRaw:mkT('Inflate'),
|
|
11
|
+
createBrotliCompress:()=>{throw new Error('brotli: not in webcrypto/CompressionStream — use gzip instead');},
|
|
12
|
+
createBrotliDecompress:()=>{throw new Error('brotli: not supported in browser');},
|
|
13
|
+
brotliCompressSync:()=>{throw new Error('brotli: not supported — use gzipSync');},
|
|
14
|
+
brotliDecompressSync:()=>{throw new Error('brotli: not supported');},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function makeVmModule(){
|
|
19
|
+
const contexts=new WeakMap();
|
|
20
|
+
const registry=typeof FinalizationRegistry!=='undefined'?new FinalizationRegistry(iframe=>{try{iframe.remove();}catch{}}):{register(){}};
|
|
21
|
+
const hasDom=typeof document!=='undefined';
|
|
22
|
+
const cloneAcross=v=>{if(v==null||typeof v!=='object'&&typeof v!=='function')return v;if(typeof v==='function')return v;try{return structuredClone(v);}catch{return v;}};
|
|
23
|
+
const mkIframe=ctx=>{if(!hasDom)return{iframe:null,win:globalThis};const f=document.createElement('iframe');f.style.display='none';f.setAttribute('sandbox','allow-scripts allow-same-origin');document.body.appendChild(f);const win=f.contentWindow;if(ctx)for(const k of Object.keys(ctx))win[k]=cloneAcross(ctx[k]);registry.register(ctx||{},f);return{iframe:f,win};};
|
|
24
|
+
const syncBack=(ctx,win)=>{for(const k of Object.keys(ctx))if(k in win)ctx[k]=cloneAcross(win[k]);for(const k of Object.keys(win))if(!(k in ctx)&&!['window','self','document','location','navigator','parent','top','frames','opener','localStorage','sessionStorage'].includes(k))ctx[k]=cloneAcross(win[k]);};
|
|
25
|
+
return{
|
|
26
|
+
runInThisContext:code=>(0,eval)(code),
|
|
27
|
+
runInNewContext:(code,ctx={})=>{const{win}=mkIframe(ctx);const r=win.eval?win.eval(code):(0,eval)(code);if(win.eval)syncBack(ctx,win);return cloneAcross(r);},
|
|
28
|
+
runInContext:(code,ctxObj)=>{const ref=contexts.get(ctxObj);if(!ref)throw new Error('vm.runInContext: context not created via createContext');const r=ref.win.eval(code);syncBack(ctxObj,ref.win);return cloneAcross(r);},
|
|
29
|
+
createContext:(ctx={})=>{const ref=mkIframe(ctx);contexts.set(ctx,ref);return ctx;},
|
|
30
|
+
isContext:ctx=>contexts.has(ctx),
|
|
31
|
+
Script:class Script{constructor(code){this.code=code;}runInThisContext(){return(0,eval)(this.code);}runInNewContext(ctx={}){const{win}=mkIframe(ctx);const r=win.eval?win.eval(this.code):(0,eval)(this.code);if(win.eval)syncBack(ctx,win);return cloneAcross(r);}runInContext(ctx){const ref=contexts.get(ctx);if(!ref)throw new Error('Script.runInContext: createContext first');const r=ref.win.eval(this.code);syncBack(ctx,ref.win);return cloneAcross(r);}},
|
|
32
|
+
compileFunction:(code,params=[])=>new Function(...params,code),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function makeModuleRegister(MODULES,snap,pathmod){
|
|
37
|
+
const hooks=[];
|
|
38
|
+
return{
|
|
39
|
+
register(specifier,parentURL){hooks.push({specifier,parentURL:parentURL||'file:///'});return{addEventListener(){}};},
|
|
40
|
+
_runResolve:async(specifier,context)=>{let result={url:specifier,shortCircuit:false};for(const h of hooks){try{const m=await import(h.specifier);if(m.resolve){const nextResolve=async(s,c)=>({url:s,shortCircuit:true});const r=await m.resolve(specifier,context,nextResolve);if(r&&r.shortCircuit){result=r;break;}if(r)result=r;}}catch{}}return result;},
|
|
41
|
+
_runLoad:async(url,context)=>{let result={format:'module',source:null,shortCircuit:false};for(const h of hooks){try{const m=await import(h.specifier);if(m.load){const nextLoad=async(u,c)=>({format:'module',source:null,shortCircuit:true});const r=await m.load(url,context,nextLoad);if(r&&r.shortCircuit){result=r;break;}if(r)result=r;}}catch{}}return result;},
|
|
42
|
+
_hooks:hooks,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function makeHttp2(){
|
|
47
|
+
const mkStream=()=>{const handlers={};const on=(e,f)=>{(handlers[e]=handlers[e]||[]).push(f);return stream;};const emit=(e,...a)=>{for(const f of handlers[e]||[])f(...a);};const stream={on,once:on,emit,close(){emit('close');},end(){emit('end');},write(){},pipe:d=>d,setEncoding(){return stream;}};return{stream,emit};};
|
|
48
|
+
return{
|
|
49
|
+
connect(authority){
|
|
50
|
+
const h={};let closed=false;
|
|
51
|
+
const session={
|
|
52
|
+
on:(e,f)=>{(h[e]=h[e]||[]).push(f);return session;},
|
|
53
|
+
once:(e,f)=>session.on(e,f),
|
|
54
|
+
emit:(e,...a)=>{for(const f of h[e]||[])f(...a);},
|
|
55
|
+
close(){closed=true;session.emit('close');},
|
|
56
|
+
destroy(){closed=true;session.emit('close');},
|
|
57
|
+
request(headers){
|
|
58
|
+
const {stream,emit}=mkStream();
|
|
59
|
+
const method=headers[':method']||'GET';
|
|
60
|
+
const path=headers[':path']||'/';
|
|
61
|
+
const url=authority.toString().replace(/\/$/,'')+path;
|
|
62
|
+
const fetchHeaders={};for(const[k,v]of Object.entries(headers))if(!k.startsWith(':'))fetchHeaders[k]=v;
|
|
63
|
+
fetch(url,{method,headers:fetchHeaders}).then(async r=>{const respHeaders={':status':r.status};r.headers.forEach((v,k)=>{respHeaders[k]=v;});emit('response',respHeaders,0);const reader=r.body?.getReader();if(reader)for(;;){const{value,done}=await reader.read();if(done)break;emit('data',new Uint8Array(value));}emit('end');emit('close');}).catch(e=>emit('error',e));
|
|
64
|
+
return stream;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
queueMicrotask(()=>session.emit('connect',session));
|
|
68
|
+
return session;
|
|
69
|
+
},
|
|
70
|
+
constants:{NGHTTP2_REFUSED_STREAM:0xb,HTTP2_HEADER_METHOD:':method',HTTP2_HEADER_PATH:':path',HTTP2_HEADER_STATUS:':status'},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let wasiPromise=null;
|
|
75
|
+
async function getWasi(){if(!wasiPromise)wasiPromise=import('https://esm.sh/@bjorn3/browser_wasi_shim@0.3.0/es2022/browser_wasi_shim.mjs').then(m=>m.default||m);return wasiPromise;}
|
|
76
|
+
|
|
77
|
+
export function makeWasi(){
|
|
78
|
+
return{
|
|
79
|
+
WASI:class WASI{
|
|
80
|
+
constructor(opts={}){this._opts=opts;this._lib=null;this.wasiImport={};}
|
|
81
|
+
async _load(){if(!this._lib){this._lib=await getWasi();const args=this._opts.args||[];const env=Object.entries(this._opts.env||{}).map(([k,v])=>`${k}=${v}`);const fds=[new this._lib.OpenFile(new this._lib.File([])),new this._lib.OpenFile(new this._lib.File([])),new this._lib.OpenFile(new this._lib.File([]))];this._wasi=new this._lib.WASI(args,env,fds);this.wasiImport=this._wasi.wasiImport;}return this._wasi;}
|
|
82
|
+
async start(instance){await this._load();return this._wasi.start(instance);}
|
|
83
|
+
async initialize(instance){await this._load();return this._wasi.initialize(instance);}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
let brotliMod=null;let brotliPromise=null;
|
|
2
|
+
|
|
3
|
+
async function loadBrotli(){
|
|
4
|
+
if(!brotliPromise)brotliPromise=import('https://esm.sh/brotli-wasm@3.0.1/es2022/brotli-wasm.mjs').then(async m=>{const lib=m.default||m;if(lib.then)return await lib;if(lib.compress&&lib.decompress)return lib;return lib;});
|
|
5
|
+
return brotliPromise;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function preloadBrotli(){brotliMod=await loadBrotli();return brotliMod;}
|
|
9
|
+
|
|
10
|
+
export function makeBrotli(streamMod,Buf){
|
|
11
|
+
const need=()=>{if(!brotliMod)throw new Error('brotli: call preloadBrotli() once before sync brotli calls (auto-preloaded on node entry)');return brotliMod;};
|
|
12
|
+
const toBytes=d=>d instanceof Uint8Array?d:new TextEncoder().encode(String(d));
|
|
13
|
+
const encodeErr=fn=>{try{return fn();}catch(e){throw new Error('brotli: '+e.message);}};
|
|
14
|
+
return{
|
|
15
|
+
brotliCompressSync:b=>Buf.from(encodeErr(()=>need().compress(toBytes(b)))),
|
|
16
|
+
brotliDecompressSync:b=>Buf.from(encodeErr(()=>need().decompress(toBytes(b)))),
|
|
17
|
+
brotliCompress:async(b,cb)=>{try{await loadBrotli();const out=Buf.from(need().compress(toBytes(b)));if(cb)cb(null,out);return out;}catch(e){if(cb)cb(e);else throw e;}},
|
|
18
|
+
brotliDecompress:async(b,cb)=>{try{await loadBrotli();const out=Buf.from(need().decompress(toBytes(b)));if(cb)cb(null,out);return out;}catch(e){if(cb)cb(e);else throw e;}},
|
|
19
|
+
createBrotliCompress:()=>{const chunks=[];return new streamMod.Transform({transform(c,e,cb){chunks.push(toBytes(c));cb();},flush(cb){try{const all=new Uint8Array(chunks.reduce((s,c)=>s+c.length,0));let off=0;for(const c of chunks){all.set(c,off);off+=c.length;}this.push(Buf.from(need().compress(all)));cb();}catch(e){cb(e);}}});},
|
|
20
|
+
createBrotliDecompress:()=>{const chunks=[];return new streamMod.Transform({transform(c,e,cb){chunks.push(toBytes(c));cb();},flush(cb){try{const all=new Uint8Array(chunks.reduce((s,c)=>s+c.length,0));let off=0;for(const c of chunks){all.set(c,off);off+=c.length;}this.push(Buf.from(need().decompress(all)));cb();}catch(e){cb(e);}}});},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const BUS_CHAN='plugkit-busnet';
|
|
2
|
+
const bus=typeof BroadcastChannel!=='undefined'?new BroadcastChannel(BUS_CHAN):null;
|
|
3
|
+
const listeners=new Map();
|
|
4
|
+
const connections=new Map();let connSeq=1;
|
|
5
|
+
const serviceRegistry=new Map();
|
|
6
|
+
let _origin=null;
|
|
7
|
+
function getOrigin(){if(_origin)return _origin;_origin=(globalThis.crypto?.randomUUID?.()||String(Math.random())).slice(0,8);return _origin;}
|
|
8
|
+
|
|
9
|
+
function handleMsg(m){
|
|
10
|
+
if(!m)return;
|
|
11
|
+
if(m.type==='discover'&&m.origin!==getOrigin()){for(const[port,l] of listeners)post({type:'announce',port,service:l.service,origin:getOrigin()});return;}
|
|
12
|
+
if(m.type==='connect'&&listeners.has(m.port)){const l=listeners.get(m.port);const conn=createConnection(m.id,m.from,true);l.onConnection?.(conn);connections.set(m.id,conn);post({type:'connected',id:m.id,from:getOrigin(),to:m.from});return;}
|
|
13
|
+
if(m.type==='connected'&&connections.has(m.id)){queueMicrotask(()=>connections.get(m.id)?._onOpen());return;}
|
|
14
|
+
if(m.type==='data'&&connections.has(m.id)&&m.from!==null&&m.to===getOrigin()){connections.get(m.id)._onData(m.data);return;}
|
|
15
|
+
if(m.type==='data-local'&&connections.has(m.id)){connections.get(m.id)._onData(m.data);return;}
|
|
16
|
+
if(m.type==='close'&&connections.has(m.id)){connections.get(m.id)._onClose();connections.delete(m.id);return;}
|
|
17
|
+
if(m.type==='announce'&&m.origin!==getOrigin()){serviceRegistry.set(m.port+'@'+m.origin,{port:m.port,service:m.service,origin:m.origin,seen:Date.now()});return;}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function post(msg){if(bus)bus.postMessage(msg);}
|
|
21
|
+
|
|
22
|
+
if(bus)bus.addEventListener('message',e=>{if(e.data?.origin===getOrigin())return;handleMsg(e.data);});
|
|
23
|
+
|
|
24
|
+
function createConnection(id,remote,fromServer=false){
|
|
25
|
+
const handlers={data:[],end:[],close:[],open:[],error:[]};
|
|
26
|
+
const conn={
|
|
27
|
+
id,remote,fromServer,readable:true,writable:true,
|
|
28
|
+
on(ev,fn){(handlers[ev]=handlers[ev]||[]).push(fn);return this;},
|
|
29
|
+
once(ev,fn){const w=(...a)=>{this.off(ev,w);fn(...a);};return this.on(ev,w);},
|
|
30
|
+
off(ev,fn){handlers[ev]=(handlers[ev]||[]).filter(x=>x!==fn);return this;},
|
|
31
|
+
write(data){const peer=connections.get(conn._peerId);if(peer){queueMicrotask(()=>peer._onData(data));return true;}post({type:'data',id,data,from:getOrigin(),to:remote});return true;},
|
|
32
|
+
end(data){if(data)conn.write(data);for(const h of handlers.close)h();connections.delete(id);},
|
|
33
|
+
destroy(){for(const h of handlers.close)h();connections.delete(id);},
|
|
34
|
+
_onOpen(){for(const h of handlers.open)h();},
|
|
35
|
+
_onData(d){for(const h of handlers.data)h(d);},
|
|
36
|
+
_onClose(){for(const h of handlers.close)h();},
|
|
37
|
+
_onError(e){for(const h of handlers.error)h(e);},
|
|
38
|
+
_peerId:null,
|
|
39
|
+
};
|
|
40
|
+
return conn;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function makeBusnet(){
|
|
44
|
+
return{
|
|
45
|
+
listen(port,service,onConnection){
|
|
46
|
+
if(listeners.has(port))throw Object.assign(new Error('EADDRINUSE'),{code:'EADDRINUSE',port});
|
|
47
|
+
listeners.set(port,{port,service:service||'generic',onConnection,origin:getOrigin()});
|
|
48
|
+
if(bus)bus.postMessage({type:'announce',port,service:service||'generic',origin:getOrigin()});
|
|
49
|
+
return{port,close:()=>{listeners.delete(port);}};
|
|
50
|
+
},
|
|
51
|
+
connect(port,targetOrigin,cb){
|
|
52
|
+
const id=getOrigin()+'-'+(connSeq++);
|
|
53
|
+
const clientConn=createConnection(id,targetOrigin);
|
|
54
|
+
connections.set(id,clientConn);
|
|
55
|
+
if(cb)clientConn.on('open',cb);
|
|
56
|
+
if(listeners.has(port)&&(!targetOrigin||targetOrigin===getOrigin())){
|
|
57
|
+
const srvId=id+'-srv';
|
|
58
|
+
const serverConn=createConnection(srvId,getOrigin(),true);
|
|
59
|
+
connections.set(srvId,serverConn);
|
|
60
|
+
clientConn._peerId=srvId; serverConn._peerId=id;
|
|
61
|
+
const l=listeners.get(port);
|
|
62
|
+
queueMicrotask(()=>{l.onConnection?.(serverConn);serverConn._onOpen();clientConn._onOpen();});
|
|
63
|
+
}else{
|
|
64
|
+
post({type:'connect',id,port,from:getOrigin(),to:targetOrigin});
|
|
65
|
+
}
|
|
66
|
+
return clientConn;
|
|
67
|
+
},
|
|
68
|
+
discover(filter){
|
|
69
|
+
if(bus)bus.postMessage({type:'discover',origin:getOrigin()});
|
|
70
|
+
return new Promise(r=>setTimeout(()=>{const out=[...serviceRegistry.values()];r(filter?.service?out.filter(s=>s.service===filter.service):out);},200));
|
|
71
|
+
},
|
|
72
|
+
getListeners(){return[...listeners.keys()];},
|
|
73
|
+
getServices(){return[...serviceRegistry.values()];},
|
|
74
|
+
origin:getOrigin(),
|
|
75
|
+
_bus:bus,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function makeBusHttp(busnet){
|
|
80
|
+
const respond=(conn,status,headers,body)=>{const res=`HTTP/1.1 ${status} ${status===200?'OK':'ERR'}\r\n`+Object.entries(headers||{}).map(([k,v])=>`${k}: ${v}`).join('\r\n')+`\r\nContent-Length: ${body.length}\r\n\r\n${body}`;conn.write(res);};
|
|
81
|
+
return{
|
|
82
|
+
createServer(handler){
|
|
83
|
+
return{
|
|
84
|
+
listen(port,host,cb){if(typeof host==='function'){cb=host;host=null;}busnet.listen(port,'http',conn=>{conn.on('data',raw=>{const[reqLine,...rest]=String(raw).split('\r\n');const[method,url]=reqLine.split(' ');const bodyIdx=rest.indexOf('');const headers={};for(const line of rest.slice(0,bodyIdx)){const [k,v]=line.split(':');if(k)headers[k.trim().toLowerCase()]=v?.trim();}const req={method,url,headers,body:rest.slice(bodyIdx+1).join('\r\n')};const res={statusCode:200,_headers:{},setHeader(k,v){this._headers[k]=v;},end(body){respond(conn,this.statusCode,this._headers,body||'');conn.end();}};handler(req,res);});});cb?.();return this;},
|
|
85
|
+
close(cb){cb?.();},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
request(opts,cb){const port=opts.port,targetOrigin=opts.origin||null;const conn=busnet.connect(port,targetOrigin,()=>{const path=opts.path||'/';const method=opts.method||'GET';const hdrs=Object.entries(opts.headers||{}).map(([k,v])=>`${k}: ${v}`).join('\r\n');conn.write(`${method} ${path} HTTP/1.1\r\n${hdrs}\r\n\r\n`);});const handlers={response:[]};conn.on('data',raw=>{const[status,...rest]=String(raw).split('\r\n');const s=status.match(/HTTP\/\S+\s+(\d+)/);const bodyIdx=rest.indexOf('');const headers={};for(const line of rest.slice(0,bodyIdx)){const [k,v]=line.split(':');if(k)headers[k.trim().toLowerCase()]=v?.trim();}const body=rest.slice(bodyIdx+1).join('\r\n');const res={statusCode:s?+s[1]:200,headers,body,on(ev,fn){if(ev==='data')queueMicrotask(()=>fn(body));if(ev==='end')queueMicrotask(()=>fn());return res;}};for(const h of handlers.response)h(res);cb?.(res);});const req={on(ev,fn){if(ev==='response')handlers.response.push(fn);return req;},end(){},write(){}};return req;},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const ALG_MAP={
|
|
2
|
+
'aes-128-gcm':{name:'AES-GCM',length:128},'aes-192-gcm':{name:'AES-GCM',length:192},'aes-256-gcm':{name:'AES-GCM',length:256},
|
|
3
|
+
'aes-128-cbc':{name:'AES-CBC',length:128},'aes-192-cbc':{name:'AES-CBC',length:192},'aes-256-cbc':{name:'AES-CBC',length:256},
|
|
4
|
+
'aes-128-ctr':{name:'AES-CTR',length:128},'aes-256-ctr':{name:'AES-CTR',length:256},
|
|
5
|
+
};
|
|
6
|
+
const toBytes=d=>typeof d==='string'?new TextEncoder().encode(d):d instanceof Uint8Array?d:new Uint8Array(d);
|
|
7
|
+
const concat=list=>{const t=list.reduce((s,c)=>s+c.length,0);const out=new Uint8Array(t);let o=0;for(const c of list){out.set(c,o);o+=c.length;}return out;};
|
|
8
|
+
|
|
9
|
+
async function importKey(alg,keyBytes,usages){
|
|
10
|
+
return crypto.subtle.importKey('raw',keyBytes,{name:alg.name,length:alg.length},false,usages);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function makeCipher(alg,keyBytes,iv,decrypt=false){
|
|
14
|
+
const spec=ALG_MAP[alg];
|
|
15
|
+
if(!spec)throw new Error(`cipher algorithm not supported: ${alg}`);
|
|
16
|
+
const chunks=[];let authTag=null;let aad=null;let final=null;
|
|
17
|
+
const usage=decrypt?['decrypt']:['encrypt'];
|
|
18
|
+
return {
|
|
19
|
+
update(data,inputEnc,outputEnc){const bytes=inputEnc?Buffer.from(data,inputEnc):toBytes(data);chunks.push(bytes);return Buffer.alloc(0);},
|
|
20
|
+
async final(enc){const input=concat(chunks);const key=await importKey(spec,toBytes(keyBytes),usage);const params={name:spec.name,iv:toBytes(iv)};if(spec.name==='AES-GCM'&&aad)params.additionalData=aad;if(decrypt&&spec.name==='AES-GCM'&&authTag){const full=concat([input,authTag]);const out=new Uint8Array(await crypto.subtle.decrypt(params,key,full));return enc?Buffer.from(out).toString(enc):Buffer.from(out);}const op=decrypt?crypto.subtle.decrypt.bind(crypto.subtle):crypto.subtle.encrypt.bind(crypto.subtle);const out=new Uint8Array(await op(params,key,input));if(!decrypt&&spec.name==='AES-GCM'){authTag=out.slice(-16);const ct=out.slice(0,-16);final=Buffer.from(ct);return enc?final.toString(enc):final;}return enc?Buffer.from(out).toString(enc):Buffer.from(out);},
|
|
21
|
+
setAAD(d){aad=toBytes(d);return this;},
|
|
22
|
+
setAuthTag(t){authTag=toBytes(t);return this;},
|
|
23
|
+
getAuthTag(){return authTag?Buffer.from(authTag):null;},
|
|
24
|
+
setAutoPadding(){return this;},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function extendCrypto(cryptoMod,Buf){
|
|
29
|
+
globalThis.Buffer=globalThis.Buffer||Buf;
|
|
30
|
+
cryptoMod.createCipheriv=(alg,key,iv)=>makeCipher(alg.toLowerCase(),key,iv,false);
|
|
31
|
+
cryptoMod.createDecipheriv=(alg,key,iv)=>makeCipher(alg.toLowerCase(),key,iv,true);
|
|
32
|
+
cryptoMod.createCipher=()=>{throw new Error('crypto.createCipher: deprecated and unsafe — use createCipheriv');};
|
|
33
|
+
cryptoMod.createDecipher=()=>{throw new Error('crypto.createDecipher: deprecated — use createDecipheriv');};
|
|
34
|
+
cryptoMod.generateKeyPair=(type,opts,cb)=>{cryptoMod.generateKeyPairAsync?.(type,opts).then(r=>cb(null,r.publicKey,r.privateKey),cb);};
|
|
35
|
+
cryptoMod.generateKeyPairSync=()=>{throw new Error('crypto.generateKeyPairSync: synchronous keypair generation not available in browser — use generateKeyPair (async)');};
|
|
36
|
+
cryptoMod.generateKeyPairAsync=async(type,opts={})=>{const algMap={rsa:{name:'RSASSA-PKCS1-v1_5',modulusLength:opts.modulusLength||2048,publicExponent:new Uint8Array([1,0,1]),hash:'SHA-256'},ec:{name:'ECDSA',namedCurve:opts.namedCurve||'P-256'}};const alg=algMap[type];if(!alg)throw new Error(`unsupported key type: ${type}`);const kp=await crypto.subtle.generateKey(alg,true,type==='rsa'?['sign','verify']:['sign','verify']);const pub=new Uint8Array(await crypto.subtle.exportKey('spki',kp.publicKey));const priv=new Uint8Array(await crypto.subtle.exportKey('pkcs8',kp.privateKey));const pem=(b,label)=>`-----BEGIN ${label}-----\n${btoa(String.fromCharCode(...b)).match(/.{1,64}/g).join('\n')}\n-----END ${label}-----\n`;return {publicKey:pem(pub,'PUBLIC KEY'),privateKey:pem(priv,'PRIVATE KEY')};};
|
|
37
|
+
const pemToBytes=pem=>{const m=pem.match(/-----BEGIN [^-]+-----([\s\S]+?)-----END/);if(!m)throw new Error('invalid PEM');return Uint8Array.from(atob(m[1].replace(/\s/g,'')),c=>c.charCodeAt(0));};
|
|
38
|
+
const hashFromAlg=a=>{const u=a.toUpperCase();if(u.includes('SHA512')||u.includes('SHA-512'))return'SHA-512';if(u.includes('SHA384')||u.includes('SHA-384'))return'SHA-384';if(u.includes('SHA1')||u.includes('SHA-1'))return'SHA-1';return'SHA-256';};
|
|
39
|
+
const isEcBytes=bytes=>{const s=String.fromCharCode(...bytes.slice(0,Math.min(bytes.length,80)));return s.includes('\x2A\x86\x48\xCE\x3D\x02\x01');};
|
|
40
|
+
const importForSign=async(pem,hash)=>{const bytes=pemToBytes(pem);const ec=isEcBytes(bytes);const params=ec?{name:'ECDSA',namedCurve:'P-256'}:{name:'RSASSA-PKCS1-v1_5',hash};const k=await crypto.subtle.importKey('pkcs8',bytes,params,false,['sign']);return{key:k,alg:ec?{name:'ECDSA',hash}:{name:'RSASSA-PKCS1-v1_5'}};};
|
|
41
|
+
const importForVerify=async(pem,hash)=>{const bytes=pemToBytes(pem);const ec=isEcBytes(bytes);const params=ec?{name:'ECDSA',namedCurve:'P-256'}:{name:'RSASSA-PKCS1-v1_5',hash};const k=await crypto.subtle.importKey('spki',bytes,params,false,['verify']);return{key:k,alg:ec?{name:'ECDSA',hash}:{name:'RSASSA-PKCS1-v1_5'}};};
|
|
42
|
+
cryptoMod.signAsync=async(alg,data,keyPem)=>{const pem=typeof keyPem==='string'?keyPem:keyPem.key;const {key,alg:a}=await importForSign(pem,hashFromAlg(alg));const sig=await crypto.subtle.sign(a,key,toBytes(data));return Buf.from(new Uint8Array(sig));};
|
|
43
|
+
cryptoMod.verifyAsync=async(alg,data,keyPem,sig)=>{const pem=typeof keyPem==='string'?keyPem:keyPem.key;const {key,alg:a}=await importForVerify(pem,hashFromAlg(alg));return crypto.subtle.verify(a,key,toBytes(sig),toBytes(data));};
|
|
44
|
+
cryptoMod.hkdf=(digest,ikm,salt,info,keylen,cb)=>{cryptoMod.hkdfAsync(digest,ikm,salt,info,keylen).then(r=>cb(null,r),cb);};
|
|
45
|
+
cryptoMod.hkdfSync=()=>{throw new Error('crypto.hkdfSync: use async hkdf in browser (webcrypto is async-only)');};
|
|
46
|
+
cryptoMod.hkdfAsync=async(digest,ikm,salt,info,keylen)=>{const hash=hashFromAlg(digest);const k=await crypto.subtle.importKey('raw',toBytes(ikm),'HKDF',false,['deriveBits']);const bits=await crypto.subtle.deriveBits({name:'HKDF',hash,salt:toBytes(salt),info:toBytes(info)},k,keylen*8);return Buf.from(new Uint8Array(bits));};
|
|
47
|
+
cryptoMod.createECDH=curve=>{const nc=curve==='prime256v1'?'P-256':curve==='secp384r1'?'P-384':curve==='secp521r1'?'P-521':curve;let kp=null;return{generateKeys:async()=>{kp=await crypto.subtle.generateKey({name:'ECDH',namedCurve:nc},true,['deriveBits']);const pub=await crypto.subtle.exportKey('raw',kp.publicKey);return Buf.from(new Uint8Array(pub));},getPublicKey:async()=>{if(!kp)throw new Error('generateKeys first');const pub=await crypto.subtle.exportKey('raw',kp.publicKey);return Buf.from(new Uint8Array(pub));},computeSecret:async otherPub=>{if(!kp)throw new Error('generateKeys first');const other=await crypto.subtle.importKey('raw',toBytes(otherPub),{name:'ECDH',namedCurve:nc},false,[]);const bits=await crypto.subtle.deriveBits({name:'ECDH',public:other},kp.privateKey,256);return Buf.from(new Uint8Array(bits));}};};
|
|
48
|
+
cryptoMod.createDiffieHellman=()=>{throw new Error('crypto.createDiffieHellman: classic modp DH not supported — use createECDH(\'prime256v1\')');};
|
|
49
|
+
cryptoMod.sign=(alg,data,key,cb)=>{if(cb)cryptoMod.signAsync(alg,data,key).then(r=>cb(null,r),cb);else return cryptoMod.signAsync(alg,data,key);};
|
|
50
|
+
cryptoMod.verify=(alg,data,key,sig,cb)=>{if(cb)cryptoMod.verifyAsync(alg,data,key,sig).then(r=>cb(null,r),cb);else return cryptoMod.verifyAsync(alg,data,key,sig);};
|
|
51
|
+
cryptoMod.createSign=alg=>{const parts=[];return{update(d){parts.push(toBytes(d));return this;},async sign(key,enc){const data=concat(parts);const sig=await cryptoMod.signAsync(alg,data,key);return enc?sig.toString(enc):sig;}};};
|
|
52
|
+
cryptoMod.createVerify=alg=>{const parts=[];return{update(d){parts.push(toBytes(d));return this;},async verify(key,sig,enc){const data=concat(parts);const sigBytes=typeof sig==='string'?Buf.from(sig,enc):sig;return cryptoMod.verifyAsync(alg,data,key,sigBytes);}};};
|
|
53
|
+
cryptoMod.getCiphers=()=>Object.keys(ALG_MAP);
|
|
54
|
+
cryptoMod.getHashes=()=>['sha1','sha256','sha512','md5'];
|
|
55
|
+
cryptoMod.getCurves=()=>['P-256','P-384','P-521'];
|
|
56
|
+
cryptoMod.timingSafeEqual=(a,b)=>{if(a.length!==b.length)return false;let r=0;for(let i=0;i<a.length;i++)r|=a[i]^b[i];return r===0;};
|
|
57
|
+
cryptoMod.diffieHellman=()=>{throw new Error('crypto.diffieHellman: use webcrypto ECDH')};
|
|
58
|
+
cryptoMod.scrypt=(pw,salt,len,opts,cb)=>{if(typeof opts==='function'){cb=opts;opts={};}queueMicrotask(()=>{try{const k=cryptoMod.pbkdf2Sync(pw,salt,16384,len,'sha256');cb(null,Buf.from(k));}catch(e){cb(e);}});};
|
|
59
|
+
cryptoMod.scryptSync=(pw,salt,len)=>Buf.from(cryptoMod.pbkdf2Sync(pw,salt,16384,len,'sha256'));
|
|
60
|
+
return cryptoMod;
|
|
61
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const CHANNEL='plugkit-cluster';
|
|
2
|
+
const isWorkerUrl=()=>typeof location!=='undefined'&&location.hash==='#cluster-worker';
|
|
3
|
+
|
|
4
|
+
export function makeCluster(){
|
|
5
|
+
if(typeof BroadcastChannel==='undefined')return null;
|
|
6
|
+
const bc=new BroadcastChannel(CHANNEL);
|
|
7
|
+
const workers=new Map();let nextId=1;
|
|
8
|
+
const isMaster=!isWorkerUrl();
|
|
9
|
+
const handlers={message:[],exit:[],online:[],listening:[],disconnect:[]};
|
|
10
|
+
const onBC=e=>{const{from,to,type,data,id}=e.data||{};if(isMaster){if(to==='master'){const w=workers.get(from);if(!w)return;if(type==='ready'){w.state='online';for(const f of handlers.online)f(w);for(const f of w._h.online||[])f();}if(type==='message'){for(const f of handlers.message)f(w,data);for(const f of w._h.message||[])f(data);}if(type==='exit'){w.state='dead';for(const f of handlers.exit)f(w,data?.code||0);for(const f of w._h.exit||[])f(data?.code||0);workers.delete(from);}}}else{if(to==='worker'||to==='all'){if(type==='message')for(const f of workerHandlers.message)f(data);}}};
|
|
11
|
+
bc.addEventListener('message',onBC);
|
|
12
|
+
const workerHandlers={message:[]};
|
|
13
|
+
const cluster={
|
|
14
|
+
isMaster,
|
|
15
|
+
isPrimary:isMaster,
|
|
16
|
+
isWorker:!isMaster,
|
|
17
|
+
workers:{},
|
|
18
|
+
SCHED_RR:1,SCHED_NONE:0,
|
|
19
|
+
schedulingPolicy:1,
|
|
20
|
+
settings:{},
|
|
21
|
+
setupMaster:opts=>Object.assign(cluster.settings,opts||{}),
|
|
22
|
+
setupPrimary:opts=>Object.assign(cluster.settings,opts||{}),
|
|
23
|
+
fork(env){if(!isMaster)throw new Error('cluster.fork: only master can fork');const id=nextId++;const w={id,state:'starting',_h:{},process:{pid:id,send:(msg)=>{bc.postMessage({from:'master',to:id,type:'message',data:msg});return true;},kill:()=>{bc.postMessage({from:'master',to:id,type:'kill'});}},send(msg){return this.process.send(msg);},on(ev,fn){(this._h[ev]=this._h[ev]||[]).push(fn);return this;},disconnect(){bc.postMessage({from:'master',to:id,type:'disconnect'});},kill(){this.process.kill();}};workers.set(id,w);cluster.workers[id]=w;const url=(cluster.settings.exec||location.href.split('#')[0])+'#cluster-worker';try{window.open(url,'_blank','noopener');}catch{}return w;},
|
|
24
|
+
disconnect(cb){for(const w of workers.values())w.disconnect();cb&&cb();},
|
|
25
|
+
on(ev,fn){(handlers[ev]=handlers[ev]||[]).push(fn);return cluster;},
|
|
26
|
+
worker:isMaster?null:{id:0,process:{send:msg=>{bc.postMessage({from:0,to:'master',type:'message',data:msg});return true;},on(ev,fn){if(ev==='message')workerHandlers.message.push(fn);}}},
|
|
27
|
+
_bc:bc,
|
|
28
|
+
_workerSend:msg=>bc.postMessage({from:0,to:'master',type:'message',data:msg}),
|
|
29
|
+
_workerReady:()=>bc.postMessage({from:0,to:'master',type:'ready'}),
|
|
30
|
+
};
|
|
31
|
+
if(!isMaster)queueMicrotask(()=>cluster._workerReady());
|
|
32
|
+
return cluster;
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function makeCoreutils(ctx,procFs){
|
|
2
|
+
const term=ctx.term;
|
|
3
|
+
const out=s=>term.write(s+'\r\n');
|
|
4
|
+
return{
|
|
5
|
+
uname:args=>{const flags=args.join('');const info={kernel:'Linux',node:'thebird',release:'6.0.0-browser',version:'#1 SMP',machine:'x86_64',os:'thebird'};if(flags.includes('a'))out(`${info.kernel} ${info.node} ${info.release} ${info.version} ${info.machine} ${info.os}`);else if(flags.includes('r'))out(info.release);else if(flags.includes('n'))out(info.node);else if(flags.includes('m'))out(info.machine);else if(flags.includes('s'))out(info.kernel);else if(flags.includes('o'))out(info.os);else out(info.kernel);return 0;},
|
|
6
|
+
whoami:()=>{out(ctx.env.USER||'root');return 0;},
|
|
7
|
+
hostname:args=>{if(args[0]){ctx.env.HOSTNAME=args[0];return 0;}out(ctx.env.HOSTNAME||'thebird');return 0;},
|
|
8
|
+
id:args=>{out('uid=0(root) gid=0(root) groups=0(root)');return 0;},
|
|
9
|
+
df:args=>{const used=performance.memory?.usedJSHeapSize||5e8;const total=performance.memory?.jsHeapSizeLimit||2e9;out('Filesystem 1K-blocks Used Available Use% Mounted on');out(`idbfs ${(total/1024)|0} ${(used/1024)|0} ${((total-used)/1024)|0} ${((used/total)*100)|0}% /`);return 0;},
|
|
10
|
+
free:args=>{const m=performance.memory||{totalJSHeapSize:1e9,usedJSHeapSize:5e8,jsHeapSizeLimit:2e9};const kb=n=>(n/1024)|0;out(' total used free shared buff/cache available');out(`Mem: ${kb(m.jsHeapSizeLimit).toString().padStart(11)} ${kb(m.usedJSHeapSize).toString().padStart(11)} ${kb(m.jsHeapSizeLimit-m.usedJSHeapSize).toString().padStart(11)} 0 0 ${kb(m.jsHeapSizeLimit-m.usedJSHeapSize).toString().padStart(11)}`);out('Swap: 0 0 0');return 0;},
|
|
11
|
+
uptime:args=>{const sec=(performance.now()/1000)|0;const h=(sec/3600)|0,m=((sec%3600)/60)|0;out(`${new Date().toTimeString().slice(0,8)} up ${h}:${String(m).padStart(2,'0')}, 1 user, load average: 0.00, 0.00, 0.00`);return 0;},
|
|
12
|
+
ps:args=>{out(' PID TTY TIME CMD');out(' 1 pts/0 00:00:00 sh');out(' 2 pts/0 00:00:00 ps');return 0;},
|
|
13
|
+
nproc:args=>{out(String(navigator?.hardwareConcurrency||1));return 0;},
|
|
14
|
+
arch:()=>{out('x86_64');return 0;},
|
|
15
|
+
yes:args=>{const s=args.length?args.join(' '):'y';for(let i=0;i<1000;i++)out(s);return 0;},
|
|
16
|
+
true:()=>0,
|
|
17
|
+
false:()=>1,
|
|
18
|
+
sleep:args=>new Promise(r=>setTimeout(()=>r(0),(parseFloat(args[0])||0)*1000)),
|
|
19
|
+
seq:args=>{const[first,...rest]=args.map(Number);const last=rest.length?rest[rest.length-1]:first;const step=rest.length===2?rest[0]:1;const start=rest.length?first:1;for(let i=start;step>0?i<=last:i>=last;i+=step)out(String(i));return 0;},
|
|
20
|
+
tac:args=>{if(args[0]){const c=ctx.readFile?.(args[0])||'';out(c.split('\n').reverse().join('\n'));}return 0;},
|
|
21
|
+
rev:args=>{if(args[0]){const c=ctx.readFile?.(args[0])||'';for(const line of c.split('\n'))out([...line].reverse().join(''));}return 0;},
|
|
22
|
+
nl:args=>{if(args[0]){const c=ctx.readFile?.(args[0])||'';c.split('\n').forEach((line,i)=>out(` ${i+1}\t${line}`));}return 0;},
|
|
23
|
+
fold:args=>{const w=args.includes('-w')?parseInt(args[args.indexOf('-w')+1])||80:80;const text=args.filter(a=>!a.startsWith('-'))[0];if(text){const c=ctx.readFile?.(text)||'';for(const line of c.split('\n'))for(let i=0;i<line.length;i+=w)out(line.slice(i,i+w));}return 0;},
|
|
24
|
+
tr:args=>{return 0;},
|
|
25
|
+
od:args=>{if(args[0]){const c=ctx.readFile?.(args[0])||'';const b=typeof c==='string'?new TextEncoder().encode(c):c;for(let i=0;i<b.length;i+=16){const hex=[...b.slice(i,i+16)].map(x=>x.toString(16).padStart(2,'0')).join(' ');out(i.toString(8).padStart(7,'0')+' '+hex);}}return 0;},
|
|
26
|
+
xxd:args=>{if(args[0]){const c=ctx.readFile?.(args[0])||'';const b=typeof c==='string'?new TextEncoder().encode(c):c;for(let i=0;i<b.length;i+=16){const row=b.slice(i,i+16);const hex=[...row].map(x=>x.toString(16).padStart(2,'0')).join(' ').padEnd(47);const asc=[...row].map(x=>x>=0x20&&x<0x7f?String.fromCharCode(x):'.').join('');out(i.toString(16).padStart(8,'0')+': '+hex+' '+asc);}}return 0;},
|
|
27
|
+
dirname:args=>{out((args[0]||'').replace(/\/[^/]*$/,'')||'.');return 0;},
|
|
28
|
+
basename:args=>{const p=args[0]||'';const ext=args[1]||'';let b=p.split('/').pop()||'';if(ext&&b.endsWith(ext))b=b.slice(0,-ext.length);out(b);return 0;},
|
|
29
|
+
pwd:()=>{out(ctx.cwd||'/');return 0;},
|
|
30
|
+
groups:()=>{out('root');return 0;},
|
|
31
|
+
logname:()=>{out('root');return 0;},
|
|
32
|
+
tty:()=>{out('/dev/pts/0');return 0;},
|
|
33
|
+
stty:()=>0,
|
|
34
|
+
locale:()=>{out('LANG=C\nLC_ALL=\n');return 0;},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const K256 = new Uint32Array([0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2]);
|
|
2
|
+
const rotr32 = (x, n) => (x >>> n) | (x << (32 - n));
|
|
3
|
+
|
|
4
|
+
function padMsg(msg, blockSize, lenBytes, little = false) {
|
|
5
|
+
const bitLen = msg.length * 8;
|
|
6
|
+
const padLen = ((msg.length + lenBytes + 1 + blockSize - 1) & ~(blockSize - 1));
|
|
7
|
+
const p = new Uint8Array(padLen); p.set(msg); p[msg.length] = 0x80;
|
|
8
|
+
const v = new DataView(p.buffer);
|
|
9
|
+
if (little) { v.setUint32(padLen - 8, bitLen >>> 0, true); v.setUint32(padLen - 4, Math.floor(bitLen / 0x100000000), true); }
|
|
10
|
+
else { v.setUint32(padLen - 4, bitLen >>> 0); v.setUint32(padLen - 8, Math.floor(bitLen / 0x100000000)); }
|
|
11
|
+
return p;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function sha1(msg) {
|
|
15
|
+
const p = padMsg(msg, 64, 8); const v = new DataView(p.buffer);
|
|
16
|
+
let h0 = 0x67452301, h1 = 0xefcdab89, h2 = 0x98badcfe, h3 = 0x10325476, h4 = 0xc3d2e1f0;
|
|
17
|
+
const W = new Uint32Array(80);
|
|
18
|
+
for (let i = 0; i < p.length; i += 64) {
|
|
19
|
+
for (let j = 0; j < 16; j++) W[j] = v.getUint32(i + j * 4);
|
|
20
|
+
for (let j = 16; j < 80; j++) W[j] = rotr32(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16], 31);
|
|
21
|
+
let a = h0, b = h1, c = h2, d = h3, e = h4;
|
|
22
|
+
for (let j = 0; j < 80; j++) {
|
|
23
|
+
const f = j < 20 ? (b & c) | (~b & d) : j < 40 ? b ^ c ^ d : j < 60 ? (b & c) | (b & d) | (c & d) : b ^ c ^ d;
|
|
24
|
+
const k = j < 20 ? 0x5a827999 : j < 40 ? 0x6ed9eba1 : j < 60 ? 0x8f1bbcdc : 0xca62c1d6;
|
|
25
|
+
const t = (rotr32(a, 27) + f + e + k + W[j]) >>> 0;
|
|
26
|
+
e = d; d = c; c = rotr32(b, 2); b = a; a = t;
|
|
27
|
+
}
|
|
28
|
+
h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0; h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0; h4 = (h4 + e) >>> 0;
|
|
29
|
+
}
|
|
30
|
+
const out = new Uint8Array(20); const ov = new DataView(out.buffer);
|
|
31
|
+
ov.setUint32(0, h0); ov.setUint32(4, h1); ov.setUint32(8, h2); ov.setUint32(12, h3); ov.setUint32(16, h4);
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function sha256(msg) {
|
|
36
|
+
const p = padMsg(msg, 64, 8); const v = new DataView(p.buffer);
|
|
37
|
+
const H = new Uint32Array([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]);
|
|
38
|
+
const W = new Uint32Array(64);
|
|
39
|
+
for (let i = 0; i < p.length; i += 64) {
|
|
40
|
+
for (let j = 0; j < 16; j++) W[j] = v.getUint32(i + j * 4);
|
|
41
|
+
for (let j = 16; j < 64; j++) { const s0 = rotr32(W[j-15], 7) ^ rotr32(W[j-15], 18) ^ (W[j-15] >>> 3); const s1 = rotr32(W[j-2], 17) ^ rotr32(W[j-2], 19) ^ (W[j-2] >>> 10); W[j] = (W[j-16] + s0 + W[j-7] + s1) >>> 0; }
|
|
42
|
+
let [a, b, c, d, e, f, g, h] = H;
|
|
43
|
+
for (let j = 0; j < 64; j++) { const S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25); const ch = (e & f) ^ (~e & g); const t1 = (h + S1 + ch + K256[j] + W[j]) >>> 0; const S0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22); const mj = (a & b) ^ (a & c) ^ (b & c); const t2 = (S0 + mj) >>> 0; h = g; g = f; f = e; e = (d + t1) >>> 0; d = c; c = b; b = a; a = (t1 + t2) >>> 0; }
|
|
44
|
+
H[0] += a; H[1] += b; H[2] += c; H[3] += d; H[4] += e; H[5] += f; H[6] += g; H[7] += h;
|
|
45
|
+
}
|
|
46
|
+
const out = new Uint8Array(32);
|
|
47
|
+
for (let i = 0; i < 8; i++) new DataView(out.buffer).setUint32(i * 4, H[i]);
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function md5(msg) {
|
|
52
|
+
const p = padMsg(msg, 64, 8, true); const v = new DataView(p.buffer);
|
|
53
|
+
let a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
|
|
54
|
+
const s = [7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21];
|
|
55
|
+
const K = new Uint32Array([0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391]);
|
|
56
|
+
for (let i = 0; i < p.length; i += 64) {
|
|
57
|
+
const M = new Uint32Array(16); for (let j = 0; j < 16; j++) M[j] = v.getUint32(i + j * 4, true);
|
|
58
|
+
let A = a0, B = b0, C = c0, D = d0;
|
|
59
|
+
for (let j = 0; j < 64; j++) { let F, g; if (j < 16) { F = (B & C) | (~B & D); g = j; } else if (j < 32) { F = (D & B) | (~D & C); g = (5 * j + 1) % 16; } else if (j < 48) { F = B ^ C ^ D; g = (3 * j + 5) % 16; } else { F = C ^ (B | ~D); g = (7 * j) % 16; }
|
|
60
|
+
F = (F + A + K[j] + M[g]) >>> 0; A = D; D = C; C = B; B = (B + (((F << s[j]) | (F >>> (32 - s[j]))) >>> 0)) >>> 0;
|
|
61
|
+
}
|
|
62
|
+
a0 = (a0 + A) >>> 0; b0 = (b0 + B) >>> 0; c0 = (c0 + C) >>> 0; d0 = (d0 + D) >>> 0;
|
|
63
|
+
}
|
|
64
|
+
const out = new Uint8Array(16); const ov = new DataView(out.buffer);
|
|
65
|
+
ov.setUint32(0, a0, true); ov.setUint32(4, b0, true); ov.setUint32(8, c0, true); ov.setUint32(12, d0, true);
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const K512 = [0x428a2f98d728ae22n,0x7137449123ef65cdn,0xb5c0fbcfec4d3b2fn,0xe9b5dba58189dbbcn,0x3956c25bf348b538n,0x59f111f1b605d019n,0x923f82a4af194f9bn,0xab1c5ed5da6d8118n,0xd807aa98a3030242n,0x12835b0145706fben,0x243185be4ee4b28cn,0x550c7dc3d5ffb4e2n,0x72be5d74f27b896fn,0x80deb1fe3b1696b1n,0x9bdc06a725c71235n,0xc19bf174cf692694n,0xe49b69c19ef14ad2n,0xefbe4786384f25e3n,0x0fc19dc68b8cd5b5n,0x240ca1cc77ac9c65n,0x2de92c6f592b0275n,0x4a7484aa6ea6e483n,0x5cb0a9dcbd41fbd4n,0x76f988da831153b5n,0x983e5152ee66dfabn,0xa831c66d2db43210n,0xb00327c898fb213fn,0xbf597fc7beef0ee4n,0xc6e00bf33da88fc2n,0xd5a79147930aa725n,0x06ca6351e003826fn,0x142929670a0e6e70n,0x27b70a8546d22ffcn,0x2e1b21385c26c926n,0x4d2c6dfc5ac42aedn,0x53380d139d95b3dfn,0x650a73548baf63den,0x766a0abb3c77b2a8n,0x81c2c92e47edaee6n,0x92722c851482353bn,0xa2bfe8a14cf10364n,0xa81a664bbc423001n,0xc24b8b70d0f89791n,0xc76c51a30654be30n,0xd192e819d6ef5218n,0xd69906245565a910n,0xf40e35855771202an,0x106aa07032bbd1b8n,0x19a4c116b8d2d0c8n,0x1e376c085141ab53n,0x2748774cdf8eeb99n,0x34b0bcb5e19b48a8n,0x391c0cb3c5c95a63n,0x4ed8aa4ae3418acbn,0x5b9cca4f7763e373n,0x682e6ff3d6b2b8a3n,0x748f82ee5defb2fcn,0x78a5636f43172f60n,0x84c87814a1f0ab72n,0x8cc702081a6439ecn,0x90befffa23631e28n,0xa4506cebde82bde9n,0xbef9a3f7b2c67915n,0xc67178f2e372532bn,0xca273eceea26619cn,0xd186b8c721c0c207n,0xeada7dd6cde0eb1en,0xf57d4f7fee6ed178n,0x06f067aa72176fban,0x0a637dc5a2c898a6n,0x113f9804bef90daen,0x1b710b35131c471bn,0x28db77f523047d84n,0x32caab7b40c72493n,0x3c9ebe0a15c9bebcn,0x431d67c49c100d4cn,0x4cc5d4becb3e42b6n,0x597f299cfc657e2an,0x5fcb6fab3ad6faecn,0x6c44198c4a475817n];
|
|
70
|
+
const MASK64 = 0xffffffffffffffffn;
|
|
71
|
+
const rotr64 = (x, n) => (((x >> BigInt(n)) | (x << BigInt(64 - n))) & MASK64);
|
|
72
|
+
|
|
73
|
+
export function sha512(msg) {
|
|
74
|
+
const p = padMsg(msg, 128, 16); const v = new DataView(p.buffer);
|
|
75
|
+
const H = [0x6a09e667f3bcc908n, 0xbb67ae8584caa73bn, 0x3c6ef372fe94f82bn, 0xa54ff53a5f1d36f1n, 0x510e527fade682d1n, 0x9b05688c2b3e6c1fn, 0x1f83d9abfb41bd6bn, 0x5be0cd19137e2179n];
|
|
76
|
+
const W = new Array(80);
|
|
77
|
+
for (let i = 0; i < p.length; i += 128) {
|
|
78
|
+
for (let j = 0; j < 16; j++) W[j] = (BigInt(v.getUint32(i + j * 8)) << 32n) | BigInt(v.getUint32(i + j * 8 + 4));
|
|
79
|
+
for (let j = 16; j < 80; j++) { const s0 = rotr64(W[j-15], 1) ^ rotr64(W[j-15], 8) ^ (W[j-15] >> 7n); const s1 = rotr64(W[j-2], 19) ^ rotr64(W[j-2], 61) ^ (W[j-2] >> 6n); W[j] = (W[j-16] + s0 + W[j-7] + s1) & MASK64; }
|
|
80
|
+
let [a, b, c, d, e, f, g, h] = H;
|
|
81
|
+
for (let j = 0; j < 80; j++) { const S1 = rotr64(e, 14) ^ rotr64(e, 18) ^ rotr64(e, 41); const ch = (e & f) ^ (~e & MASK64 & g); const t1 = (h + S1 + ch + K512[j] + W[j]) & MASK64; const S0 = rotr64(a, 28) ^ rotr64(a, 34) ^ rotr64(a, 39); const mj = (a & b) ^ (a & c) ^ (b & c); const t2 = (S0 + mj) & MASK64; h = g; g = f; f = e; e = (d + t1) & MASK64; d = c; c = b; b = a; a = (t1 + t2) & MASK64; }
|
|
82
|
+
H[0] = (H[0] + a) & MASK64; H[1] = (H[1] + b) & MASK64; H[2] = (H[2] + c) & MASK64; H[3] = (H[3] + d) & MASK64; H[4] = (H[4] + e) & MASK64; H[5] = (H[5] + f) & MASK64; H[6] = (H[6] + g) & MASK64; H[7] = (H[7] + h) & MASK64;
|
|
83
|
+
}
|
|
84
|
+
const out = new Uint8Array(64); const ov = new DataView(out.buffer);
|
|
85
|
+
for (let i = 0; i < 8; i++) { ov.setUint32(i * 8, Number(H[i] >> 32n)); ov.setUint32(i * 8 + 4, Number(H[i] & 0xffffffffn)); }
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function concat(a, b) { const o = new Uint8Array(a.length + b.length); o.set(a); o.set(b, a.length); return o; }
|
|
90
|
+
function toBytes(d) { return typeof d === 'string' ? new TextEncoder().encode(d) : d; }
|
|
91
|
+
|
|
92
|
+
const HASH_IMPLS = { sha1: { fn: sha1, size: 64, len: 20 }, sha256: { fn: sha256, size: 64, len: 32 }, sha512: { fn: sha512, size: 128, len: 64 }, md5: { fn: md5, size: 64, len: 16 } };
|
|
93
|
+
|
|
94
|
+
export function createHash(alg) {
|
|
95
|
+
const a = alg.toLowerCase();
|
|
96
|
+
const spec = HASH_IMPLS[a];
|
|
97
|
+
if (!spec) throw new Error('hash algorithm not supported: ' + a);
|
|
98
|
+
const chunks = [];
|
|
99
|
+
return {
|
|
100
|
+
update(data) { chunks.push(toBytes(data)); return this; },
|
|
101
|
+
digest(enc) { const total = chunks.reduce((s, c) => s + c.length, 0); const buf = new Uint8Array(total); let off = 0; for (const c of chunks) { buf.set(c, off); off += c.length; } const out = spec.fn(buf); if (enc === 'hex') return [...out].map(b => b.toString(16).padStart(2, '0')).join(''); if (enc === 'base64') return btoa(String.fromCharCode(...out)); return out; },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function createHmac(alg, key) {
|
|
106
|
+
const spec = HASH_IMPLS[alg.toLowerCase()];
|
|
107
|
+
if (!spec) throw new Error('hmac algorithm not supported: ' + alg);
|
|
108
|
+
let k = toBytes(key);
|
|
109
|
+
if (k.length > spec.size) k = spec.fn(k);
|
|
110
|
+
if (k.length < spec.size) { const pad = new Uint8Array(spec.size); pad.set(k); k = pad; }
|
|
111
|
+
const ipad = new Uint8Array(spec.size), opad = new Uint8Array(spec.size);
|
|
112
|
+
for (let i = 0; i < spec.size; i++) { ipad[i] = k[i] ^ 0x36; opad[i] = k[i] ^ 0x5c; }
|
|
113
|
+
const chunks = [];
|
|
114
|
+
return {
|
|
115
|
+
update(data) { chunks.push(toBytes(data)); return this; },
|
|
116
|
+
digest(enc) { const total = chunks.reduce((s, c) => s + c.length, 0); const buf = new Uint8Array(total); let off = 0; for (const c of chunks) { buf.set(c, off); off += c.length; } const inner = spec.fn(concat(ipad, buf)); const out = spec.fn(concat(opad, inner)); if (enc === 'hex') return [...out].map(b => b.toString(16).padStart(2, '0')).join(''); if (enc === 'base64') return btoa(String.fromCharCode(...out)); return out; },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function pbkdf2Sync(password, salt, iterations, keylen, digest) {
|
|
121
|
+
const spec = HASH_IMPLS[digest.toLowerCase()];
|
|
122
|
+
if (!spec) throw new Error('pbkdf2 digest not supported: ' + digest);
|
|
123
|
+
const hLen = spec.len;
|
|
124
|
+
const saltB = toBytes(salt);
|
|
125
|
+
const blocks = Math.ceil(keylen / hLen);
|
|
126
|
+
const out = new Uint8Array(blocks * hLen);
|
|
127
|
+
for (let i = 1; i <= blocks; i++) {
|
|
128
|
+
const block = new Uint8Array(saltB.length + 4); block.set(saltB); const dv = new DataView(block.buffer); dv.setUint32(saltB.length, i);
|
|
129
|
+
let U = createHmac(digest, password).update(block).digest();
|
|
130
|
+
let T = new Uint8Array(U);
|
|
131
|
+
for (let j = 1; j < iterations; j++) { U = createHmac(digest, password).update(U).digest(); for (let k = 0; k < hLen; k++) T[k] ^= U[k]; }
|
|
132
|
+
out.set(T, (i - 1) * hLen);
|
|
133
|
+
}
|
|
134
|
+
return out.slice(0, keylen);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function randomBytes(n) { const out = new Uint8Array(n); (globalThis.crypto || { getRandomValues: a => { for (let i = 0; i < a.length; i++) a[i] = Math.random() * 256 | 0; return a; } }).getRandomValues(out); return out; }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const DOH='https://cloudflare-dns.com/dns-query';
|
|
2
|
+
const DOH_FALLBACK='https://dns.google/resolve';
|
|
3
|
+
|
|
4
|
+
async function query(name,type='A'){
|
|
5
|
+
const TYPES={A:1,AAAA:28,MX:15,TXT:16,NS:2,CNAME:5,SOA:6,PTR:12,SRV:33};
|
|
6
|
+
const t=TYPES[type]||1;
|
|
7
|
+
try{
|
|
8
|
+
const r=await fetch(`${DOH}?name=${encodeURIComponent(name)}&type=${t}`,{headers:{accept:'application/dns-json'}});
|
|
9
|
+
const j=await r.json();
|
|
10
|
+
return j.Answer||[];
|
|
11
|
+
}catch{
|
|
12
|
+
const r=await fetch(`${DOH_FALLBACK}?name=${encodeURIComponent(name)}&type=${t}`);
|
|
13
|
+
const j=await r.json();
|
|
14
|
+
return j.Answer||[];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function makeDns(){
|
|
19
|
+
const extract=(answers,want)=>answers.filter(a=>a.type===want).map(a=>a.data.replace(/^"|"$/g,''));
|
|
20
|
+
return{
|
|
21
|
+
promises:{
|
|
22
|
+
async resolve(name,type='A'){const a=await query(name,type);return extract(a,{A:1,AAAA:28,CNAME:5,NS:2,TXT:16,MX:15,PTR:12,SRV:33,SOA:6}[type]);},
|
|
23
|
+
async resolve4(name){return this.resolve(name,'A');},
|
|
24
|
+
async resolve6(name){return this.resolve(name,'AAAA');},
|
|
25
|
+
async resolveMx(name){const a=await query(name,'MX');return extract(a,15).map(s=>{const[priority,exchange]=s.split(' ');return{priority:+priority,exchange};});},
|
|
26
|
+
async resolveTxt(name){const a=await query(name,'TXT');return extract(a,16).map(s=>[s]);},
|
|
27
|
+
async resolveCname(name){return this.resolve(name,'CNAME');},
|
|
28
|
+
async resolveNs(name){return this.resolve(name,'NS');},
|
|
29
|
+
async resolveSrv(name){const a=await query(name,'SRV');return extract(a,33).map(s=>{const[priority,weight,port,name]=s.split(' ');return{priority:+priority,weight:+weight,port:+port,name};});},
|
|
30
|
+
async reverse(ip){const rev=ip.includes(':')?ip:ip.split('.').reverse().join('.')+'.in-addr.arpa';const a=await query(rev,'PTR');return extract(a,12);},
|
|
31
|
+
async lookup(hostname,opts={}){if(hostname==='localhost')return{address:'127.0.0.1',family:4};const t=opts.family===6?'AAAA':'A';const results=await query(hostname,t);if(!results.length)throw Object.assign(new Error('getaddrinfo ENOTFOUND '+hostname),{code:'ENOTFOUND',hostname});return{address:results[0].data,family:opts.family===6?6:4};},
|
|
32
|
+
async lookupService(ip,port){return{hostname:ip,service:String(port)};},
|
|
33
|
+
getServers(){return['1.1.1.1','8.8.8.8'];},
|
|
34
|
+
setServers(){},
|
|
35
|
+
},
|
|
36
|
+
get resolve(){const p=this.promises;return(name,type,cb)=>{if(typeof type==='function'){cb=type;type='A';}p.resolve(name,type).then(r=>cb(null,r),cb);};},
|
|
37
|
+
lookup:(h,o,cb)=>{if(typeof o==='function'){cb=o;o={};}query(h,o.family===6?'AAAA':'A').then(a=>a.length?cb(null,a[0].data,o.family===6?6:4):cb(Object.assign(new Error('ENOTFOUND'),{code:'ENOTFOUND'})),cb);},
|
|
38
|
+
ADDRCONFIG:0x20,V4MAPPED:0x8,ALL:0x10,
|
|
39
|
+
NODATA:'ENODATA',FORMERR:'EFORMERR',SERVFAIL:'ESERVFAIL',NOTFOUND:'ENOTFOUND',
|
|
40
|
+
};
|
|
41
|
+
}
|