@holoscript/robotics-plugin 1.0.0 → 2.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # @holoscript/robotics-plugin
2
+
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [c64fc1a]
8
+ - @holoscript/core@8.0.6
9
+
10
+ ## 2.0.0
11
+
12
+ ### Patch Changes
13
+
14
+ - @holoscript/core@6.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 HoloScript Contributors
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/dist/ast.d.ts CHANGED
@@ -1,12 +1,59 @@
1
1
  /**
2
2
  * Abstract Syntax Tree (AST) node types for HoloScript robotics
3
+ *
4
+ * Extended for Isaac Lab sim-to-real interop (Path A):
5
+ * - Domain randomization blocks
6
+ * - Actuator group configurations
7
+ * - Delayed actuator export hints
3
8
  */
4
9
  export type PropertyValue = string | number | boolean | number[] | PropertyValue[];
10
+ export interface DomainRandomizationConfig {
11
+ /** Physics randomization: mass, friction, damping */
12
+ physics?: {
13
+ massScale?: [number, number];
14
+ frictionRange?: [number, number];
15
+ dampingRange?: [number, number];
16
+ armatureRange?: [number, number];
17
+ };
18
+ /** Actuator randomization */
19
+ actuator?: {
20
+ kpNoise?: number;
21
+ kdNoise?: number;
22
+ latencyNoise?: number;
23
+ };
24
+ /** Observation noise */
25
+ observation?: {
26
+ jointPosNoise?: number;
27
+ jointVelNoise?: number;
28
+ imuNoise?: number;
29
+ };
30
+ /** Initial state randomization */
31
+ initialState?: {
32
+ jointPosRange?: Record<string, [number, number]>;
33
+ rootPoseRange?: [number, number, number, number, number, number];
34
+ };
35
+ /** Disturbance forces */
36
+ disturbance?: {
37
+ forceRange?: [number, number];
38
+ intervalRange?: [number, number];
39
+ };
40
+ }
41
+ export interface ActuatorGroupConfig {
42
+ name: string;
43
+ type: 'IdealPDActuator' | 'DCMotorActuator' | 'DelayedPDActuator' | 'RemotizedPDActuator' | 'ImplicitActuator';
44
+ jointNames: string[];
45
+ stiffness?: number;
46
+ damping?: number;
47
+ friction?: number;
48
+ latency?: number;
49
+ }
5
50
  export interface ObjectNode {
6
51
  type: 'object';
7
52
  name: string;
8
53
  traits: string[];
9
54
  properties: Record<string, PropertyValue>;
55
+ domainRandomization?: DomainRandomizationConfig;
56
+ actuatorGroups?: ActuatorGroupConfig[];
10
57
  template?: string;
11
58
  line?: number;
12
59
  column?: number;
@@ -15,6 +62,7 @@ export interface CompositionNode {
15
62
  type: 'composition';
16
63
  name: string;
17
64
  objects: ObjectNode[];
65
+ domainRandomization?: DomainRandomizationConfig;
18
66
  line?: number;
19
67
  column?: number;
20
68
  }
package/dist/ast.js CHANGED
@@ -1,5 +1,9 @@
1
- "use strict";
2
1
  /**
3
2
  * Abstract Syntax Tree (AST) node types for HoloScript robotics
3
+ *
4
+ * Extended for Isaac Lab sim-to-real interop (Path A):
5
+ * - Domain randomization blocks
6
+ * - Actuator group configurations
7
+ * - Delayed actuator export hints
4
8
  */
5
- Object.defineProperty(exports, "__esModule", { value: true });
9
+ export {};
package/dist/index.d.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  *
5
5
  * Features:
6
6
  * - USD codegen for NVIDIA Isaac Sim (from holoscript-compiler)
7
+ * - Isaac Lab sim-to-real interop (Path A)
8
+ * - PhysicsDriveAPI for PD actuator control
9
+ * - PhysxJointAxisAPI for per-axis friction assumptions
10
+ * - Domain randomization configuration blocks
7
11
  * - URDF/SDF export for ROS2/Gazebo
8
12
  * - ROS2 runtime integration (roslibjs)
9
13
  * - VR teleoperation and digital twins
@@ -11,9 +15,13 @@
11
15
  * @packageDocumentation
12
16
  */
13
17
  export { USDCodeGen } from './usd-codegen';
14
- export { Lexer, Token, TokenType } from './lexer';
18
+ export type { IsaacLabConfig } from './usd-codegen';
19
+ export { Lexer, TokenType } from './lexer';
20
+ export type { Token } from './lexer';
15
21
  export { Parser } from './parser';
16
22
  export * from './ast';
23
+ export type { DomainRandomizationConfig, ActuatorGroupConfig } from './ast';
24
+ import type { CompositionNode, DomainRandomizationConfig, ActuatorGroupConfig } from './ast';
17
25
  export interface ROS2Config {
18
26
  ros_bridge_url: string;
19
27
  namespace?: string;
@@ -51,8 +59,69 @@ export interface RobotLink {
51
59
  geometry: string;
52
60
  };
53
61
  }
62
+ export interface RobotTransmission {
63
+ name: string;
64
+ joint: string;
65
+ actuator: string;
66
+ mechanicalReduction: number;
67
+ hardwareInterface: 'position' | 'velocity' | 'effort';
68
+ }
69
+ export interface HoloTraitLike {
70
+ name: string;
71
+ properties?: Record<string, unknown>;
72
+ }
73
+ export interface HoloCompositionNodeLike {
74
+ id?: string;
75
+ name: string;
76
+ type?: string;
77
+ properties?: Record<string, unknown>;
78
+ traits?: HoloTraitLike[];
79
+ children?: HoloCompositionNodeLike[];
80
+ }
81
+ export interface HoloCompositionTreeLike {
82
+ name: string;
83
+ children?: HoloCompositionNodeLike[];
84
+ }
85
+ export interface ExtractedRobotModel {
86
+ name: string;
87
+ links: RobotLink[];
88
+ joints: RobotJoint[];
89
+ transmissions: RobotTransmission[];
90
+ xml: string;
91
+ }
92
+ export interface URDFExtractionOptions {
93
+ defaultJointType?: RobotJoint['type'];
94
+ defaultAxis?: [number, number, number];
95
+ baseLinkName?: string;
96
+ hardwareScale?: number;
97
+ }
98
+ export declare function buildURDFXML(model: Omit<ExtractedRobotModel, 'xml'>): string;
99
+ export declare function extractURDFFromHoloComposition(composition: HoloCompositionTreeLike, options?: URDFExtractionOptions): ExtractedRobotModel;
100
+ export * from './traits/ROS2HardwareLoopTrait';
54
101
  export declare const VERSION = "1.0.0";
55
102
  declare const _default: {
56
103
  VERSION: string;
104
+ buildURDFXML: typeof buildURDFXML;
105
+ extractURDFFromHoloComposition: typeof extractURDFFromHoloComposition;
106
+ generateIsaacLabFeed: typeof generateIsaacLabFeed;
57
107
  };
58
108
  export default _default;
109
+ export interface IsaacLabFeedReceipt {
110
+ sourceHoloHash: string;
111
+ usdHash: string;
112
+ configHash: string;
113
+ generatedAt: string;
114
+ isaacLabVersion: string;
115
+ readyForTraining: boolean;
116
+ notes: string;
117
+ }
118
+ export interface IsaacLabFeedBundle {
119
+ usd: string;
120
+ taskConfig: Record<string, unknown>;
121
+ randomization: DomainRandomizationConfig | undefined;
122
+ actuators: ActuatorGroupConfig[];
123
+ receipt: IsaacLabFeedReceipt;
124
+ }
125
+ export declare function generateIsaacLabFeed(ast: CompositionNode, opts?: {
126
+ isaacLabVersion?: string;
127
+ }): IsaacLabFeedBundle;
package/dist/index.js CHANGED
@@ -1,44 +1,349 @@
1
- "use strict";
2
1
  /**
3
2
  * @holoscript/robotics-plugin v1.0.0
4
3
  * Complete robotics solution: Compile-time (USD/URDF) + Runtime (ROS2/Gazebo)
5
4
  *
6
5
  * Features:
7
6
  * - USD codegen for NVIDIA Isaac Sim (from holoscript-compiler)
7
+ * - Isaac Lab sim-to-real interop (Path A)
8
+ * - PhysicsDriveAPI for PD actuator control
9
+ * - PhysxJointAxisAPI for per-axis friction assumptions
10
+ * - Domain randomization configuration blocks
8
11
  * - URDF/SDF export for ROS2/Gazebo
9
12
  * - ROS2 runtime integration (roslibjs)
10
13
  * - VR teleoperation and digital twins
11
14
  *
12
15
  * @packageDocumentation
13
16
  */
14
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- var desc = Object.getOwnPropertyDescriptor(m, k);
17
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
- desc = { enumerable: true, get: function() { return m[k]; } };
19
- }
20
- Object.defineProperty(o, k2, desc);
21
- }) : (function(o, m, k, k2) {
22
- if (k2 === undefined) k2 = k;
23
- o[k2] = m[k];
24
- }));
25
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.VERSION = exports.Parser = exports.TokenType = exports.Lexer = exports.USDCodeGen = void 0;
30
17
  // Compile-time: USD/URDF codegen (from holoscript-compiler)
31
- var usd_codegen_1 = require("./usd-codegen");
32
- Object.defineProperty(exports, "USDCodeGen", { enumerable: true, get: function () { return usd_codegen_1.USDCodeGen; } });
33
- var lexer_1 = require("./lexer");
34
- Object.defineProperty(exports, "Lexer", { enumerable: true, get: function () { return lexer_1.Lexer; } });
35
- Object.defineProperty(exports, "TokenType", { enumerable: true, get: function () { return lexer_1.TokenType; } });
36
- var parser_1 = require("./parser");
37
- Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return parser_1.Parser; } });
38
- __exportStar(require("./ast"), exports);
18
+ export { USDCodeGen } from './usd-codegen';
19
+ export { Lexer, TokenType } from './lexer';
20
+ export { Parser } from './parser';
21
+ export * from './ast';
22
+ import { USDCodeGen } from './usd-codegen';
23
+ const DEFAULT_URDF_AXIS = [0, 0, 1];
24
+ function normalizeTraitName(name) {
25
+ return name.replace(/^@/, '').trim().toLowerCase();
26
+ }
27
+ function sanitizeRobotName(name) {
28
+ return name.trim().replace(/[^A-Za-z0-9_]+/g, '_') || 'robot';
29
+ }
30
+ function xmlEscape(value) {
31
+ return value
32
+ .replace(/&/g, '&amp;')
33
+ .replace(/</g, '&lt;')
34
+ .replace(/>/g, '&gt;')
35
+ .replace(/"/g, '&quot;')
36
+ .replace(/'/g, '&apos;');
37
+ }
38
+ function asRecord(value) {
39
+ return value && typeof value === 'object' && !Array.isArray(value)
40
+ ? value
41
+ : {};
42
+ }
43
+ function asNumber(value, fallback) {
44
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
45
+ }
46
+ function asString(value, fallback) {
47
+ return typeof value === 'string' && value.trim().length > 0 ? value : fallback;
48
+ }
49
+ function asVec3(value, fallback) {
50
+ if (Array.isArray(value) && value.length >= 3) {
51
+ return [asNumber(value[0], fallback[0]), asNumber(value[1], fallback[1]), asNumber(value[2], fallback[2])];
52
+ }
53
+ return fallback;
54
+ }
55
+ function asJointLimits(value, fallback) {
56
+ if (Array.isArray(value) && value.length >= 4) {
57
+ return {
58
+ lower: asNumber(value[0], fallback?.lower ?? -1.57),
59
+ upper: asNumber(value[1], fallback?.upper ?? 1.57),
60
+ effort: asNumber(value[2], fallback?.effort ?? 10),
61
+ velocity: asNumber(value[3], fallback?.velocity ?? 1),
62
+ };
63
+ }
64
+ const record = asRecord(value);
65
+ if (Object.keys(record).length > 0) {
66
+ return {
67
+ lower: asNumber(record.lower, fallback?.lower ?? -1.57),
68
+ upper: asNumber(record.upper, fallback?.upper ?? 1.57),
69
+ effort: asNumber(record.effort, fallback?.effort ?? 10),
70
+ velocity: asNumber(record.velocity, fallback?.velocity ?? 1),
71
+ };
72
+ }
73
+ return fallback;
74
+ }
75
+ function findTrait(node, predicate) {
76
+ for (const trait of node.traits ?? []) {
77
+ const normalizedName = normalizeTraitName(trait.name);
78
+ if (predicate(normalizedName)) {
79
+ return { normalizedName, trait };
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+ function getJointTrait(node) {
85
+ return findTrait(node, (name) => name === 'joint' ||
86
+ name === 'hinge' ||
87
+ name === 'slider' ||
88
+ name.startsWith('joint_'));
89
+ }
90
+ function getMotorTrait(node) {
91
+ return findTrait(node, (name) => name === 'motor' || name === 'servo' || name === 'actuator' || name.endsWith('_motor'));
92
+ }
93
+ function jointTypeFromTrait(jointTrait, nodeProps, defaultJointType) {
94
+ const explicitType = normalizeTraitName(asString(nodeProps.joint_type ?? nodeProps.type, defaultJointType));
95
+ switch (explicitType) {
96
+ case 'revolute':
97
+ case 'prismatic':
98
+ case 'fixed':
99
+ case 'continuous':
100
+ return explicitType;
101
+ case 'hinge':
102
+ return 'revolute';
103
+ case 'slider':
104
+ return 'prismatic';
105
+ }
106
+ switch (jointTrait?.normalizedName) {
107
+ case 'joint_revolute':
108
+ case 'hinge':
109
+ return 'revolute';
110
+ case 'joint_prismatic':
111
+ case 'slider':
112
+ return 'prismatic';
113
+ case 'joint_continuous':
114
+ return 'continuous';
115
+ case 'joint_fixed':
116
+ return 'fixed';
117
+ default:
118
+ return defaultJointType;
119
+ }
120
+ }
121
+ function hardwareInterfaceFromMotor(motorProps) {
122
+ const commandMode = normalizeTraitName(asString(motorProps.commandMode ?? motorProps.mode, 'position'));
123
+ if (commandMode === 'velocity')
124
+ return 'velocity';
125
+ if (commandMode === 'effort' || commandMode === 'torque')
126
+ return 'effort';
127
+ return 'position';
128
+ }
129
+ function scaleMass(mass, hardwareScale) {
130
+ return Number((mass * Math.pow(hardwareScale, 3)).toFixed(6));
131
+ }
132
+ function mapNodeToRobotLink(node, hardwareScale) {
133
+ const props = asRecord(node.properties);
134
+ const baseMass = asNumber(props.mass, 1);
135
+ const inertia = Array.isArray(props.inertia)
136
+ ? props.inertia.map((value) => asNumber(value, 0))
137
+ : [0.01, 0.01, 0.01, 0, 0, 0];
138
+ const geometry = asString(props.geometry, node.type ?? 'box');
139
+ const material = typeof props.material === 'string' ? props.material : undefined;
140
+ const collisionGeometry = asString(props.collisionGeometry ?? props.geometry, geometry);
141
+ return {
142
+ name: sanitizeRobotName(node.name || node.id || 'link'),
143
+ inertial: {
144
+ mass: scaleMass(baseMass, hardwareScale),
145
+ inertia,
146
+ },
147
+ visual: {
148
+ geometry,
149
+ material,
150
+ },
151
+ collision: {
152
+ geometry: collisionGeometry,
153
+ },
154
+ };
155
+ }
156
+ function createTransmission(jointName, motorProps, hardwareScale) {
157
+ return {
158
+ name: sanitizeRobotName(asString(motorProps.name, `${jointName}_transmission`)),
159
+ joint: jointName,
160
+ actuator: sanitizeRobotName(asString(motorProps.actuator ?? motorProps.name, `${jointName}_motor`)),
161
+ mechanicalReduction: Number((asNumber(motorProps.mechanicalReduction ?? motorProps.gearRatio, 1) * hardwareScale).toFixed(6)),
162
+ hardwareInterface: hardwareInterfaceFromMotor(motorProps),
163
+ };
164
+ }
165
+ function buildLinkXml(link) {
166
+ const lines = [` <link name="${xmlEscape(link.name)}">`];
167
+ if (link.inertial) {
168
+ const [ixx = 0.01, iyy = 0.01, izz = 0.01, ixy = 0, ixz = 0, iyz = 0] = link.inertial.inertia;
169
+ lines.push(' <inertial>');
170
+ lines.push(` <mass value="${link.inertial.mass}"/>`);
171
+ lines.push(` <inertia ixx="${ixx}" iyy="${iyy}" izz="${izz}" ixy="${ixy}" ixz="${ixz}" iyz="${iyz}"/>`);
172
+ lines.push(' </inertial>');
173
+ }
174
+ if (link.visual) {
175
+ lines.push(' <visual>');
176
+ lines.push(` <geometry><mesh filename="${xmlEscape(link.visual.geometry)}"/></geometry>`);
177
+ if (link.visual.material) {
178
+ lines.push(` <material name="${xmlEscape(link.visual.material)}"/>`);
179
+ }
180
+ lines.push(' </visual>');
181
+ }
182
+ if (link.collision) {
183
+ lines.push(' <collision>');
184
+ lines.push(` <geometry><mesh filename="${xmlEscape(link.collision.geometry)}"/></geometry>`);
185
+ lines.push(' </collision>');
186
+ }
187
+ lines.push(' </link>');
188
+ return lines.join('\n');
189
+ }
190
+ function buildJointXml(joint) {
191
+ const lines = [` <joint name="${xmlEscape(joint.name)}" type="${joint.type}">`];
192
+ lines.push(` <parent link="${xmlEscape(joint.parent_link)}"/>`);
193
+ lines.push(` <child link="${xmlEscape(joint.child_link)}"/>`);
194
+ if (joint.axis) {
195
+ lines.push(` <axis xyz="${joint.axis.join(' ')}"/>`);
196
+ }
197
+ if (joint.limits && joint.type !== 'fixed') {
198
+ lines.push(` <limit lower="${joint.limits.lower}" upper="${joint.limits.upper}" effort="${joint.limits.effort}" velocity="${joint.limits.velocity}"/>`);
199
+ }
200
+ lines.push(' </joint>');
201
+ return lines.join('\n');
202
+ }
203
+ function buildTransmissionXml(transmission) {
204
+ return [
205
+ ` <transmission name="${xmlEscape(transmission.name)}">`,
206
+ ' <type>transmission_interface/SimpleTransmission</type>',
207
+ ` <joint name="${xmlEscape(transmission.joint)}">`,
208
+ ` <hardwareInterface>${transmission.hardwareInterface}</hardwareInterface>`,
209
+ ' </joint>',
210
+ ` <actuator name="${xmlEscape(transmission.actuator)}">`,
211
+ ' <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>',
212
+ ` <mechanicalReduction>${transmission.mechanicalReduction}</mechanicalReduction>`,
213
+ ' </actuator>',
214
+ ' </transmission>',
215
+ ].join('\n');
216
+ }
217
+ export function buildURDFXML(model) {
218
+ const lines = [`<robot name="${xmlEscape(model.name)}">`];
219
+ lines.push(...model.links.map(buildLinkXml));
220
+ lines.push(...model.joints.map(buildJointXml));
221
+ lines.push(...model.transmissions.map(buildTransmissionXml));
222
+ lines.push('</robot>');
223
+ return lines.join('\n');
224
+ }
225
+ export function extractURDFFromHoloComposition(composition, options = {}) {
226
+ const defaultJointType = options.defaultJointType ?? 'fixed';
227
+ const defaultAxis = options.defaultAxis ?? DEFAULT_URDF_AXIS;
228
+ const hardwareScale = options.hardwareScale ?? 1;
229
+ const links = [];
230
+ const joints = [];
231
+ const transmissions = [];
232
+ const visitNode = (node, parentLinkName) => {
233
+ const props = asRecord(node.properties);
234
+ const link = mapNodeToRobotLink(node, hardwareScale);
235
+ links.push(link);
236
+ const jointTrait = getJointTrait(node);
237
+ const motorTrait = getMotorTrait(node);
238
+ const jointName = sanitizeRobotName(asString(props.jointName, `${link.name}_joint`));
239
+ if (parentLinkName && jointTrait) {
240
+ const limits = asJointLimits(props.joint_limits ?? props.limits, {
241
+ lower: -1.57,
242
+ upper: 1.57,
243
+ effort: 10 * hardwareScale,
244
+ velocity: 1 * hardwareScale,
245
+ });
246
+ const joint = {
247
+ name: jointName,
248
+ type: jointTypeFromTrait(jointTrait, props, defaultJointType),
249
+ parent_link: sanitizeRobotName(parentLinkName),
250
+ child_link: link.name,
251
+ axis: asVec3(props.joint_axis ?? props.axis, defaultAxis),
252
+ limits,
253
+ };
254
+ joints.push(joint);
255
+ if (motorTrait) {
256
+ const motorProps = {
257
+ ...props,
258
+ ...asRecord(motorTrait.trait.properties),
259
+ };
260
+ transmissions.push(createTransmission(joint.name, motorProps, hardwareScale));
261
+ }
262
+ }
263
+ for (const child of node.children ?? []) {
264
+ visitNode(child, link.name);
265
+ }
266
+ };
267
+ const baseLinkName = sanitizeRobotName(options.baseLinkName ?? `${composition.name}_base`);
268
+ links.push({
269
+ name: baseLinkName,
270
+ inertial: {
271
+ mass: scaleMass(1, hardwareScale),
272
+ inertia: [0.01, 0.01, 0.01, 0, 0, 0],
273
+ },
274
+ visual: {
275
+ geometry: 'base_link',
276
+ },
277
+ collision: {
278
+ geometry: 'base_link',
279
+ },
280
+ });
281
+ for (const child of composition.children ?? []) {
282
+ visitNode(child, baseLinkName);
283
+ }
284
+ const modelWithoutXml = {
285
+ name: sanitizeRobotName(composition.name),
286
+ links,
287
+ joints,
288
+ transmissions,
289
+ };
290
+ return {
291
+ ...modelWithoutXml,
292
+ xml: buildURDFXML(modelWithoutXml),
293
+ };
294
+ }
295
+ // Traits
296
+ export * from './traits/ROS2HardwareLoopTrait';
39
297
  // Version
40
- exports.VERSION = '1.0.0';
298
+ export const VERSION = '1.0.0';
41
299
  // Re-export for convenience
42
- exports.default = {
43
- VERSION: exports.VERSION,
300
+ export default {
301
+ VERSION,
302
+ buildURDFXML,
303
+ extractURDFFromHoloComposition,
304
+ generateIsaacLabFeed,
44
305
  };
306
+ export function generateIsaacLabFeed(ast, opts = {}) {
307
+ const version = opts.isaacLabVersion || '2.3';
308
+ const codegen = new USDCodeGen({ isaacLabVersion: version });
309
+ const usd = codegen.generate(ast);
310
+ // Pull randomization + actuators that the AST already carries
311
+ const randomization = ast.domainRandomization;
312
+ const actuators = [];
313
+ for (const obj of ast.objects ?? []) {
314
+ if (obj.actuatorGroups)
315
+ actuators.push(...obj.actuatorGroups);
316
+ }
317
+ // Minimal Isaac Lab task config stub (observations / actions / randomization ranges)
318
+ const taskConfig = {
319
+ env: 'HoloScriptRobotEnv',
320
+ num_envs: 4096,
321
+ seed: 42,
322
+ observations: ['joint_pos', 'joint_vel', 'imu'],
323
+ actions: ['joint_torques'],
324
+ randomization: randomization || {},
325
+ actuator_groups: actuators,
326
+ source: 'holoscript',
327
+ usd_prim: ast.name,
328
+ };
329
+ // Simple content hashes for the receipt (evidence loop)
330
+ const sourceHoloHash = `holo:${ast.name}:${(ast.objects?.length || 0)}`;
331
+ const usdHash = `usd:${usd.length}`;
332
+ const configHash = `cfg:${JSON.stringify(taskConfig).length}`;
333
+ const receipt = {
334
+ sourceHoloHash,
335
+ usdHash,
336
+ configHash,
337
+ generatedAt: new Date().toISOString(),
338
+ isaacLabVersion: version,
339
+ readyForTraining: true,
340
+ notes: 'Narrow feed bundle — assets + randomization + actuator config. Policy training & real-robot eval are downstream.',
341
+ };
342
+ return {
343
+ usd,
344
+ taskConfig,
345
+ randomization,
346
+ actuators,
347
+ receipt,
348
+ };
349
+ }
package/dist/lexer.js CHANGED
@@ -1,13 +1,10 @@
1
- "use strict";
2
1
  /**
3
2
  * HoloScript Lexer (Tokenizer)
4
3
  *
5
4
  * Converts HoloScript source code into a stream of tokens for parsing.
6
5
  * Supports: keywords, identifiers, traits (@trait), strings, numbers, symbols
7
6
  */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.Lexer = exports.TokenType = void 0;
10
- var TokenType;
7
+ export var TokenType;
11
8
  (function (TokenType) {
12
9
  // Keywords
13
10
  TokenType["KEYWORD"] = "KEYWORD";
@@ -26,8 +23,8 @@ var TokenType;
26
23
  // Special
27
24
  TokenType["COMMENT"] = "COMMENT";
28
25
  TokenType["EOF"] = "EOF";
29
- })(TokenType || (exports.TokenType = TokenType = {}));
30
- class Lexer {
26
+ })(TokenType || (TokenType = {}));
27
+ export class Lexer {
31
28
  constructor(source) {
32
29
  this.position = 0;
33
30
  this.line = 1;
@@ -95,7 +92,8 @@ class Lexer {
95
92
  const startColumn = this.column;
96
93
  this.advance(); // Skip '@'
97
94
  let trait = '';
98
- while (this.position < this.source.length && this.isAlphaNumericOrUnderscore(this.currentChar())) {
95
+ while (this.position < this.source.length &&
96
+ this.isAlphaNumericOrUnderscore(this.currentChar())) {
99
97
  trait += this.currentChar();
100
98
  this.advance();
101
99
  }
@@ -172,7 +170,8 @@ class Lexer {
172
170
  const startLine = this.line;
173
171
  const startColumn = this.column;
174
172
  let ident = '';
175
- while (this.position < this.source.length && this.isAlphaNumericOrUnderscore(this.currentChar())) {
173
+ while (this.position < this.source.length &&
174
+ this.isAlphaNumericOrUnderscore(this.currentChar())) {
176
175
  ident += this.currentChar();
177
176
  this.advance();
178
177
  }
@@ -250,26 +249,3 @@ class Lexer {
250
249
  return /[a-z0-9_]/i.test(char);
251
250
  }
252
251
  }
253
- exports.Lexer = Lexer;
254
- // Example usage
255
- if (require.main === module) {
256
- const source = `
257
- composition "TwoLinkArm" {
258
- object "Base" @static {
259
- geometry: "cylinder"
260
- dimensions: [0.1, 0.05]
261
- }
262
-
263
- object "Link1" @joint_revolute {
264
- joint_axis: [0, 0, 1]
265
- joint_limits: [-3.14, 3.14]
266
- }
267
- }
268
- `;
269
- const lexer = new Lexer(source);
270
- const tokens = lexer.tokenize();
271
- console.log('Tokens:');
272
- tokens.forEach((token, i) => {
273
- console.log(`${i}: ${token.type.padEnd(15)} "${token.value}" (L${token.line}:C${token.column})`);
274
- });
275
- }
package/dist/parser.d.ts CHANGED
@@ -19,4 +19,12 @@ export declare class Parser {
19
19
  private parseObject;
20
20
  private parseValue;
21
21
  private parseArray;
22
+ private parseDomainRandomizationBlock;
23
+ private parsePhysicsRandomization;
24
+ private parseActuatorRandomization;
25
+ private parseObservationRandomization;
26
+ private parseInitialStateRandomization;
27
+ private parseNumberRangeMap;
28
+ private parseDisturbanceRandomization;
29
+ private parseActuatorGroupBlock;
22
30
  }