39 lines
2.7 KiB
JavaScript
39 lines
2.7 KiB
JavaScript
// Instance-level shell verbs. Intentionally separate from pluginsCommand's bus transport.
|
|
import { readdirSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { CliError } from './errors.mjs';
|
|
import { refuseInsideAgent } from './transport.mjs';
|
|
import { configDir } from '../../organization/src/organization-config.mjs';
|
|
import { assertAccountsOwner } from '../../organization/src/accounts.mjs';
|
|
import { withRegistry } from '../../organization/src/org-registry.mjs';
|
|
import { readJsonFile } from '../../organization/src/util.mjs';
|
|
import { installPlugin, removePlugin } from '../../plugins/src/install.mjs';
|
|
import { readPluginLock } from '../../plugins/src/pinned.mjs';
|
|
|
|
export async function pluginInstallCommand(args,io,{system,dir=configDir(io.env)}) {
|
|
refuseInsideAgent(io.env);
|
|
try {
|
|
assertAccountsOwner(dir);
|
|
const [verb,value,...rest]=args;
|
|
if(!['install','list','remove'].includes(verb))throw new CliError('mosaic plugins install SOURCE --sha256 HASH [--commit FULL_SHA] | list | remove NAME',4);
|
|
const opts={};
|
|
if(verb==='install') {
|
|
for(let i=0;i<rest.length;i+=2){if(!['--sha256','--commit'].includes(rest[i]) || !rest[i+1] || Object.hasOwn(opts,rest[i]))throw new CliError('invalid plugin install options',4);opts[rest[i]]=rest[i+1];}
|
|
if(!value || !opts['--sha256'])throw new CliError('install requires SOURCE and --sha256 HASH',4);
|
|
}else if(rest.length || (verb==='list'?value!==undefined:!value))throw new CliError('invalid plugin management arguments',4);
|
|
let result;
|
|
if(verb==='list') result=readPluginLock(system.dataRoot);
|
|
else if(verb==='install')result=installPlugin({dataRoot:system.dataRoot,source:value,commit:opts['--commit'],expectedHash:opts['--sha256'],print:line=>io.stdout.write(line+'\n')});
|
|
else result=await withRegistry({configDir:dir,instance:system.instance ?? dir.split('/').pop().replace(/^mosaic-/, '')},()=>{
|
|
// All organization files, including archived organizations, must be readable. The registry
|
|
// lock prevents a simultaneous create from disappearing from this check.
|
|
let files=[];try{files=readdirSync(join(dir,'businesses'));}catch(e){if(e.code!=='ENOENT')throw e;}
|
|
return removePlugin({dataRoot:system.dataRoot,name:value,enabled:name=>files.filter(n=>n.endsWith('.json')).some(n=>{
|
|
const doc=readJsonFile(join(dir,'businesses',n),'organization file');
|
|
return Object.values(doc.plugins ?? {}).flat().includes(name);
|
|
})});
|
|
},{write:false});
|
|
io.stdout.write(JSON.stringify(result,null,2)+'\n');
|
|
}catch(e){if(e instanceof CliError)throw e;throw new CliError(`plugin management refused: ${/^[a-z-]+$/.test(e.code ?? '')?e.code:'plugin-refused'}`,3);}
|
|
}
|