pcremotecommander 0.0.0-stage → 0.7.0-beta.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 +6 -2
- package/bin/pcremotecommander.cjs +95 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# PC Remote Commander staging beta
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
On Windows with Node.js 20 or newer, run `npx --yes pcremotecommander@beta setup`. The launcher downloads a fixed, versioned runtime archive from the [public staging site](https://pc-remote-commander-staging.channelflow.workers.dev/devices), verifies its pinned SHA-256 before unpacking or running it, and caches it under your Windows user profile. The runtime includes Python; a separate Python installation is not needed.
|
|
4
|
+
|
|
5
|
+
Setup asks you to choose read-only or full computer control. It then opens a matching-code approval page on the public staging site. Review the computer name and requested permissions before approving. Keep the agent terminal running while an MCP client or the local CLI uses the computer. The agent uses your Windows account privileges.
|
|
6
|
+
|
|
7
|
+
This is a staging beta. It has not completed a production security or reliability review. Use an isolated workspace for testing.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const fs=require('node:fs');
|
|
4
|
+
const path=require('node:path');
|
|
5
|
+
const os=require('node:os');
|
|
6
|
+
const https=require('node:https');
|
|
7
|
+
const crypto=require('node:crypto');
|
|
8
|
+
const {pipeline}=require('node:stream/promises');
|
|
9
|
+
const {spawn,spawnSync}=require('node:child_process');
|
|
10
|
+
|
|
11
|
+
const version='0.7.0-beta.2';
|
|
12
|
+
const sha256='ae6dae8de2a3a3a7453053a637ae5a1942eaad6a7ff4019f987821698d6c2799';
|
|
13
|
+
const url='https://pc-remote-commander-staging.channelflow.workers.dev/downloads/windows/pcremotecommander-0.7.0-beta.2.tgz';
|
|
14
|
+
const maximumBytes=100*1024*1024;
|
|
15
|
+
|
|
16
|
+
function cacheRoot(env=process.env) {
|
|
17
|
+
if(process.platform!=='win32'||!env.LOCALAPPDATA||!path.isAbsolute(env.LOCALAPPDATA))throw Error('This staging preview requires Windows with a normal user profile.');
|
|
18
|
+
return path.join(env.LOCALAPPDATA,'PCRemoteCommander','packages',version);
|
|
19
|
+
}
|
|
20
|
+
function shaFile(file) {
|
|
21
|
+
const hash=crypto.createHash('sha256');
|
|
22
|
+
const fd=fs.openSync(file,'r');
|
|
23
|
+
try {const buffer=Buffer.allocUnsafe(1024*1024);for(;;){const length=fs.readSync(fd,buffer,0,buffer.length,null);if(!length)break;hash.update(buffer.subarray(0,length));}} finally {fs.closeSync(fd);}
|
|
24
|
+
return hash.digest('hex');
|
|
25
|
+
}
|
|
26
|
+
function verified(file,expected=sha256) {return fs.existsSync(file)&&shaFile(file)===expected;}
|
|
27
|
+
function validateEntries(output) {
|
|
28
|
+
const entries=output.split(/\r?\n/).filter(Boolean);
|
|
29
|
+
if(!entries.length)throw Error('Downloaded runtime archive is empty.');
|
|
30
|
+
for(const entry of entries) {
|
|
31
|
+
const parts=entry.split('/');
|
|
32
|
+
if(parts[0]!=='package'||entry.startsWith('/')||entry.includes('\\')||parts.some(part=>part==='..'))throw Error('Downloaded runtime archive has an unsafe path.');
|
|
33
|
+
}
|
|
34
|
+
return entries.length;
|
|
35
|
+
}
|
|
36
|
+
function download(file,request=https.get) {
|
|
37
|
+
return new Promise((resolve,reject)=>{
|
|
38
|
+
const output=fs.createWriteStream(file,{flags:'wx',mode:0o600});
|
|
39
|
+
output.on('error',reject);
|
|
40
|
+
const req=request(url,response=>{
|
|
41
|
+
if(response.statusCode!==200){response.resume();output.destroy();reject(Error(`Runtime download failed (HTTP ${response.statusCode}). Try again later.`));return;}
|
|
42
|
+
const length=Number(response.headers['content-length']);
|
|
43
|
+
if(!Number.isFinite(length)||length<1||length>maximumBytes){response.resume();output.destroy();reject(Error('Runtime download has an invalid size.'));return;}
|
|
44
|
+
let received=0;
|
|
45
|
+
response.on('data',chunk=>{received+=chunk.length;if(received>maximumBytes)response.destroy(Error('Runtime download exceeded its size limit.'));});
|
|
46
|
+
pipeline(response,output).then(()=>received===length?resolve():reject(Error('Runtime download was incomplete.')),reject);
|
|
47
|
+
});
|
|
48
|
+
req.setTimeout(120000,()=>req.destroy(Error('Runtime download timed out.')));
|
|
49
|
+
req.on('error',error=>{output.destroy();reject(error);});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function ensurePayload({root=cacheRoot(),fetchArchive=download,runTar=spawnSync,expected=sha256}={}) {
|
|
53
|
+
const payload=path.join(root,'package');
|
|
54
|
+
const archive=path.join(root,'runtime.tgz');
|
|
55
|
+
const entry=path.join(payload,'bin','npx-onboard.cjs');
|
|
56
|
+
fs.mkdirSync(root,{recursive:true});
|
|
57
|
+
if(fs.lstatSync(root).isSymbolicLink())throw Error('Runtime cache cannot be a symbolic link.');
|
|
58
|
+
if(fs.existsSync(entry)&&verified(archive,expected))return entry;
|
|
59
|
+
if(!verified(archive,expected)) {
|
|
60
|
+
const temporary=path.join(root,`runtime-${process.pid}-${crypto.randomBytes(6).toString('hex')}.tgz`);
|
|
61
|
+
try {
|
|
62
|
+
console.log('Downloading the pinned PC Remote Commander staging runtime...');
|
|
63
|
+
await fetchArchive(temporary);
|
|
64
|
+
if(!verified(temporary,expected))throw Error('Runtime SHA-256 verification failed. No downloaded code was run.');
|
|
65
|
+
if(fs.existsSync(archive))fs.rmSync(archive,{force:true});
|
|
66
|
+
fs.renameSync(temporary,archive);
|
|
67
|
+
} finally {fs.rmSync(temporary,{force:true});}
|
|
68
|
+
}
|
|
69
|
+
const staging=fs.mkdtempSync(path.join(root,'unpack-'));
|
|
70
|
+
try {
|
|
71
|
+
const listing=runTar('tar.exe',['-tzf',archive],{encoding:'utf8',windowsHide:true,maxBuffer:1024*1024});
|
|
72
|
+
if(listing.error||listing.status!==0)throw Error('Could not inspect the runtime archive. Windows tar.exe is required.');
|
|
73
|
+
validateEntries(listing.stdout);
|
|
74
|
+
const extracted=runTar('tar.exe',['-xzf',archive,'-C',staging],{encoding:'utf8',windowsHide:true,maxBuffer:1024*1024});
|
|
75
|
+
if(extracted.error||extracted.status!==0)throw Error('Could not unpack the verified runtime archive.');
|
|
76
|
+
if(!fs.existsSync(path.join(staging,'package','bin','npx-onboard.cjs')))throw Error('Verified runtime archive is missing its launcher.');
|
|
77
|
+
if(path.relative(root,payload)!=='package')throw Error('Unsafe runtime cache path.');
|
|
78
|
+
if(fs.existsSync(payload))fs.rmSync(payload,{recursive:true,force:true});
|
|
79
|
+
fs.renameSync(path.join(staging,'package'),payload);
|
|
80
|
+
} finally {fs.rmSync(staging,{recursive:true,force:true});}
|
|
81
|
+
return entry;
|
|
82
|
+
}
|
|
83
|
+
async function main(argv=process.argv.slice(2)) {
|
|
84
|
+
if(Number(process.versions.node.split('.')[0])<20)throw Error('Node.js 20 or newer is required.');
|
|
85
|
+
if(['help','--help','-h'].includes(argv[0])) {
|
|
86
|
+
console.log('PC Remote Commander staging beta\n\nUsage: pcremotecommander [setup|connect|cli|doctor] [options]\nSetup downloads a pinned runtime, verifies its SHA-256, then starts pairing. Windows and Node.js 20+ are required.');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const entry=await ensurePayload();
|
|
90
|
+
const child=spawn(process.execPath,[entry,...argv],{stdio:'inherit',windowsHide:false,shell:false});
|
|
91
|
+
child.on('error',error=>{console.error('PC Remote Commander runtime could not start: '+error.message);process.exitCode=1;});
|
|
92
|
+
child.on('exit',(code,signal)=>{process.exitCode=signal?130:code??1;});
|
|
93
|
+
}
|
|
94
|
+
if(require.main===module)main().catch(error=>{console.error('PC Remote Commander: '+error.message);process.exitCode=1;});
|
|
95
|
+
module.exports={cacheRoot,shaFile,verified,validateEntries,ensurePayload,main};
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pcremotecommander",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
}
|
|
3
|
+
"version": "0.7.0-beta.2",
|
|
4
|
+
"description": "PC Remote Commander Windows staging launcher; downloads a pinned full-control runtime",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"bin": {"pcremotecommander": "bin/pcremotecommander.cjs"},
|
|
7
|
+
"os": ["win32"],
|
|
8
|
+
"engines": {"node": ">=20"},
|
|
9
|
+
"publishConfig": {"access": "public", "tag": "beta"},
|
|
10
|
+
"files": ["bin"]
|
|
11
|
+
}
|