0xray 4.0.14 → 4.0.15
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/.grok-plugin/plugin.json +1 -1
- package/.opencode/skills/orchestrator/SKILL.md +19 -1
- package/.xray/features.json +2 -2
- package/AGENTS-consumer.md +10 -5
- package/AGENTS.md +10 -7
- package/README.md +8 -7
- package/SKILLS.md +3 -2
- package/dist/AGENTS.md +10 -7
- package/dist/CHANGELOG.md +48 -0
- package/dist/README.md +8 -7
- package/dist/SKILLS.md +3 -2
- package/dist/cli/commands/hermes-install.js +2 -1
- package/dist/cli/commands/openclaw-install.js +2 -1
- package/dist/cli/commands/opencode-install.js +2 -1
- package/dist/cli/index.js +2 -1
- package/dist/integrations/cursor/hooks/cursor-hook-utils.js +9 -0
- package/dist/integrations/grok/grok-cli.js +2 -1
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/reporting/log-parser.js +2 -2
- package/dist/skills/orchestrator/SKILL.md +19 -1
- package/llms.txt +8 -7
- package/package.json +2 -1
- package/scripts/foundry/CHANGELOG.md +33 -1
- package/scripts/foundry/README.md +12 -5
- package/scripts/foundry/assert-packed-dist-cli.mjs +6 -0
- package/scripts/foundry/blip-kapow.cjs +2 -0
- package/scripts/foundry/blip-looker.cjs +2 -0
- package/scripts/foundry/blip-render.cjs +2 -1237
- package/scripts/foundry/blip-rippel-organs.cjs +2 -0
- package/scripts/foundry/blip-rippel.cjs +2 -1963
- package/scripts/foundry/blip-scene.cjs +2 -0
- package/scripts/foundry/blip-stroke.cjs +2 -0
- package/scripts/foundry/blip-vibe.cjs +2 -0
- package/scripts/foundry/blip.mjs +23 -97
- package/scripts/foundry/cli.mjs +2 -2
- package/scripts/foundry/inspect.mjs +62 -19
- package/scripts/foundry/mill-plant.cjs +598 -0
- package/scripts/foundry/mint-suit.cjs +189 -99
- package/scripts/foundry/package.json +12 -1
- package/scripts/foundry/plant/agents/blip-inspect.yml +1 -1
- package/scripts/foundry/plant/agents/blip-looker.yml +5 -0
- package/scripts/foundry/plant/agents/blip-vibe.yml +5 -0
- package/scripts/foundry/plant/agents/blip.yml +1 -1
- package/scripts/foundry/plant/agents/inspect.yml +1 -1
- package/scripts/foundry/plant/agents/mill.yml +1 -1
- package/scripts/foundry/plant/agents/sound-inspect.yml +1 -1
- package/scripts/foundry/plant/agents/sound-mixer.yml +5 -0
- package/scripts/foundry/plant/agents/sound.yml +1 -1
- package/scripts/foundry/plant/motions/MOTION-REGISTRY.md +15 -9
- package/scripts/foundry/plant/motions/registry.json +5 -4
- package/scripts/foundry/plant/skills/blip/SKILL.md +4 -4
- package/scripts/foundry/plant/skills/blip-inspect/SKILL.md +2 -2
- package/scripts/foundry/plant/skills/blip-looker/SKILL.md +30 -0
- package/scripts/foundry/plant/skills/blip-vibe/SKILL.md +30 -0
- package/scripts/foundry/plant/skills/inspect/SKILL.md +2 -2
- package/scripts/foundry/plant/skills/sound/SKILL.md +3 -1
- package/scripts/foundry/plant/skills/sound-inspect/SKILL.md +1 -1
- package/scripts/foundry/plant/skills/sound-mixer/SKILL.md +28 -0
- package/scripts/foundry/sound-bed.cjs +2 -454
- package/scripts/foundry/sound-mixer.cjs +2 -0
- package/scripts/foundry/sound-rippel.cjs +2 -1434
- package/scripts/foundry/sound-taste.cjs +2 -0
- package/scripts/foundry/sound-voice.cjs +2 -0
- package/scripts/foundry/sound.mjs +23 -81
- package/scripts/node/install-bridges.cjs +39 -4
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/skills/orchestrator/SKILL.md +19 -1
- package/xray/features.json +2 -2
|
@@ -11,6 +11,7 @@ const os = require("os");
|
|
|
11
11
|
const path = require("path");
|
|
12
12
|
|
|
13
13
|
const { attachInventoryDna, inventoryDna } = require("./mill-dna.cjs");
|
|
14
|
+
const millPlantProtocol = require("./mill-plant.cjs");
|
|
14
15
|
|
|
15
16
|
const DEFAULT_PARAMS = {
|
|
16
17
|
codex: "xray/codex.json",
|
|
@@ -24,15 +25,13 @@ const DEFAULT_PARAMS = {
|
|
|
24
25
|
/** Factory shop plant. First-class with mill plant. Not 45/42 costume. */
|
|
25
26
|
const FACTORY_SHOP_SKILLS = ["shop-extract", "shop-witness", "shop-pin"];
|
|
26
27
|
|
|
27
|
-
/**
|
|
28
|
+
/** Builtin mill only. Package plants (blip, sound, @scope/pkg/prefix) use mill-plant.cjs. */
|
|
28
29
|
const FACTORY_PLANT_CATALOG = {
|
|
29
30
|
mill: { skills: ["mill", "inspect"], agents: ["mill.yml", "inspect.yml"] },
|
|
30
|
-
sound: { skills: ["sound", "sound-inspect"], agents: ["sound.yml", "sound-inspect.yml"] },
|
|
31
|
-
blip: { skills: ["blip", "blip-inspect"], agents: ["blip.yml", "blip-inspect.yml"] },
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
function isFactoryPlantKind(kind) {
|
|
35
|
-
return Boolean(kind
|
|
34
|
+
return Boolean(millPlantProtocol.parsePlantToken(kind));
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
function deepMerge(src, dest) {
|
|
@@ -122,51 +121,51 @@ function wantsCostume(targetDir) {
|
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
function millPlantDir(millPackageRoot) {
|
|
125
|
-
|
|
126
|
-
if (isDirectory(nested)) return nested;
|
|
127
|
-
const packed = path.join(millPackageRoot, "plant");
|
|
128
|
-
if (isDirectory(packed)) return packed;
|
|
129
|
-
return null;
|
|
124
|
+
return millPlantProtocol.foundryPlantDir(millPackageRoot);
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
function emptyPlantFiles() {
|
|
133
128
|
return { skills: [], agents: [] };
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
function catalogPlant(kind) {
|
|
137
|
-
|
|
138
|
-
|
|
131
|
+
function catalogPlant(kind, millPackageRoot) {
|
|
132
|
+
if (!kind || kind === "mill") {
|
|
133
|
+
const spec = FACTORY_PLANT_CATALOG.mill;
|
|
134
|
+
return spec ? { skills: [...spec.skills], agents: [...spec.agents] } : emptyPlantFiles();
|
|
135
|
+
}
|
|
136
|
+
const parsed = millPlantProtocol.parsePlantToken(kind);
|
|
137
|
+
if (!parsed || parsed.builtin) {
|
|
138
|
+
const spec = FACTORY_PLANT_CATALOG.mill;
|
|
139
|
+
return spec ? { skills: [...spec.skills], agents: [...spec.agents] } : emptyPlantFiles();
|
|
140
|
+
}
|
|
141
|
+
const pkgRoot = millPlantProtocol.resolvePackageRoot(parsed.packageName, {
|
|
142
|
+
targetDir: millPackageRoot,
|
|
143
|
+
millPackageRoot,
|
|
144
|
+
cwd: process.cwd(),
|
|
145
|
+
});
|
|
146
|
+
if (!pkgRoot) throw millPlantProtocol.unresolvedPackageError(parsed.packageName);
|
|
147
|
+
const plantId = parsed.plantId || millPlantProtocol.readProtocol(pkgRoot).default;
|
|
148
|
+
if (!plantId) {
|
|
149
|
+
throw millPlantProtocol.plantError(
|
|
150
|
+
`foundry-plant: mill package "${parsed.packageName}" has no default plant id`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const files = millPlantProtocol.selectPlantFiles(pkgRoot, plantId);
|
|
154
|
+
return { skills: [...files.skills], agents: [...files.agents] };
|
|
139
155
|
}
|
|
140
156
|
|
|
141
|
-
/** foundry.json plant:
|
|
142
|
-
function loadFactoryPlantKinds(targetDir) {
|
|
157
|
+
/** foundry.json plant: mill | @scope/name | @scope/name/id | blip|sound aliases | array. millPlant:false strips mill. */
|
|
158
|
+
function loadFactoryPlantKinds(targetDir, millPackageRoot) {
|
|
143
159
|
const extra = loadFoundryExtra(targetDir);
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
if (Array.isArray(raw)) {
|
|
147
|
-
for (const item of raw) {
|
|
148
|
-
if (isFactoryPlantKind(item)) kinds.push(item);
|
|
149
|
-
}
|
|
150
|
-
} else if (isFactoryPlantKind(raw)) {
|
|
151
|
-
kinds.push(raw);
|
|
152
|
-
} else if (extra.soundPlant === true || extra.blipPlant === true) {
|
|
153
|
-
if (extra.millPlant !== false) kinds.push("mill");
|
|
154
|
-
if (extra.soundPlant === true) kinds.push("sound");
|
|
155
|
-
if (extra.blipPlant === true) kinds.push("blip");
|
|
156
|
-
} else {
|
|
157
|
-
kinds.push("mill");
|
|
158
|
-
}
|
|
159
|
-
if (extra.millPlant === false) {
|
|
160
|
-
return [...new Set(kinds.filter((kind) => kind !== "mill"))];
|
|
161
|
-
}
|
|
162
|
-
return [...new Set(kinds)];
|
|
160
|
+
const seats = millPlantProtocol.resolvePlantSeats(targetDir, millPackageRoot, extra);
|
|
161
|
+
return millPlantProtocol.plantKindsFromSeats(seats);
|
|
163
162
|
}
|
|
164
163
|
|
|
165
|
-
function unionPlantFiles(kinds) {
|
|
164
|
+
function unionPlantFiles(kinds, millPackageRoot) {
|
|
166
165
|
const skills = [];
|
|
167
166
|
const agents = [];
|
|
168
167
|
for (const kind of kinds) {
|
|
169
|
-
const spec = catalogPlant(kind);
|
|
168
|
+
const spec = catalogPlant(kind, millPackageRoot);
|
|
170
169
|
for (const name of spec.skills) {
|
|
171
170
|
if (!skills.includes(name)) skills.push(name);
|
|
172
171
|
}
|
|
@@ -190,27 +189,23 @@ function existingPlantFiles(plantDir, spec) {
|
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
function factoryPlantAllowlist(millPackageRoot, targetDir) {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
const extra = loadFoundryExtra(targetDir);
|
|
193
|
+
const seats = millPlantProtocol.resolvePlantSeats(targetDir, millPackageRoot, extra);
|
|
194
|
+
const skills = [];
|
|
195
|
+
const agents = [];
|
|
196
|
+
for (const seat of seats) {
|
|
197
|
+
for (const name of seat.skills || []) {
|
|
198
|
+
if (!skills.includes(name)) skills.push(name);
|
|
199
|
+
}
|
|
200
|
+
for (const file of seat.agents || []) {
|
|
201
|
+
if (!agents.includes(file)) agents.push(file);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return { kinds: millPlantProtocol.plantKindsFromSeats(seats), skills, agents, seats };
|
|
198
205
|
}
|
|
199
206
|
|
|
200
207
|
function inventoryPlantKinds(inventory) {
|
|
201
|
-
|
|
202
|
-
if (Array.isArray(inventory.plant) && inventory.plant.length > 0) {
|
|
203
|
-
return inventory.plant.filter((kind) => isFactoryPlantKind(kind));
|
|
204
|
-
}
|
|
205
|
-
if (isFactoryPlantKind(inventory.plant)) return [inventory.plant];
|
|
206
|
-
const hasSound = Array.isArray(inventory.soundPlant?.skills) && inventory.soundPlant.skills.length > 0;
|
|
207
|
-
const hasBlip = Array.isArray(inventory.blipPlant?.skills) && inventory.blipPlant.skills.length > 0;
|
|
208
|
-
const hasMill = Array.isArray(inventory.millPlant?.skills) && inventory.millPlant.skills.length > 0;
|
|
209
|
-
const kinds = [];
|
|
210
|
-
if (hasMill) kinds.push("mill");
|
|
211
|
-
if (hasSound) kinds.push("sound");
|
|
212
|
-
if (hasBlip) kinds.push("blip");
|
|
213
|
-
return kinds.length > 0 ? kinds : ["mill"];
|
|
208
|
+
return millPlantProtocol.plantKindsFromInventory(inventory);
|
|
214
209
|
}
|
|
215
210
|
|
|
216
211
|
function listSkillNamesAt(skillsSrc) {
|
|
@@ -237,64 +232,99 @@ function listAgentFilesAt(agentsSrc) {
|
|
|
237
232
|
}
|
|
238
233
|
|
|
239
234
|
function copyPlantFiles(plant, spec, targetDir) {
|
|
240
|
-
const skillsSrc = path.join(plant, "skills");
|
|
241
|
-
const agentsSrc = path.join(plant, "agents");
|
|
235
|
+
const skillsSrc = spec.skillsDir || (plant ? path.join(plant, "skills") : null);
|
|
236
|
+
const agentsSrc = spec.agentsDir || (plant ? path.join(plant, "agents") : null);
|
|
242
237
|
const skills = spec.skills || [];
|
|
243
238
|
const agents = spec.agents || [];
|
|
244
239
|
const skillDirs = listProjectSkillDirs(targetDir);
|
|
245
240
|
const agentsDest = path.join(targetDir, ".opencode", "agents");
|
|
246
|
-
|
|
247
|
-
for (const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
241
|
+
if (skillsSrc) {
|
|
242
|
+
for (const dir of skillDirs) {
|
|
243
|
+
for (const name of skills) {
|
|
244
|
+
const src = path.join(skillsSrc, name, "SKILL.md");
|
|
245
|
+
const destMd = path.join(dir, name, "SKILL.md");
|
|
246
|
+
if (!fs.existsSync(src)) continue;
|
|
247
|
+
if (path.resolve(src) === path.resolve(destMd)) continue;
|
|
248
|
+
if (fs.existsSync(destMd)) continue;
|
|
249
|
+
fs.mkdirSync(path.dirname(destMd), { recursive: true });
|
|
250
|
+
fs.copyFileSync(src, destMd);
|
|
251
|
+
}
|
|
255
252
|
}
|
|
256
253
|
}
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
254
|
+
if (agentsSrc) {
|
|
255
|
+
for (const file of agents) {
|
|
256
|
+
const src = path.join(agentsSrc, file);
|
|
257
|
+
const dest = path.join(agentsDest, file);
|
|
258
|
+
if (!fs.existsSync(src)) continue;
|
|
259
|
+
if (path.resolve(src) === path.resolve(dest)) continue;
|
|
260
|
+
if (fs.existsSync(dest)) continue;
|
|
261
|
+
fs.mkdirSync(agentsDest, { recursive: true });
|
|
262
|
+
fs.copyFileSync(src, dest);
|
|
263
|
+
}
|
|
265
264
|
}
|
|
266
265
|
return { skills, agents };
|
|
267
266
|
}
|
|
268
267
|
|
|
269
|
-
/** Fasten requested
|
|
268
|
+
/** Fasten requested plants. Mill files come from this foundry plant/. Mill-package organs come from that package. */
|
|
270
269
|
function fastenMillPlant(millPackageRoot, targetDir, log) {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
|
|
270
|
+
const extra = loadFoundryExtra(targetDir);
|
|
271
|
+
const seats = millPlantProtocol.resolvePlantSeats(targetDir, millPackageRoot, extra);
|
|
272
|
+
const kinds = millPlantProtocol.plantKindsFromSeats(seats);
|
|
273
|
+
const millSeat = seats.find((seat) => seat.builtin && seat.kind === "mill");
|
|
274
|
+
const soundSeat = seats.find((seat) => seat.kind === "sound");
|
|
275
|
+
const blipSeat = seats.find((seat) => seat.kind === "blip");
|
|
276
|
+
const mill = millSeat
|
|
277
|
+
? { skills: [...millSeat.skills], agents: [...millSeat.agents] }
|
|
278
278
|
: emptyPlantFiles();
|
|
279
|
-
const sound =
|
|
280
|
-
?
|
|
279
|
+
const sound = soundSeat
|
|
280
|
+
? { skills: [...soundSeat.skills], agents: [...soundSeat.agents] }
|
|
281
281
|
: emptyPlantFiles();
|
|
282
|
-
const blip =
|
|
283
|
-
?
|
|
282
|
+
const blip = blipSeat
|
|
283
|
+
? { skills: [...blipSeat.skills], agents: [...blipSeat.agents] }
|
|
284
284
|
: emptyPlantFiles();
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
const plants = {};
|
|
286
|
+
const fastenedSkills = [];
|
|
287
|
+
const fastenedAgents = [];
|
|
288
|
+
for (const seat of seats) {
|
|
289
|
+
if (!seat.builtin) {
|
|
290
|
+
plants[seat.kind] = { skills: [...seat.skills], agents: [...seat.agents] };
|
|
291
|
+
}
|
|
292
|
+
const copied = copyPlantFiles(
|
|
293
|
+
seat.packageRoot,
|
|
294
|
+
{
|
|
295
|
+
skills: seat.skills,
|
|
296
|
+
agents: seat.agents,
|
|
297
|
+
skillsDir: seat.skillsDir,
|
|
298
|
+
agentsDir: seat.agentsDir,
|
|
299
|
+
},
|
|
300
|
+
targetDir,
|
|
301
|
+
);
|
|
302
|
+
for (const name of copied.skills) {
|
|
303
|
+
if (!fastenedSkills.includes(name)) fastenedSkills.push(name);
|
|
304
|
+
}
|
|
305
|
+
for (const file of copied.agents) {
|
|
306
|
+
if (!fastenedAgents.includes(file)) fastenedAgents.push(file);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const millPkgSeat = seats.find((seat) => seat.packageName);
|
|
310
|
+
if (log && (fastenedSkills.length > 0 || fastenedAgents.length > 0)) {
|
|
291
311
|
log("foundry-mint", "Fastened factory plant", "info", {
|
|
292
312
|
plant: kinds,
|
|
293
|
-
skills:
|
|
294
|
-
agents:
|
|
313
|
+
skills: fastenedSkills.length,
|
|
314
|
+
agents: fastenedAgents.length,
|
|
295
315
|
});
|
|
296
316
|
}
|
|
297
|
-
return {
|
|
317
|
+
return {
|
|
318
|
+
skills: mill.skills,
|
|
319
|
+
agents: mill.agents,
|
|
320
|
+
kinds,
|
|
321
|
+
sound,
|
|
322
|
+
blip,
|
|
323
|
+
plants,
|
|
324
|
+
seats,
|
|
325
|
+
millPackage: millPkgSeat ? millPkgSeat.packageName : null,
|
|
326
|
+
millProtocol: millPkgSeat ? millPkgSeat.protocol || millPlantProtocol.PROTOCOL : null,
|
|
327
|
+
};
|
|
298
328
|
}
|
|
299
329
|
|
|
300
330
|
function isDirectory(p) {
|
|
@@ -500,6 +530,14 @@ function previousTreeAllowlist(targetDir) {
|
|
|
500
530
|
? normalizeNameList(blipFromInv)
|
|
501
531
|
: normalizeNameList(blipFromInv?.skills);
|
|
502
532
|
const blipAgents = normalizeNameList(blipFromInv?.agents);
|
|
533
|
+
const extraPlantSkills = [];
|
|
534
|
+
const extraPlantAgents = [];
|
|
535
|
+
if (inventory?.plants && typeof inventory.plants === "object") {
|
|
536
|
+
for (const spec of Object.values(inventory.plants)) {
|
|
537
|
+
extraPlantSkills.push(...normalizeNameList(spec?.skills));
|
|
538
|
+
extraPlantAgents.push(...normalizeNameList(spec?.agents));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
503
541
|
return {
|
|
504
542
|
skills: [
|
|
505
543
|
...(Array.isArray(inventory?.tree?.skills) ? inventory.tree.skills : []),
|
|
@@ -507,12 +545,14 @@ function previousTreeAllowlist(targetDir) {
|
|
|
507
545
|
...shopSkills,
|
|
508
546
|
...soundSkills,
|
|
509
547
|
...blipSkills,
|
|
548
|
+
...extraPlantSkills,
|
|
510
549
|
],
|
|
511
550
|
agents: [
|
|
512
551
|
...(Array.isArray(inventory?.tree?.agents) ? inventory.tree.agents : []),
|
|
513
552
|
...(Array.isArray(inventory?.millPlant?.agents) ? inventory.millPlant.agents : []),
|
|
514
553
|
...soundAgents,
|
|
515
554
|
...blipAgents,
|
|
555
|
+
...extraPlantAgents,
|
|
516
556
|
],
|
|
517
557
|
};
|
|
518
558
|
} catch {
|
|
@@ -520,26 +560,40 @@ function previousTreeAllowlist(targetDir) {
|
|
|
520
560
|
}
|
|
521
561
|
}
|
|
522
562
|
|
|
523
|
-
function
|
|
563
|
+
function millPackagePlantFromTree(targetDir, tree, plantId, millPackageRoot) {
|
|
564
|
+
const seats = Array.isArray(tree?.seats)
|
|
565
|
+
? tree.seats
|
|
566
|
+
: millPlantProtocol.resolvePlantSeats(targetDir, millPackageRoot || tree?.millPackageRoot);
|
|
567
|
+
const seat = seats.find((item) => item.kind === plantId);
|
|
568
|
+
return seat ? { skills: [...seat.skills], agents: [...seat.agents] } : emptyPlantFiles();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function soundPlantFromTree(targetDir, tree, millPackageRoot) {
|
|
524
572
|
if (Array.isArray(tree?.soundPlantSkills) || Array.isArray(tree?.sound?.skills)) {
|
|
525
573
|
return {
|
|
526
574
|
skills: normalizeNameList(tree.soundPlantSkills || tree.sound?.skills),
|
|
527
575
|
agents: normalizeNameList(tree.soundPlantAgents || tree.sound?.agents),
|
|
528
576
|
};
|
|
529
577
|
}
|
|
530
|
-
const kinds = Array.isArray(tree?.plantKinds)
|
|
531
|
-
|
|
578
|
+
const kinds = Array.isArray(tree?.plantKinds)
|
|
579
|
+
? tree.plantKinds
|
|
580
|
+
: loadFactoryPlantKinds(targetDir, millPackageRoot);
|
|
581
|
+
if (!kinds.includes("sound")) return emptyPlantFiles();
|
|
582
|
+
return millPackagePlantFromTree(targetDir, tree, "sound", millPackageRoot);
|
|
532
583
|
}
|
|
533
584
|
|
|
534
|
-
function blipPlantFromTree(targetDir, tree) {
|
|
585
|
+
function blipPlantFromTree(targetDir, tree, millPackageRoot) {
|
|
535
586
|
if (Array.isArray(tree?.blipPlantSkills) || Array.isArray(tree?.blip?.skills)) {
|
|
536
587
|
return {
|
|
537
588
|
skills: normalizeNameList(tree.blipPlantSkills || tree.blip?.skills),
|
|
538
589
|
agents: normalizeNameList(tree.blipPlantAgents || tree.blip?.agents),
|
|
539
590
|
};
|
|
540
591
|
}
|
|
541
|
-
const kinds = Array.isArray(tree?.plantKinds)
|
|
542
|
-
|
|
592
|
+
const kinds = Array.isArray(tree?.plantKinds)
|
|
593
|
+
? tree.plantKinds
|
|
594
|
+
: loadFactoryPlantKinds(targetDir, millPackageRoot);
|
|
595
|
+
if (!kinds.includes("blip")) return emptyPlantFiles();
|
|
596
|
+
return millPackagePlantFromTree(targetDir, tree, "blip", millPackageRoot);
|
|
543
597
|
}
|
|
544
598
|
|
|
545
599
|
/** Extra worn names that are neither mill/sound/blip plant, their plant, shop plant, nor a prior overlay. */
|
|
@@ -548,10 +602,19 @@ function costumeDumpExtras(targetDir, millPlant, tree) {
|
|
|
548
602
|
const shopPlant = loadShopPlant(targetDir);
|
|
549
603
|
const soundPlant = soundPlantFromTree(targetDir, tree);
|
|
550
604
|
const blipPlant = blipPlantFromTree(targetDir, tree);
|
|
605
|
+
const plantMapSkills = [];
|
|
606
|
+
const plantMapAgents = [];
|
|
607
|
+
if (millPlant?.plants && typeof millPlant.plants === "object") {
|
|
608
|
+
for (const spec of Object.values(millPlant.plants)) {
|
|
609
|
+
if (Array.isArray(spec?.skills)) plantMapSkills.push(...spec.skills);
|
|
610
|
+
if (Array.isArray(spec?.agents)) plantMapAgents.push(...spec.agents);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
551
613
|
const allowedSkills = new Set([
|
|
552
614
|
...(Array.isArray(millPlant?.skills) ? millPlant.skills : []),
|
|
553
615
|
...(Array.isArray(millPlant?.sound?.skills) ? millPlant.sound.skills : []),
|
|
554
616
|
...(Array.isArray(millPlant?.blip?.skills) ? millPlant.blip.skills : []),
|
|
617
|
+
...plantMapSkills,
|
|
555
618
|
...soundPlant.skills,
|
|
556
619
|
...blipPlant.skills,
|
|
557
620
|
...(Array.isArray(tree?.skills) ? tree.skills : []),
|
|
@@ -562,6 +625,7 @@ function costumeDumpExtras(targetDir, millPlant, tree) {
|
|
|
562
625
|
...(Array.isArray(millPlant?.agents) ? millPlant.agents : []),
|
|
563
626
|
...(Array.isArray(millPlant?.sound?.agents) ? millPlant.sound.agents : []),
|
|
564
627
|
...(Array.isArray(millPlant?.blip?.agents) ? millPlant.blip.agents : []),
|
|
628
|
+
...plantMapAgents,
|
|
565
629
|
...soundPlant.agents,
|
|
566
630
|
...blipPlant.agents,
|
|
567
631
|
...(Array.isArray(tree?.agents) ? tree.agents : []),
|
|
@@ -701,6 +765,13 @@ function mintConsumerFromSsot(packageRoot, targetDir, log, tree) {
|
|
|
701
765
|
},
|
|
702
766
|
mintedAt: new Date().toISOString(),
|
|
703
767
|
};
|
|
768
|
+
if (tree?.millPackage) {
|
|
769
|
+
inventory.millPackage = tree.millPackage;
|
|
770
|
+
inventory.millProtocol = tree.millProtocol || millPlantProtocol.PROTOCOL;
|
|
771
|
+
}
|
|
772
|
+
if (tree?.plants && typeof tree.plants === "object" && Object.keys(tree.plants).length > 0) {
|
|
773
|
+
inventory.plants = tree.plants;
|
|
774
|
+
}
|
|
704
775
|
const withDna = attachInventoryDna(inventory);
|
|
705
776
|
const xrayDir = path.join(targetDir, ".xray");
|
|
706
777
|
if (!fs.existsSync(xrayDir)) fs.mkdirSync(xrayDir, { recursive: true });
|
|
@@ -735,6 +806,11 @@ function mintConsumerSuit(millPackageRoot, targetDir, log) {
|
|
|
735
806
|
tree.blipPlantSkills = millPlant.blip?.skills || [];
|
|
736
807
|
tree.blipPlantAgents = millPlant.blip?.agents || [];
|
|
737
808
|
tree.blip = millPlant.blip || emptyPlantFiles();
|
|
809
|
+
tree.plants = millPlant.plants || {};
|
|
810
|
+
tree.seats = millPlant.seats || [];
|
|
811
|
+
tree.millPackage = millPlant.millPackage || null;
|
|
812
|
+
tree.millProtocol = millPlant.millProtocol || null;
|
|
813
|
+
tree.millPackageRoot = millPackageRoot;
|
|
738
814
|
tree.shopPlantSkills = loadShopPlant(targetDir);
|
|
739
815
|
tree.codex = overlayJsonFacet(
|
|
740
816
|
resolveInside(targetDir, params.codex),
|
|
@@ -764,6 +840,20 @@ module.exports = {
|
|
|
764
840
|
DEFAULT_PARAMS,
|
|
765
841
|
FACTORY_SHOP_SKILLS,
|
|
766
842
|
FACTORY_PLANT_CATALOG,
|
|
843
|
+
FOUNDRY_PLANT_PROTOCOL: millPlantProtocol.PROTOCOL,
|
|
844
|
+
PROTOCOL: millPlantProtocol.PROTOCOL,
|
|
845
|
+
PACKAGE_ALIASES: millPlantProtocol.PACKAGE_ALIASES,
|
|
846
|
+
parsePlantToken: millPlantProtocol.parsePlantToken,
|
|
847
|
+
parsePlantRef: millPlantProtocol.parsePlantRef,
|
|
848
|
+
resolvePackageRoot: millPlantProtocol.resolvePackageRoot,
|
|
849
|
+
readProtocol: millPlantProtocol.readProtocol,
|
|
850
|
+
prefixSelect: millPlantProtocol.prefixSelect,
|
|
851
|
+
selectPlantFiles: millPlantProtocol.selectPlantFiles,
|
|
852
|
+
declaredPlantFiles: millPlantProtocol.declaredPlantFiles,
|
|
853
|
+
resolvePlantSeats: millPlantProtocol.resolvePlantSeats,
|
|
854
|
+
loadPlantRequests: millPlantProtocol.loadPlantRequests,
|
|
855
|
+
declaredReceiptSeats: millPlantProtocol.declaredReceiptSeats,
|
|
856
|
+
inventoryFilesForKind: millPlantProtocol.inventoryFilesForKind,
|
|
767
857
|
isFactoryPlantKind,
|
|
768
858
|
deepMerge,
|
|
769
859
|
loadFoundryParams,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xray/foundry",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "0xRay mill: mint their suit params, stamp, gate, publish, CI report, git hooks. Not the worn exo.",
|
|
@@ -37,15 +37,26 @@
|
|
|
37
37
|
"./release-gate": "./release-gate.mjs",
|
|
38
38
|
"./mill-root": "./mill-root.mjs",
|
|
39
39
|
"./mint-suit": "./mint-suit.cjs",
|
|
40
|
+
"./mill-plant": "./mill-plant.cjs",
|
|
40
41
|
"./inspect": "./inspect.mjs",
|
|
41
42
|
"./sound": "./sound.mjs",
|
|
42
43
|
"./sound-bed": "./sound-bed.cjs",
|
|
44
|
+
"./sound-mixer": "./sound-mixer.cjs",
|
|
43
45
|
"./blip": "./blip.mjs",
|
|
44
46
|
"./blip-render": "./blip-render.cjs",
|
|
47
|
+
"./blip-vibe": "./blip-vibe.cjs",
|
|
48
|
+
"./blip-looker": "./blip-looker.cjs",
|
|
49
|
+
"./blip-scene": "./blip-scene.cjs",
|
|
50
|
+
"./sound-taste": "./sound-taste.cjs",
|
|
51
|
+
"./sound-voice": "./sound-voice.cjs",
|
|
52
|
+
"./blip-stroke": "./blip-stroke.cjs",
|
|
45
53
|
"./ci-monitor": "./ci-monitor.mjs",
|
|
46
54
|
"./hooks": "./hooks.mjs",
|
|
47
55
|
"./docs-build": "./docs-build.mjs"
|
|
48
56
|
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@0xray/blip": "^0.1.3"
|
|
59
|
+
},
|
|
49
60
|
"engines": {
|
|
50
61
|
"node": ">=18"
|
|
51
62
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Motion registry (factory-blip)
|
|
2
2
|
|
|
3
|
-
Law: motions are a dynamic registry (`id → renderer`). Rippel names are **imports**, not a frozen enum. Unknown id FAIL. `kapow` is a
|
|
3
|
+
Law: motions are a dynamic registry (`id → renderer`). Rippel names are **imports**, not a frozen enum. Unknown id FAIL. `kapow` is a design opt — two-tier stamp on hook → turn → tag.
|
|
4
4
|
|
|
5
5
|
On-disk SSOT: `registry.json` next to this file.
|
|
6
6
|
|
|
@@ -12,24 +12,30 @@ On-disk SSOT: `registry.json` next to this file.
|
|
|
12
12
|
|
|
13
13
|
Default motion path is the Rippel converter spine, not ffmpeg geometry. Look is **sharp** (opaque body + crisp rim + short glow) on all five. Motion is a **4.44s blip**: fast abstract travel, soft gold tints — no hard strobe. LFOs share the crystal-mill genre tempo + `CircleConfig.frequency` with the audio bed.
|
|
14
14
|
|
|
15
|
+
One mill suit on every body: Wu hairline, Power Plant satellites, short glow, hook → turn → tag on the shared motion grid. Picture and bed seed-pick the same genre. Rippel geo stays (petals / N-gons / synapse / cosmos / aurora) and wears that suit — not a second CAD look.
|
|
16
|
+
|
|
15
17
|
- **SSOT** `htafolla/rippel-synapse-flow@e5014cd`
|
|
16
18
|
- `animationIcons.ts` → `ANIMATION_TO_VISUALIZATION`
|
|
17
19
|
- `types/index.ts` → `ChecksumResponse.visualConfig` (`CircleConfig[]`, `CanvasConfig`, `tlmCommand`)
|
|
18
20
|
- `SimplifiedVisualConverter.tsx` → Animation → viz id, canvas **1280×720** @ ~30fps
|
|
19
21
|
- `MiniAnimationViewer` / `FiveDimensionalVisualizer` → viz backends
|
|
20
22
|
|
|
21
|
-
| id | viz id |
|
|
22
|
-
|
|
23
|
-
| `orb` | `canvas` |
|
|
24
|
-
| `swirl` | `3d-sacred` |
|
|
25
|
-
| `snap` | `neural` |
|
|
26
|
-
| `waves` | `waveform` |
|
|
27
|
-
| `spark` | `particles` |
|
|
23
|
+
| id | viz id | mill body | rippel body |
|
|
24
|
+
|----|--------|-----------|-------------|
|
|
25
|
+
| `orb` | `canvas` | `focus` disc **or** `cage` Wu + nucleus | `mandala` |
|
|
26
|
+
| `swirl` | `3d-sacred` | platonic `mesh` | `sacred-flow` |
|
|
27
|
+
| `snap` | `neural` | `strike` (quantized) | `synapse` |
|
|
28
|
+
| `waves` | `waveform` | sharp `ribbons` | `liquid-waves` |
|
|
29
|
+
| `spark` | `particles` | `embers` | `cosmic-dance` |
|
|
28
30
|
|
|
29
|
-
Every mint wears a **sparse seed mesh** (12 families · 5 gaits) plus a **field** (stars · grids `floor`/`
|
|
31
|
+
Every mint wears a **sparse seed mesh** (12 families · 5 gaits · 6 cameras) plus a **field** (stars · grids `floor`/`none` · gradients · blinkers) **under** the drawing. No outer-edge tick ruler or meridian box. Phrase eases spin: hook crawl → turn whip → tag hold. Camera is a seed shot (`front` `three-quarter` `top` `low` `dutch` `side`) with a turn push-in — same organ, different silhouette. Turn ruptures cage (gold lantern) and snap (held gold slash). Focus wears one large eclipse moon the noun eats. Mill swirl is a cyan/gold jewel, snap is a ghost hull + 2–3 strikes, spark is coals on verts and midpoints. Receipt `visualConfig.mesh` + `visualConfig.field` + `organ` are the NFT fingerprints.
|
|
30
32
|
|
|
31
33
|
Motions encode **≥720p**. The old 320×180 ffmpeg wireframe is **`--engine wireframe` only**.
|
|
32
34
|
|
|
33
35
|
Every Blip muxes a **4.44s audio bed** (`--bed PATH` or auto sound mill). No audio stream = inspect FAIL.
|
|
34
36
|
|
|
35
37
|
`still` is the **Power Plant ident** — titlecard / corridor / rain / endcard dissolve over 4.44s at ≥720p. Not a frozen poster. Palette `#08090B` `#F5F7FA` `#3DE0E8` `#F5C518` `#4A7FD4`.
|
|
38
|
+
|
|
39
|
+
## Design opt — `kapow`
|
|
40
|
+
|
|
41
|
+
Not a Rippel import and not a `--look`. `--mode motion:kapow` paints a comic two-tier burst: 8–10 outer points, 6–8 inner, some spikes longer than others. Ink outline, sparse speed lines, readable `KAPOW!`. Hook winds the outer ring. The jewel cut slams the inner burst and the word. Tag holds. One noun sits under the word. `blip-vibe` scores still + Rippel five + kapow on the same organ — every type must hit **8** to ship. Same seed camera / genre grid / 4.44s bed as the five.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"law": "Motions are a dynamic registry (id → renderer). Rippel names are imports. Kapow is a
|
|
3
|
+
"law": "Motions are a dynamic registry (id → renderer). Rippel names are imports. Kapow is a design opt — two-tier stamp on the stanza.",
|
|
4
4
|
"v0": ["still", "orb", "swirl", "snap", "waves", "spark"],
|
|
5
|
+
"opt": ["kapow"],
|
|
5
6
|
"palette": {
|
|
6
7
|
"void": "#08090B",
|
|
7
8
|
"ink": "#F5F7FA",
|
|
@@ -73,9 +74,9 @@
|
|
|
73
74
|
"id": "kapow",
|
|
74
75
|
"kind": "motion",
|
|
75
76
|
"source": "blips",
|
|
76
|
-
"renderer":
|
|
77
|
-
"status": "
|
|
78
|
-
"notes": "
|
|
77
|
+
"renderer": "kapow",
|
|
78
|
+
"status": "opt",
|
|
79
|
+
"notes": "Two-tier KAPOW on hook → turn → tag. Outer burst winds, inner burst + word stamp the jewel cut. Not a look. ≥720p + 4.44s bed."
|
|
79
80
|
}
|
|
80
81
|
]
|
|
81
82
|
}
|
|
@@ -13,7 +13,7 @@ This **factory plant** fastens a tiny-video factory. 4.44 seconds of picture **w
|
|
|
13
13
|
|
|
14
14
|
`still` · `orb` · `swirl` · `snap` · `waves` · `spark`
|
|
15
15
|
|
|
16
|
-
Rippel five are **imports**.
|
|
16
|
+
Rippel five are **imports**. Four seed + flag axes: `--look focus|cage`, `--body mill|rippel`, `--genre ambient|techno|phonk|jazz|rock|timeless`, `--camera front|three-quarter|top|low|dutch|side` (unknown camera FAIL). Suit is void + five hexes + Wu + hook → turn → tag — not a circle pack on every organ. Focus keeps the disc + one large eclipse moon the noun eats. Cage and snap rupture on the turn (gold lantern / held slash). Mill swirl is Wu mesh, snap is strike, spark is embers. Same organ, different shot — top flattens, low lifts, dutch slashes the floor. Turn dollies in. Cage recoils then whips. Receipt `lookKind` + `bodyKind` + `genre` + `organ` + `camera` + `stereoImage`. `kapow` is a design opt (`--mode motion:kapow`) — two-tier stamp, not a third look. Unknown id FAIL.
|
|
17
17
|
|
|
18
18
|
`still` is the **Power Plant ident** — titlecard / corridor / rain / endcard dissolve over 4.44s at ≥720p, not a frozen poster: void `#08090B` · ink `#F5F7FA` · cyan `#3DE0E8` · gold `#F5C518` · agent blue `#4A7FD4`. Motions use that palette as the Rippel `VisualConfig.circles` theme. The mill is the renderer — no LLM in the pixel/audio loop.
|
|
19
19
|
|
|
@@ -21,8 +21,8 @@ Rippel five are **imports**. Each mint is a **4.44s Short** — hook → turn
|
|
|
21
21
|
|
|
22
22
|
1. **Brief** → checksum seed → Rippel `VisualConfig` (`CircleConfig[]`).
|
|
23
23
|
2. **Mode** → `still` or `motion:<id>` from the registry.
|
|
24
|
-
3. **Render** → 4.44s mp4. Motions are Rippel v2 viz at ≥720p (`--look focus|cage`, seed picks if omitted). `--engine wireframe` is emergency only.
|
|
25
|
-
4. **Bed** → `--bed PATH` or auto sound mill. Auto bed **syncopates** to the motion grid (same seed + tempo + phase; downbeat kick / AND hat). Every mp4 muxes **stereo AAC**. Missing stream or inaudible bed = inspect FAIL.
|
|
24
|
+
3. **Render** → 4.44s mp4. Motions are Rippel v2 viz at ≥720p (`--look focus|cage --body mill|rippel`, seed picks if omitted). `--engine wireframe` is emergency only.
|
|
25
|
+
4. **Bed** → `--bed PATH` or auto sound mill. Auto bed **syncopates** to the motion grid (same seed + tempo + phase; downbeat kick / AND hat). The crash + motif answer land on the jewel cut. Every mp4 muxes **stereo AAC**. Missing stream or inaudible bed = inspect FAIL.
|
|
26
26
|
5. **Inspect gate** → file, duration 4.44s±tol, mode id, **audio stream**, motion ≥720p. Fail-closed.
|
|
27
27
|
6. **Receipt** → `.xray/blip/receipt.json` (`PASS` / `FAIL` + pictureMode + motionId + visualConfig).
|
|
28
28
|
|
|
@@ -34,6 +34,6 @@ npx @0xray/foundry blip inspect
|
|
|
34
34
|
npx @0xray/foundry inspect --skip-live
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
Mint fastens `blip` + `blip-inspect`. Chat is not a receipt.
|
|
37
|
+
Mint fastens `blip` + `blip-inspect` + `blip-vibe`. Chat is not a receipt.
|
|
38
38
|
|
|
39
39
|
A friend would hear: Blips should look like Rippel living motions with sound, not a silent wireframe box.
|