@volter/supercode-teams 0.2.46 → 0.2.48
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/bin/workspace.mjs +5 -2
- package/connector/index.mjs +9 -4
- package/package.json +1 -1
package/bin/workspace.mjs
CHANGED
|
@@ -720,11 +720,14 @@ async function connectorCommand(verb, { flags, rest, output, client, context, te
|
|
|
720
720
|
// callers the server admits; its host key id rides the connector's heartbeat.
|
|
721
721
|
const daemonHome = teamsHome();
|
|
722
722
|
const hostKeyId = publicKeyId(loadOrCreateKeyPair(paths(daemonHome).machineKey).publicKeyPem);
|
|
723
|
-
|
|
723
|
+
// Large transcripts load through a harness process of their own: one serve process answers one request at a
|
|
724
|
+
// time, and a long load would hold discovery and every small session's capture behind it.
|
|
725
|
+
const largeCore = new SupercodeHarnessClient({ command: binary, args: ['harness', 'serve'] });
|
|
726
|
+
const connector = new TeamsConnector({ server: context.origin, teamId: context.team_id, machineId: saved.machine.id, sourceId: saved.source.id, credential: saved.credential.token, stateDir: state.directory, core, largeCore, hostKeyId });
|
|
724
727
|
// A connected machine is a person's: each pane it launches opens a window on its screen (SUPERCODE_POPUPS=0: none).
|
|
725
728
|
const machine = new TeamsMachine({ home: daemonHome, supercode: binary, popups: process.env.SUPERCODE_POPUPS !== '0', ...(flags.tmux_socket ? { tmuxSocket: String(flags.tmux_socket) } : {}), link: { server: context.origin, teamId: context.team_id, machineId: saved.machine.id, credential: saved.credential.token, incarnationId: connector.state.incarnation_id, configuration: () => connector.configuration() }, onTurn: (turn) => connector.noteTurn(turn) });
|
|
726
729
|
machine.on('link', (event) => { if (event.event === 'replaced') process.stderr.write(`supercode teams: ${event.error}. Two connectors were running for this machine; stop one of them (\`supercode teams disconnect\` where it should not run).\n`); });
|
|
727
|
-
const stopAll = async () => { await machine.stop().catch(() => {}); await connector.stop(); await core.close(); await lifecycle.close(); state.update(current => current ? ({ ...current, process: undefined }) : null); };
|
|
730
|
+
const stopAll = async () => { await machine.stop().catch(() => {}); await connector.stop(); await core.close(); await largeCore.close(); await lifecycle.close(); state.update(current => current ? ({ ...current, process: undefined }) : null); };
|
|
728
731
|
try { await connector.start({ roots: () => saved.roots }); await machine.start(); } catch (error) { await stopAll(); throw error; }
|
|
729
732
|
output({ version: 1, context: scope(context), data: { machine: saved.machine, source: saved.source, roots: saved.roots.map(({ path: _path, ...item }) => item), socket: machine.socketPath, foreground: true } });
|
|
730
733
|
await new Promise(resolveStop => { let stopped = false; stopConnector = async () => { if (stopped) return; stopped = true; await stopAll(); resolveStop(); }; for (const signal of ['SIGINT', 'SIGTERM', ...(process.platform === 'win32' ? ['SIGHUP', 'SIGBREAK'] : [])]) process.once(signal, stopConnector); });
|
package/connector/index.mjs
CHANGED
|
@@ -88,7 +88,7 @@ function atomic(path, value) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
export class TeamsConnector {
|
|
91
|
-
constructor({server,teamId,machineId,sourceId,credential,stateDir,core,hostKeyId=null,effectExecutor=null,clock={nowMs:()=>Date.now()},fetch:fetchImpl=globalThis.fetch,heartbeatMs=15000,nativeIdleMs=NATIVE_IDLE_MS,nativeIdleBytes=NATIVE_IDLE_BYTES}){Object.assign(this,{server:serverOrigin(server),teamId,machineId,sourceId,credential,stateDir,core,hostKeyId,effectExecutor,clock,fetch:fetchImpl,heartbeatMs,nativeIdleMs,nativeIdleBytes});mkdirSync(stateDir,{recursive:true,mode:0o700});chmodSync(stateDir,0o700);this.statePath=join(stateDir,'connector.json');this.state=existsSync(this.statePath)?JSON.parse(readFileSync(this.statePath,'utf8')):{version:1,sequence:0,origins:{},outbox:[]};this.state.effects??={};if(Array.isArray(this.state.sync_status?.problems))this.state.sync_status.problems=this.state.sync_status.problems.filter(item=>item?.code!==CAPACITY_CODE);this.state.native_cache??={};this.state.incarnation_id=`inc_${randomBytes(16).toString('hex')}`;this.abort=new AbortController();this.work=Promise.resolve();this.nativeEffects=new NativeEffects(this);this.effects=new EffectConsumer(this,effectExecutor?{prepare:async effect=>({execute:()=>effectExecutor(effect,{core:this.core,signal:this.abort.signal})})}:this.nativeEffects);this.save();}
|
|
91
|
+
constructor({server,teamId,machineId,sourceId,credential,stateDir,core,largeCore=core,hostKeyId=null,effectExecutor=null,clock={nowMs:()=>Date.now()},fetch:fetchImpl=globalThis.fetch,heartbeatMs=15000,nativeIdleMs=NATIVE_IDLE_MS,nativeIdleBytes=NATIVE_IDLE_BYTES}){Object.assign(this,{server:serverOrigin(server),teamId,machineId,sourceId,credential,stateDir,core,largeCore,hostKeyId,effectExecutor,clock,fetch:fetchImpl,heartbeatMs,nativeIdleMs,nativeIdleBytes});mkdirSync(stateDir,{recursive:true,mode:0o700});chmodSync(stateDir,0o700);this.statePath=join(stateDir,'connector.json');this.state=existsSync(this.statePath)?JSON.parse(readFileSync(this.statePath,'utf8')):{version:1,sequence:0,origins:{},outbox:[]};this.state.effects??={};if(Array.isArray(this.state.sync_status?.problems))this.state.sync_status.problems=this.state.sync_status.problems.filter(item=>item?.code!==CAPACITY_CODE);this.state.native_cache??={};this.state.incarnation_id=`inc_${randomBytes(16).toString('hex')}`;this.abort=new AbortController();this.work=Promise.resolve();this.nativeEffects=new NativeEffects(this);this.effects=new EffectConsumer(this,effectExecutor?{prepare:async effect=>({execute:()=>effectExecutor(effect,{core:this.core,signal:this.abort.signal})})}:this.nativeEffects);this.save();}
|
|
92
92
|
save(){atomic(this.statePath,this.state);}
|
|
93
93
|
// What `supercode teams status` shows about this connector: its last error, and the sessions it could not capture
|
|
94
94
|
// (most recent first, bounded; an entry clears when that session next captures). They are kept in connector.json,
|
|
@@ -443,7 +443,10 @@ export class TeamsConnector {
|
|
|
443
443
|
}
|
|
444
444
|
async runLargeLane(){
|
|
445
445
|
while(this.largeQueue.size&&!this.abort.signal.aborted){
|
|
446
|
-
|
|
446
|
+
// The session written most recently goes first: a watched live session never waits behind a slow old one.
|
|
447
|
+
const written=([,item])=>{try{return statSync(realpathSync(item.locator.storage.path)).mtimeMs;}catch{return 0;}};
|
|
448
|
+
const [key,options]=[...this.largeQueue.entries()].reduce((best,entry)=>written(entry)>written(best)?entry:best);this.largeQueue.delete(key);
|
|
449
|
+
let started=null;
|
|
447
450
|
try{
|
|
448
451
|
const artifactBefore=statSync(realpathSync(options.locator.storage.path));
|
|
449
452
|
// A session is loaded again no sooner than ten times its last load took (a 0.7 s load: every 7 s; a load
|
|
@@ -451,12 +454,14 @@ export class TeamsConnector {
|
|
|
451
454
|
// A change that arrives sooner is captured when its wait ends.
|
|
452
455
|
this.largeCaptures??=new Map();const last=this.largeCaptures.get(key),wait=last?Math.min(LARGE_CAPTURE_EVERY_MS,last.ms*10)-(this.clock.nowMs()-last.at):0;
|
|
453
456
|
if(wait>0){this.largeRetry??=new Map();if(!this.largeRetry.has(key))this.largeRetry.set(key,setTimeout(()=>{this.largeRetry.delete(key);this.captureLarge(options);},wait).unref?.());continue;}
|
|
454
|
-
|
|
455
|
-
const loaded=await this.
|
|
457
|
+
started=this.clock.nowMs();
|
|
458
|
+
const loaded=await this.largeCore.session(options.locator).load({timeoutMs:Math.max(30000,Math.ceil(artifactBefore.size/(1024*1024))*1000)});
|
|
456
459
|
this.largeCaptures.set(key,{at:started,ms:this.clock.nowMs()-started});
|
|
457
460
|
await this.capture({...options,preloaded:{loaded,artifactBefore}});
|
|
458
461
|
}catch(error){
|
|
459
462
|
if(this.abort.signal.aborted)return;
|
|
463
|
+
// A load that failed or timed out waits like a slow one, so it does not hold the lane on every pass.
|
|
464
|
+
if(started!==null)this.largeCaptures.set(key,{at:started,ms:this.clock.nowMs()-started});
|
|
460
465
|
this.noteSessionProblem(options,error);
|
|
461
466
|
process.stderr.write(`supercode teams: ${options.harness} session ${options.nativeSessionId} not synced: ${error.message}\n`);
|
|
462
467
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|