Ctrl K
ComunidadGuías…

lib/personal-provider-install.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Exact human-settings review, one-use receipts, and scoped installer lifecycle.
var crypto = require('crypto');
var locks = new Map();
function attach(ctx, personal) {
  var pending = new WeakMap(), running = new Map();
  function emit(ws, request, state, extra) {
    if (ws.readyState === 1) ctx.sendTo(ws, Object.assign({type:'personal_provider_install',vendor:request.vendor,personalRequestId:request.personalRequestId,state:state,requestId:request.requestId},extra || {}));
  }
  function valid(item) { return item.authority.validate() && Date.now() < item.expires; }
  function release(ws) {
    pending.delete(ws);
    var item = running.get(ws); if (item) stop(ws,item);
  }
  async function stop(ws,item) {
    if (item.stopping) return;
    item.stopping = true; clearInterval(item.timer);
    emit(ws,item.request,'stopping',{message:'Stopping the installation terminal…'});
    try {
      await ctx.tm.closeAndWait(item.terminal.id,{identity:item.terminal.identity,revalidate:function () { if (running.get(ws) !== item) throw new Error('Installation terminal changed.'); }});
      emit(ws,item.request,'stopped',{message:'Installation terminal exited. Partial files may remain. Refresh status before explicitly reviewing another attempt.'});
      locks.delete(item.key); running.delete(ws);
    } catch(error) {
      // Keep the lock: an uncertain effect must never be replayed automatically.
      emit(ws,item.request,'unknown',{message:'Terminal shutdown could not be confirmed. Ask the server operator to inspect it before trying again.'});
    }
  }
  function handle(ws,msg) {
    if (typeof msg.type !== 'string' || msg.type.indexOf('personal_provider_install_') !== 0) return false;
    try {
      if (msg.type === 'personal_provider_install_stop') {
        var active = running.get(ws);
        if (active && active.request.personalRequestId === msg.personalRequestId && active.request.vendor === msg.vendor) stop(ws,active);
        return true;
      }
      if (msg.type === 'personal_provider_install_cancel') {
        var review = pending.get(ws);
        if (review && review.token === msg.reviewId && review.request.personalRequestId === msg.personalRequestId && review.request.vendor === msg.vendor) pending.delete(ws);
        return true;
      }
      if (msg.type === 'personal_provider_install_review') {
        if (running.has(ws)) throw new Error('An installation is already running or its result is uncertain.');
        var authority = personal.resolveInstall(ws,msg);
        var key = JSON.stringify([authority.runtime.uid,authority.runtime.home,msg.vendor]);
        if (locks.has(key)) throw new Error('An installation for this runtime account is already running or needs inspection.');
        var item = {authority:authority,key:key,request:{vendor:msg.vendor,personalRequestId:msg.personalRequestId,requestId:msg.requestId},token:crypto.randomUUID(),expires:Date.now()+120000};
        pending.set(ws,item);
        emit(ws,msg,'review',{reviewId:item.token,command:authority.recipe.command,source:authority.recipe.source,effects:authority.recipe.effects,runtime:authority.runtime,message:'Review this exact installation. Review expires in two minutes; leaving settings cancels it.'});
        return true;
      }
      if (msg.type === 'personal_provider_install_start') {
        var item = pending.get(ws); pending.delete(ws);
        if (!item || item.token !== msg.reviewId || item.request.vendor !== msg.vendor || item.request.personalRequestId !== msg.personalRequestId || !valid(item) || locks.has(item.key)) throw new Error('Installation review expired, changed or was already used. Refresh status and review again.');
        var fresh = personal.resolveInstall(ws,msg);
        if (fresh.recipe !== item.authority.recipe || JSON.stringify(fresh.runtime) !== JSON.stringify(item.authority.runtime)) throw new Error('Installation target changed.');
        item.request.requestId = msg.requestId;
        locks.set(item.key,item); running.set(ws,item);
        var terminal;
        try {
          terminal = ctx.tm.create(100,24,item.authority.runtime,ws,{kind:'provider-install',title:'Install '+msg.vendor,providerInstall:msg.vendor,
            revalidateInstall:function () { if (!valid(item) || running.get(ws) !== item || locks.get(item.key) !== item) throw new Error('Installation permission or identity changed.'); },
            onExit:function (session) {
              if (item.stopping || running.get(ws) !== item) return;
              clearInterval(item.timer); running.delete(ws); locks.delete(item.key);
              if (!valid(item)) { emit(ws,item.request,'unknown',{message:'Installation terminal exited after account context changed. Refresh status; no task was replayed.'}); return; }
              var installed = null;
              try { installed = !!require('./yoke/vendor-registry').getVendorInfo(msg.vendor).resolveExecutable({user:item.authority.identity.linuxUser}); } catch(error) {}
              if (!valid(item)) { emit(ws,item.request,'unknown',{message:'Installation discovery ended after account context changed. Refresh status before continuing.'}); return; }
              emit(ws,item.request,session.exitCode === 0 ? 'completed' : 'failed',{installed:installed,exitCode:session.exitCode,message:session.exitCode === 0 ? (installed === null ? 'Installer exited successfully, but runtime discovery is unavailable. Refresh status to check the installation.' : installed ? 'Installer exited successfully; runtime discovery now finds this provider. Authentication remains unknown.' : 'Installer exited successfully, but runtime discovery has not found the provider. Check ~/.local/bin on the runtime PATH and refresh status.') : 'Installer did not report success. Refresh status and inspect the runtime setup before explicitly reviewing another attempt.'});
            }});
          if (!terminal) throw new Error('The installation terminal could not be created.');
          item.terminal = terminal;
          // Review expiry bounds launch, not a running install. Authority stays live.
          item.expires = Infinity;
          item.timer = setInterval(function () { if (!valid(item)) stop(ws,item); },250);
          item.timer.unref();
          emit(ws,msg,'started',{message:'Installation started in the reviewed runtime account. Waiting for terminal exit; downloads may be quiet.'});
        } catch(error) { emit(ws,item.request,'unknown',{message:'Installation launch could not be confirmed. Ask the server operator to inspect the runtime before retrying.'}); }
        return true;
      }
      throw new Error('Unsupported installation operation.');
    } catch(error) { emit(ws,msg,'refused',{message:error.message}); return true; }
  }
  return {handle:handle,release:release,destroy:function () { pending = new WeakMap(); running.forEach(function(item,ws){stop(ws,item);}); }};
}
module.exports = {attach:attach};