@volter/supercode-teams 0.2.79 → 0.2.80
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/lifecycle.mjs +3 -0
- package/bin/workspace.mjs +5 -3
- package/connector/index.mjs +3 -2
- package/package.json +1 -1
|
@@ -27,6 +27,9 @@ const sameOwner = owner => {
|
|
|
27
27
|
try { process.kill(owner.pid, 0); return true; } catch (error) { return error.code === 'EPERM'; }
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
/** Whether the process holding this state directory's lifecycle lock is still the one that took it. */
|
|
31
|
+
export function lifecycleHeld(directory) { return sameOwner(readOwner(join(directory, 'lifecycle.lock'))); }
|
|
32
|
+
|
|
30
33
|
export async function activeSocket(path) {
|
|
31
34
|
return new Promise(resolve => {
|
|
32
35
|
const socket = createConnection(path);
|
package/bin/workspace.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import { teamsHome, paths } from '@volter/teams/home';
|
|
|
17
17
|
import { loadOrCreateKeyPair, publicKeyId } from '@volter/teams/identity';
|
|
18
18
|
import { duration, parseArgs, positiveInteger, requireArgs, usage } from './workspace/args.mjs';
|
|
19
19
|
import { reserveArtifactDirectory, reserveProtected, writeProtected } from './workspace/files.mjs';
|
|
20
|
-
import { acquireLifecycle, activeSocket, requestStop } from './workspace/lifecycle.mjs';
|
|
20
|
+
import { acquireLifecycle, activeSocket, lifecycleHeld, requestStop } from './workspace/lifecycle.mjs';
|
|
21
21
|
import { volterDeviceSignIn } from './workspace/volter-device.mjs';
|
|
22
22
|
import { loginSummary, signInToTeams } from './workspace/volter-login.mjs';
|
|
23
23
|
import { readSharedSignIn, signOut } from '@volter/identity';
|
|
@@ -653,12 +653,14 @@ async function connectorCommand(verb, { flags, rest, output, client, context, te
|
|
|
653
653
|
if (verb === 'status') {
|
|
654
654
|
let reachable = true; let account = null; try { account = await client.me(); } catch { reachable = false; }
|
|
655
655
|
const saved = state.read(); const running = saved?.process?.socket ? await activeSocket(saved.process.socket) : false;
|
|
656
|
+
// A connector deep in a large capture can miss the probe's second while its process lives on: it is busy, not stopped.
|
|
657
|
+
const busy = !running && Boolean(saved?.process) && lifecycleHeld(state.directory);
|
|
656
658
|
// What the connector could not sync, as it last wrote it to its state (it is written while it runs).
|
|
657
659
|
const sync = saved ? readSyncStatus(state.directory) ?? { not_syncing: [], last_error: null, queued_captures: 0 } : null;
|
|
658
|
-
const value = { version: 1, context: scope(context), data: { reachable, account, connector: saved ? { enrolled: true, running, machine: saved.machine, source: saved.source, sync } : { enrolled: false, running: false }, sources: reachable ? (await team.sources.list()).items : [] } };
|
|
660
|
+
const value = { version: 1, context: scope(context), data: { reachable, account, connector: saved ? { enrolled: true, running: running || busy, ...(busy ? { busy: true } : {}), machine: saved.machine, source: saved.source, sync } : { enrolled: false, running: false }, sources: reachable ? (await team.sources.list()).items : [] } };
|
|
659
661
|
if (flags.json) return output(value);
|
|
660
662
|
const lines = [`Team ${context.team_name ?? context.team_id} · ${new URL(context.origin).host}${reachable ? '' : ' · unreachable'}`];
|
|
661
|
-
lines.push(saved?.machine ? `Connector ${saved.machine.name ?? saved.machine.id} · ${running ? 'running' : 'not running'}${saved.service?.kind ? ` (${saved.service.kind})` : ''}` : 'Connector not connected (supercode teams connect)');
|
|
663
|
+
lines.push(saved?.machine ? `Connector ${saved.machine.name ?? saved.machine.id} · ${running ? 'running' : busy ? 'running (busy: its control socket did not answer within 1 s)' : 'not running'}${saved.service?.kind ? ` (${saved.service.kind})` : ''}` : 'Connector not connected (supercode teams connect)');
|
|
662
664
|
lines.push(...syncStatusLines(sync));
|
|
663
665
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
664
666
|
return;
|
package/connector/index.mjs
CHANGED
|
@@ -439,7 +439,8 @@ export class TeamsConnector {
|
|
|
439
439
|
const artifactBefore=path?statSync(path):null;
|
|
440
440
|
options={...options,preloaded:{loaded:await (options.live?this.liveCore:this.core).session(locator).load(),artifactBefore}};
|
|
441
441
|
}
|
|
442
|
-
|
|
442
|
+
// A capture the server refused and the outbox dropped stays that session's problem; any other capture clears it.
|
|
443
|
+
const work=this.work.then(()=>this.captureSerial(options)).then(receipt=>{if(!this.droppedCaptures?.delete(`${options.harness}:${options.nativeSessionId}`))this.clearSessionProblem(options.harness,options.nativeSessionId);return receipt;});
|
|
443
444
|
this.work=work.catch(()=>{});
|
|
444
445
|
return work;
|
|
445
446
|
}
|
|
@@ -701,6 +702,6 @@ export class TeamsConnector {
|
|
|
701
702
|
if(!Array.isArray(configuration?.rules))return false;
|
|
702
703
|
return !configuration.rules.some(rule=>rule.id===item.begin.rule_id&&rule.revision===item.begin.rule_revision&&rule.state==='active');
|
|
703
704
|
}
|
|
704
|
-
async flushOutbox(){let latest=null;while(this.state.outbox.length){const item=this.state.outbox[0];let receipt;this.dropLostArtifacts(item);this.refitParts(item);try{receipt=await this.sendCapture(item);}catch(error){if(error.status===409&&error.code==='carry_mismatch'){const key=`${item.begin.harness}:${item.begin.native_session_id}`;this.lastError=error;process.stderr.write(`supercode teams: the server does not hold what a capture of ${item.begin.harness} session ${item.begin.native_session_id} carried; it goes whole next time\n`);this.state.outbox.shift();if(this.state.origins[key])this.state.origins[key]={...this.state.origins[key],observed_keys:[],observed_digests:[],carry_refused:true};this.save();continue;}if(error.status===400&&error.code==='invalid_request'||error.status===413){this.lastError=error;this.noteSessionProblem({harness:item.begin.harness,nativeSessionId:item.begin.native_session_id,ruleId:item.begin.rule_id??null},error);process.stderr.write(`supercode teams: dropped a queued capture of ${item.begin.harness} session ${item.begin.native_session_id} the server refused${error.status===413?' (one message is larger than a request may be)':' as malformed'}: ${error.message}\n`);this.state.outbox.shift();this.save();continue;}if(!await this.ruleRejected(item,error))throw error;this.lastError=error;process.stderr.write(`supercode teams: dropped a queued capture of ${item.begin.harness} session ${item.begin.native_session_id}; its sync rule changed or was removed: ${error.message}\n`);this.state.outbox.shift();this.save();continue;}const key=`${item.begin.harness}:${item.begin.native_session_id}`,prior=this.state.origins[key]??{},next={...prior,generation:item.begin.generation,sequence:item.begin.sequence,digest:item.commit.digest,observed_keys:item.observed?.keys??prior.observed_keys,observed_digests:item.observed?.digests??prior.observed_digests,last_rule_revision:item.begin.rule_revision,backfill_mode:item.backfillMode};if(item.clearBaseline){delete next.baseline;delete next.baseline_digests;delete next.baseline_tool_tail;}if(item.nativeDeferred)next.native_deferred=true;else delete next.native_deferred;if(!item.begin.carry)delete next.carry_refused;if(item.stamp&&item.begin.rule_id)next.capture_stamps={...prior.capture_stamps,[item.begin.rule_id]:{stamp:item.stamp,rule_revision:item.begin.rule_revision,backfill_mode:item.backfillMode}};this.state.origins[key]=next;this.state.outbox.shift();this.save();if(item.artifacts?.length)this.pruneSpill();latest=receipt;}return latest;}
|
|
705
|
+
async flushOutbox(){let latest=null;while(this.state.outbox.length){const item=this.state.outbox[0];let receipt;this.dropLostArtifacts(item);this.refitParts(item);try{receipt=await this.sendCapture(item);}catch(error){if(error.status===409&&error.code==='carry_mismatch'){const key=`${item.begin.harness}:${item.begin.native_session_id}`;this.lastError=error;process.stderr.write(`supercode teams: the server does not hold what a capture of ${item.begin.harness} session ${item.begin.native_session_id} carried; it goes whole next time\n`);this.state.outbox.shift();if(this.state.origins[key])this.state.origins[key]={...this.state.origins[key],observed_keys:[],observed_digests:[],carry_refused:true};this.save();continue;}if(error.status===400&&error.code==='invalid_request'||error.status===413){this.lastError=error;this.noteSessionProblem({harness:item.begin.harness,nativeSessionId:item.begin.native_session_id,ruleId:item.begin.rule_id??null},error);(this.droppedCaptures??=new Set()).add(`${item.begin.harness}:${item.begin.native_session_id}`);process.stderr.write(`supercode teams: dropped a queued capture of ${item.begin.harness} session ${item.begin.native_session_id} the server refused${error.status===413?' (one message is larger than a request may be)':' as malformed'}: ${error.message}\n`);this.state.outbox.shift();this.save();continue;}if(!await this.ruleRejected(item,error))throw error;this.lastError=error;process.stderr.write(`supercode teams: dropped a queued capture of ${item.begin.harness} session ${item.begin.native_session_id}; its sync rule changed or was removed: ${error.message}\n`);this.state.outbox.shift();this.save();continue;}const key=`${item.begin.harness}:${item.begin.native_session_id}`,prior=this.state.origins[key]??{},next={...prior,generation:item.begin.generation,sequence:item.begin.sequence,digest:item.commit.digest,observed_keys:item.observed?.keys??prior.observed_keys,observed_digests:item.observed?.digests??prior.observed_digests,last_rule_revision:item.begin.rule_revision,backfill_mode:item.backfillMode};if(item.clearBaseline){delete next.baseline;delete next.baseline_digests;delete next.baseline_tool_tail;}if(item.nativeDeferred)next.native_deferred=true;else delete next.native_deferred;if(!item.begin.carry)delete next.carry_refused;if(item.stamp&&item.begin.rule_id)next.capture_stamps={...prior.capture_stamps,[item.begin.rule_id]:{stamp:item.stamp,rule_revision:item.begin.rule_revision,backfill_mode:item.backfillMode}};this.state.origins[key]=next;this.state.outbox.shift();this.save();if(item.artifacts?.length)this.pruneSpill();latest=receipt;}return latest;}
|
|
705
706
|
stop(){if(this.timer)clearInterval(this.timer);if(this.heartbeatTimer)clearInterval(this.heartbeatTimer);this.abort.abort();for(const follower of this.followers?.values()??[])follower.controller.abort();return this.nativeEffects.close();}
|
|
706
707
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.80",
|
|
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",
|