@volter/supercode-orchestrator 0.5.209 → 0.5.210

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.
@@ -34,7 +34,7 @@ if (process.argv[2] === 'workflow') {
34
34
  catch(error) {noteCommandError(error);process.stderr.write(`supercode workflow: ${error.message??error}\n${error?.errcode!=null&&error.stack?`${error.stack}\n`:''}`);code=1;}
35
35
  // the board documents' git work this command queued (a ztrack board's commit and push) is finished before it exits
36
36
  try{const{settleDocuments}=await import('../board/git-document.mjs');await settleDocuments();}catch{/* nothing queued */}
37
- await Promise.all([process.stdout,process.stderr].map(stream=>new Promise(done=>stream.write('',done))));
37
+ await Promise.all([process.stdout,process.stderr].map(stream=>new Promise(done=>stream.destroyed?done():stream.write('',done))));
38
38
  process.exit(code);
39
39
  } else if (process.argv[2] === 'connect') {
40
40
  const {parseArgs}=await import('node:util');
@@ -3,6 +3,7 @@
3
3
  import { watch, existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
4
4
  import { dirname, basename, join, resolve } from 'node:path';
5
5
  import { createHash } from 'node:crypto';
6
+ import { once } from 'node:events';
6
7
  import { listBoards, openBoard, backingPaths } from './store.mjs';
7
8
 
8
9
  const identity = root => createHash('sha256').update(realpathSync(root)).digest('hex');
@@ -117,14 +118,47 @@ export async function* followBoardPublications({root,after,signal}) {
117
118
 
118
119
  export async function streamBoardPublications(options) {
119
120
  if(options.once&&options.after)throw new Error('--once requires a fresh snapshot; omit --after');
121
+ const parent=process.ppid, reader=Number(process.env.SUPERCODE_WORKFLOW_READER_PID??parent);
122
+ if(!Number.isSafeInteger(reader)||reader<1||reader>2147483647)throw new Error('Invalid SUPERCODE_WORKFLOW_READER_PID');
120
123
  const abort=new AbortController();const stop=()=>abort.abort();
124
+ let outputError=null;
125
+ const failed=error=>{outputError=error;stop();};
121
126
  process.once('SIGINT',stop);process.once('SIGTERM',stop);
127
+ process.stdout.on('error',failed);process.stdout.once('close',stop);
128
+ // Quiet streams never meet EPIPE. The connector names the real reader across native/npm/home hand-offs; direct
129
+ // callers use their parent. Unix also detects reparenting (including death before this command finished starting).
130
+ // On Windows ppid does not change, but probing the named reader still detects its exit.
131
+ const checkReader=()=>{
132
+ if(process.platform!=='win32'&&(parent===1||process.ppid!==parent)){stop();return;}
133
+ try{process.kill(reader,0);}catch(error){if(error.code==='ESRCH')stop();}
134
+ };
135
+ const lifetime=setInterval(checkReader,1000);lifetime.unref();checkReader();
136
+ const write=async frame=>{
137
+ if(abort.signal.aborted)return;
138
+ try{
139
+ if(!process.stdout.write(`${JSON.stringify(frame)}\n`))await once(process.stdout,'drain',{signal:abort.signal});
140
+ }catch(error){
141
+ if(error.name==='AbortError'&&abort.signal.aborted)return;
142
+ if(error.code==='EPIPE'){stop();return;}
143
+ throw error;
144
+ }
145
+ };
146
+ let code=0;
122
147
  try {
123
148
  for await(const frame of followBoardPublications({...options,signal:abort.signal})){
124
- if(!process.stdout.write(`${JSON.stringify(frame)}\n`))await new Promise(done=>process.stdout.once('drain',done));
149
+ if(abort.signal.aborted)break;
150
+ await write(frame);
151
+ if(abort.signal.aborted)break;
125
152
  if(options.once&&frame.type==='snapshot_end')break;
126
153
  }
127
- }catch(error){if(error.code==='resync_required'){process.stdout.write(`${JSON.stringify({type:'resync_required',reason:error.message})}\n`);return 1;}throw error;}
128
- finally{process.removeListener('SIGINT',stop);process.removeListener('SIGTERM',stop);}
129
- return 0;
154
+ }catch(error){if(error.code==='resync_required'){await write({type:'resync_required',reason:error.message});code=1;}else throw error;}
155
+ finally{
156
+ clearInterval(lifetime);process.removeListener('SIGINT',stop);process.removeListener('SIGTERM',stop);
157
+ // No final flush can drain into a dead or stalled reader. Returning the iterator above closes its board and
158
+ // watchers first; discard pending bytes on cancellation so the public entry can finish too.
159
+ if(abort.signal.aborted)process.stdout.destroy();
160
+ process.stdout.removeListener('error',failed);process.stdout.removeListener('close',stop);
161
+ }
162
+ if(outputError&&outputError.code!=='EPIPE')throw outputError;
163
+ return code;
130
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.5.209",
3
+ "version": "0.5.210",
4
4
  "type": "module",
5
5
  "description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
6
6
  "exports": {
@@ -32,7 +32,7 @@
32
32
  "check-types": "node scripts/generate-types.mjs --check"
33
33
  },
34
34
  "dependencies": {
35
- "@volter/supercode-teams": "^0.3.344",
35
+ "@volter/supercode-teams": "^0.3.346",
36
36
  "@volter/supercode-terminal": "^0.3.10",
37
37
  "@volter/supercode-harness-sdk": "^0.3.104",
38
38
  "@volter/teams": "0.2.98",
@@ -61,5 +61,6 @@
61
61
  "board",
62
62
  "apply",
63
63
  "README.md"
64
- ]
64
+ ],
65
+ "gitHead": "b2585dc5c6b40d48be96fd3c4148f2c53d7a7ef2"
65
66
  }