next-issue 2.5.6 → 2.5.8

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/dist/exec.mjs CHANGED
@@ -1,6 +1,8 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
1
2
  import { spawn } from 'node:child_process';
2
3
  let observer;
3
- let defaultTimeoutMs = 10 * 60_000;
4
+ const DEFAULT_TIMEOUT_MS = 10 * 60_000;
5
+ const defaultTimeout = new AsyncLocalStorage();
4
6
  const STDERR_TAIL = 200_000;
5
7
  const groups = new Set();
6
8
  const stoppers = new Set();
@@ -50,12 +52,12 @@ export function track(child) {
50
52
  export function setCommandObserver(next) {
51
53
  observer = next;
52
54
  }
53
- export function setDefaultTimeout(ms) {
54
- defaultTimeoutMs = ms;
55
+ export function withDefaultTimeout(ms, body) {
56
+ return defaultTimeout.run(ms, body);
55
57
  }
56
58
  export function run(command, args, options = {}) {
57
59
  const started = Date.now();
58
- const limit = options.timeoutMs ?? defaultTimeoutMs;
60
+ const limit = options.timeoutMs ?? defaultTimeout.getStore() ?? DEFAULT_TIMEOUT_MS;
59
61
  return new Promise((resolve, reject) => {
60
62
  const child = spawn(command, args, {
61
63
  cwd: options.cwd,
package/dist/github.mjs CHANGED
@@ -100,11 +100,25 @@ export function labelArgs(add, remove, current) {
100
100
  return args;
101
101
  }
102
102
  export async function setLabel(repo, kind, number, add, remove) {
103
- await ensureLabel(repo, add);
103
+ await createUnknownLabel(repo, add);
104
104
  const current = await labelsOf(repo, kind, number);
105
- await gh(repo, [kind, 'edit', String(number), ...labelArgs(add, remove, current)]);
105
+ const args = [kind, 'edit', String(number), ...labelArgs(add, remove, current)];
106
+ try {
107
+ await gh(repo, args);
108
+ }
109
+ catch (error) {
110
+ KNOWN_LABELS.delete(slug(repo));
111
+ const created = await createUnknownLabel(repo, add);
112
+ if (created === 'known') {
113
+ throw error;
114
+ }
115
+ if (created instanceof Error) {
116
+ throw created;
117
+ }
118
+ await gh(repo, args);
119
+ }
106
120
  }
107
- async function ensureLabel(repo, label) {
121
+ async function createUnknownLabel(repo, label) {
108
122
  let known = KNOWN_LABELS.get(slug(repo));
109
123
  if (known === undefined) {
110
124
  const raw = await gh(repo, ['label', 'list', '--limit', '1000', '--json', 'name']);
@@ -112,14 +126,16 @@ async function ensureLabel(repo, label) {
112
126
  KNOWN_LABELS.set(slug(repo), known);
113
127
  }
114
128
  if (known.has(label)) {
115
- return;
129
+ return 'known';
116
130
  }
117
- const result = await run('gh', ['label', 'create', label, '--repo', slug(repo)], {
118
- cwd: repo.root,
119
- });
120
- if (result.code === 0) {
121
- known.add(label);
131
+ try {
132
+ await ghOnce(repo, ['label', 'create', label]);
133
+ }
134
+ catch (error) {
135
+ return error instanceof Error ? error : new Error(String(error));
122
136
  }
137
+ known.add(label);
138
+ return 'created';
123
139
  }
124
140
  export async function findPr(repo, branch) {
125
141
  const raw = await gh(repo, [
@@ -0,0 +1,37 @@
1
+ export function isNewer(candidate, current) {
2
+ const [leftCore = '', ...leftRest] = candidate.replace(/\+.*$/, '').split('-');
3
+ const [rightCore = '', ...rightRest] = current.replace(/\+.*$/, '').split('-');
4
+ const left = leftCore.split('.').map(Number);
5
+ const right = rightCore.split('.').map(Number);
6
+ for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
7
+ const difference = (left[index] ?? 0) - (right[index] ?? 0);
8
+ if (difference !== 0) {
9
+ return difference > 0;
10
+ }
11
+ }
12
+ const leftPrerelease = leftRest.length > 0 ? leftRest.join('-').split('.') : [];
13
+ const rightPrerelease = rightRest.length > 0 ? rightRest.join('-').split('.') : [];
14
+ if (leftPrerelease.length === 0 || rightPrerelease.length === 0) {
15
+ return leftPrerelease.length === 0 && rightPrerelease.length > 0;
16
+ }
17
+ for (let index = 0; index < Math.max(leftPrerelease.length, rightPrerelease.length); index += 1) {
18
+ const leftPart = leftPrerelease[index];
19
+ const rightPart = rightPrerelease[index];
20
+ if (leftPart === undefined || rightPart === undefined) {
21
+ return rightPart === undefined;
22
+ }
23
+ if (leftPart === rightPart) {
24
+ continue;
25
+ }
26
+ const leftNumeric = /^\d+$/.test(leftPart);
27
+ const rightNumeric = /^\d+$/.test(rightPart);
28
+ if (leftNumeric && rightNumeric) {
29
+ return Number(leftPart) > Number(rightPart);
30
+ }
31
+ if (leftNumeric || rightNumeric) {
32
+ return rightNumeric;
33
+ }
34
+ return leftPart > rightPart;
35
+ }
36
+ return false;
37
+ }
package/dist/run.mjs CHANGED
@@ -2,7 +2,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import { writeFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { loadConfig } from './config.mjs';
5
- import { setCommandObserver, setDefaultTimeout } from './exec.mjs';
5
+ import { setCommandObserver, withDefaultTimeout } from './exec.mjs';
6
6
  import { detectRepo } from './git.mjs';
7
7
  import { defaultBranch } from './github.mjs';
8
8
  import { exitCode, runIssues, watchIssues } from './loop.mjs';
@@ -25,7 +25,9 @@ export function recordCommands(recorder, work) {
25
25
  }
26
26
  export async function runRepo(root, args, controls) {
27
27
  const config = await loadConfig(root);
28
- setDefaultTimeout(config.commandTimeoutMinutes * 60_000);
28
+ return withDefaultTimeout(config.commandTimeoutMinutes * 60_000, () => runWithConfig(root, args, controls, config));
29
+ }
30
+ async function runWithConfig(root, args, controls, config) {
29
31
  const repo = await detectRepo(root, config.remote);
30
32
  const recorder = await Recorder.create(repo.root, args.level, controls.onEvent);
31
33
  const pruned = await pruneRuns(repo.root, config.keepRunDays);
package/dist/serve.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { rmSync } from 'node:fs';
2
- import { chmod, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import { readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import { createServer } from 'node:http';
4
4
  import { join } from 'node:path';
5
5
  import { getAgentDir } from '@earendil-works/pi-coding-agent';
@@ -8,6 +8,7 @@ import { parseArgs } from './args.mjs';
8
8
  import { stopChildren } from './exec.mjs';
9
9
  import { message } from './observe.mjs';
10
10
  import { applyEvent, startProgress } from './progress.mjs';
11
+ import { isNewer } from './protocol.mjs';
11
12
  import { runRepo } from './run.mjs';
12
13
  import { packageVersion } from './version.mjs';
13
14
  export function socketPaths(agentDir = getAgentDir()) {
@@ -16,17 +17,6 @@ export function socketPaths(agentDir = getAgentDir()) {
16
17
  lockPath: join(agentDir, 'next-issue.lock'),
17
18
  };
18
19
  }
19
- export function isNewer(candidate, current) {
20
- const left = candidate.split('.').map(Number);
21
- const right = current.split('.').map(Number);
22
- for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
23
- const difference = (left[index] ?? 0) - (right[index] ?? 0);
24
- if (difference !== 0) {
25
- return difference > 0;
26
- }
27
- }
28
- return false;
29
- }
30
20
  function alive(pid) {
31
21
  try {
32
22
  process.kill(pid, 0);
@@ -191,12 +181,17 @@ export async function startWorker(options) {
191
181
  });
192
182
  await new Promise((resolve, reject) => {
193
183
  server.once('error', reject);
194
- server.listen(options.socketPath, () => {
195
- server.off('error', reject);
196
- resolve();
197
- });
184
+ const umask = process.umask(0o177);
185
+ try {
186
+ server.listen(options.socketPath, () => {
187
+ server.off('error', reject);
188
+ resolve();
189
+ });
190
+ }
191
+ finally {
192
+ process.umask(umask);
193
+ }
198
194
  });
199
- await chmod(options.socketPath, 0o600);
200
195
  return { closed, close };
201
196
  }
202
197
  export async function serve() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-issue",
3
- "version": "2.5.6",
3
+ "version": "2.5.8",
4
4
  "description": "Autonomous issue harness driven by the pi SDK",
5
5
  "keywords": [
6
6
  "github",