@volter/supercode-teams 0.2.45 → 0.2.46

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.
@@ -446,10 +446,14 @@ export class TeamsConnector {
446
446
  const [key,options]=this.largeQueue.entries().next().value;this.largeQueue.delete(key);
447
447
  try{
448
448
  const artifactBefore=statSync(realpathSync(options.locator.storage.path));
449
- this.largeCaptures??=new Map();const last=this.largeCaptures.get(key);
450
- if(last!=null&&this.clock.nowMs()-last<LARGE_CAPTURE_EVERY_MS)continue;
451
- this.largeCaptures.set(key,this.clock.nowMs());
449
+ // A session is loaded again no sooner than ten times its last load took (a 0.7 s load: every 7 s; a load
450
+ // that takes minutes: every LARGE_CAPTURE_EVERY_MS), so a live session streams while the lane stays light.
451
+ // A change that arrives sooner is captured when its wait ends.
452
+ 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
+ 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
+ const started=this.clock.nowMs();
452
455
  const loaded=await this.core.session(options.locator).load({timeoutMs:Math.max(30000,Math.ceil(artifactBefore.size/(1024*1024))*1000)});
456
+ this.largeCaptures.set(key,{at:started,ms:this.clock.nowMs()-started});
453
457
  await this.capture({...options,preloaded:{loaded,artifactBefore}});
454
458
  }catch(error){
455
459
  if(this.abort.signal.aborted)return;
@@ -465,8 +469,7 @@ export class TeamsConnector {
465
469
  if(!configuration)configuration=await this.configuration();const rule=configuration.rules.find(item=>item.id===ruleId&&item.revision===ruleRevision&&item.state==='active');if(!rule)throw Object.assign(new Error('sync rule is absent, inactive, or stale'),{code:'forbidden'});visibility=rule.visibility;
466
470
  const artifactSource=locator?.storage?.kind==='file'&&existsSync(locator.storage.path)?realpathSync(locator.storage.path):null;
467
471
  const artifactBefore=preloaded?preloaded.artifactBefore:artifactSource?statSync(artifactSource):null;
468
- const large=(artifactBefore?.size??0)>=LARGE_CAPTURE_BYTES,sessionKey=`${harness}:${nativeSessionId}`;
469
- if(large&&!preloaded){this.largeCaptures??=new Map();const last=this.largeCaptures.get(sessionKey);if(last!=null&&this.clock.nowMs()-last<LARGE_CAPTURE_EVERY_MS)return null;this.largeCaptures.set(sessionKey,this.clock.nowMs());}
472
+ const large=(artifactBefore?.size??0)>=LARGE_CAPTURE_BYTES;
470
473
  const loaded=preloaded?preloaded.loaded:await this.core.session(locator).load(large?{timeoutMs:Math.max(30000,Math.ceil(artifactBefore.size/(1024*1024))*1000)}:undefined),configuredBackfill=rule.backfill??backfill??'all',backfillPolicy=typeof configuredBackfill==='string'?{mode:configuredBackfill,since:null}:configuredBackfill;
471
474
  const key=`${harness}:${nativeSessionId}`,previous=this.state.origins[key]??null;
472
475
  let messages=(loaded.messages??loaded.turns??[]).map((entry,index)=>{const value=entry.value??Object.fromEntries(Object.entries(entry).filter(([key])=>!['id','native_key','recorded_at','timestamp'].includes(key)));return{native_key:entry.id??entry.native_key??String(index),recorded_at:recordedAt(entry),value};});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-teams",
3
- "version": "0.2.45",
3
+ "version": "0.2.46",
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",
@@ -243,7 +243,10 @@ async function sourceConfig(api,res,auth,id,url,requestId){if(url.searchParams.g
243
243
  async function sourceAuth(api,auth,id){if(!auth||!['machine','ingest'].includes(auth.kind)||auth.source_id!==id)fail(403,'forbidden','source credential required');const s=await api.store.one('SELECT * FROM sources WHERE team_id=? AND id=?',[auth.team_id,id]);if(!s)fail(404,'not_found','not found');if(s.state==='revoked')fail(403,'source_revoked','source revoked');if(s.state==='paused')fail(409,'source_paused','source paused');if(auth.kind==='machine'){const machine=await api.store.one('SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND revoked_at IS NULL',[auth.team_id,auth.machine_id]);if(!machine||s.machine_id!==auth.machine_id)fail(403,'forbidden','machine enrollment is not active');}return s;}
244
244
  async function expireStaging(api,teamId,sourceId,id,table,row){if(!row||row.state&&row.state!=='staging'||row.committed===1||row.created_at+86400000>api.clock.nowMs())return false;await api.store.transaction([{sql:'INSERT OR REPLACE INTO capture_expiries(team_id,source_id,id,expired_at) VALUES (?,?,?,?)',params:[teamId,sourceId,id,api.clock.nowMs()]},{sql:`DELETE FROM ${table} WHERE team_id=? AND id=? AND source_id=?`,params:[teamId,id,sourceId]}]);return true;}
245
245
  async function captureBegin(api,req,res,auth,id,requestId){const source=await sourceAuth(api,auth,id);const b=await parseJson(req);validateCaptureBegin(b);if(source.kind==='connector'){if(b.rule_id===null)fail(403,'forbidden','connector capture requires an active sync rule');const rule=await api.store.one('SELECT * FROM sync_rules WHERE team_id=? AND id=? AND source_id=? AND machine_id=?',[auth.team_id,b.rule_id,id,auth.machine_id]);if(!rule||rule.state!=='active'||rule.revision!==b.rule_revision)fail(403,'forbidden','sync rule is absent, inactive, or stale');if(b.visibility!==rule.visibility)fail(400,'invalid_request','capture visibility differs from sync rule');}else if(b.visibility!==source.visibility)fail(400,'invalid_request','capture visibility differs from source policy');const expired=await api.store.one('SELECT id FROM capture_expiries WHERE team_id=? AND source_id=? AND id=?',[auth.team_id,id,b.capture_id]);if(expired)fail(409,'resync_required','capture reservation expired');const existing=await api.store.one('SELECT * FROM captures WHERE team_id=? AND id=?',[auth.team_id,b.capture_id]);if(await expireStaging(api,auth.team_id,id,b.capture_id,'captures',existing))fail(409,'resync_required','capture reservation expired');if(existing){if(existing.begin_json!==canonicalJson(b))fail(409,'capture_conflict','capture id reused with different request');if(existing.state==='committed')return send(res,200,result(api,auth,{state:'committed',receipt:JSON.parse(existing.receipt_json)}),requestId);const next=(await api.store.one('SELECT count(*) n FROM capture_parts WHERE team_id=? AND capture_id=?',[auth.team_id,b.capture_id])).n;return send(res,200,result(api,auth,{state:'staging',capture_id:b.capture_id,next_part:next}),requestId);}
246
- const origin=await api.store.one('SELECT * FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=?',[auth.team_id,id,b.harness,b.native_session_id]);if(origin?.deleted_at!=null)fail(410,'session_deleted','session deleted');const expected=origin?{generation:origin.generation,sequence:origin.sequence+1,digest:origin.digest}:{generation:1,sequence:1,digest:null};if(b.generation!==expected.generation||b.sequence!==expected.sequence||b.previous_digest!==expected.digest)fail(409,'batch_gap','capture is not the next source revision',expected);const now=api.clock.nowMs(),response=result(api,auth,{state:'staging',capture_id:b.capture_id,next_part:0}),guards=[{sql:"SELECT 1 ok FROM credentials c JOIN sources s ON s.team_id=c.team_id AND s.id=c.source_id LEFT JOIN machines m ON m.team_id=s.team_id AND m.id=c.machine_id LEFT JOIN sync_rules r ON r.team_id=s.team_id AND r.id=? WHERE c.id=? AND c.team_id=? AND c.source_id=? AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?) AND s.state='active' AND ((s.kind!='connector' AND s.visibility=?) OR (s.kind='connector' AND c.kind='machine' AND m.id=c.machine_id AND m.revoked_at IS NULL AND r.source_id=s.id AND r.machine_id=m.id AND r.state='active' AND r.revision=? AND r.visibility=?))",params:[b.rule_id,auth.credential_id,auth.team_id,id,now,b.visibility,b.rule_revision,b.visibility],code:'forbidden',status:403,message:'source authorization changed'},{sql:"SELECT 1 ok WHERE NOT EXISTS (SELECT 1 FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=? AND deleted_at IS NOT NULL) AND ((?=1 AND ?=1 AND ? IS NULL AND NOT EXISTS (SELECT 1 FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=?)) OR EXISTS (SELECT 1 FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=? AND generation=? AND sequence=?-1 AND digest IS ?))",params:[auth.team_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.previous_digest,auth.team_id,id,b.harness,b.native_session_id,auth.team_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.previous_digest],code:'batch_gap',status:409,message:'capture is not the next source revision'}];await guardedMutation(api,res,auth,guards,[{sql:"INSERT INTO captures(team_id,id,source_id,harness,native_id,generation,sequence,previous_digest,state,begin_json,created_at) VALUES (?,?,?,?,?,?,?,?, 'staging',?,?)",params:[auth.team_id,b.capture_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.previous_digest,canonicalJson(b),now]}],{status:201,body:response,targetId:b.capture_id});send(res,201,response,requestId);}
246
+ const origin=await api.store.one('SELECT * FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=?',[auth.team_id,id,b.harness,b.native_session_id]);if(origin?.deleted_at!=null)fail(410,'session_deleted','session deleted');const expected=origin?{generation:origin.generation,sequence:origin.sequence+1,digest:origin.digest}:{generation:1,sequence:1,digest:null};if(b.generation!==expected.generation||b.sequence!==expected.sequence||b.previous_digest!==expected.digest)fail(409,'batch_gap','capture is not the next source revision',expected);const now=api.clock.nowMs(),response=result(api,auth,{state:'staging',capture_id:b.capture_id,next_part:0}),guards=[{sql:"SELECT 1 ok FROM credentials c JOIN sources s ON s.team_id=c.team_id AND s.id=c.source_id LEFT JOIN machines m ON m.team_id=s.team_id AND m.id=c.machine_id LEFT JOIN sync_rules r ON r.team_id=s.team_id AND r.id=? WHERE c.id=? AND c.team_id=? AND c.source_id=? AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?) AND s.state='active' AND ((s.kind!='connector' AND s.visibility=?) OR (s.kind='connector' AND c.kind='machine' AND m.id=c.machine_id AND m.revoked_at IS NULL AND r.source_id=s.id AND r.machine_id=m.id AND r.state='active' AND r.revision=? AND r.visibility=?))",params:[b.rule_id,auth.credential_id,auth.team_id,id,now,b.visibility,b.rule_revision,b.visibility],code:'forbidden',status:403,message:'source authorization changed'},{sql:"SELECT 1 ok WHERE NOT EXISTS (SELECT 1 FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=? AND deleted_at IS NOT NULL) AND ((?=1 AND ?=1 AND ? IS NULL AND NOT EXISTS (SELECT 1 FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=?)) OR EXISTS (SELECT 1 FROM origins WHERE team_id=? AND source_id=? AND harness=? AND native_id=? AND generation=? AND sequence=?-1 AND digest IS ?))",params:[auth.team_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.previous_digest,auth.team_id,id,b.harness,b.native_session_id,auth.team_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.previous_digest],code:'batch_gap',status:409,message:'capture is not the next source revision'}];// A reservation this source abandoned for the same revision (its connector restarted, or dropped the capture,
247
+ // before committing) is superseded by the new begin, never left to refuse it until the reservation expires.
248
+ const abandoned="FROM captures WHERE team_id=? AND source_id=? AND harness=? AND native_id=? AND generation=? AND sequence=? AND state='staging' AND id<>?",abandonedParams=[auth.team_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.capture_id];
249
+ await guardedMutation(api,res,auth,guards,[{sql:`DELETE FROM capture_parts WHERE team_id=? AND capture_id IN (SELECT id ${abandoned})`,params:[auth.team_id,...abandonedParams]},{sql:`DELETE ${abandoned}`,params:abandonedParams},{sql:"INSERT INTO captures(team_id,id,source_id,harness,native_id,generation,sequence,previous_digest,state,begin_json,created_at) VALUES (?,?,?,?,?,?,?,?, 'staging',?,?)",params:[auth.team_id,b.capture_id,id,b.harness,b.native_session_id,b.generation,b.sequence,b.previous_digest,canonicalJson(b),now]}],{status:201,body:response,targetId:b.capture_id});send(res,201,response,requestId);}
247
250
  async function capturePart(api,req,res,auth,sourceId,captureId,index,requestId){await sourceAuth(api,auth,sourceId);const b=await parseJson(req);validateCapturePart(b);if(b.capture_id!==captureId||b.index!==index)fail(400,'invalid_request','capture path/body mismatch');const cap=await api.store.one('SELECT * FROM captures WHERE team_id=? AND id=? AND source_id=?',[auth.team_id,captureId,sourceId]);if(await expireStaging(api,auth.team_id,sourceId,captureId,'captures',cap))fail(409,'resync_required','capture reservation expired');if(!cap||cap.state!=='staging')fail(404,'not_found','capture not staging');const payload=canonicalJson(b),digest=sha256(payload),old=await api.store.one('SELECT * FROM capture_parts WHERE team_id=? AND capture_id=? AND part_index=?',[auth.team_id,captureId,index]);if(old){if(old.digest!==digest)fail(409,'capture_conflict','part replay differs');return send(res,200,result(api,auth,{next_part:index+1}),requestId);}const count=(await api.store.one('SELECT count(*) n FROM capture_parts WHERE team_id=? AND capture_id=?',[auth.team_id,captureId])).n;if(index!==count)fail(409,'batch_gap','capture parts must be contiguous');const now=api.clock.nowMs(),response=result(api,auth,{next_part:index+1}),guards=[{sql:"SELECT 1 ok FROM credentials c JOIN sources s ON s.team_id=c.team_id AND s.id=c.source_id JOIN captures cap ON cap.team_id=s.team_id AND cap.source_id=s.id WHERE c.id=? AND c.source_id=? AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?) AND s.state='active' AND cap.id=? AND cap.state='staging' AND cap.created_at+86400000>? AND (SELECT count(*) FROM capture_parts WHERE team_id=cap.team_id AND capture_id=cap.id)=?",params:[auth.credential_id,sourceId,now,captureId,now,index],code:'batch_gap',status:409,message:'capture authorization or checkpoint changed'}];await guardedMutation(api,res,auth,guards,[{sql:'INSERT INTO capture_parts(team_id,capture_id,part_index,digest,payload_json) VALUES (?,?,?,?,?)',params:[auth.team_id,captureId,index,digest,payload]}],{status:200,body:response,targetId:captureId});send(res,200,response,requestId);}
248
251
  async function artifactChunk(api,req,res,auth,sourceId,uploadId,index,requestId){
249
252
  await sourceAuth(api,auth,sourceId);