ruvnet-constellation 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +38 -2
- package/catalog.json +423 -0
- package/constellation.json +12 -0
- package/package.json +54 -4
- package/src/cli.js +47 -0
- package/src/index.d.ts +12 -0
- package/src/index.js +203 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 rUv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ruvnet-constellation — discover, plan and scaffold useful packages
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Explore a supplied package catalog, build an explicit installation plan and scaffold a reviewable local adapter. The onramp installs nothing automatically and keeps namespace proposals separate from ownership proof.
|
|
4
|
+
|
|
5
|
+
**Topics:** package discovery · explicit install plans · local scaffolding · modular integrations.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install ruvnet-constellation@0.1.0
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
[Package on npm](https://www.npmjs.com/package/ruvnet-constellation) · [Core 0.2.0 entrypoint: `ruv-fann/constellation`](https://github.com/ruvnet/ruv-FANN/tree/main/packages/core#capability-entrypoints) · [Interactive ruv-fann field guide](https://ruvnet.github.io/ruv-FANN/) · [Toolkit API and release guide](https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit#readme)
|
|
12
|
+
|
|
13
|
+
Choose this standalone package for its declared dependency footprint, or use `ruv-fann/constellation` from the complete `ruv-fann@0.2.0` facade. Package version: **0.1.0**. [Umbrella release and evidence](https://github.com/ruvnet/ruv-FANN/releases/tag/ruv-fann-toolkit-v0.2.0).
|
|
14
|
+
|
|
15
|
+
A lightweight explicit onramp for a constellation of independently useful packages. ESM, Node 20.9+. Its only runtime dependency is `ruvnet-constellation-contracts@0.1.0`; no ML runtime, model, provider or telemetry collector is pulled in by this package.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
constellation help
|
|
19
|
+
constellation catalog
|
|
20
|
+
constellation plan weights
|
|
21
|
+
constellation plan local-loop
|
|
22
|
+
constellation scaffold ./new-json-adapter --name @your-scope/json-adapter --integration ruv-fann-wsp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`readCatalog(path?)` reads a bounded local JSON file; by default it uses the packaged `catalog.json`. `planProfile(catalog,id)` validates and owns the catalog, and returns `registrySpecs` for declared published packages, `localCandidates` separately, and `publicInstallAvailable:false` if any profile member is only a candidate. Planning executes no install command and never substitutes candidates for public packages. Catalog status and proposed scopes are metadata, not namespace ownership proofs.
|
|
26
|
+
|
|
27
|
+
`scaffoldSatellite({directory,name,description?,integrations?})` creates a new explicit directory containing a bounded identity JSON adapter, TypeScript declaration, module manifest, independent malformed-input/snapshot tests, a small local benchmark and publication-preparation guidance. It refuses existing targets, symlinks, traversal and unknown/executable options. Parents must already exist and consist of real directories. Scaffolding requires Linux with `O_DIRECTORY`, `O_NOFOLLOW` and `/proc/self/fd`; it fails closed on unsupported platforms, while catalog and planning remain portable. It opens every parent/child directory through a held directory descriptor, anchors exclusive leaf writes to those descriptors, and verifies directory inode relationships before writes and completion. Child-directory substitution by symlinks cannot redirect output into an existing outside path. It performs no recursive cleanup on failure. These controls do not make a hardened sandbox against arbitrary same-UID code, privileged mount changes, or an attacker moving entire owned directory trees. An I/O or detected namespace-change failure can leave an inspectable partial candidate rather than risk deleting unrelated paths.
|
|
28
|
+
|
|
29
|
+
The scaffold is a template, not an AI-generated domain solution. Its default checks do not establish WSP correctness, task accuracy or benchmark superiority. Add useful domain mapping and independent cases before publication. Optional integrations are metadata; no irrelevant peer dependencies or nucleus installs are added to inflate dependency volume.
|
|
30
|
+
|
|
31
|
+
All commands are local unless the user separately runs a package manager or provider. No automatic install, provider request, background telemetry, registry publication, scope claim or credential configuration occurs. `--json` is accepted for catalog/plan/scaffold; data output is JSON by default. CLI usage/input failures exit 2.
|
|
32
|
+
|
|
33
|
+
<!-- constellation-links:start -->
|
|
34
|
+
|
|
35
|
+
Explore the [ruv-fann field guide](https://ruvnet.github.io/ruv-FANN/), [constellation onramp](https://www.npmjs.com/package/ruvnet-constellation) and [shared contracts](https://www.npmjs.com/package/ruvnet-constellation-contracts). Optional ecosystem primitives: [ruv-fann-wsp](https://www.npmjs.com/package/ruv-fann-wsp), [AgentDB](https://www.npmjs.com/package/agentdb), [RuVector](https://www.npmjs.com/package/ruvector). Optional orchestration: [MetaHarness](https://www.npmjs.com/package/metaharness) and [Ruflo](https://www.npmjs.com/package/ruflo).
|
|
36
|
+
|
|
37
|
+
These links describe integrations; package.json defines install-time dependencies. The [versioned catalog](https://github.com/ruvnet/ruv-FANN/blob/main/packages/wsp-toolkit/packages/constellation/catalog.json) declares release profiles and package versions; registry availability and publication provenance require independent verification.
|
|
38
|
+
|
|
39
|
+
<!-- constellation-links:end -->
|
package/catalog.json
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "ruvnet.constellation-catalog/1",
|
|
3
|
+
"repository": "ruvnet/ruv-FANN",
|
|
4
|
+
"packages": [
|
|
5
|
+
{
|
|
6
|
+
"name": "metaharness",
|
|
7
|
+
"version": "0.4.23",
|
|
8
|
+
"role": "nucleus",
|
|
9
|
+
"family": "metaharness",
|
|
10
|
+
"status": "published",
|
|
11
|
+
"repository": "ruvnet/metaharness",
|
|
12
|
+
"description": "Developer harness scaffolding and orchestration",
|
|
13
|
+
"links": {
|
|
14
|
+
"npm": "https://www.npmjs.com/package/metaharness",
|
|
15
|
+
"source": "https://github.com/ruvnet/metaharness"
|
|
16
|
+
},
|
|
17
|
+
"proposedScope": "@metaharness"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "@metaharness/router",
|
|
21
|
+
"version": "0.4.1",
|
|
22
|
+
"role": "nucleus",
|
|
23
|
+
"family": "metaharness",
|
|
24
|
+
"status": "published",
|
|
25
|
+
"repository": "ruvnet/metaharness",
|
|
26
|
+
"description": "Harness routing entry point",
|
|
27
|
+
"links": {
|
|
28
|
+
"npm": "https://www.npmjs.com/package/@metaharness/router",
|
|
29
|
+
"source": "https://github.com/ruvnet/metaharness"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "@claude-flow/cli",
|
|
34
|
+
"version": "3.25.6",
|
|
35
|
+
"role": "nucleus",
|
|
36
|
+
"family": "ruflo",
|
|
37
|
+
"status": "published",
|
|
38
|
+
"repository": "ruvnet/ruflo",
|
|
39
|
+
"description": "Validated Ruflo coordination CLI pin; latest version is observed separately",
|
|
40
|
+
"links": {
|
|
41
|
+
"npm": "https://www.npmjs.com/package/@claude-flow/cli",
|
|
42
|
+
"source": "https://github.com/ruvnet/ruflo"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "ruflo",
|
|
47
|
+
"version": "3.75.3",
|
|
48
|
+
"role": "nucleus",
|
|
49
|
+
"family": "ruflo",
|
|
50
|
+
"status": "published",
|
|
51
|
+
"repository": "ruvnet/ruflo",
|
|
52
|
+
"description": "Public Ruflo convenience entry point",
|
|
53
|
+
"links": {
|
|
54
|
+
"npm": "https://www.npmjs.com/package/ruflo",
|
|
55
|
+
"source": "https://github.com/ruvnet/ruflo"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "agentdb",
|
|
60
|
+
"version": "3.10.0",
|
|
61
|
+
"role": "primitive",
|
|
62
|
+
"family": "agentdb",
|
|
63
|
+
"status": "published",
|
|
64
|
+
"repository": "ruvnet/agentdb",
|
|
65
|
+
"description": "Persistent model programs, proposals and revision histories",
|
|
66
|
+
"links": {
|
|
67
|
+
"npm": "https://www.npmjs.com/package/agentdb",
|
|
68
|
+
"source": "https://github.com/ruvnet/agentdb"
|
|
69
|
+
},
|
|
70
|
+
"contracts": [
|
|
71
|
+
"ruv-fann.dense-layers/1",
|
|
72
|
+
"ruv-fann.weight-patch/1",
|
|
73
|
+
"ruv-fann.weight-evaluation/1"
|
|
74
|
+
],
|
|
75
|
+
"proposedScope": "@agentdb"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "ruvector",
|
|
79
|
+
"version": "0.3.24",
|
|
80
|
+
"role": "primitive",
|
|
81
|
+
"family": "ruvector",
|
|
82
|
+
"status": "published",
|
|
83
|
+
"repository": "ruvnet/RuVector",
|
|
84
|
+
"description": "Independent vector retrieval primitive",
|
|
85
|
+
"links": {
|
|
86
|
+
"npm": "https://www.npmjs.com/package/ruvector",
|
|
87
|
+
"source": "https://github.com/ruvnet/RuVector"
|
|
88
|
+
},
|
|
89
|
+
"proposedScope": "@ruvector"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "ruv-fann",
|
|
93
|
+
"version": "0.2.0",
|
|
94
|
+
"role": "primitive",
|
|
95
|
+
"family": "ruv-fann",
|
|
96
|
+
"status": "published",
|
|
97
|
+
"repository": "ruvnet/ruv-FANN",
|
|
98
|
+
"description": "Dense neural-network WebAssembly facade with optional weight-space toolkit entry points",
|
|
99
|
+
"links": {
|
|
100
|
+
"npm": "https://www.npmjs.com/package/ruv-fann",
|
|
101
|
+
"source": "https://github.com/ruvnet/ruv-FANN"
|
|
102
|
+
},
|
|
103
|
+
"contracts": [
|
|
104
|
+
"ruv-fann.dense-layers/1"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "ruv-fann-wasm",
|
|
109
|
+
"version": "0.1.2",
|
|
110
|
+
"role": "primitive",
|
|
111
|
+
"family": "ruv-fann",
|
|
112
|
+
"status": "published",
|
|
113
|
+
"repository": "ruvnet/ruv-FANN",
|
|
114
|
+
"description": "Precompiled scalar and SIMD WebAssembly runtime",
|
|
115
|
+
"links": {
|
|
116
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wasm",
|
|
117
|
+
"source": "https://github.com/ruvnet/ruv-FANN"
|
|
118
|
+
},
|
|
119
|
+
"contracts": [
|
|
120
|
+
"ruv-fann.dense-layers/1"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "ruv-fann-wsp",
|
|
125
|
+
"version": "0.2.0",
|
|
126
|
+
"role": "primitive",
|
|
127
|
+
"family": "ruv-fann",
|
|
128
|
+
"status": "published",
|
|
129
|
+
"repository": "ruvnet/ruv-FANN",
|
|
130
|
+
"description": "Bounded parameter patches, diagnostics and regression gates",
|
|
131
|
+
"links": {
|
|
132
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp",
|
|
133
|
+
"source": "https://github.com/ruvnet/ruv-FANN"
|
|
134
|
+
},
|
|
135
|
+
"contracts": [
|
|
136
|
+
"ruv-fann.dense-layers/1",
|
|
137
|
+
"ruv-fann.weight-patch/1",
|
|
138
|
+
"ruv-fann.weight-evaluation/1"
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"name": "agentic-flow",
|
|
143
|
+
"version": "3.5.4",
|
|
144
|
+
"role": "satellite",
|
|
145
|
+
"family": "agentic-flow",
|
|
146
|
+
"status": "published",
|
|
147
|
+
"repository": "ruvnet/agentic-flow",
|
|
148
|
+
"description": "Optional agent workflow integration",
|
|
149
|
+
"links": {
|
|
150
|
+
"npm": "https://www.npmjs.com/package/agentic-flow",
|
|
151
|
+
"source": "https://github.com/ruvnet/agentic-flow"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"name": "@ruvector/attention",
|
|
156
|
+
"version": "2.3.1",
|
|
157
|
+
"role": "satellite",
|
|
158
|
+
"family": "ruvector",
|
|
159
|
+
"status": "published",
|
|
160
|
+
"repository": "ruvnet/RuVector",
|
|
161
|
+
"description": "Specialized attention integration",
|
|
162
|
+
"links": {
|
|
163
|
+
"npm": "https://www.npmjs.com/package/@ruvector/attention",
|
|
164
|
+
"source": "https://github.com/ruvnet/RuVector"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "@ruvector/ruvllm",
|
|
169
|
+
"version": "4.2.2",
|
|
170
|
+
"role": "satellite",
|
|
171
|
+
"family": "ruvector",
|
|
172
|
+
"status": "published",
|
|
173
|
+
"repository": "ruvnet/RuVector",
|
|
174
|
+
"description": "Specialized language-model integration",
|
|
175
|
+
"links": {
|
|
176
|
+
"npm": "https://www.npmjs.com/package/@ruvector/ruvllm",
|
|
177
|
+
"source": "https://github.com/ruvnet/RuVector"
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"name": "ruv-fann-wsp-lifecycle",
|
|
182
|
+
"version": "0.1.0",
|
|
183
|
+
"role": "satellite",
|
|
184
|
+
"family": "ruv-fann",
|
|
185
|
+
"status": "published",
|
|
186
|
+
"repository": "ruvnet/ruv-FANN",
|
|
187
|
+
"directory": "packages/wsp-toolkit/packages/lifecycle",
|
|
188
|
+
"description": "Evaluate bounded neural weight patches across named regression suites and independent candidate branches.",
|
|
189
|
+
"contracts": [
|
|
190
|
+
"ruv-fann.dense-layers/1",
|
|
191
|
+
"ruv-fann.weight-patch/1",
|
|
192
|
+
"ruv-fann.weight-evaluation/1"
|
|
193
|
+
],
|
|
194
|
+
"links": {
|
|
195
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/lifecycle",
|
|
196
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-lifecycle"
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"name": "ruv-fann-wsp-search",
|
|
201
|
+
"version": "0.1.0",
|
|
202
|
+
"role": "satellite",
|
|
203
|
+
"family": "ruv-fann",
|
|
204
|
+
"status": "published",
|
|
205
|
+
"repository": "ruvnet/ruv-FANN",
|
|
206
|
+
"directory": "packages/wsp-toolkit/packages/search",
|
|
207
|
+
"description": "Search explicit neural weight values without gradients; propose bounded Boolean-table and experimental Hebbian edits.",
|
|
208
|
+
"contracts": [
|
|
209
|
+
"ruv-fann.dense-layers/1",
|
|
210
|
+
"ruv-fann.weight-patch/1"
|
|
211
|
+
],
|
|
212
|
+
"links": {
|
|
213
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/search",
|
|
214
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-search"
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"name": "ruv-fann-wsp-huggingface",
|
|
219
|
+
"version": "0.1.0",
|
|
220
|
+
"role": "satellite",
|
|
221
|
+
"family": "ruv-fann",
|
|
222
|
+
"status": "published",
|
|
223
|
+
"repository": "ruvnet/ruv-FANN",
|
|
224
|
+
"directory": "packages/wsp-toolkit/packages/huggingface",
|
|
225
|
+
"description": "Load public Hugging Face dense JSON checkpoints with pinned revisions, SHA-256 integrity and bounded WSP validation.",
|
|
226
|
+
"contracts": [
|
|
227
|
+
"ruv-fann.dense-layers/1"
|
|
228
|
+
],
|
|
229
|
+
"links": {
|
|
230
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/huggingface",
|
|
231
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-huggingface"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "ruv-fann-wsp-cloudflare",
|
|
236
|
+
"version": "0.1.0",
|
|
237
|
+
"role": "satellite",
|
|
238
|
+
"family": "ruv-fann",
|
|
239
|
+
"status": "published",
|
|
240
|
+
"repository": "ruvnet/ruv-FANN",
|
|
241
|
+
"directory": "packages/wsp-toolkit/packages/cloudflare",
|
|
242
|
+
"description": "Run bounded neural weight patches and regression evaluations in Cloudflare Workers with static WebAssembly modules.",
|
|
243
|
+
"contracts": [
|
|
244
|
+
"ruv-fann.dense-layers/1",
|
|
245
|
+
"ruv-fann.weight-patch/1",
|
|
246
|
+
"ruv-fann.weight-evaluation/1"
|
|
247
|
+
],
|
|
248
|
+
"links": {
|
|
249
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/cloudflare",
|
|
250
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-cloudflare"
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"name": "ruv-fann-wsp-ruos",
|
|
255
|
+
"version": "0.1.0",
|
|
256
|
+
"role": "satellite",
|
|
257
|
+
"family": "ruv-fann",
|
|
258
|
+
"status": "published",
|
|
259
|
+
"repository": "ruvnet/ruv-FANN",
|
|
260
|
+
"directory": "packages/wsp-toolkit/packages/ruos",
|
|
261
|
+
"description": "Validate real WebAssembly canary output and bind trusted ruOS executor completion evidence to explicit requests.",
|
|
262
|
+
"contracts": [
|
|
263
|
+
"ruv-fann.dense-layers/1",
|
|
264
|
+
"ruv-fann.weight-patch/1"
|
|
265
|
+
],
|
|
266
|
+
"links": {
|
|
267
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/ruos",
|
|
268
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-ruos"
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"name": "ruv-fann-wsp-invariants",
|
|
273
|
+
"version": "0.1.0",
|
|
274
|
+
"role": "satellite",
|
|
275
|
+
"family": "ruv-fann",
|
|
276
|
+
"status": "published",
|
|
277
|
+
"repository": "ruvnet/ruv-FANN",
|
|
278
|
+
"directory": "packages/wsp-toolkit/packages/invariants",
|
|
279
|
+
"description": "Check finite-domain neural output bounds and expected values using real scalar or SIMD WebAssembly inference.",
|
|
280
|
+
"contracts": [
|
|
281
|
+
"ruv-fann.dense-layers/1"
|
|
282
|
+
],
|
|
283
|
+
"links": {
|
|
284
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/invariants",
|
|
285
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-invariants"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"name": "ruv-fann-wsp-swarm",
|
|
290
|
+
"version": "0.1.0",
|
|
291
|
+
"role": "satellite",
|
|
292
|
+
"family": "ruv-fann",
|
|
293
|
+
"status": "published",
|
|
294
|
+
"repository": "ruvnet/ruv-FANN",
|
|
295
|
+
"directory": "packages/wsp-toolkit/packages/swarm",
|
|
296
|
+
"description": "Run bounded neural patch and search proposals in real Node.js worker threads with isolated WebAssembly instances.",
|
|
297
|
+
"contracts": [
|
|
298
|
+
"ruv-fann.dense-layers/1",
|
|
299
|
+
"ruv-fann.weight-patch/1"
|
|
300
|
+
],
|
|
301
|
+
"links": {
|
|
302
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/swarm",
|
|
303
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-swarm"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"name": "ruv-fann-wsp-registry",
|
|
308
|
+
"version": "0.1.0",
|
|
309
|
+
"role": "satellite",
|
|
310
|
+
"family": "ruv-fann",
|
|
311
|
+
"status": "published",
|
|
312
|
+
"repository": "ruvnet/ruv-FANN",
|
|
313
|
+
"directory": "packages/wsp-toolkit/packages/registry",
|
|
314
|
+
"description": "Adapt AgentDB weight programs with serialized operations, revision checks and caller-supplied persistence acknowledgment.",
|
|
315
|
+
"contracts": [
|
|
316
|
+
"ruv-fann.dense-layers/1",
|
|
317
|
+
"ruv-fann.weight-patch/1",
|
|
318
|
+
"ruv-fann.weight-evaluation/1"
|
|
319
|
+
],
|
|
320
|
+
"links": {
|
|
321
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/registry",
|
|
322
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-registry"
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"name": "ruv-fann-wsp-loop",
|
|
327
|
+
"version": "0.1.0",
|
|
328
|
+
"role": "satellite",
|
|
329
|
+
"family": "ruv-fann",
|
|
330
|
+
"status": "published",
|
|
331
|
+
"repository": "ruvnet/ruv-FANN",
|
|
332
|
+
"directory": "packages/wsp-toolkit/packages/loop",
|
|
333
|
+
"description": "Coordinate bounded local weight evolution with worker proposals, independent regression gates and revision-based promotion.",
|
|
334
|
+
"contracts": [
|
|
335
|
+
"ruv-fann.dense-layers/1",
|
|
336
|
+
"ruv-fann.weight-patch/1",
|
|
337
|
+
"ruv-fann.weight-evaluation/1"
|
|
338
|
+
],
|
|
339
|
+
"links": {
|
|
340
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/loop",
|
|
341
|
+
"npm": "https://www.npmjs.com/package/ruv-fann-wsp-loop"
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"name": "ruvnet-constellation-contracts",
|
|
346
|
+
"version": "0.1.0",
|
|
347
|
+
"role": "primitive",
|
|
348
|
+
"family": "constellation",
|
|
349
|
+
"status": "published",
|
|
350
|
+
"repository": "ruvnet/ruv-FANN",
|
|
351
|
+
"directory": "packages/wsp-toolkit/packages/contracts",
|
|
352
|
+
"description": "Validate bounded JSON catalogs and module manifests for independently installable RuvNet packages.",
|
|
353
|
+
"links": {
|
|
354
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/contracts",
|
|
355
|
+
"npm": "https://www.npmjs.com/package/ruvnet-constellation-contracts"
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"name": "ruvnet-constellation",
|
|
360
|
+
"version": "0.1.0",
|
|
361
|
+
"role": "nucleus",
|
|
362
|
+
"family": "constellation",
|
|
363
|
+
"status": "published",
|
|
364
|
+
"repository": "ruvnet/ruv-FANN",
|
|
365
|
+
"directory": "packages/wsp-toolkit/packages/constellation",
|
|
366
|
+
"description": "Discover versioned RuvNet packages, plan explicit installs and scaffold local integration adapters with a lightweight CLI.",
|
|
367
|
+
"links": {
|
|
368
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/constellation",
|
|
369
|
+
"npm": "https://www.npmjs.com/package/ruvnet-constellation"
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"name": "ruvnet-constellation-telemetry",
|
|
374
|
+
"version": "0.1.0",
|
|
375
|
+
"role": "satellite",
|
|
376
|
+
"family": "constellation",
|
|
377
|
+
"status": "published",
|
|
378
|
+
"repository": "ruvnet/ruv-FANN",
|
|
379
|
+
"directory": "packages/wsp-toolkit/packages/telemetry",
|
|
380
|
+
"description": "Analyze public npm and GitHub observations, consented project samples and standalone package dashboards.",
|
|
381
|
+
"links": {
|
|
382
|
+
"source": "https://github.com/ruvnet/ruv-FANN/tree/main/packages/wsp-toolkit/packages/telemetry",
|
|
383
|
+
"npm": "https://www.npmjs.com/package/ruvnet-constellation-telemetry"
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
],
|
|
387
|
+
"profiles": [
|
|
388
|
+
{
|
|
389
|
+
"id": "weights",
|
|
390
|
+
"description": "Use the independently installable published weight-editing primitive",
|
|
391
|
+
"packages": [
|
|
392
|
+
"ruv-fann-wsp"
|
|
393
|
+
]
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"id": "memory",
|
|
397
|
+
"description": "Combine durable model programs with bounded parameter edits",
|
|
398
|
+
"packages": [
|
|
399
|
+
"agentdb",
|
|
400
|
+
"ruv-fann-wsp"
|
|
401
|
+
]
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"id": "orchestration",
|
|
405
|
+
"description": "Optional public harness and validated CLI entry points",
|
|
406
|
+
"packages": [
|
|
407
|
+
"metaharness",
|
|
408
|
+
"@metaharness/router",
|
|
409
|
+
"@claude-flow/cli"
|
|
410
|
+
]
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"id": "local-loop",
|
|
414
|
+
"description": "Plan explicit version-pinned installs for the local weight-evolution loop and its supporting packages",
|
|
415
|
+
"packages": [
|
|
416
|
+
"ruv-fann-wsp-loop",
|
|
417
|
+
"ruv-fann-wsp-registry",
|
|
418
|
+
"ruv-fann-wsp-swarm",
|
|
419
|
+
"ruv-fann-wsp-invariants"
|
|
420
|
+
]
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-constellation",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Discover versioned RuvNet packages, plan explicit installs and scaffold local integration adapters with a lightweight CLI.",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"types": "./src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.d.ts",
|
|
11
|
+
"import": "./src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./catalog.json": "./catalog.json"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"constellation": "./src/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"src",
|
|
20
|
+
"catalog.json",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"constellation.json"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20.9.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"ruvnet-constellation-contracts": "0.1.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "node --test test/*.test.mjs"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/ruvnet/ruv-FANN.git",
|
|
38
|
+
"directory": "packages/wsp-toolkit/packages/constellation"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"ruvnet",
|
|
45
|
+
"constellation",
|
|
46
|
+
"package-discovery",
|
|
47
|
+
"cli",
|
|
48
|
+
"integration",
|
|
49
|
+
"scaffolding",
|
|
50
|
+
"package-catalog"
|
|
51
|
+
],
|
|
52
|
+
"homepage": "https://ruvnet.github.io/ruv-FANN/",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/ruvnet/ruv-FANN/issues"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {readCatalog,planProfile,scaffoldSatellite} from './index.js';
|
|
3
|
+
const help=`constellation — explicit local discovery, planning and satellite scaffolding
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
constellation help
|
|
7
|
+
constellation catalog [--json]
|
|
8
|
+
constellation plan <profile> [--json]
|
|
9
|
+
constellation scaffold <new-directory> --name <npm-name> [--description <text>] [--integration <npm-name> ...] [--json]
|
|
10
|
+
|
|
11
|
+
Plans describe verified published pins and local candidates separately. No command
|
|
12
|
+
installs dependencies, starts an agent, collects telemetry or publishes a package.
|
|
13
|
+
Scaffolding requires Linux with /proc/self/fd, creates a new directory only,
|
|
14
|
+
and refuses existing targets and symlinks. Catalog and planning remain portable.
|
|
15
|
+
|
|
16
|
+
Nucleus: https://www.npmjs.com/package/metaharness
|
|
17
|
+
https://www.npmjs.com/package/@claude-flow/cli
|
|
18
|
+
Memory: https://www.npmjs.com/package/agentdb
|
|
19
|
+
Vectors: https://www.npmjs.com/package/ruvector
|
|
20
|
+
Weights: https://www.npmjs.com/package/ruv-fann-wsp
|
|
21
|
+
`;
|
|
22
|
+
try {
|
|
23
|
+
const args=process.argv.slice(2),command=args.shift()??'help';
|
|
24
|
+
if(command==='help'||command==='--help'||command==='-h'){
|
|
25
|
+
if(args.length)throw new Error('help accepts no arguments');
|
|
26
|
+
process.stdout.write(help);
|
|
27
|
+
}else if(command==='catalog'){
|
|
28
|
+
if(args.length&&!(args.length===1&&args[0]==='--json'))throw new Error('catalog accepts only --json');
|
|
29
|
+
process.stdout.write(JSON.stringify(await readCatalog(),null,2)+'\n');
|
|
30
|
+
}else if(command==='plan'){
|
|
31
|
+
const profile=args.shift();if(!profile||(args.length&&!(args.length===1&&args[0]==='--json')))throw new Error('usage: constellation plan <profile> [--json]');
|
|
32
|
+
process.stdout.write(JSON.stringify(planProfile(await readCatalog(),profile),null,2)+'\n');
|
|
33
|
+
}else if(command==='scaffold'){
|
|
34
|
+
const directory=args.shift();if(!directory||directory.startsWith('--'))throw new Error('scaffold requires an explicit new directory');
|
|
35
|
+
const config={directory},integrations=[];const seen=new Set();
|
|
36
|
+
while(args.length){
|
|
37
|
+
const flag=args.shift();
|
|
38
|
+
if(flag==='--json'){if(seen.has(flag))throw new Error('duplicate --json');seen.add(flag);continue;}
|
|
39
|
+
if(!['--name','--description','--integration'].includes(flag))throw new Error('unknown scaffold option');
|
|
40
|
+
const value=args.shift();if(!value||value.startsWith('--'))throw new Error(`missing value for ${flag}`);
|
|
41
|
+
if(flag==='--integration')integrations.push(value);
|
|
42
|
+
else {if(seen.has(flag))throw new Error(`duplicate ${flag}`);seen.add(flag);config[flag.slice(2)]=value;}
|
|
43
|
+
}
|
|
44
|
+
if(integrations.length)config.integrations=integrations;
|
|
45
|
+
process.stdout.write(JSON.stringify(await scaffoldSatellite(config),null,2)+'\n');
|
|
46
|
+
}else throw new Error('unknown command; use constellation help');
|
|
47
|
+
}catch(error){process.stderr.write(`constellation: ${error.message}\n`);process.exitCode=2;}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type {ConstellationCatalog,CatalogPackage} from 'ruvnet-constellation-contracts';
|
|
2
|
+
export interface ConstellationPlan {
|
|
3
|
+
schema:'ruvnet.constellation-plan/1'; profile:string; description:string; packages:CatalogPackage[];
|
|
4
|
+
registrySpecs:string[]; localCandidates:{name:string;directory?:string;repository:string}[];
|
|
5
|
+
publicInstallAvailable:boolean; automaticInstall:false; telemetry:'disabled';
|
|
6
|
+
}
|
|
7
|
+
export interface SatelliteScaffoldOptions {directory:string;name:string;description?:string;integrations?:string[]}
|
|
8
|
+
export interface SatelliteScaffold {schema:'ruvnet.constellation-scaffold/1';directory:string;name:string;status:'candidate';files:string[];automaticInstall:false;telemetry:'disabled';published:false}
|
|
9
|
+
export function readCatalog(path?:string|URL):Promise<ConstellationCatalog>;
|
|
10
|
+
export function planProfile(catalog:ConstellationCatalog,id:string):ConstellationPlan;
|
|
11
|
+
/** Scaffolding requires Linux directory descriptors and /proc/self/fd; fails closed elsewhere. */
|
|
12
|
+
export function scaffoldSatellite(options:SatelliteScaffoldOptions):Promise<SatelliteScaffold>;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { open, mkdir, lstat } from 'node:fs/promises';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { resolve, dirname, basename, parse, relative, join, sep } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { CONTRACTS, CONTRACT_LIMITS, validateCatalog, validateModuleManifest } from 'ruvnet-constellation-contracts';
|
|
6
|
+
|
|
7
|
+
/** Read only an explicitly selected local catalog; no registry or network access. */
|
|
8
|
+
export async function readCatalog(path = new URL('../catalog.json', import.meta.url)) {
|
|
9
|
+
if (path instanceof URL) path = fileURLToPath(path);
|
|
10
|
+
if (typeof path !== 'string' || !path.length || path.length > 4096 || /[\x00-\x1f\x7f]/.test(path)) throw new TypeError('catalog path must be bounded local text');
|
|
11
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
12
|
+
try {
|
|
13
|
+
if (!(await handle.stat()).isFile()) throw new TypeError('catalog must be a regular file');
|
|
14
|
+
const bytes = Buffer.alloc(CONTRACT_LIMITS.maxBytes + 1);
|
|
15
|
+
let length = 0;
|
|
16
|
+
while (length < bytes.length) {
|
|
17
|
+
const { bytesRead } = await handle.read(bytes, length, bytes.length - length, null);
|
|
18
|
+
if (!bytesRead) break;
|
|
19
|
+
length += bytesRead;
|
|
20
|
+
}
|
|
21
|
+
if (length > CONTRACT_LIMITS.maxBytes) throw new RangeError('catalog exceeds byte limit');
|
|
22
|
+
return validateCatalog(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes.subarray(0, length))));
|
|
23
|
+
} finally { await handle.close(); }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Planning is data only. Candidate sources never become purported public install specs. */
|
|
27
|
+
export function planProfile(input, id) {
|
|
28
|
+
const catalog = validateCatalog(input);
|
|
29
|
+
if (typeof id !== 'string' || !/^[a-z][a-z0-9-]{0,47}$/.test(id)) throw new TypeError('profile must be a bounded identifier');
|
|
30
|
+
const profile = catalog.profiles.find(profile => profile.id === id);
|
|
31
|
+
if (!profile) throw new Error(`unknown constellation profile: ${id}`);
|
|
32
|
+
const entries = new Map(catalog.packages.map(entry => [entry.name, entry]));
|
|
33
|
+
const packages = profile.packages.map(name => entries.get(name));
|
|
34
|
+
return { schema: 'ruvnet.constellation-plan/1', profile: id, description: profile.description,
|
|
35
|
+
packages, registrySpecs: packages.filter(entry => entry.status === 'published').map(entry => `${entry.name}@${entry.version}`),
|
|
36
|
+
localCandidates: packages.filter(entry => entry.status === 'candidate').map(entry => ({name:entry.name, directory:entry.directory, repository:entry.repository})),
|
|
37
|
+
publicInstallAvailable: packages.every(entry => entry.status === 'published'), automaticInstall: false, telemetry: 'disabled' };
|
|
38
|
+
}
|
|
39
|
+
function argumentsRecord(value) {
|
|
40
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new TypeError('scaffold options must be a plain record');
|
|
41
|
+
const result = {};
|
|
42
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
43
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
44
|
+
if (typeof key !== 'string' || !['directory','name','description','integrations'].includes(key) || !property || !Object.hasOwn(property,'value'))
|
|
45
|
+
throw new TypeError('unknown scaffold option or accessor');
|
|
46
|
+
result[key] = property.value;
|
|
47
|
+
}
|
|
48
|
+
if (!Object.hasOwn(result, 'directory') || !Object.hasOwn(result, 'name')) throw new TypeError('scaffold requires directory and name');
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function markdown(value) { return value.replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replace(/[\\`*_{}\[\]()#+.!|]/g, '\\$&'); }
|
|
52
|
+
function directoryAlias(handle) { return `/proc/self/fd/${handle.fd}`; }
|
|
53
|
+
async function closeDirectories(resources) {
|
|
54
|
+
const results = await Promise.allSettled([...resources].reverse().map(resource => resource.handle.close()));
|
|
55
|
+
const failed = results.find(result => result.status === 'rejected');
|
|
56
|
+
if (failed) throw failed.reason;
|
|
57
|
+
}
|
|
58
|
+
async function verifyDirectories(resources) {
|
|
59
|
+
for (const resource of resources) {
|
|
60
|
+
const held = await resource.handle.stat();
|
|
61
|
+
const entry = await lstat(resource.parent ? `${directoryAlias(resource.parent)}/${resource.name}` : resource.root);
|
|
62
|
+
if (!held.isDirectory() || entry.isSymbolicLink() || !entry.isDirectory() || entry.dev !== held.dev || entry.ino !== held.ino)
|
|
63
|
+
throw new Error('scaffold directory relationship changed during creation');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function openParentDirectories(path) {
|
|
67
|
+
if (process.platform !== 'linux' || typeof constants.O_DIRECTORY !== 'number' || typeof constants.O_NOFOLLOW !== 'number')
|
|
68
|
+
throw new Error('scaffolding requires Linux directory descriptors and /proc/self/fd; catalog and planning remain portable');
|
|
69
|
+
const root = parse(path).root, parts = relative(root, path).split(sep).filter(Boolean), resources = [];
|
|
70
|
+
if (parts.length > 64) throw new RangeError('scaffold parent path exceeds directory depth limit');
|
|
71
|
+
const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;
|
|
72
|
+
try {
|
|
73
|
+
// Each next component resolves through a held descriptor. No unverified ancestor
|
|
74
|
+
// path is reused, and O_NOFOLLOW applies to the newly opened component.
|
|
75
|
+
let current = await open(root, flags);
|
|
76
|
+
resources.push({ handle: current, root });
|
|
77
|
+
// Verify the kernel-provided descriptor namespace before creating any output.
|
|
78
|
+
const proc = await lstat('/proc/self/fd');
|
|
79
|
+
if (!proc.isDirectory()) throw new Error('Linux /proc/self/fd directory is unavailable');
|
|
80
|
+
for (const name of parts) {
|
|
81
|
+
const parent = current;
|
|
82
|
+
try { current = await open(`${directoryAlias(parent)}/${name}`, flags); }
|
|
83
|
+
catch (error) { throw new TypeError('scaffold parent path must consist of existing real directories', {cause:error}); }
|
|
84
|
+
resources.push({ handle: current, parent, name });
|
|
85
|
+
}
|
|
86
|
+
await verifyDirectories(resources);
|
|
87
|
+
return resources;
|
|
88
|
+
} catch (error) { await closeDirectories(resources); throw error; }
|
|
89
|
+
}
|
|
90
|
+
async function createDirectory(resources, parent, name) {
|
|
91
|
+
await verifyDirectories(resources);
|
|
92
|
+
const path = `${directoryAlias(parent)}/${name}`;
|
|
93
|
+
// mkdir refuses an existing target, including dangling symlinks. Opening the
|
|
94
|
+
// result refuses substitution by a symlink before its descriptor is acquired.
|
|
95
|
+
await mkdir(path, {mode:0o700});
|
|
96
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
97
|
+
resources.push({handle,parent,name});
|
|
98
|
+
await verifyDirectories(resources);
|
|
99
|
+
return handle;
|
|
100
|
+
}
|
|
101
|
+
async function writeNew(directoryHandle, name, content) {
|
|
102
|
+
// Only fixed leaf names reach this function. O_EXCL prevents existing leaf
|
|
103
|
+
// replacement; the held parent descriptor prevents intermediate symlink races.
|
|
104
|
+
const path = `${directoryAlias(directoryHandle)}/${name}`;
|
|
105
|
+
const handle = await open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
106
|
+
try { await handle.writeFile(content); } finally { await handle.close(); }
|
|
107
|
+
}
|
|
108
|
+
const adapterSource = `// Bounded JSON adapter template. Define and independently test domain mapping before release.
|
|
109
|
+
export function adaptRecord(input) {
|
|
110
|
+
let nodes = 0, text = 0;
|
|
111
|
+
const ancestors = new Set();
|
|
112
|
+
function copy(value, depth) {
|
|
113
|
+
if (++nodes > 8192 || depth > 8) throw new RangeError('adapter structure exceeds limits');
|
|
114
|
+
if (value === null || typeof value === 'boolean') return value;
|
|
115
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value === 0 ? 0 : value;
|
|
116
|
+
if (typeof value === 'string') { text += value.length; if (text > 65536) throw new RangeError('adapter text exceeds limit'); return value; }
|
|
117
|
+
if (!value || typeof value !== 'object' || ancestors.has(value)) throw new TypeError('adapter accepts finite acyclic JSON data only');
|
|
118
|
+
const array = Array.isArray(value);
|
|
119
|
+
if (!(array ? Object.getPrototypeOf(value) === Array.prototype : [Object.prototype, null].includes(Object.getPrototypeOf(value)))) throw new TypeError('adapter requires plain data');
|
|
120
|
+
if (array && value.length > 8192) throw new RangeError('adapter array exceeds limit');
|
|
121
|
+
const result = array ? [] : Object.create(null); ancestors.add(value);
|
|
122
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
123
|
+
if (array && key === 'length') continue;
|
|
124
|
+
if (typeof key !== 'string' || ['__proto__','constructor','prototype'].includes(key) || (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length))) throw new TypeError('adapter contains unsafe properties');
|
|
125
|
+
text += key.length; if (text > 65536) throw new RangeError('adapter text exceeds limit');
|
|
126
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
127
|
+
if (!property || !Object.hasOwn(property, 'value')) throw new TypeError('adapter accessors are forbidden');
|
|
128
|
+
result[key] = copy(property.value, depth + 1);
|
|
129
|
+
}
|
|
130
|
+
ancestors.delete(value);
|
|
131
|
+
if (array && (result.length !== value.length || Object.keys(result).length !== value.length)) throw new TypeError('adapter arrays must be dense');
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
const result = copy(input, 0);
|
|
135
|
+
if (new TextEncoder().encode(JSON.stringify(result)).length > 65536) throw new RangeError('adapter bytes exceed limit');
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
const templateTests = `import test from 'node:test';
|
|
140
|
+
import assert from 'node:assert/strict';
|
|
141
|
+
import { adaptRecord } from '../src/index.js';
|
|
142
|
+
test('copies JSON without retaining caller objects', () => {
|
|
143
|
+
const input = {format:'example', values:[1,2,null]}; const output = adaptRecord(input);
|
|
144
|
+
input.values[0] = 10; assert.equal(output.values[0],1); assert.equal(output.format,'example');
|
|
145
|
+
});
|
|
146
|
+
test('rejects executable, nonfinite and unsafe inputs without invoking getters', () => {
|
|
147
|
+
let calls=0; const input={get value(){calls++; return 1;}};
|
|
148
|
+
assert.throws(()=>adaptRecord(input));assert.equal(calls,0);
|
|
149
|
+
for(const value of [Infinity,()=>{},new Array(2),JSON.parse('{"__proto__":{"polluted":true}}')]) assert.throws(()=>adaptRecord(value));
|
|
150
|
+
assert.equal({}.polluted,undefined);
|
|
151
|
+
});
|
|
152
|
+
test('bounds copied data and refuses cycles', () => {
|
|
153
|
+
const cycle={};cycle.self=cycle;
|
|
154
|
+
assert.throws(()=>adaptRecord(cycle));assert.throws(()=>adaptRecord('x'.repeat(65537)));
|
|
155
|
+
});
|
|
156
|
+
`;
|
|
157
|
+
const benchmarkSource = `import {performance} from 'node:perf_hooks';
|
|
158
|
+
import assert from 'node:assert/strict';
|
|
159
|
+
import {adaptRecord} from './src/index.js';
|
|
160
|
+
const input={format:'example',values:[1,2,3]},iterations=1000,start=performance.now();
|
|
161
|
+
for(let index=0;index<iterations;index++)assert.equal(adaptRecord(input).values[2],3);
|
|
162
|
+
console.log(JSON.stringify({schema:'ruvnet.satellite-benchmark/1',iterations,elapsedMs:performance.now()-start,note:'Local JSON-copy observation, not a domain accuracy or WASM benchmark.'}));
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
/** Create a reviewable local candidate. Never overwrites a target or installs dependencies. */
|
|
166
|
+
export async function scaffoldSatellite(options) {
|
|
167
|
+
// Capture all configuration before asynchronous filesystem operations.
|
|
168
|
+
const config = argumentsRecord(options), directory = config.directory;
|
|
169
|
+
if (typeof directory !== 'string' || !directory.length || directory.length > 4096 || /[\x00-\x1f\x7f]/.test(directory) ||
|
|
170
|
+
directory.split(/[\\/]/).some(part => part === '..') || (sep === '/' && directory.includes('\\')))
|
|
171
|
+
throw new TypeError('directory must be bounded text without traversal, control characters or ambiguous separators');
|
|
172
|
+
const module = validateModuleManifest({schema:CONTRACTS.module,name:config.name,version:'0.1.0',role:'satellite',contracts:[CONTRACTS.module],integrations:config.integrations === undefined ? [] : config.integrations});
|
|
173
|
+
const description = config.description === undefined ? 'Bounded JSON adapter candidate; replace mapping with independently tested domain logic.' : config.description;
|
|
174
|
+
if (typeof description !== 'string' || !description.length || description.length > 2000 || /[\x00-\x1f\x7f]/.test(description)) throw new TypeError('description must be bounded plain text');
|
|
175
|
+
const target = resolve(directory), parent = dirname(target);
|
|
176
|
+
if (target === parent) throw new TypeError('scaffold target cannot be a filesystem root');
|
|
177
|
+
const resources = await openParentDirectories(parent);
|
|
178
|
+
try {
|
|
179
|
+
const parentHandle = resources.at(-1).handle;
|
|
180
|
+
const rootHandle = await createDirectory(resources, parentHandle, basename(target));
|
|
181
|
+
const sourceHandle = await createDirectory(resources, rootHandle, 'src');
|
|
182
|
+
const testHandle = await createDirectory(resources, rootHandle, 'test');
|
|
183
|
+
const manifest = {name:module.name,version:module.version,description,type:'module',main:'./src/index.js',types:'./src/index.d.ts',
|
|
184
|
+
exports:{'.':{types:'./src/index.d.ts',import:'./src/index.js'}},files:['src','constellation.json','README.md','LICENSE'],
|
|
185
|
+
engines:{node:'>=20.9.0'},scripts:{test:'node --test test/*.test.mjs',check:'npm test',benchmark:'node benchmark.mjs','pack:check':'npm pack --dry-run --ignore-scripts'},license:'MIT',publishConfig:{access:'public'}};
|
|
186
|
+
const integrationLinks = module.integrations.length ? module.integrations.map(name => `- [${name}](https://www.npmjs.com/package/${name}) — optional integration metadata; no dependency is installed.`).join('\n') : 'No integration is selected. Add only integrations the module actually uses.';
|
|
187
|
+
const readme = `# ${markdown(module.name)}\n\n${markdown(description)}\n\nThis is a local candidate with a bounded identity JSON adapter template. It has not been published or proven useful for a domain. Replace the mapping and add independent domain cases before release.\n\n\`adaptRecord(value)\` owns finite acyclic JSON, rejects accessors/prototype keys, and bounds depth, elements and bytes. It executes no callbacks and performs no network requests. These checks are not a WSP or domain-accuracy benchmark.\n\nRun \`npm test\`, \`npm run benchmark\` and \`npm run pack:check\`. No install or provider is required for the template.\n\n## Optional constellation integrations\n\n${integrationLinks}\n\nDiscover [MetaHarness](https://www.npmjs.com/package/metaharness), [Ruflo](https://www.npmjs.com/package/@claude-flow/cli), [AgentDB](https://www.npmjs.com/package/agentdb), [RuVector](https://www.npmjs.com/package/ruvector) and [WSP](https://www.npmjs.com/package/ruv-fann-wsp) independently. The template does not force any nucleus dependency.\n\n## Publication preparation\n\nReview the generated files, package name/namespace ownership, license and quality evidence. Configure this package's npm trusted publisher for the exact owner, repository, workflow and optional environment; use a protected exact-source/archive release with supported Node/npm and OIDC provenance. The scaffold neither configures a publisher nor publishes. See [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).\n`;
|
|
188
|
+
const files = new Map([
|
|
189
|
+
['package.json',JSON.stringify(manifest,null,2)+'\n'],['constellation.json',JSON.stringify(module,null,2)+'\n'],
|
|
190
|
+
['README.md',readme],['src/index.js',adapterSource],['src/index.d.ts','/** Owns bounded finite JSON data. Replace this template with domain-specific types. */\nexport function adaptRecord(input: unknown): unknown;\n'],
|
|
191
|
+
['test/adapter.test.mjs',templateTests],['benchmark.mjs',benchmarkSource],
|
|
192
|
+
['LICENSE',`MIT License\n\nCopyright (c) ${new Date().getUTCFullYear()} contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n`]
|
|
193
|
+
]);
|
|
194
|
+
for (const [path,content] of files) {
|
|
195
|
+
await verifyDirectories(resources);
|
|
196
|
+
const parts = path.split('/');
|
|
197
|
+
const directoryHandle = parts.length === 1 ? rootHandle : parts[0] === 'src' ? sourceHandle : testHandle;
|
|
198
|
+
await writeNew(directoryHandle, parts.at(-1), content);
|
|
199
|
+
}
|
|
200
|
+
await verifyDirectories(resources);
|
|
201
|
+
return {schema:'ruvnet.constellation-scaffold/1',directory:target,name:module.name,status:'candidate',files:[...files.keys()],automaticInstall:false,telemetry:'disabled',published:false};
|
|
202
|
+
} finally { await closeDirectories(resources); }
|
|
203
|
+
}
|