enigma-memory 0.1.13 → 0.1.14
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/README.md +36 -17
- package/apps/cli/bin/enigma.mjs +320 -42
- package/deploy/docker-compose.local-production-simulation.yml +10 -11
- package/docs/benchmark-attestation-network.md +487 -487
- package/docs/benchmark-reproducibility.md +10 -9
- package/docs/demo-proof-network.md +275 -275
- package/docs/developer-ecosystem.md +223 -223
- package/docs/developer-proof-quickstart.md +325 -325
- package/docs/enigma-memory-ready-conformance.md +376 -376
- package/docs/hosted-cloud-product.md +10 -0
- package/docs/install-anywhere.md +34 -17
- package/docs/installers-and-desktop.md +9 -7
- package/docs/proof-network-build-notes.md +240 -240
- package/docs/proof-network.md +257 -257
- package/docs/sdk-api.md +324 -324
- package/docs/solana-devnet-acceptance.md +48 -0
- package/docs/solana-proof-rail.md +453 -453
- package/examples/ci/github-actions.yml +6 -8
- package/package.json +8 -1
- package/packages/mcp-server/src/index.js +1 -1
- package/packages/passport/src/index.js +9 -5
- package/scripts/build-benchmark-proof-release.mjs +391 -0
- package/scripts/build-goal-completion-audit.mjs +11 -5
- package/scripts/build-hosted-api-key-lifecycle.mjs +1 -1
- package/scripts/build-hosted-customer-lifecycle.mjs +1 -1
- package/scripts/build-installer-assets.mjs +126 -10
- package/scripts/build-production-handoff-packet.mjs +7 -6
- package/scripts/build-production-unblocker.mjs +409 -0
- package/scripts/build-proof-network-packet.mjs +1 -1
- package/scripts/release-audit.mjs +71 -2
- package/scripts/run-standard-memory-benchmarks.mjs +1 -1
- package/scripts/wait-for-backend-ready.mjs +4 -2
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
|
|
7
7
|
export const INSTALLER_ASSET_SCHEMA = 'enigma.installer_assets.v1';
|
|
8
8
|
export const INSTALLER_ASSET_PACKAGE = 'enigma-memory';
|
|
9
|
-
export const INSTALLER_ASSET_VERSION = '0.1.
|
|
9
|
+
export const INSTALLER_ASSET_VERSION = '0.1.14';
|
|
10
10
|
export const INSTALLER_ASSET_GENERATED_AT = '1970-01-01T00:00:00.000Z';
|
|
11
11
|
|
|
12
12
|
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
@@ -94,14 +94,48 @@ function assertPublicSafe(content, label = 'asset') {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function windowsInstallerScript() {
|
|
97
|
-
return `# Enigma Memory source installer for Windows PowerShell
|
|
97
|
+
return `# Enigma Memory source installer for Windows PowerShell.
|
|
98
|
+
# Dry-run is the default; pass -Execute to mutate global npm state and create local setup files.
|
|
99
|
+
param(
|
|
100
|
+
[switch]$Execute,
|
|
101
|
+
[string]$Bundle = '.\\.enigma\\bundle.json'
|
|
102
|
+
)
|
|
103
|
+
$ErrorActionPreference = 'Stop'
|
|
104
|
+
$PackageName = '${INSTALLER_ASSET_PACKAGE}'
|
|
105
|
+
$NextClientConnect = 'enigma connect <client> --dry-run'
|
|
106
|
+
|
|
107
|
+
Write-Output 'Enigma Memory Windows installer source asset'
|
|
108
|
+
Write-Output 'Default mode: dry-run. No native .exe, code-signing, tokens, or hosted credentials are included.'
|
|
109
|
+
Write-Output "Package: $PackageName"
|
|
110
|
+
Write-Output "Bundle: $Bundle"
|
|
111
|
+
|
|
112
|
+
$steps = @(
|
|
113
|
+
'npm install -g enigma-memory',
|
|
114
|
+
'enigma test-drive --dry-run',
|
|
115
|
+
'enigma setup --bundle <bundle> --overwrite',
|
|
116
|
+
'enigma doctor',
|
|
117
|
+
"Next client-connect preview: $NextClientConnect"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if (-not $Execute) {
|
|
121
|
+
Write-Output 'Preview only. Re-run with -Execute after reviewing these steps.'
|
|
122
|
+
$steps | ForEach-Object { Write-Output "DRY-RUN: $_" }
|
|
123
|
+
exit 0
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
npm install -g $PackageName
|
|
127
|
+
enigma test-drive --dry-run
|
|
128
|
+
enigma setup --bundle $Bundle --overwrite
|
|
129
|
+
enigma doctor
|
|
130
|
+
Write-Output "Next client-connect preview: $NextClientConnect"
|
|
131
|
+
`;
|
|
98
132
|
}
|
|
99
133
|
|
|
100
134
|
function linuxInstallerScript() {
|
|
101
135
|
return [
|
|
102
136
|
'#!/usr/bin/env sh',
|
|
103
137
|
'# Enigma Memory source installer for Linux.',
|
|
104
|
-
'# Dry-run is the default; pass --execute to mutate global npm state and create local
|
|
138
|
+
'# Dry-run is the default; pass --execute to mutate global npm state and create local setup files.',
|
|
105
139
|
'set -eu',
|
|
106
140
|
'',
|
|
107
141
|
'execute=0',
|
|
@@ -112,7 +146,7 @@ function linuxInstallerScript() {
|
|
|
112
146
|
' --dry-run) execute=0 ;;',
|
|
113
147
|
' --bundle) shift; bundle="${1:-}" ;;',
|
|
114
148
|
' --help|-h)',
|
|
115
|
-
" printf '%s\\n' 'Usage: ./install-linux.sh [--execute] [--bundle ./.enigma/bundle.json]'",
|
|
149
|
+
" printf '%s\\n' 'Usage: ./install-linux.sh [--execute|--dry-run] [--bundle ./.enigma/bundle.json]'",
|
|
116
150
|
' exit 0',
|
|
117
151
|
' ;;',
|
|
118
152
|
' *)',
|
|
@@ -124,6 +158,7 @@ function linuxInstallerScript() {
|
|
|
124
158
|
'done',
|
|
125
159
|
'',
|
|
126
160
|
`package='${INSTALLER_ASSET_PACKAGE}'`,
|
|
161
|
+
"next_client_connect='enigma connect <client> --dry-run'",
|
|
127
162
|
"printf '%s\\n' 'Enigma Memory Linux installer source asset'",
|
|
128
163
|
"printf '%s\\n' 'Default mode: dry-run. No native package, signing key, tokens, or hosted credentials are included.'",
|
|
129
164
|
'printf \'%s\\n\' "Package: $package"',
|
|
@@ -132,24 +167,86 @@ function linuxInstallerScript() {
|
|
|
132
167
|
'if [ "$execute" -ne 1 ]; then',
|
|
133
168
|
" printf '%s\\n' 'Preview only. Re-run with --execute after reviewing these steps.'",
|
|
134
169
|
" printf '%s\\n' 'DRY-RUN: npm install -g enigma-memory'",
|
|
135
|
-
" printf '%s\\n' 'DRY-RUN: enigma
|
|
170
|
+
" printf '%s\\n' 'DRY-RUN: enigma test-drive --dry-run'",
|
|
171
|
+
" printf '%s\\n' 'DRY-RUN: enigma setup --bundle <bundle> --overwrite'",
|
|
136
172
|
" printf '%s\\n' 'DRY-RUN: enigma doctor'",
|
|
173
|
+
' printf \'%s\\n\' "DRY-RUN: Next client-connect preview: $next_client_connect"',
|
|
137
174
|
' exit 0',
|
|
138
175
|
'fi',
|
|
139
176
|
'',
|
|
140
177
|
'npm install -g "$package"',
|
|
141
|
-
'enigma
|
|
178
|
+
'enigma test-drive --dry-run',
|
|
179
|
+
'enigma setup --bundle "$bundle" --overwrite',
|
|
142
180
|
'enigma doctor',
|
|
181
|
+
'printf \'%s\\n\' "Next client-connect preview: $next_client_connect"',
|
|
143
182
|
'',
|
|
144
183
|
].join('\n');
|
|
145
184
|
}
|
|
146
185
|
|
|
147
186
|
function homebrewFormulaDraft() {
|
|
148
|
-
return `# Draft only. This formula is not submitted to a Homebrew tap by this generator
|
|
187
|
+
return `# Draft only. This formula is not submitted to a Homebrew tap by this generator.
|
|
188
|
+
# Release engineering must replace the tarball URL and sha256 after a real source archive exists.
|
|
189
|
+
# Post-install smoke path:
|
|
190
|
+
# enigma test-drive --dry-run
|
|
191
|
+
# enigma setup --overwrite
|
|
192
|
+
# enigma doctor
|
|
193
|
+
# enigma connect <client> --dry-run
|
|
194
|
+
class EnigmaMemory < Formula
|
|
195
|
+
desc "Provider-agnostic AI memory passport and offline-verifiable proof layer"
|
|
196
|
+
homepage "https://github.com/Enigma-Memory/enigma-memory"
|
|
197
|
+
url "https://example.invalid/enigma-memory-${INSTALLER_ASSET_VERSION}.tar.gz"
|
|
198
|
+
sha256 "REPLACE_WITH_RELEASE_TARBALL_SHA256"
|
|
199
|
+
license "Apache-2.0"
|
|
200
|
+
|
|
201
|
+
depends_on "node"
|
|
202
|
+
|
|
203
|
+
def install
|
|
204
|
+
system "npm", "install", *Language::Node.local_npm_install_args
|
|
205
|
+
bin.install_symlink libexec/"bin/enigma"
|
|
206
|
+
bin.install_symlink libexec/"bin/enigma-verify"
|
|
207
|
+
bin.install_symlink libexec/"bin/enigma-mcp"
|
|
208
|
+
bin.install_symlink libexec/"bin/enigma-relay"
|
|
209
|
+
bin.install_symlink libexec/"bin/enigma-gateway"
|
|
210
|
+
bin.install_symlink libexec/"bin/enigma-native-host"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
test do
|
|
214
|
+
assert_match "enigma", shell_output("#{bin}/enigma --help")
|
|
215
|
+
assert_match "enigma.test_drive.v1", shell_output("#{bin}/enigma test-drive --dry-run")
|
|
216
|
+
assert_match "enigma.setup.v1", shell_output("#{bin}/enigma setup --dry-run")
|
|
217
|
+
shell_output("#{bin}/enigma doctor")
|
|
218
|
+
puts "Next client-connect preview: enigma connect <client> --dry-run"
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
`;
|
|
149
222
|
}
|
|
150
223
|
|
|
151
224
|
function macosPkgReadme() {
|
|
152
|
-
return `# macOS pkgbuild source manifest
|
|
225
|
+
return `# macOS pkgbuild source manifest
|
|
226
|
+
|
|
227
|
+
This directory is an honest source plan for a future macOS package. It is not a signed .pkg and it does not claim notarization.
|
|
228
|
+
|
|
229
|
+
Current supported path:
|
|
230
|
+
|
|
231
|
+
\`\`\`sh
|
|
232
|
+
npm install -g ${INSTALLER_ASSET_PACKAGE}
|
|
233
|
+
enigma test-drive --dry-run
|
|
234
|
+
enigma setup --overwrite
|
|
235
|
+
enigma doctor
|
|
236
|
+
enigma connect <client> --dry-run
|
|
237
|
+
\`\`\`
|
|
238
|
+
|
|
239
|
+
\`enigma connect <client> --dry-run\` is the next client-connect preview command. Remove \`--dry-run\` only after reviewing the planned client config change.
|
|
240
|
+
|
|
241
|
+
Blockers before shipping a native .pkg:
|
|
242
|
+
|
|
243
|
+
- A reproducible package staging tree for the npm-installed command shims.
|
|
244
|
+
- macOS pkgbuild/productbuild tooling on a macOS release runner.
|
|
245
|
+
- Developer ID Installer certificate, signing identity selection, and notarization workflow.
|
|
246
|
+
- Human review that package scripts do not print local absolute paths, credentials, account identifiers, raw memory, or provider transcripts.
|
|
247
|
+
|
|
248
|
+
The generated JSON manifest in this directory records those blockers explicitly.
|
|
249
|
+
`;
|
|
153
250
|
}
|
|
154
251
|
|
|
155
252
|
function macosPkgManifest() {
|
|
@@ -159,13 +256,19 @@ function macosPkgManifest() {
|
|
|
159
256
|
version: INSTALLER_ASSET_VERSION,
|
|
160
257
|
generated_native_pkg: false,
|
|
161
258
|
source_only: true,
|
|
162
|
-
package_id: '
|
|
259
|
+
package_id: '<reverse-dns-package-id>',
|
|
163
260
|
install_prefix: '<homebrew-or-npm-managed-prefix>',
|
|
164
261
|
commands: [
|
|
165
262
|
['npm', 'install', '-g', INSTALLER_ASSET_PACKAGE],
|
|
166
|
-
['enigma', '
|
|
263
|
+
['enigma', 'test-drive', '--dry-run'],
|
|
264
|
+
['enigma', 'setup', '--overwrite'],
|
|
167
265
|
['enigma', 'doctor'],
|
|
168
266
|
],
|
|
267
|
+
next_client_connect: ['enigma', 'connect', '<client>', '--dry-run'],
|
|
268
|
+
execution_gate: {
|
|
269
|
+
default_mode: 'dry-run',
|
|
270
|
+
mutation_requires: ['--execute', '-Execute'],
|
|
271
|
+
},
|
|
169
272
|
blockers: [
|
|
170
273
|
{ code: 'MACOS_PKGBUILD_TOOLING_REQUIRED', message: 'pkgbuild/productbuild must run on a macOS release runner.' },
|
|
171
274
|
{ code: 'MACOS_SIGNING_REQUIRED', message: 'A Developer ID Installer certificate and notarization workflow are required before distributing a .pkg.' },
|
|
@@ -215,10 +318,23 @@ export function buildInstallerAssets(options = {}, runtime = {}) {
|
|
|
215
318
|
generated_native_installers: false,
|
|
216
319
|
files,
|
|
217
320
|
native_installers: nativeInstallerBlockers(runtime.tooling),
|
|
321
|
+
installer_smoke: {
|
|
322
|
+
default_preview_commands: [
|
|
323
|
+
'npm install -g enigma-memory',
|
|
324
|
+
'enigma test-drive --dry-run',
|
|
325
|
+
'enigma setup --overwrite',
|
|
326
|
+
'enigma doctor',
|
|
327
|
+
],
|
|
328
|
+
next_client_connect: 'enigma connect <client> --dry-run',
|
|
329
|
+
mutation_requires: ['--execute', '-Execute'],
|
|
330
|
+
},
|
|
218
331
|
safety: {
|
|
219
332
|
embeds_tokens: false,
|
|
220
333
|
embeds_local_absolute_paths: false,
|
|
221
334
|
embeds_raw_memory: false,
|
|
335
|
+
embeds_account_ids: false,
|
|
336
|
+
embeds_provider_responses: false,
|
|
337
|
+
embeds_private_keys: false,
|
|
222
338
|
default_dry_run: true,
|
|
223
339
|
requires_write_flag_for_filesystem_mutation: true,
|
|
224
340
|
},
|
|
@@ -344,6 +344,13 @@ export async function buildProductionHandoffPacket(input = {}, options = {}) {
|
|
|
344
344
|
const liveUrl = optionalText(input.liveUrl, domain ? `https://${domain}/` : null);
|
|
345
345
|
const expectTitle = optionalText(input.expectTitle, 'Enigma');
|
|
346
346
|
|
|
347
|
+
const providedInfrastructure = requireInfrastructureReadinessResult(await loadInfrastructureReadiness(input));
|
|
348
|
+
const operatorPacket = await loadOperatorAcceptancePacket(input) ?? await buildOperatorAcceptancePacket({ generated_at: generatedAt });
|
|
349
|
+
assertNoSecretOutput('operator acceptance packet', operatorPacket);
|
|
350
|
+
const operatorAcceptance = validateOperatorAcceptancePacket(operatorPacket, { generated_at: generatedAt });
|
|
351
|
+
if (operatorAcceptance.ok === true) requireInfrastructureReadinessResult(operatorPacket.readiness);
|
|
352
|
+
const providedReleaseAudit = requireReleaseAuditResult(await loadReleaseAudit(input));
|
|
353
|
+
|
|
347
354
|
const pages = await buildCloudflarePagesReleasePacket({
|
|
348
355
|
site,
|
|
349
356
|
projectName,
|
|
@@ -356,7 +363,6 @@ export async function buildProductionHandoffPacket(input = {}, options = {}) {
|
|
|
356
363
|
fetchImpl: options.fetchImpl ?? globalThis.fetch,
|
|
357
364
|
});
|
|
358
365
|
|
|
359
|
-
const providedInfrastructure = requireInfrastructureReadinessResult(await loadInfrastructureReadiness(input));
|
|
360
366
|
const infrastructure = providedInfrastructure ?? await runInfrastructureReadiness({
|
|
361
367
|
manifest: await buildProductionReadinessManifest({
|
|
362
368
|
env,
|
|
@@ -369,11 +375,6 @@ export async function buildProductionHandoffPacket(input = {}, options = {}) {
|
|
|
369
375
|
now: new Date(generatedAt),
|
|
370
376
|
fetchImpl: options.fetchImpl ?? globalThis.fetch,
|
|
371
377
|
});
|
|
372
|
-
const operatorPacket = await loadOperatorAcceptancePacket(input) ?? await buildOperatorAcceptancePacket({ generated_at: generatedAt });
|
|
373
|
-
assertNoSecretOutput('operator acceptance packet', operatorPacket);
|
|
374
|
-
const operatorAcceptance = validateOperatorAcceptancePacket(operatorPacket, { generated_at: generatedAt });
|
|
375
|
-
if (operatorAcceptance.ok === true) requireInfrastructureReadinessResult(operatorPacket.readiness);
|
|
376
|
-
const providedReleaseAudit = requireReleaseAuditResult(await loadReleaseAudit(input));
|
|
377
378
|
|
|
378
379
|
const hostedProbe = buildHostedProbeWorkerBundle({ generated_at: generatedAt });
|
|
379
380
|
const pageSummary = summarizePages(pages);
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
export const PRODUCTION_UNBLOCKER_SCHEMA = 'enigma.production_unblocker.v1';
|
|
7
|
+
export const CURRENT_PUBLIC_PACKAGE_VERSION = '0.1.13';
|
|
8
|
+
|
|
9
|
+
const STATUS_VALUES = Object.freeze([
|
|
10
|
+
'ready_now',
|
|
11
|
+
'contract_ready',
|
|
12
|
+
'blocked_external_dependency',
|
|
13
|
+
'operator_evidence_required',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const SECRET_VALUE_RE = /(?:Bearer\s+[A-Za-z0-9._~+/=-]{12,}|Basic\s+[A-Za-z0-9+/=-]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|https?:\/\/[^\s/@]+:[^\s/@]+@|sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16}|cf-[A-Za-z0-9_-]{12,})/iu;
|
|
17
|
+
const LOCAL_PATH_RE = /(?<![A-Za-z])(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+|\/(?:Users|home|tmp|var|etc|mnt|Volumes)\/[^\s"']+)/u;
|
|
18
|
+
const FORBIDDEN_KEY_RE = /(?:password|passwd|pwd|api[_-]?key|private[_-]?key|secret|token|account[_-]?id|raw[_-]?memory|plaintext|plain[_-]?text|prompt|completion|transcript|embedding|provider[_-]?response|cookie|session|seed|mnemonic|wallet[_-]?private|private[_-]?path|customer[_-]?name|tenant[_-]?name)/iu;
|
|
19
|
+
const SAFE_FIELD_NAMES = new Set([
|
|
20
|
+
'credentials_required',
|
|
21
|
+
'credentials_used',
|
|
22
|
+
'requires_credentials',
|
|
23
|
+
'secret_values_printed',
|
|
24
|
+
'transaction_submitted',
|
|
25
|
+
'raw_memory_on_chain',
|
|
26
|
+
'raw_memory_in_public_artifacts',
|
|
27
|
+
'hosted_saas_live',
|
|
28
|
+
'hosted_cloud_live_claim',
|
|
29
|
+
'solana_mainnet_live_claim',
|
|
30
|
+
'solana_transaction_claim',
|
|
31
|
+
'provider_response_claim',
|
|
32
|
+
'model_forgetting_claim',
|
|
33
|
+
'provider_deletion_claim',
|
|
34
|
+
'benchmark_leadership_claim',
|
|
35
|
+
'token_roi_claim',
|
|
36
|
+
'token_profit_claim',
|
|
37
|
+
'provider_invoice_savings_claim',
|
|
38
|
+
'external_provider_called',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
class UsageError extends Error {
|
|
42
|
+
constructor(message) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = 'UsageError';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isPlainObject(value) {
|
|
49
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function assertPublicSafe(value, path = 'production_unblocker') {
|
|
53
|
+
if (typeof value === 'string') {
|
|
54
|
+
if (SECRET_VALUE_RE.test(value)) throw new Error(`${path} contains secret-looking material`);
|
|
55
|
+
if (LOCAL_PATH_RE.test(value)) throw new Error(`${path} contains a local absolute path`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
value.forEach((item, index) => assertPublicSafe(item, `${path}[${index}]`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!isPlainObject(value)) return;
|
|
63
|
+
for (const [key, child] of Object.entries(value)) {
|
|
64
|
+
if (FORBIDDEN_KEY_RE.test(key) && !SAFE_FIELD_NAMES.has(key)) throw new Error(`${path}.${key} is not allowed in public production unblocker input`);
|
|
65
|
+
assertPublicSafe(child, `${path}.${key}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function publicPackageSummary(packageJson = {}, options = {}) {
|
|
70
|
+
const bins = isPlainObject(packageJson.bin) ? Object.keys(packageJson.bin).sort() : [];
|
|
71
|
+
const sourceVersion = typeof packageJson.version === 'string' ? packageJson.version : null;
|
|
72
|
+
const currentPublicVersion = typeof options.currentPublicVersion === 'string' ? options.currentPublicVersion : CURRENT_PUBLIC_PACKAGE_VERSION;
|
|
73
|
+
return {
|
|
74
|
+
name: typeof packageJson.name === 'string' ? packageJson.name : 'enigma-memory',
|
|
75
|
+
version: sourceVersion,
|
|
76
|
+
source_version: sourceVersion,
|
|
77
|
+
current_public_version: currentPublicVersion,
|
|
78
|
+
node: typeof packageJson.engines?.node === 'string' ? packageJson.engines.node : '>=24',
|
|
79
|
+
bins,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const ALLOWED_INPUT_KEYS = new Set(['packageJson', 'currentPublicVersion', 'evidence']);
|
|
84
|
+
|
|
85
|
+
function assertKnownInputs(inputs) {
|
|
86
|
+
if (!isPlainObject(inputs)) throw new Error('production unblocker inputs must be an object');
|
|
87
|
+
for (const key of Object.keys(inputs)) {
|
|
88
|
+
if (!ALLOWED_INPUT_KEYS.has(key)) throw new Error(`unsupported production unblocker input field: ${key}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
function command(id, purpose, value, options = {}) {
|
|
94
|
+
return {
|
|
95
|
+
id,
|
|
96
|
+
purpose,
|
|
97
|
+
command: value,
|
|
98
|
+
mode: options.mode ?? 'dry_run_or_planning',
|
|
99
|
+
requires_credentials: options.requires_credentials === true,
|
|
100
|
+
mutates_external_systems: options.mutates_external_systems === true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function section(id, title, status, summary, details) {
|
|
105
|
+
if (!STATUS_VALUES.includes(status)) throw new Error(`invalid unblocker status for ${id}`);
|
|
106
|
+
return {
|
|
107
|
+
id,
|
|
108
|
+
title,
|
|
109
|
+
status,
|
|
110
|
+
summary,
|
|
111
|
+
evidence_available: details.evidence_available ?? [],
|
|
112
|
+
blockers: details.blockers ?? [],
|
|
113
|
+
non_claims: details.non_claims ?? [],
|
|
114
|
+
next_commands: details.next_commands ?? [],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildSections(packageSummary) {
|
|
119
|
+
const packageRef = `${packageSummary.name}@${packageSummary.version ?? '<version>'}`;
|
|
120
|
+
return [
|
|
121
|
+
section(
|
|
122
|
+
'npm_install_readiness',
|
|
123
|
+
'npm install readiness',
|
|
124
|
+
'ready_now',
|
|
125
|
+
'The npm install path is the current supported production install tier; this report distinguishes the current public package from the local source release candidate.',
|
|
126
|
+
{
|
|
127
|
+
evidence_available: [
|
|
128
|
+
`Current public install baseline: ${packageSummary.name}@${packageSummary.current_public_version}.`,
|
|
129
|
+
`Local source release candidate: ${packageSummary.name}@${packageSummary.source_version ?? '<unknown>'}.`,
|
|
130
|
+
`Package metadata requires Node ${packageSummary.node}.`,
|
|
131
|
+
`CLI bins listed in package metadata: ${packageSummary.bins.join(', ') || 'none listed'}.`,
|
|
132
|
+
],
|
|
133
|
+
blockers: [],
|
|
134
|
+
non_claims: [
|
|
135
|
+
'npm install readiness does not mean the source release candidate has already been published.',
|
|
136
|
+
'Installing the package does not make hosted cloud live.',
|
|
137
|
+
'Installing the package does not submit Solana transactions.',
|
|
138
|
+
'Installing the package does not prove provider deletion, model forgetting, compliance, ROI, or provider invoice savings.',
|
|
139
|
+
],
|
|
140
|
+
next_commands: [
|
|
141
|
+
command('build-unblocker-report', 'Regenerate this consolidated public-safe unblocker report.', 'npm run production:unblocker -- --out .enigma/production-unblocker.json'),
|
|
142
|
+
command('registry-install-plan-current', 'Validate the current public registry install plan without network install.', `npm run registry:verify -- --package ${packageSummary.name} --version ${packageSummary.current_public_version} --skip-network`),
|
|
143
|
+
command('registry-install-execute-current', 'Collect real current public registry install evidence in an isolated temporary prefix.', `npm run registry:verify -- --package ${packageSummary.name} --version ${packageSummary.current_public_version} --execute`, { mode: 'explicit_execute_network', mutates_external_systems: false }),
|
|
144
|
+
command('registry-install-execute-candidate', 'After release publication only, collect registry install evidence for the local source release candidate.', `npm run registry:verify -- --package ${packageSummary.name} --version ${packageSummary.source_version ?? '<candidate-version>'} --execute`, { mode: 'explicit_execute_network_after_publish', mutates_external_systems: false }),
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
),
|
|
148
|
+
section(
|
|
149
|
+
'hosted_cloud_external_blockers',
|
|
150
|
+
'Hosted cloud external blockers',
|
|
151
|
+
'blocked_external_dependency',
|
|
152
|
+
'Hosted-cloud package contracts exist, but sellable hosted SaaS remains blocked on real provider wiring, legal/security approvals, operations ownership, and operator go-live evidence.',
|
|
153
|
+
{
|
|
154
|
+
evidence_available: [
|
|
155
|
+
'Hosted-cloud builders and validators can produce public-safe customer lifecycle and API-key lifecycle packets.',
|
|
156
|
+
'Operator evidence starter and backend env kit commands can generate templates and public-safe fill plans.',
|
|
157
|
+
],
|
|
158
|
+
blockers: [
|
|
159
|
+
'Auth provider, billing provider, legal terms, data-processing terms, support ownership, and external security review evidence are not created by the package.',
|
|
160
|
+
'Hosted relay/gateway/storage/KMS/SIEM/backup/monitoring/domain/TLS evidence must be supplied by an operator.',
|
|
161
|
+
'Operator go-live approval is required before hosted_cloud_sellable can be true.',
|
|
162
|
+
],
|
|
163
|
+
non_claims: [
|
|
164
|
+
'No hosted account, customer tenant, customer API key, invoice, support ticket, Cloudflare resource, provider resource, or deployment is created by this report.',
|
|
165
|
+
'Contract readiness is not live hosted SaaS readiness.',
|
|
166
|
+
],
|
|
167
|
+
next_commands: [
|
|
168
|
+
command('operator-evidence-starter', 'Generate public-safe hosted evidence templates and fill order.', 'npm run production:evidence-starter -- --out-dir .enigma/operator-evidence --domain enigmamemory.com --tenant <tenant-id>'),
|
|
169
|
+
command('backend-env-kit', 'Generate the backend environment kit and public summary for operator completion outside public artifacts.', 'npm run production:backend-env -- --out-dir .enigma/backend-env-kit --domain enigmamemory.com --tenant <tenant-id> --environment production'),
|
|
170
|
+
command('hosted-live-validate', 'Validate completed hosted live evidence after real operator refs exist.', 'npm run production:hosted-live -- --evidence .enigma/operator-evidence/hosted-backend-live.json', { mode: 'operator_evidence_validation' }),
|
|
171
|
+
command('hosted-customer-lifecycle', 'Build hosted customer lifecycle contract evidence from public-safe refs.', 'npm run production:hosted-customer -- --tenant <tenant-id> --domain enigmamemory.com --environment production --out .enigma/hosted-customer-lifecycle.json'),
|
|
172
|
+
command('hosted-api-key-lifecycle', 'Build customer API-key lifecycle contract evidence from refs, not key material.', 'npm run production:hosted-api-key -- --tenant <tenant-id> --subject <opaque-subject-ref> --operation issue --out .enigma/hosted-api-key-lifecycle.json'),
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
),
|
|
176
|
+
section(
|
|
177
|
+
'solana_proof_rail_status',
|
|
178
|
+
'Solana proof rail status',
|
|
179
|
+
'contract_ready',
|
|
180
|
+
'The proof rail is local planning and verification: artifacts are Solana-ready commitments, not submitted transactions or live mainnet infrastructure.',
|
|
181
|
+
{
|
|
182
|
+
evidence_available: [
|
|
183
|
+
'Local chain CLI commands create anchor, grant, revocation, benchmark-attestation, and verification artifacts with transaction_submitted:false.',
|
|
184
|
+
'The proof packet builder hashes benchmark reports and emits public-safe proof-network packets.',
|
|
185
|
+
],
|
|
186
|
+
blockers: [
|
|
187
|
+
'Devnet/mainnet program deployment, transaction submission, program security review, governance, indexer evidence, and operational ownership are separate external work.',
|
|
188
|
+
'Any future network mutation must be explicit and must not be implied by local planning artifacts.',
|
|
189
|
+
],
|
|
190
|
+
non_claims: [
|
|
191
|
+
'No Solana transaction is submitted by this report or by default local proof commands.',
|
|
192
|
+
'Raw memory is not placed on-chain.',
|
|
193
|
+
'No token ROI, profit, provider invoice savings, provider deletion, model-forgetting, compliance, or hosted SaaS claim is created by proof artifacts.',
|
|
194
|
+
],
|
|
195
|
+
next_commands: [
|
|
196
|
+
command('chain-attest-local', 'Create a local benchmark attestation from a reviewed report hash/file and public refs.', `enigma chain attest --report-file .enigma/standard-memory-benchmark-sample.json --dataset-ref sha256:<dataset-manifest-hash> --runner-ref run-standard-memory-benchmarks.mjs@<reviewed-revision> --package-ref ${packageRef} --score retrieval_evidence_proxy=<value-copied-from-report> --out .enigma/standard-memory-benchmark-attestation.json`),
|
|
197
|
+
command('chain-verify-local', 'Verify a local proof-network artifact offline.', 'enigma chain verify --file .enigma/standard-memory-benchmark-attestation.json'),
|
|
198
|
+
command('proof-packet-local', 'Bundle public-safe roots and a benchmark report hash into a local proof packet.', 'npm run proof:packet -- --active-root sha256:<active-root> --receipt-root sha256:<receipt-root> --benchmark-report .enigma/standard-memory-benchmark-sample.json --dataset-hash sha256:<dataset-manifest-hash> --runner-hash sha256:<runner-hash> --operator-ref operator:<public-ref> --out .enigma/proof-network-packet.json'),
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
),
|
|
202
|
+
section(
|
|
203
|
+
'benchmark_claim_status',
|
|
204
|
+
'Benchmark claim status',
|
|
205
|
+
'operator_evidence_required',
|
|
206
|
+
'Local and official-dataset retrieval/evidence proxy benchmark commands exist; public claims require exact report, dataset manifest, runner/package refs, and claim-boundary review.',
|
|
207
|
+
{
|
|
208
|
+
evidence_available: [
|
|
209
|
+
'The local deterministic memory suite can produce enigma.memory_benchmark_suite.v1 without provider credentials.',
|
|
210
|
+
'The standard runner can score local LoCoMo/LongMemEval files as retrieval/evidence proxy evidence only.',
|
|
211
|
+
],
|
|
212
|
+
blockers: [
|
|
213
|
+
'Official dataset files and hashes must be collected locally before standard benchmark claims.',
|
|
214
|
+
'Provider answer-accuracy, competitor, leaderboard, savings, compliance, model-forgetting, and provider-deletion claims require separate reviewed runs and are not present by default.',
|
|
215
|
+
],
|
|
216
|
+
non_claims: [
|
|
217
|
+
'No provider API, hosted memory service, competitor SDK, ChatGPT native memory, or Claude memory tool is called by the local default benchmark paths.',
|
|
218
|
+
'Retrieval/evidence proxy metrics are not LLM answer accuracy or benchmark leadership.',
|
|
219
|
+
],
|
|
220
|
+
next_commands: [
|
|
221
|
+
command('benchmark-dataset-plan', 'Preview dataset fetches without downloading raw data.', 'npm run benchmark:datasets -- --dry-run'),
|
|
222
|
+
command('benchmark-dataset-download', 'Download supported official datasets and write a public-safe manifest.', 'npm run benchmark:datasets -- --execute --dataset all --out-dir .enigma/benchmarks/datasets --manifest .enigma/benchmarks/dataset-manifest.json', { mode: 'explicit_execute_network' }),
|
|
223
|
+
command('benchmark-standard-sample', 'Run a bounded standard retrieval/evidence proxy benchmark from local dataset files.', 'npm run benchmark:standard -- --locomo .enigma/benchmarks/datasets/locomo10.json --longmemeval .enigma/benchmarks/datasets/longmemeval_s_cleaned.json --max-locomo-qa 25 --max-longmemeval-items 25 --top-k 5 --out .enigma/standard-memory-benchmark-sample.json'),
|
|
224
|
+
command('benchmark-local-suite', 'Run the local deterministic package benchmark suite.', 'npm run benchmark:memory-suite -- --out .enigma/memory-benchmark-suite.json'),
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
),
|
|
228
|
+
section(
|
|
229
|
+
'installer_distribution_status',
|
|
230
|
+
'Installer distribution status',
|
|
231
|
+
'blocked_external_dependency',
|
|
232
|
+
'npm is the production install tier now; native signed installer distribution remains blocked on external signing, notarization, release runners, and review evidence.',
|
|
233
|
+
{
|
|
234
|
+
evidence_available: [
|
|
235
|
+
'The installer asset generator can produce reviewable source installer assets with dry-run/planning defaults.',
|
|
236
|
+
'Generated source installer assets do not by themselves publish a Homebrew tap, signed Windows executable, or signed/notarized macOS package.',
|
|
237
|
+
],
|
|
238
|
+
blockers: [
|
|
239
|
+
'Windows distribution needs installer builder selection, code-signing certificate, signing workflow, and release review.',
|
|
240
|
+
'macOS distribution needs package build runner, Developer ID Installer certificate, notarization workflow, and release review.',
|
|
241
|
+
'Homebrew distribution needs a real release archive URL/SHA and tap workflow approval.',
|
|
242
|
+
],
|
|
243
|
+
non_claims: [
|
|
244
|
+
'No signed native installer exists from this report.',
|
|
245
|
+
'Installer source assets are not a completed OS-native distribution channel.',
|
|
246
|
+
],
|
|
247
|
+
next_commands: [
|
|
248
|
+
command('installer-assets-preview', 'Preview reviewable installer source assets.', 'npm run installer:assets -- --out-dir dist/installer-assets'),
|
|
249
|
+
command('installer-assets-write', 'Write reviewable installer source assets for release engineering.', 'npm run installer:assets -- --out-dir dist/installer-assets --write', { mode: 'local_write_explicit' }),
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
),
|
|
253
|
+
section(
|
|
254
|
+
'monitoring_ops_status',
|
|
255
|
+
'Monitoring and operations status',
|
|
256
|
+
'operator_evidence_required',
|
|
257
|
+
'Ops validators and dry-run monitors exist, but production monitoring, alert routing, backup/restore, incident drill, support/SLA, and acceptance evidence must be supplied by operators.',
|
|
258
|
+
{
|
|
259
|
+
evidence_available: [
|
|
260
|
+
'Live endpoint monitoring has a dry-run mode and public-safe endpoint constraints.',
|
|
261
|
+
'Production validators exist for monitoring/alerting, backup/restore drill, incident drill, support/SLA, legal/compliance, network, KMS, usage, settlement, site security, and threat model evidence.',
|
|
262
|
+
],
|
|
263
|
+
blockers: [
|
|
264
|
+
'Monitoring provider, alert routing, on-call ownership, incident commander, support escalation, backup target, restore rehearsal, and legal/security approvals are external operator evidence.',
|
|
265
|
+
'Public site health is not hosted backend readiness; relay/gateway readiness requires exact live endpoint and operator acceptance evidence.',
|
|
266
|
+
],
|
|
267
|
+
non_claims: [
|
|
268
|
+
'Dry-run monitoring does not prove future availability or contractual SLA performance.',
|
|
269
|
+
'Validator acceptance of a ref packet is not a compliance certification.',
|
|
270
|
+
],
|
|
271
|
+
next_commands: [
|
|
272
|
+
command('live-monitor-dry-run', 'Validate default public endpoint monitor configuration without network probes.', 'npm run production:live-monitor:dry-run'),
|
|
273
|
+
command('live-monitor-public-safe', 'Collect live endpoint monitoring evidence only after endpoints are intentionally public and secret-free.', 'npm run production:live-monitor -- --endpoint public_site=https://enigmamemory.com/ --endpoint relay_readyz=https://relay.enigmamemory.com/readyz --endpoint gateway_readyz=https://gateway.enigmamemory.com/readyz --out .enigma/live-endpoint-monitor.json', { mode: 'operator_network_observation' }),
|
|
274
|
+
command('monitoring-validator', 'Validate operator monitoring/alerting evidence packet.', 'npm run production:monitoring -- --monitoring .enigma/operator-evidence/monitoring-alerting.json --out .enigma/monitoring-alerting-result.json', { mode: 'operator_evidence_validation' }),
|
|
275
|
+
command('backup-drill-validator', 'Validate operator backup/restore drill evidence packet.', 'npm run production:backup-drill -- --drill .enigma/operator-evidence/backup-restore-drill.json --out .enigma/backup-restore-drill-result.json', { mode: 'operator_evidence_validation' }),
|
|
276
|
+
command('incident-drill-validator', 'Validate operator incident drill evidence packet.', 'npm run production:incident-drill -- --drill .enigma/operator-evidence/incident-drill.json --out .enigma/incident-drill-result.json', { mode: 'operator_evidence_validation' }),
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
),
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function statusCounts(sections) {
|
|
284
|
+
const counts = Object.fromEntries(STATUS_VALUES.map((status) => [status, 0]));
|
|
285
|
+
for (const item of sections) counts[item.status] += 1;
|
|
286
|
+
return counts;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function flattenCommands(sections) {
|
|
290
|
+
return sections.flatMap((item) => item.next_commands.map((entry) => ({ section: item.id, ...entry })));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function buildProductionUnblocker(inputs = {}, options = {}) {
|
|
294
|
+
assertKnownInputs(inputs);
|
|
295
|
+
if (inputs.evidence !== undefined) assertPublicSafe(inputs.evidence, 'production_unblocker.evidence');
|
|
296
|
+
const generatedAt = options.generated_at ?? options.generatedAt ?? new Date().toISOString();
|
|
297
|
+
const packageSummary = publicPackageSummary(inputs.packageJson ?? {}, { currentPublicVersion: inputs.currentPublicVersion ?? options.current_public_version ?? options.currentPublicVersion });
|
|
298
|
+
const sections = buildSections(packageSummary);
|
|
299
|
+
const blockers = sections.flatMap((item) => item.blockers.map((blocker) => ({ section: item.id, status: item.status, blocker })));
|
|
300
|
+
const report = {
|
|
301
|
+
schema: PRODUCTION_UNBLOCKER_SCHEMA,
|
|
302
|
+
generated_at: generatedAt,
|
|
303
|
+
mode: 'dry_run_planning',
|
|
304
|
+
credentials_required: false,
|
|
305
|
+
credentials_used: false,
|
|
306
|
+
mutates_external_systems: false,
|
|
307
|
+
overall_status: blockers.length === 0 ? 'ready_now' : 'blocked_external_dependency',
|
|
308
|
+
package: packageSummary,
|
|
309
|
+
status_legend: {
|
|
310
|
+
ready_now: 'Usable now within the stated local/package boundary.',
|
|
311
|
+
contract_ready: 'Interfaces, validators, or local planning artifacts exist, but live external operation is not claimed.',
|
|
312
|
+
blocked_external_dependency: 'External provider, credential, signing, deployment, legal, security, or operator system is required.',
|
|
313
|
+
operator_evidence_required: 'The repository supplies the command or validator, but an operator must provide current public-safe evidence.',
|
|
314
|
+
},
|
|
315
|
+
status_counts: statusCounts(sections),
|
|
316
|
+
sections,
|
|
317
|
+
go_live_blockers: blockers,
|
|
318
|
+
operator_next_commands: flattenCommands(sections),
|
|
319
|
+
live_claims: {
|
|
320
|
+
hosted_saas_live: false,
|
|
321
|
+
hosted_cloud_live_claim: false,
|
|
322
|
+
solana_mainnet_live_claim: false,
|
|
323
|
+
solana_transaction_claim: false,
|
|
324
|
+
transaction_submitted: false,
|
|
325
|
+
raw_memory_on_chain: false,
|
|
326
|
+
raw_memory_in_public_artifacts: false,
|
|
327
|
+
external_provider_called: false,
|
|
328
|
+
provider_response_claim: false,
|
|
329
|
+
model_forgetting_claim: false,
|
|
330
|
+
provider_deletion_claim: false,
|
|
331
|
+
benchmark_leadership_claim: false,
|
|
332
|
+
token_roi_claim: false,
|
|
333
|
+
token_profit_claim: false,
|
|
334
|
+
provider_invoice_savings_claim: false,
|
|
335
|
+
},
|
|
336
|
+
claim_boundary: [
|
|
337
|
+
'This report is public-safe planning evidence. It does not publish, deploy, create accounts, submit transactions, create credentials, call model providers, or mutate external systems.',
|
|
338
|
+
'ready_now means ready within the named local/package boundary only; it does not imply hosted SaaS, Solana mainnet, benchmark leadership, compliance, provider deletion, model forgetting, ROI, profit, or provider invoice savings.',
|
|
339
|
+
'contract_ready means schemas, validators, CLI planning, or local proof artifacts exist; an operator must still supply external evidence before live or sellable claims.',
|
|
340
|
+
'Commands marked explicit_execute_network or operator_network_observation should only be run by an operator with approval for that environment.',
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
assertPublicSafe(report);
|
|
344
|
+
return report;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
348
|
+
const args = {
|
|
349
|
+
out: null,
|
|
350
|
+
generatedAt: null,
|
|
351
|
+
help: false,
|
|
352
|
+
};
|
|
353
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
354
|
+
const token = argv[index];
|
|
355
|
+
if (token === '--help' || token === '-h') {
|
|
356
|
+
args.help = true;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const readValue = () => {
|
|
360
|
+
const value = argv[index + 1];
|
|
361
|
+
if (typeof value !== 'string' || value.length === 0 || value.startsWith('--')) throw new UsageError(`${token} requires a value`);
|
|
362
|
+
index += 1;
|
|
363
|
+
return value;
|
|
364
|
+
};
|
|
365
|
+
if (token === '--out') args.out = readValue();
|
|
366
|
+
else if (token === '--generated-at' || token === '--generatedAt') args.generatedAt = readValue();
|
|
367
|
+
else throw new UsageError(`unknown production unblocker option: ${token}`);
|
|
368
|
+
}
|
|
369
|
+
return args;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function usage() {
|
|
373
|
+
return `Usage: node scripts/build-production-unblocker.mjs [--out <file>] [--generated-at <iso>]
|
|
374
|
+
|
|
375
|
+
Builds a dependency-free public-safe production unblocker report. The command is dry-run/planning by default: it requires no credentials, does not publish, does not deploy, does not submit Solana transactions, does not call providers, and does not mutate external systems.
|
|
376
|
+
`;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function readPackageJson() {
|
|
380
|
+
const packageUrl = new URL('../package.json', import.meta.url);
|
|
381
|
+
try {
|
|
382
|
+
return JSON.parse(await readFile(packageUrl, 'utf8'));
|
|
383
|
+
} catch {
|
|
384
|
+
return {};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
389
|
+
const args = parseArgs(argv);
|
|
390
|
+
if (args.help) return { text: usage(), code: 0 };
|
|
391
|
+
const report = buildProductionUnblocker({ packageJson: await readPackageJson() }, { generated_at: args.generatedAt ?? new Date().toISOString() });
|
|
392
|
+
const text = `${JSON.stringify(report, null, 2)}\n`;
|
|
393
|
+
if (args.out) {
|
|
394
|
+
const outPath = resolve(args.out);
|
|
395
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
396
|
+
await writeFile(outPath, text, 'utf8');
|
|
397
|
+
}
|
|
398
|
+
return { text, code: 0 };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
402
|
+
main().then(({ text, code }) => {
|
|
403
|
+
process.stdout.write(text);
|
|
404
|
+
process.exitCode = code;
|
|
405
|
+
}).catch((error) => {
|
|
406
|
+
process.stderr.write(`${error.name ?? 'Error'}: ${error.message ?? 'failed to build production unblocker'}\n`);
|
|
407
|
+
process.exitCode = 2;
|
|
408
|
+
});
|
|
409
|
+
}
|