@knights-eye-chess/ai-analysis 0.1.0-dev.14 → 0.1.0-dev.17

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/NPM-PUBLISHING.md CHANGED
@@ -2,16 +2,20 @@
2
2
 
3
3
  The owner authorized public npm prereleases on 10 October 2026. Use **`next`** and preserve any existing `latest` tag. Licensing is documented in [LICENSING.md](LICENSING.md).
4
4
 
5
+ The first registry prerelease is `0.1.0-dev.14`. The source checkout now prepares
6
+ `0.1.0-dev.17` with Move Classifier `0.1.0-dev.13`.
7
+ This next candidate has not been published; `@next` remains the verified registry release.
8
+
5
9
  ## Prepare the exact candidate
6
10
 
7
- Use Node 24 and npm 11:
11
+ Use Node 24.19.0 and npm 11.9.0:
8
12
 
9
13
  ```sh
10
14
  npm ci
11
15
  npm run publish:prepare
12
16
  ```
13
17
 
14
- Review `artifacts/knights-eye-chess-ai-analysis-0.1.0-dev.14.tgz`, including its files, exports, declarations, licenses and exact registry dependencies. Reusable source and offline owner tests are intentional; application traces, credentials, vendor archives and source lockfiles are excluded. The checkout stays `private: true`; publish only the staged archive. Archive contents are immutable: use a new version for changed bytes.
18
+ Review `artifacts/knights-eye-chess-ai-analysis-0.1.0-dev.17.tgz`, including its files, exports, declarations, licenses and exact registry dependencies. Reusable source and offline owner tests are intentional; application traces, credentials, vendor archives and source lockfiles are excluded. The checkout stays `private: true`; publish only the staged archive. Archive contents are immutable: use a new version for changed bytes.
15
19
 
16
20
  Before every coordinated release, run `node tools/release-tooling/sync.mjs --check` in the reviewed app checkout alongside all six component revisions. Record those commits and the result.
17
21
 
package/README.md CHANGED
@@ -22,19 +22,17 @@ engine hosting, persistence and privacy settings for chess data and traces.
22
22
 
23
23
  ## Get started
24
24
 
25
- Use Node 24 or newer. This preview has not been published to npm. From a checkout
26
- of [this repository](https://github.com/knights-eye-chess/ai-analysis):
25
+ Use Node 24 or newer. Install the published prerelease with the chess library
26
+ imported by the example below:
27
27
 
28
28
  ```sh
29
- npm ci
30
- npm run pack:checked
29
+ npm install @knights-eye-chess/ai-analysis@next chess.js@1.4.0
31
30
  ```
32
31
 
33
- Install the resulting archive in your app:
34
-
35
- ```sh
36
- npm install /absolute/path/to/ai-analysis/artifacts/knights-eye-chess-ai-analysis-0.1.0-dev.12.tgz
37
- ```
32
+ The first reviewed registry candidate is `0.1.0-dev.14`; pin the exact version
33
+ for a reproducible install. Source developers can use `npm ci` and
34
+ `npm run pack:checked` in this checkout.
35
+ [SOURCE-ORIGIN.json](SOURCE-ORIGIN.json) records historical archive provenance.
38
36
 
39
37
  This runnable example uses a local connector with a fixed answer. It needs no
40
38
  provider credentials or engine, and demonstrates host wiring rather than model
package/npm-release.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "registryDependencies": {
3
- "@knights-eye-chess/move-classifier": "0.1.0-dev.10"
3
+ "@knights-eye-chess/move-classifier": "0.1.0-dev.13"
4
4
  }
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knights-eye-chess/ai-analysis",
3
- "version": "0.1.0-dev.14",
3
+ "version": "0.1.0-dev.17",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -51,7 +51,7 @@
51
51
  "dependencies": {
52
52
  "@ai-sdk/provider": "4.0.9",
53
53
  "@ai-sdk/provider-utils": "5.0.34",
54
- "@knights-eye-chess/move-classifier": "0.1.0-dev.10",
54
+ "@knights-eye-chess/move-classifier": "0.1.0-dev.13",
55
55
  "@types/json-schema": "7.0.15",
56
56
  "@types/node": "22.19.19",
57
57
  "ai": "7.0.85",
@@ -11,15 +11,15 @@ export function npmFailure(result) {
11
11
  for (const output of [result.stdout, result.stderr]) {
12
12
  try { const parsed = JSON.parse(output || '{}'); error ||= parsed.error; } catch {}
13
13
  }
14
- const allowed = new Set(['EOTP', 'E401', 'E403', 'E404', 'E409', 'E422', 'E429', 'E500', 'E502', 'E503', 'E504', 'EPROVENANCE', 'EUSAGE', 'ENEEDAUTH', 'ENOTP', 'EPRIVATE', 'EPUBLISHCONFLICT', 'EUNSCOPED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND']);
15
- const candidate = typeof error?.code === 'string' ? error.code : (result.stderr || '').match(/^npm (?:error|ERR!) code ([A-Z][A-Z0-9]+)\s*$/m)?.[1];
14
+ const allowed = new Set(['EOTP', 'E401', 'E403', 'E404', 'E408', 'E409', 'E422', 'E429', 'E500', 'E502', 'E503', 'E504', 'EPROVENANCE', 'EUSAGE', 'ENEEDAUTH', 'ENOTP', 'EPRIVATE', 'EPUBLISHCONFLICT', 'EUNSCOPED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'EAI_AGAIN', 'ECONNREFUSED']);
15
+ const candidate = typeof error?.code === 'string' ? error.code : result.error?.code || (result.stderr || '').match(/^npm (?:error|ERR!) code ([A-Z][A-Z0-9_]+)\s*$/m)?.[1];
16
16
  const code = allowed.has(candidate) ? candidate : 'UNKNOWN';
17
17
  const rawStatus = error?.statusCode ?? error?.status ?? (/^E[45]\d\d$/.test(code) ? Number(code.slice(1)) : undefined);
18
18
  const httpStatus = Number.isInteger(rawStatus) && rawStatus >= 400 && rawStatus <= 599 ? rawStatus : 'unknown';
19
19
  const exitStatus = Number.isInteger(result.status) && result.status >= 0 && result.status <= 255 ? result.status : 'unknown';
20
20
  return {code, httpStatus, exitStatus};
21
21
  }
22
- export function publishReviewed({root, version, sha256, env = process.env, run = spawnSync, wait = () => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2000)}) {
22
+ export function publishReviewed({root, version, sha256, env = process.env, run = spawnSync, wait = milliseconds => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds), now = Date.now}) {
23
23
  if (env.GITHUB_ACTIONS !== 'true' || env.GITHUB_EVENT_NAME !== 'workflow_dispatch' || env.GITHUB_REF !== 'refs/heads/dev') throw Error('Publishing requires manual GitHub Actions dispatch on dev');
24
24
  if (env.GITHUB_REPOSITORY_VISIBILITY !== 'public') throw Error('Provenance publishing requires a public GitHub repository');
25
25
  if (!env.NODE_AUTH_TOKEN?.trim()) throw Error('Missing NODE_AUTH_TOKEN; publication has no authentication fallback');
@@ -35,26 +35,31 @@ export function publishReviewed({root, version, sha256, env = process.env, run =
35
35
  const pkg = JSON.parse(metadataResult.stdout);
36
36
  if (pkg.name !== source.name || pkg.version !== version || pkg.private || pkg.publishConfig?.access !== 'public' || pkg.publishConfig?.registry !== 'https://registry.npmjs.org/' || pkg.publishConfig?.tag !== 'next') throw Error('Expected reviewed staged registry candidate');
37
37
  const integrity = 'sha512-' + createHash('sha512').update(bytes).digest('base64');
38
- const lookupArgs = ['view', `${pkg.name}@${version}`, '--json', '--registry', 'https://registry.npmjs.org/'];
38
+ const lookupArgs = ['view', `${pkg.name}@${version}`, '--json', '--registry', 'https://registry.npmjs.org/', '--prefer-online', '--fetch-retries=0', '--fetch-timeout=15000'];
39
39
  const lookupOptions = { ...options, timeout: 15000 };
40
40
  const lookup = run('npm', lookupArgs, lookupOptions);
41
41
  if (lookup.status === 0) {
42
- const existing = JSON.parse(lookup.stdout);
42
+ let existing;
43
+ try { existing = JSON.parse(lookup.stdout); } catch { throw Error('Registry lookup returned invalid JSON; refusing publication'); }
43
44
  if (existing.name !== pkg.name || existing.version !== version || existing.dist?.integrity !== integrity) throw Error('Published version exists with different identity or integrity');
44
45
  return { name: pkg.name, version, sha256, integrity, published: false, alreadyPublished: true, registryConfirmed: true };
45
46
  }
46
- let error;
47
- try { error = JSON.parse(lookup.stdout || '{}').error; } catch {}
48
- if (error?.code !== 'E404') throw Error('Registry lookup failed; refusing publication');
47
+ const absent = npmFailure(lookup);
48
+ if (absent.code !== 'E404') throw Error(`Registry lookup failed; refusing publication: code=${absent.code}, httpStatus=${absent.httpStatus}, exitStatus=${absent.exitStatus}`);
49
49
  const result = run('npm', ['publish', archive, '--ignore-scripts', '--access', 'public', '--tag', 'next', '--registry', 'https://registry.npmjs.org/', '--provenance', '--json'], options);
50
50
  if (result.status !== 0) {
51
51
  const {code, httpStatus, exitStatus} = npmFailure(result);
52
52
  throw Error(`npm publish failed for ${pkg.name}@${version}: code=${code}, httpStatus=${httpStatus}, exitStatus=${exitStatus}`);
53
53
  }
54
- let visible = false;
55
- for (let attempt = 0; attempt < 3; attempt++) {
56
- if (attempt) wait();
57
- const confirmation = run('npm', lookupArgs, lookupOptions);
54
+ let visible = false, attempts = 0, lastFailure;
55
+ const started = now(), deadline = started + 240000;
56
+ const transientCodes = new Set(['E404', 'E408', 'E429', 'E500', 'E502', 'E503', 'E504', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'EAI_AGAIN', 'ECONNREFUSED']);
57
+ for (let attempt = 0; attempt < 25; attempt++) {
58
+ if (attempt) wait(Math.min(10000, Math.max(0, deadline - now())));
59
+ const remaining = deadline - now();
60
+ if (remaining <= 0) break;
61
+ const confirmation = run('npm', lookupArgs, { ...lookupOptions, timeout: Math.min(15000, remaining) });
62
+ attempts++;
58
63
  if (confirmation.status === 0) {
59
64
  let existing;
60
65
  try { existing = JSON.parse(confirmation.stdout); } catch {}
@@ -62,8 +67,16 @@ export function publishReviewed({root, version, sha256, env = process.env, run =
62
67
  visible = true;
63
68
  break;
64
69
  }
70
+ lastFailure = npmFailure(confirmation);
71
+ const {code, httpStatus, exitStatus} = lastFailure;
72
+ if (['E401', 'E403', 'ENEEDAUTH', 'EOTP', 'ENOTP'].includes(code) || [401, 403].includes(httpStatus) || !(transientCodes.has(code) || [408, 429, 500, 502, 503, 504].includes(httpStatus))) {
73
+ throw Error(`Postpublication registry confirmation failed for ${pkg.name}@${version}: code=${code}, httpStatus=${httpStatus}, exitStatus=${exitStatus}; publication was not retried`);
74
+ }
75
+ }
76
+ if (!visible) {
77
+ const {code = 'UNKNOWN', httpStatus = 'unknown', exitStatus = 'unknown'} = lastFailure || {};
78
+ throw Error(`npm publication returned success but registry confirmation pending for ${pkg.name}@${version}: attempts=${attempts}, elapsedMs=${Math.max(0, Math.min(240000, now() - started))}, code=${code}, httpStatus=${httpStatus}, exitStatus=${exitStatus}; bounded confirmation ended, publication was not retried`);
65
79
  }
66
- if (!visible) throw Error(`npm publication returned success but registry confirmation failed for ${pkg.name}@${version}; publication was not retried`);
67
80
  return { name: pkg.name, version, sha256, integrity, published: true, alreadyPublished: false, registryConfirmed: true };
68
81
  }
69
82
  if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
@@ -3,7 +3,7 @@
3
3
  "canonical": "knightseye-net/tools/release-tooling",
4
4
  "sha256": {
5
5
  "scripts/pack.mjs": "334aa4041e59b14fd2b072969f50beea64449136f7f9146d63d7277fc2e4808a",
6
- "scripts/publish-reviewed.mjs": "e9e8a6f5eeff37d5050b96e75fda2fa33084b1f9bc5d5a6688b0aef737dd51c5",
6
+ "scripts/publish-reviewed.mjs": "b91c7aa578bc34c490b42006038de4ee8b9c596a592aa079bacd836d02feb647",
7
7
  ".github/workflows/npm-publish.yml": "b25cec136db205d69a407284dae2bb66f3d56be13ed2a1367f0db167a232050d",
8
8
  "scripts/verify-release.mjs": "d0f9baead779e553385bb7c26f1238ad4e60a189a818d4db4f721a1e5f88dedc",
9
9
  "scripts/update-dependency.mjs": "9919f172be33973f124eabf56c1dc70e41e941e601783ef01aba1c47dd70db9d",