@nm-lang/cli 0.4.0 → 0.6.0
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 +229 -166
- package/bin/bridge-host.mjs +78 -0
- package/bin/bridge-worker.mjs +27 -0
- package/bin/nm.mjs +5 -1
- package/bin/project-create.d.mts +27 -0
- package/bin/project-create.mjs +122 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +120 -25
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -2
- package/python/LICENSE.txt +57 -0
- package/python/NOTICE.txt +11 -0
- package/python/README.md +174 -0
- package/python/examples/magic.ipynb +98 -0
- package/python/examples/training.ipynb +78 -0
- package/python/nm_bridge.py +524 -0
- package/python/nm_jupyter/__init__.py +20 -0
- package/python/nm_jupyter/extension.py +135 -0
- package/python/pyproject.toml +21 -0
package/README.md
CHANGED
|
@@ -1,166 +1,229 @@
|
|
|
1
|
-
# @nm-lang/cli
|
|
2
|
-
|
|
3
|
-
N/M 0.2.0 Preview command-line tools for running, formatting, linting, testing,
|
|
4
|
-
exporting and managing local N/M workspaces.
|
|
5
|
-
|
|
6
|
-
Package **0.
|
|
7
|
-
|
|
8
|
-
## Install / Kurulum
|
|
9
|
-
|
|
10
|
-
Requires Node.js 20 or newer. Release checks use Node.js 22.23.1.
|
|
11
|
-
|
|
12
|
-
```sh
|
|
13
|
-
npm install --global @nm-lang/cli
|
|
14
|
-
nm --version
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Or run without a global install:
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
npx --package=@nm-lang/cli nm --version
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
An untagged install selects npm's `latest` channel. To pin this version,
|
|
24
|
-
append `@0.
|
|
25
|
-
Preview independently of the npm channel. npm installs the matching Core dependency.
|
|
26
|
-
No repository checkout or TypeScript build is needed to use a published package.
|
|
27
|
-
|
|
28
|
-
Türkçe: Etiketsiz kurulum npm'in `latest` kanalını seçer.
|
|
29
|
-
`@0.
|
|
30
|
-
olarak Önizleme durumundadır. Core bağımlılığı otomatik kurulur; depoyu
|
|
31
|
-
indirmeniz gerekmez.
|
|
32
|
-
|
|
33
|
-
## First program / İlk program
|
|
34
|
-
|
|
35
|
-
Save the following as `bell.nm`:
|
|
36
|
-
|
|
37
|
-
```nm
|
|
38
|
-
module npm_bell;
|
|
39
|
-
fn main() {
|
|
40
|
-
let q = qreg[2];
|
|
41
|
-
H(q[0]);
|
|
42
|
-
CNOT(q[0], q[1]);
|
|
43
|
-
let a = measure(q[0]);
|
|
44
|
-
let b = measure(q[1]);
|
|
45
|
-
return (a, b);
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
```sh
|
|
50
|
-
nm run bell.nm --json
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
The program creates and measures a Bell pair using a local simulator.
|
|
54
|
-
Türkçe: Kodu `bell.nm` olarak kaydedip komutla çalıştırın. Sonuç yerel
|
|
55
|
-
simülasyondan gelir; gerçek kuantum donanımı kanıtı değildir.
|
|
56
|
-
|
|
57
|
-
##
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
the
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
`
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
1
|
+
# @nm-lang/cli
|
|
2
|
+
|
|
3
|
+
N/M 0.2.0 Preview command-line tools for running, formatting, linting, testing,
|
|
4
|
+
exporting and managing local N/M workspaces.
|
|
5
|
+
|
|
6
|
+
Package **0.6.0** targets the N/M **0.2.0** language contract.
|
|
7
|
+
|
|
8
|
+
## Install / Kurulum
|
|
9
|
+
|
|
10
|
+
Requires Node.js 20 or newer. Release checks use Node.js 22.23.1.
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install --global @nm-lang/cli
|
|
14
|
+
nm --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or run without a global install:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npx --package=@nm-lang/cli nm --version
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
An untagged install selects npm's `latest` channel. To pin this version,
|
|
24
|
+
append `@0.6.0` to the package name. The N/M language remains
|
|
25
|
+
Preview independently of the npm channel. npm installs the matching Core dependency.
|
|
26
|
+
No repository checkout or TypeScript build is needed to use a published package.
|
|
27
|
+
|
|
28
|
+
Türkçe: Etiketsiz kurulum npm'in `latest` kanalını seçer.
|
|
29
|
+
`@0.6.0` ile sürümü sabitleyebilirsiniz. N/M dili, npm kanalından bağımsız
|
|
30
|
+
olarak Önizleme durumundadır. Core bağımlılığı otomatik kurulur; depoyu
|
|
31
|
+
indirmeniz gerekmez.
|
|
32
|
+
|
|
33
|
+
## First program / İlk program
|
|
34
|
+
|
|
35
|
+
Save the following as `bell.nm`:
|
|
36
|
+
|
|
37
|
+
```nm
|
|
38
|
+
module npm_bell;
|
|
39
|
+
fn main() {
|
|
40
|
+
let q = qreg[2];
|
|
41
|
+
H(q[0]);
|
|
42
|
+
CNOT(q[0], q[1]);
|
|
43
|
+
let a = measure(q[0]);
|
|
44
|
+
let b = measure(q[1]);
|
|
45
|
+
return (a, b);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
nm run bell.nm --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The program creates and measures a Bell pair using a local simulator.
|
|
54
|
+
Türkçe: Kodu `bell.nm` olarak kaydedip komutla çalıştırın. Sonuç yerel
|
|
55
|
+
simülasyondan gelir; gerçek kuantum donanımı kanıtı değildir.
|
|
56
|
+
|
|
57
|
+
## 0.6.0 project templates / proje şablonları
|
|
58
|
+
|
|
59
|
+
Create a complete workspace with a helper module, test and manifest:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
nm init bell-project --template bell --name bell-project --locale en
|
|
63
|
+
cd bell-project
|
|
64
|
+
nm run src/main.nm --json
|
|
65
|
+
nm test tests/main.test.nm --json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Templates are `bell`, `parametric-training` and `numerics`. Use `--locale tr`
|
|
69
|
+
for a Turkish project README; the canonical program is shared. The numerics
|
|
70
|
+
README includes the explicit classical-function/typed-value flags needed by
|
|
71
|
+
its `solve` and `eigvecsh` example. Creation accepts a new/empty real directory,
|
|
72
|
+
preserves existing files and does not grant features. There is no project `--force`.
|
|
73
|
+
The Node-only writer is exported as `@nm-lang/cli/project-create`.
|
|
74
|
+
|
|
75
|
+
Core 0.6.0 also adds QR `lstsq`, real solve derivatives with factor reuse,
|
|
76
|
+
and Hermitian eigenvectors. The bundled Python adapter supports optional
|
|
77
|
+
IPython `%%nm` cells over this CLI; it does not install IPython automatically
|
|
78
|
+
or publish a separate PyPI package. Trusted commands and caller grants remain explicit.
|
|
79
|
+
|
|
80
|
+
Türkçe: Bell, parametrik eğitim ve sayısal hesaplama için gerçek proje,
|
|
81
|
+
yardımcı modül, test ve manifest oluşturulur. `--locale tr` Türkçe README üretir.
|
|
82
|
+
Dolu klasörler ve güvensiz yollar reddedilir; kullanıcı dosyaları korunur.
|
|
83
|
+
Sayısal komutlar gereken bayrakları README'de gösterir. Python/Jupyter adaptörü
|
|
84
|
+
aynı CLI'yi kullanır; ayrıca yorumlayıcı indirmez veya izin açmaz.
|
|
85
|
+
|
|
86
|
+
## Language options / Dil seçenekleri
|
|
87
|
+
|
|
88
|
+
### Selected tests and CI reports / Test seçimi ve CI raporları
|
|
89
|
+
|
|
90
|
+
The CLI provides test selection, watch mode and JSON, JUnit and SARIF
|
|
91
|
+
reports for CI integrations.
|
|
92
|
+
|
|
93
|
+
Türkçe: CLI test seçimini, izleme modunu ve CI entegrasyonları için
|
|
94
|
+
JSON, JUnit ve SARIF raporlarını içerir.
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
nm test main.nm --name "Bell contract" --json
|
|
98
|
+
nm test main.nm --id nmtest-0123456789abcdef
|
|
99
|
+
nm test main.nm --report-format junit --output results.xml
|
|
100
|
+
nm test main.nm --report-format sarif --output results.sarif
|
|
101
|
+
nm test main.nm --watch --json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`--name` matches the exact declared name; repeat `--name` or `--id` to select a
|
|
105
|
+
union. Copy real IDs from a JSON report for the same input path and source lines.
|
|
106
|
+
Selection happens before execution, preserving surrounding setup and original
|
|
107
|
+
source locations; unknown/stale IDs or names fail with `NM-TEST-SELECT-001`.
|
|
108
|
+
Direct selection of a nested check fails with `NM-TEST-SELECT-002`; select its
|
|
109
|
+
top-level enclosing check. Source syntax and capability validation still cover
|
|
110
|
+
the complete file, including its original register allocation and qubit limits.
|
|
111
|
+
Empty suites and failed execution exit 1.
|
|
112
|
+
|
|
113
|
+
`--report-format json|junit|sarif` writes that format to stdout. `--output` also
|
|
114
|
+
replaces the explicitly named `.json`, `.xml` or `.sarif` report on each run;
|
|
115
|
+
parent directories must already exist. The default output remains human readable.
|
|
116
|
+
`--json` cannot be combined with a non-JSON report format.
|
|
117
|
+
|
|
118
|
+
`--watch` is available in the Node command-line host. It runs immediately, then
|
|
119
|
+
debounces source and workspace-manifest changes, including imported modules,
|
|
120
|
+
clears the workspace cache and queues runs without overlap. Ctrl+C/SIGTERM closes
|
|
121
|
+
watchers. With `--json`, stdout is NDJSON: each `event:"run"` has a sequence,
|
|
122
|
+
exitCode, report (or null for invocation errors) and diagnostics; lifecycle errors
|
|
123
|
+
and stopping also have event objects. JUnit/SARIF watch mode requires `--output`
|
|
124
|
+
and prints run summaries while replacing the report file. The exit status follows
|
|
125
|
+
the latest run; signal termination uses 130/143.
|
|
126
|
+
|
|
127
|
+
Türkçe: Tam test adı veya mevcut rapordaki kimlikle seçim yapın; seçenekler
|
|
128
|
+
tekrarlanabilir. Seçilmeyen test blokları yürütülmez, ortak kurulum korunur.
|
|
129
|
+
İç içe kontrolleri doğrudan seçmek yerine üst kontrolü seçin. Kaynağın tamamındaki
|
|
130
|
+
kayıt tahsisi ve qubit sınırları seçimden sonra da geçerlidir. İzleme modu bağımlı
|
|
131
|
+
`.nm` dosyaları ve manifest değişince yeniden çalışır. `--output` belirtilen rapor
|
|
132
|
+
dosyasını her çalıştırmada yeniler.
|
|
133
|
+
|
|
134
|
+
### Toolchain doctor / Araç zinciri kontrolü
|
|
135
|
+
|
|
136
|
+
The `0.3.0` CLI provides `nm doctor --json` and `nm doctor --locale tr`.
|
|
137
|
+
It compares the loaded CLI/Core and locally resolvable LSP/MCP packages, reporting
|
|
138
|
+
package, language and protocol versions separately. Missing optional packages and
|
|
139
|
+
the VS Code installation remain unobserved; this is not a check of running servers.
|
|
140
|
+
Exit code 1 means an observed mismatch or failed package probe; 0 may still mean
|
|
141
|
+
partial coverage. The report format is `nm-toolchain-doctor` version `0.1`.
|
|
142
|
+
|
|
143
|
+
Türkçe: `nm doctor` yüklenen CLI/Core ile yerelden çözümlenebilen LSP/MCP
|
|
144
|
+
paketlerini karşılaştırır. Paket, dil ve protokol sürümleri ayrı değerlendirilir.
|
|
145
|
+
Gözlenmeyen isteğe bağlı araçlar ve VS Code kurulumu doğrulanmış sayılmaz.
|
|
146
|
+
Çıkış kodu 1 uyuşmazlık veya paket sorgusu hatasıdır; 0 kısmi gözlem anlamına da
|
|
147
|
+
gelebilir. Bu komutlar `0.3.0` paketinin parçasıdır.
|
|
148
|
+
|
|
149
|
+
New CLI invocations enable the default language profile: classical functions,
|
|
150
|
+
typed values/Result, module visibility, parameter groups and compute/apply.
|
|
151
|
+
Use `--legacy-features` for the previous opt-in behavior. Other experimental
|
|
152
|
+
features still require their own flags; export/backend limits remain enforced.
|
|
153
|
+
|
|
154
|
+
Türkçe: Yeni CLI çağrılarında varsayılan dil profili açıktır. Eski davranış için
|
|
155
|
+
`--legacy-features` kullanın. Diğer deneysel özellikler ayrıca açılmalıdır.
|
|
156
|
+
|
|
157
|
+
- [CLI documentation](https://quantumsoftware.tech/en/docs/nm-language/cli)
|
|
158
|
+
- [Türkçe CLI rehberi](https://quantumsoftware.tech/tr/docs/nm-language/cli)
|
|
159
|
+
- [Support / Destek](https://quantumsoftware.tech/en/#contact)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
## 0.5.0: Trainable evolution / Eğitilebilir evrim
|
|
163
|
+
|
|
164
|
+
This package adds scalar-parameter evolution time and Hamiltonian coefficients,
|
|
165
|
+
fixed gate topology at zero time/weight, and exact-statevector circuit/operator
|
|
166
|
+
derivatives. The training host supports fixed or adaptive QWC allocation with
|
|
167
|
+
one total shot budget, separate pilots/held-out confidence evidence, and optional
|
|
168
|
+
final-cost precision targets. Required-shot estimates are informational; they do
|
|
169
|
+
not spend extra shots, increase the 4096 limit or certify convergence. These
|
|
170
|
+
features require the existing explicit Hamiltonian and grouping permissions.
|
|
171
|
+
|
|
172
|
+
Türkçe: Parametreli evrim süresi/katsayıları, sıfırda korunan kapı yapısı ve kesin
|
|
173
|
+
statevector devre/operatör türevleri eklenir. Eğitim host'u sabit/adaptif QWC,
|
|
174
|
+
tek toplam ölçüm bütçesi, ayrı pilot/değerlendirme güven kanıtı ve isteğe bağlı
|
|
175
|
+
son maliyet hassasiyetini destekler. Ölçüm tahmini bilgi amaçlıdır; ek ölçüm almaz,
|
|
176
|
+
4096 sınırını artırmaz veya yakınsamayı belgelemez. Mevcut iki açık izin korunur.
|
|
177
|
+
|
|
178
|
+
The N/M language contract remains 0.2.0; package and artifact versions are separate.
|
|
179
|
+
N/M dil sözleşmesi 0.2.0 olarak kalır; paket ve dosya biçimi sürümleri ayrıdır.
|
|
180
|
+
|
|
181
|
+
## Hamiltonian 0.4.0 / Hamiltonian 0.4.0
|
|
182
|
+
|
|
183
|
+
Version 0.4.0 includes Hamiltonian evolution and shared-shot QWC expectations.
|
|
184
|
+
Earlier releases do not include these additions.
|
|
185
|
+
Both `hamiltonianEvolution` and `measurementGrouping` require explicit permission.
|
|
186
|
+
Source printing and N/M IR v0.5 preserve original source, parser context, seed and
|
|
187
|
+
shot plans. Imports rebuild with caller permissions; stored flags never grant them.
|
|
188
|
+
QASM/framework export and source optimization/transpilation reject these semantics.
|
|
189
|
+
|
|
190
|
+
CLI: `--experimental-hamiltonian-evolution`, `--experimental-measurement-grouping`,
|
|
191
|
+
`--from-ir` for a saved v0.5 IR, `--locale en|tr` for diagnostics. MCP: the matching
|
|
192
|
+
`experimentalHamiltonianEvolution` / `experimentalMeasurementGrouping` booleans,
|
|
193
|
+
`nm_export` target `ir` and `nm_import_ir`. LSP initialization uses
|
|
194
|
+
`initializationOptions.nm.experimental`; VS Code uses
|
|
195
|
+
`nm.experimental.hamiltonianEvolution` / `nm.experimental.measurementGrouping`.
|
|
196
|
+
The editor offers completion/hover, closed-default diagnostics and original
|
|
197
|
+
source columns for generated gates in the recorded debugger.
|
|
198
|
+
|
|
199
|
+
Türkçe: 0.4.0 bu geliştirmeleri içerir; iki yeni izin varsayılan kapalıdır.
|
|
200
|
+
Yazdırma/IR özgün kaynağı ve ortak ölçüm bütçesini korur; içe aktarım
|
|
201
|
+
kaydı çalıştırma yetkisi saymaz. CLI/MCP/LSP/Monaco/VS Code aynı izinleri taşır;
|
|
202
|
+
tanılar ve ayarlar TR/EN destekler. TR/EN laboratuvarı sitede erişilebilirdir.
|
|
203
|
+
N/M dil sözleşmesi 0.2.0 ve ön izleme olgunluğu korunur.
|
|
204
|
+
|
|
205
|
+
[Release notes](https://quantumsoftware.tech/en/docs/nm-language/releases) ·
|
|
206
|
+
[Türkçe sürüm notları](https://quantumsoftware.tech/tr/docs/nm-language/releases)
|
|
207
|
+
|
|
208
|
+
## License / Lisans
|
|
209
|
+
|
|
210
|
+
Free personal, educational and commercial use; source-code rights are reserved.
|
|
211
|
+
Modification or redistribution of QuantumSoftware-owned package code requires
|
|
212
|
+
separate permission. Your N/M programs and results remain yours. See
|
|
213
|
+
[LICENSE.txt](./LICENSE.txt) and [NOTICE.txt](./NOTICE.txt).
|
|
214
|
+
|
|
215
|
+
Türkçe: Kullanım ücretsizdir; açık kaynak lisansı verilmez. Paket kodunu
|
|
216
|
+
değiştirme ve yeniden dağıtma için ayrıca izin gerekir.
|
|
217
|
+
|
|
218
|
+
## Python/Jupyter bridge / Python/Jupyter köprüsü
|
|
219
|
+
|
|
220
|
+
Version 0.5.0 adds `nm bridge` for bounded JSON `check`, `run` and
|
|
221
|
+
`train` requests through stdin. The `python/nm_bridge.py` stdlib adapter and
|
|
222
|
+
`python/examples/training.ipynb` preserve source, explicit caller permissions,
|
|
223
|
+
training options and complete measurement/confidence evidence. TR/EN output is
|
|
224
|
+
supported; saved records never self-grant replay permissions. No separate PyPI
|
|
225
|
+
release is provided. See `python/README.md` and the repository's
|
|
226
|
+
`docs/NM_PYTHON_JUPYTER_BRIDGE_GUIDE.md`. The earlier npm 0.4.0 release does not include this bridge.
|
|
227
|
+
Türkçe: 0.5.0, standart kütüphaneli yerel Python/Jupyter köprüsünü içerir;
|
|
228
|
+
kayıtlar özgün kaynak, eğitim seçenekleri, bütçe ve güven kanıtını korur.
|
|
229
|
+
Kayıt okumak çalıştırma izni vermez; ayrı bir PyPI paketi yayımlanmaz.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
|
|
3
|
+
// Transport caps also cover stdin waiting, runtime loading and forced termination.
|
|
4
|
+
export const nmBridgeTransportLimits = Object.freeze({ requestBytes: 262144, responseBytes: 4194304, defaultTimeoutMs: 30000, maxTimeoutMs: 60000 });
|
|
5
|
+
const copy = {
|
|
6
|
+
en: { input: "Invalid bridge arguments, UTF-8 input, or request size.", timeout: "Bridge deadline exceeded. The Worker was terminated; no completed measurement or precision evidence is available.", execution: "Bridge Worker failed. No completed result is available.", cancel: "Bridge cancelled; no completed result is available." },
|
|
7
|
+
tr: { input: "Köprü bağımsız değişkenleri, UTF-8 girdisi veya istek boyutu geçersiz.", timeout: "Köprü süre sınırı aşıldı. Worker sonlandırıldı; tamamlanmış ölçüm veya hassasiyet kanıtı yoktur.", execution: "Köprü Worker hatası. Tamamlanmış sonuç yoktur.", cancel: "Köprü iptal edildi; tamamlanmış sonuç yoktur." },
|
|
8
|
+
};
|
|
9
|
+
function rejection(key, locale) {
|
|
10
|
+
return { exitCode: 1, stdout: [JSON.stringify({ format: "nm-python-bridge", version: 1, success: false, status: "rejected", locale,
|
|
11
|
+
error: { code: key === "timeout" ? "NM-PY-008" : key === "cancel" ? "NM-PY-009" : key === "execution" ? "NM-PY-007" : "NM-PY-001", message: copy[locale][key] } })], stderr: [] };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function runNMBridgeHost(args) {
|
|
15
|
+
const locale = args[args.indexOf("--locale") + 1] === "tr" ? "tr" : "en";
|
|
16
|
+
const forwarded = [], seen = new Set(), features = new Set();
|
|
17
|
+
let timeoutMs = nmBridgeTransportLimits.defaultTimeoutMs;
|
|
18
|
+
for (let index = 0; index < args.length; index++) {
|
|
19
|
+
const flag = args[index];
|
|
20
|
+
if (flag === "--allow-feature") {
|
|
21
|
+
const feature = args[++index];
|
|
22
|
+
if (!/^[A-Za-z]+$/u.test(feature ?? "") || features.has(feature)) return emit(rejection("input", locale));
|
|
23
|
+
features.add(feature); forwarded.push(flag, feature); continue;
|
|
24
|
+
}
|
|
25
|
+
if (seen.has(flag)) return emit(rejection("input", locale));
|
|
26
|
+
seen.add(flag);
|
|
27
|
+
if (flag === "--json") { forwarded.push(flag); continue; }
|
|
28
|
+
if (flag === "--locale" && ["en", "tr"].includes(args[index + 1])) { forwarded.push(flag, args[++index]); continue; }
|
|
29
|
+
if (flag === "--timeout-ms") {
|
|
30
|
+
const value = args[++index];
|
|
31
|
+
if (!/^\d+$/u.test(value ?? "") || Number(value) < 100 || Number(value) > nmBridgeTransportLimits.maxTimeoutMs) return emit(rejection("input", locale));
|
|
32
|
+
timeoutMs = Number(value); continue;
|
|
33
|
+
}
|
|
34
|
+
return emit(rejection("input", locale));
|
|
35
|
+
}
|
|
36
|
+
await new Promise(resolve => {
|
|
37
|
+
let worker, settled = false, size = 0;
|
|
38
|
+
const chunks = [];
|
|
39
|
+
const finish = async result => {
|
|
40
|
+
if (settled) return;
|
|
41
|
+
settled = true; clearTimeout(timer);
|
|
42
|
+
process.stdin.removeListener("data", onData); process.stdin.removeListener("end", onEnd); process.stdin.removeListener("error", onInputError);
|
|
43
|
+
process.removeListener("SIGINT", onCancel); process.removeListener("SIGTERM", onCancel);
|
|
44
|
+
chunks.length = 0; process.stdin.destroy();
|
|
45
|
+
if (worker) await worker.terminate();
|
|
46
|
+
emit(result); resolve();
|
|
47
|
+
};
|
|
48
|
+
const onCancel = () => void finish(rejection("cancel", locale));
|
|
49
|
+
const onInputError = () => void finish(rejection("input", locale));
|
|
50
|
+
const onData = chunk => {
|
|
51
|
+
size += chunk.length;
|
|
52
|
+
if (size > nmBridgeTransportLimits.requestBytes) return void finish(rejection("input", locale));
|
|
53
|
+
chunks.push(chunk);
|
|
54
|
+
};
|
|
55
|
+
const onEnd = () => {
|
|
56
|
+
let input;
|
|
57
|
+
try { input = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)); }
|
|
58
|
+
catch { return void finish(rejection("input", locale)); }
|
|
59
|
+
chunks.length = 0;
|
|
60
|
+
try {
|
|
61
|
+
worker = new Worker(new URL("./bridge-worker.mjs", import.meta.url), {
|
|
62
|
+
workerData: { args: forwarded, input }, resourceLimits: { maxOldGenerationSizeMb: 512, stackSizeMb: 8 },
|
|
63
|
+
});
|
|
64
|
+
worker.once("message", result => {
|
|
65
|
+
const valid = result && [0, 1].includes(result.exitCode) && result.stdout?.length === 1 && result.stderr?.length === 0
|
|
66
|
+
&& typeof result.stdout[0] === "string" && Buffer.byteLength(result.stdout[0]) <= nmBridgeTransportLimits.responseBytes;
|
|
67
|
+
void finish(valid ? result : rejection("execution", locale));
|
|
68
|
+
});
|
|
69
|
+
worker.once("error", () => void finish(rejection("execution", locale)));
|
|
70
|
+
worker.once("exit", () => { if (!settled) void finish(rejection("execution", locale)); });
|
|
71
|
+
} catch { void finish(rejection("execution", locale)); }
|
|
72
|
+
};
|
|
73
|
+
const timer = setTimeout(() => void finish(rejection("timeout", locale)), timeoutMs);
|
|
74
|
+
process.once("SIGINT", onCancel); process.once("SIGTERM", onCancel);
|
|
75
|
+
process.stdin.on("data", onData); process.stdin.once("end", onEnd); process.stdin.once("error", onInputError);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function emit(result) { process.exitCode = result.exitCode; process.stdout.write(`${result.stdout.join("\n")}\n`); }
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
2
|
+
import Module from "node:module";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
// Same repository-source/installed-artifact selection as nm.mjs, inside the deadline.
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
9
|
+
const sourceCLI = path.join(root, "lib/nm/cli.ts");
|
|
10
|
+
let runtime;
|
|
11
|
+
if (existsSync(sourceCLI) && process.env.NM_CLI_FORCE_PACKED !== "1") {
|
|
12
|
+
const original = Module._resolveFilename;
|
|
13
|
+
Module._resolveFilename = function (request, ...args) {
|
|
14
|
+
if (typeof request === "string" && request.startsWith("@/")) {
|
|
15
|
+
const absolute = path.join(root, request.slice(2));
|
|
16
|
+
request = existsSync(`${absolute}.ts`) ? `${absolute}.ts` : absolute;
|
|
17
|
+
}
|
|
18
|
+
return original.call(this, request, ...args);
|
|
19
|
+
};
|
|
20
|
+
const { createJiti } = await import("jiti");
|
|
21
|
+
runtime = createJiti(import.meta.url)(sourceCLI);
|
|
22
|
+
} else runtime = await import("../dist/index.js");
|
|
23
|
+
const result = runtime.runNMCLI(["bridge", ...workerData.args], {
|
|
24
|
+
readStdin: () => workerData.input,
|
|
25
|
+
readFile: () => { throw new Error("The JSON bridge does not read filesystem sources."); },
|
|
26
|
+
});
|
|
27
|
+
parentPort.postMessage(result);
|
package/bin/nm.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createNMProject } from "./project-create.mjs";
|
|
6
7
|
|
|
7
8
|
let runNMCLI;
|
|
8
9
|
let validateNMWorkspaceManifestJsonText;
|
|
@@ -25,7 +26,9 @@ const isPackedVersionCommand =
|
|
|
25
26
|
process.env.NM_CLI_FORCE_PACKED === "1" &&
|
|
26
27
|
(cliArgs[0] === "--version" || cliArgs[0] === "version");
|
|
27
28
|
|
|
28
|
-
if (
|
|
29
|
+
if (cliArgs[0] === "bridge") {
|
|
30
|
+
await (await import("./bridge-host.mjs")).runNMBridgeHost(cliArgs.slice(1));
|
|
31
|
+
} else if (isPackedVersionCommand) {
|
|
29
32
|
const { nmVersionManifest } = await import("../dist/version.js");
|
|
30
33
|
process.stdout.write(
|
|
31
34
|
cliArgs.includes("--json")
|
|
@@ -105,6 +108,7 @@ if (isPackedVersionCommand) {
|
|
|
105
108
|
readWorkspaceDocuments: (entryPath) =>
|
|
106
109
|
getWorkspaceIndex(entryPath).documents,
|
|
107
110
|
fileExists: (filePath) => existsSync(filePath),
|
|
111
|
+
createProject: createNMProject,
|
|
108
112
|
writeFile: (filePath, content, { overwrite }) =>
|
|
109
113
|
writeFileSync(filePath, content, {
|
|
110
114
|
encoding: "utf8",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NMProjectPlan, NMProjectCreationResult } from "@nm-lang/core";
|
|
2
|
+
export type NMProjectCreationErrorCode = "NM-PROJECT-PLAN-001" | "NM-PROJECT-PATH-001" | "NM-PROJECT-LINK-001" | "NM-PROJECT-CONFLICT-001" | "NM-PROJECT-WRITE-001";
|
|
3
|
+
export class NMProjectCreationError extends Error {
|
|
4
|
+
readonly code: NMProjectCreationErrorCode;
|
|
5
|
+
readonly path?: string;
|
|
6
|
+
constructor(code: NMProjectCreationErrorCode, message: string, filePath?: string);
|
|
7
|
+
}
|
|
8
|
+
/** Minimal synchronous host contract; importing the API requires no Node type package. */
|
|
9
|
+
export interface NMProjectFileIdentity {
|
|
10
|
+
readonly dev: number | bigint;
|
|
11
|
+
readonly ino: number | bigint;
|
|
12
|
+
isDirectory(): boolean;
|
|
13
|
+
isSymbolicLink(): boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface NMProjectFileSystem {
|
|
16
|
+
lstatSync(path: string): NMProjectFileIdentity;
|
|
17
|
+
fstatSync(descriptor: number): NMProjectFileIdentity;
|
|
18
|
+
readdirSync(path: string): string[];
|
|
19
|
+
mkdirSync(path: string): unknown;
|
|
20
|
+
openSync(path: string, flags: "wx"): number;
|
|
21
|
+
closeSync(descriptor: number): void;
|
|
22
|
+
writeFileSync(descriptor: number, data: Uint8Array): void;
|
|
23
|
+
readFileSync(path: string): { readonly length: number; equals(data: Uint8Array): boolean };
|
|
24
|
+
unlinkSync(path: string): void;
|
|
25
|
+
rmdirSync(path: string): void;
|
|
26
|
+
}
|
|
27
|
+
export function createNMProject(destination: string, plan: NMProjectPlan, options?: { fileSystem?: NMProjectFileSystem }): NMProjectCreationResult;
|