chatroom-cli 1.138.0 → 1.138.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/dist/index.js CHANGED
@@ -3795,6 +3795,385 @@ var init_meshoptimizer = __esm(() => {
3795
3795
  init_meshopt_tangents();
3796
3796
  });
3797
3797
 
3798
+ // src/commands/generate-3d/asset-analysis.ts
3799
+ function toUnit(value) {
3800
+ return value / 255;
3801
+ }
3802
+ function percentile(histogram, total, fraction) {
3803
+ const target = Math.min(total, Math.ceil(fraction * total));
3804
+ let seen = 0;
3805
+ for (let value = 0;value < HISTOGRAM_BINS; value += 1) {
3806
+ seen += histogram[value] ?? 0;
3807
+ if (seen >= target)
3808
+ return toUnit(value);
3809
+ }
3810
+ return 1;
3811
+ }
3812
+ function countInRange(histogram, first, last) {
3813
+ let count = 0;
3814
+ for (let value = first;value <= last; value += 1)
3815
+ count += histogram[value] ?? 0;
3816
+ return count;
3817
+ }
3818
+ function channelStats(pixels, channel) {
3819
+ const total = Math.floor(pixels.length / CHANNELS_PER_TEXEL);
3820
+ if (total === 0)
3821
+ return EMPTY_STATS;
3822
+ const histogram = new Uint32Array(HISTOGRAM_BINS);
3823
+ let weighted = 0;
3824
+ for (let index = channel;index < pixels.length; index += CHANNELS_PER_TEXEL) {
3825
+ const value = pixels[index];
3826
+ histogram[value] += 1;
3827
+ weighted += value;
3828
+ }
3829
+ return {
3830
+ mean: weighted / (255 * total),
3831
+ p5: percentile(histogram, total, 0.05),
3832
+ p50: percentile(histogram, total, 0.5),
3833
+ p95: percentile(histogram, total, 0.95),
3834
+ fractionAtLeastHalf: countInRange(histogram, HALF_BYTE, HISTOGRAM_BINS - 1) / total,
3835
+ fractionAtMostTenth: countInRange(histogram, 0, TENTH_BYTE) / total
3836
+ };
3837
+ }
3838
+ function isFiniteBox(min, max) {
3839
+ return min.every((value) => Number.isFinite(value)) && max.every((value) => Number.isFinite(value));
3840
+ }
3841
+ function mergeBounds(boxes) {
3842
+ const min = [Infinity, Infinity, Infinity];
3843
+ const max = [-Infinity, -Infinity, -Infinity];
3844
+ for (const box of boxes) {
3845
+ min[0] = Math.min(min[0], box.min[0]);
3846
+ min[1] = Math.min(min[1], box.min[1]);
3847
+ min[2] = Math.min(min[2], box.min[2]);
3848
+ max[0] = Math.max(max[0], box.max[0]);
3849
+ max[1] = Math.max(max[1], box.max[1]);
3850
+ max[2] = Math.max(max[2], box.max[2]);
3851
+ }
3852
+ return isFiniteBox(min, max) ? { min, max } : { min: [0, 0, 0], max: [0, 0, 0] };
3853
+ }
3854
+ function extentsOf(bounds) {
3855
+ return [
3856
+ bounds.max[0] - bounds.min[0],
3857
+ bounds.max[1] - bounds.min[1],
3858
+ bounds.max[2] - bounds.min[2]
3859
+ ];
3860
+ }
3861
+ async function decodeImageRgba(image, sharpFactory) {
3862
+ const { data, info } = await sharpFactory(Buffer.from(image)).toColourspace("srgb").ensureAlpha().raw().toBuffer({ resolveWithObject: true });
3863
+ if (info.channels !== 4) {
3864
+ throw new Error(`Image decoded to ${info.channels} channels; expected 4 (RGBA).`);
3865
+ }
3866
+ return { pixels: new Uint8Array(data), width: info.width, height: info.height };
3867
+ }
3868
+ async function decodePixels(texture, sharp, cache) {
3869
+ const cached = cache.get(texture);
3870
+ if (cached !== undefined)
3871
+ return cached;
3872
+ const image = texture.getImage();
3873
+ if (image === null || image.length === 0) {
3874
+ throw new Error("A metallic-roughness texture has no embedded image.");
3875
+ }
3876
+ const { pixels } = await decodeImageRgba(image, sharp);
3877
+ cache.set(texture, pixels);
3878
+ return pixels;
3879
+ }
3880
+ async function describeMaterial(index, material, sharp, cache) {
3881
+ const texture = material.getMetallicRoughnessTexture();
3882
+ const base = {
3883
+ index,
3884
+ name: material.getName() || null,
3885
+ metallicFactor: material.getMetallicFactor(),
3886
+ roughnessFactor: material.getRoughnessFactor(),
3887
+ hasMetallicRoughnessTexture: texture !== null
3888
+ };
3889
+ if (texture === null)
3890
+ return { ...base, metal: null, roughness: null };
3891
+ if (!DECODABLE_IMAGE_MIME_TYPES.has(texture.getMimeType())) {
3892
+ return { ...base, metal: null, roughness: null };
3893
+ }
3894
+ const pixels = await decodePixels(texture, sharp, cache);
3895
+ return { ...base, metal: channelStats(pixels, 2), roughness: channelStats(pixels, 1) };
3896
+ }
3897
+ async function analyseDocument(doc) {
3898
+ const [{ getBounds }, { default: sharp }] = await Promise.all([
3899
+ import("@gltf-transform/functions"),
3900
+ import("sharp")
3901
+ ]);
3902
+ const bounds = mergeBounds(doc.getRoot().listScenes().map((scene) => getBounds(scene)));
3903
+ const cache = new Map;
3904
+ const materials = [];
3905
+ for (const [index, material] of doc.getRoot().listMaterials().entries()) {
3906
+ materials.push(await describeMaterial(index, material, sharp, cache));
3907
+ }
3908
+ return { bounds, extents: extentsOf(bounds), materials };
3909
+ }
3910
+ var CHANNELS_PER_TEXEL = 4, HALF_BYTE = 128, TENTH_BYTE = 25, HISTOGRAM_BINS = 256, EMPTY_STATS, DECODABLE_IMAGE_MIME_TYPES;
3911
+ var init_asset_analysis = __esm(() => {
3912
+ EMPTY_STATS = {
3913
+ mean: 0,
3914
+ p5: 0,
3915
+ p50: 0,
3916
+ p95: 0,
3917
+ fractionAtLeastHalf: 0,
3918
+ fractionAtMostTenth: 0
3919
+ };
3920
+ DECODABLE_IMAGE_MIME_TYPES = new Set([
3921
+ "image/jpeg",
3922
+ "image/png",
3923
+ "image/webp"
3924
+ ]);
3925
+ });
3926
+
3927
+ // src/commands/generate-3d/fit.ts
3928
+ async function currentBounds(doc, getBounds) {
3929
+ return mergeBounds(doc.getRoot().listScenes().map((scene) => getBounds(scene)));
3930
+ }
3931
+ function originOffset(bounds, origin) {
3932
+ if (origin === "as-is")
3933
+ return [0, 0, 0];
3934
+ const midX = (bounds.min[0] + bounds.max[0]) / 2;
3935
+ const midZ = (bounds.min[2] + bounds.max[2]) / 2;
3936
+ const y = origin === "feet" ? bounds.min[1] : (bounds.min[1] + bounds.max[1]) / 2;
3937
+ return [-midX, -y, -midZ];
3938
+ }
3939
+ function scaleForHeight(bounds, targetHeight) {
3940
+ const height = bounds.max[1] - bounds.min[1];
3941
+ if (!Number.isFinite(height) || height <= 0) {
3942
+ throw new Error("Cannot fit the model: its scene height is 0.");
3943
+ }
3944
+ return targetHeight / height;
3945
+ }
3946
+ function addWrapper(doc, translation, scale) {
3947
+ for (const scene of doc.getRoot().listScenes()) {
3948
+ const roots = [...scene.listChildren()];
3949
+ const wrapper = doc.createNode("Fit").setTranslation(translation).setScale([scale, scale, scale]);
3950
+ for (const root of roots)
3951
+ wrapper.addChild(root);
3952
+ scene.addChild(wrapper);
3953
+ }
3954
+ }
3955
+ function isIdentity(fit) {
3956
+ return fit.scale === 1 && fit.translation[0] === 0 && fit.translation[1] === 0 && fit.translation[2] === 0;
3957
+ }
3958
+ function applyFitTransform(doc, fit) {
3959
+ if (isIdentity(fit))
3960
+ return;
3961
+ addWrapper(doc, fit.translation, fit.scale);
3962
+ }
3963
+ function fitTransform(bounds, options) {
3964
+ const offset = originOffset(bounds, options.origin);
3965
+ const scale = options.targetHeight === undefined ? 1 : scaleForHeight(bounds, options.targetHeight);
3966
+ return {
3967
+ scale,
3968
+ translation: [offset[0] * scale, offset[1] * scale, offset[2] * scale]
3969
+ };
3970
+ }
3971
+ async function applyFit(doc, analysis, options) {
3972
+ if (options.origin === "as-is" && options.targetHeight === undefined) {
3973
+ return {
3974
+ origin: "as-is",
3975
+ targetHeight: null,
3976
+ scale: 1,
3977
+ translation: [0, 0, 0],
3978
+ bounds: analysis.bounds
3979
+ };
3980
+ }
3981
+ const { scale, translation } = fitTransform(analysis.bounds, options);
3982
+ addWrapper(doc, translation, scale);
3983
+ const { getBounds } = await import("@gltf-transform/functions");
3984
+ const bounds = await currentBounds(doc, getBounds);
3985
+ return {
3986
+ origin: options.origin,
3987
+ targetHeight: options.targetHeight ?? null,
3988
+ scale,
3989
+ translation,
3990
+ bounds: {
3991
+ min: [bounds.min[0], bounds.min[1], bounds.min[2]],
3992
+ max: [bounds.max[0], bounds.max[1], bounds.max[2]]
3993
+ }
3994
+ };
3995
+ }
3996
+ var init_fit = __esm(() => {
3997
+ init_asset_analysis();
3998
+ });
3999
+
4000
+ // src/commands/generate-3d/material-normalise.ts
4001
+ function materialName(material) {
4002
+ return material.getName() || null;
4003
+ }
4004
+ function untexturedDecision(index, material) {
4005
+ const name = materialName(material);
4006
+ if (material.getMetallicFactor() !== 1) {
4007
+ return { index, name, decision: "unchanged", reason: "explicit-factor" };
4008
+ }
4009
+ material.setMetallicFactor(0);
4010
+ return { index, name, decision: "non-metal", metalFraction: 0 };
4011
+ }
4012
+ function requireImage(texture) {
4013
+ const image = texture.getImage();
4014
+ if (image === null || image.length === 0) {
4015
+ throw new Error("A metallic-roughness texture has no embedded image.");
4016
+ }
4017
+ return image;
4018
+ }
4019
+ function isWeakMetal(value) {
4020
+ return value < METAL_MASK_THRESHOLD;
4021
+ }
4022
+ function isPartlyMetal(value) {
4023
+ return value > NON_METAL_TENTH_BYTE && value < METAL_MASK_THRESHOLD;
4024
+ }
4025
+ async function maskBlueChannel(texture, sharpFactory) {
4026
+ const { pixels, width, height } = await decodeImageRgba(requireImage(texture), sharpFactory);
4027
+ let masked = 0;
4028
+ for (let index = 2;index < pixels.length; index += 4) {
4029
+ const blue = pixels[index];
4030
+ if (!isWeakMetal(blue))
4031
+ continue;
4032
+ if (isPartlyMetal(blue))
4033
+ masked += 1;
4034
+ pixels[index] = 0;
4035
+ }
4036
+ const png = await sharpFactory(Buffer.from(pixels), {
4037
+ raw: { width, height, channels: 4 }
4038
+ }).png().toBuffer();
4039
+ texture.setImage(new Uint8Array(png)).setMimeType("image/png");
4040
+ return masked / (pixels.length / 4);
4041
+ }
4042
+ function hasUsableImage(texture, stats) {
4043
+ return DECODABLE_IMAGE_MIME_TYPES.has(texture.getMimeType()) && stats.metal !== null && stats.roughness !== null;
4044
+ }
4045
+ function canMask(texture, stats) {
4046
+ if (!hasUsableImage(texture, stats))
4047
+ return false;
4048
+ return stats.metal.fractionAtLeastHalf >= NON_METAL_FRACTION_MAX;
4049
+ }
4050
+ function isMaskCandidate(material, stats) {
4051
+ if (stats === undefined || material.getMetallicFactor() !== 1)
4052
+ return false;
4053
+ const texture = material.getMetallicRoughnessTexture();
4054
+ if (texture === null)
4055
+ return false;
4056
+ return canMask(texture, stats);
4057
+ }
4058
+ function texturedNonMaskedDecision(index, material, texture, stats) {
4059
+ const name = materialName(material);
4060
+ if (!hasUsableImage(texture, stats)) {
4061
+ return { index, name, decision: "unchanged", reason: "unsupported-image" };
4062
+ }
4063
+ if (material.getMetallicFactor() !== 1) {
4064
+ return { index, name, decision: "unchanged", reason: "explicit-factor" };
4065
+ }
4066
+ material.setMetallicFactor(0);
4067
+ return {
4068
+ index,
4069
+ name,
4070
+ decision: "non-metal",
4071
+ metalFraction: stats.metal.fractionAtLeastHalf
4072
+ };
4073
+ }
4074
+ function nonMaskedDecision(index, material, stats) {
4075
+ if (stats === undefined) {
4076
+ return {
4077
+ index,
4078
+ name: materialName(material),
4079
+ decision: "unchanged",
4080
+ reason: "missing-analysis"
4081
+ };
4082
+ }
4083
+ const texture = material.getMetallicRoughnessTexture();
4084
+ if (texture === null)
4085
+ return untexturedDecision(index, material);
4086
+ return texturedNonMaskedDecision(index, material, texture, stats);
4087
+ }
4088
+ function textureUsers(materials, texture) {
4089
+ const users = [];
4090
+ for (const [index, material] of materials.entries()) {
4091
+ if (material.getMetallicRoughnessTexture() === texture)
4092
+ users.push(index);
4093
+ }
4094
+ return users;
4095
+ }
4096
+ function needsClone(texture, users, masked) {
4097
+ const parents = texture.listParents().filter((parent) => parent.propertyType !== "Root").length;
4098
+ return users.length > masked.length || parents > users.length;
4099
+ }
4100
+ function maskedUserFor(index, material, stats) {
4101
+ if (!isMaskCandidate(material, stats))
4102
+ return null;
4103
+ if (stats === undefined || stats.metal === null)
4104
+ return null;
4105
+ return { index, material, metalFraction: stats.metal.fractionAtLeastHalf };
4106
+ }
4107
+ async function maskTextureGroup(args) {
4108
+ const { texture, masked, users, sharpFactory, decisions } = args;
4109
+ const clone = needsClone(texture, users, masked.map((user) => user.index));
4110
+ const target = clone ? texture.clone() : texture;
4111
+ const maskedFraction = await maskBlueChannel(target, sharpFactory);
4112
+ for (const user of masked) {
4113
+ user.material.setMetallicFactor(1);
4114
+ if (clone)
4115
+ user.material.setMetallicRoughnessTexture(target);
4116
+ decisions.set(user.index, {
4117
+ index: user.index,
4118
+ name: materialName(user.material),
4119
+ decision: "metal-masked",
4120
+ metalFraction: user.metalFraction,
4121
+ maskedFraction
4122
+ });
4123
+ }
4124
+ }
4125
+ function maskedEntryFor(index, material, stats) {
4126
+ const user = maskedUserFor(index, material, stats);
4127
+ if (user === null)
4128
+ return null;
4129
+ const texture = material.getMetallicRoughnessTexture();
4130
+ return texture === null ? null : { texture, user };
4131
+ }
4132
+ function maskedGroups(materials, analysis) {
4133
+ const groups = new Map;
4134
+ for (const [index, material] of materials.entries()) {
4135
+ const entry = maskedEntryFor(index, material, analysis.materials[index]);
4136
+ if (entry === null)
4137
+ continue;
4138
+ const group = groups.get(entry.texture) ?? [];
4139
+ group.push(entry.user);
4140
+ groups.set(entry.texture, group);
4141
+ }
4142
+ return groups;
4143
+ }
4144
+ function fillNonMasked(materials, analysis, decisions) {
4145
+ for (const [index, material] of materials.entries()) {
4146
+ if (decisions.has(index))
4147
+ continue;
4148
+ decisions.set(index, nonMaskedDecision(index, material, analysis.materials[index]));
4149
+ }
4150
+ }
4151
+ async function normaliseMaterials(doc, analysis) {
4152
+ const { default: sharpFactory } = await import("sharp");
4153
+ const materials = doc.getRoot().listMaterials();
4154
+ const decisions = new Map;
4155
+ for (const [texture, masked] of maskedGroups(materials, analysis)) {
4156
+ await maskTextureGroup({
4157
+ texture,
4158
+ masked,
4159
+ users: textureUsers(materials, texture),
4160
+ sharpFactory,
4161
+ decisions
4162
+ });
4163
+ }
4164
+ fillNonMasked(materials, analysis, decisions);
4165
+ return materials.map((_material, index) => decisions.get(index) ?? {
4166
+ index,
4167
+ name: null,
4168
+ decision: "unchanged",
4169
+ reason: "missing-analysis"
4170
+ });
4171
+ }
4172
+ var NON_METAL_FRACTION_MAX = 0.01, METAL_MASK_THRESHOLD = 128, NON_METAL_TENTH_BYTE = 25;
4173
+ var init_material_normalise = __esm(() => {
4174
+ init_asset_analysis();
4175
+ });
4176
+
3798
4177
  // src/commands/generate-3d/web-variant.ts
3799
4178
  import {
3800
4179
  NodeIO,
@@ -3925,6 +4304,16 @@ async function createWebVariant(glb, opts) {
3925
4304
  weld: () => weld(),
3926
4305
  simplifyAt: (ratio) => simplify({ simplifier: MeshoptSimplifier, ratio, error: 0.01, lockBorder: true })
3927
4306
  });
4307
+ let materials;
4308
+ let fit;
4309
+ if (opts.processed !== undefined) {
4310
+ materials = opts.processed.materials;
4311
+ fit = opts.processed.fit;
4312
+ } else {
4313
+ const analysis = await analyseDocument(document2);
4314
+ materials = await normaliseMaterials(document2, analysis);
4315
+ fit = await applyFit(document2, analysis, opts.fit ?? { origin: "as-is" });
4316
+ }
3928
4317
  await compressTextures({
3929
4318
  document: document2,
3930
4319
  compressTexture,
@@ -3939,12 +4328,17 @@ async function createWebVariant(glb, opts) {
3939
4328
  ...describeDocument(document2),
3940
4329
  simplified: geometry.simplified,
3941
4330
  skinnedPath: geometry.skinnedPath,
3942
- source
4331
+ source,
4332
+ materials,
4333
+ fit
3943
4334
  };
3944
4335
  }
3945
4336
  var SILENT_LOGGER, COMPRESSIBLE_MIME_TYPES, WEBP_EXTENSION = "EXT_texture_webp", DRACO_EXTENSION = "KHR_draco_mesh_compression", MESHOPT_EXTENSION = "EXT_meshopt_compression";
3946
4337
  var init_web_variant = __esm(() => {
3947
4338
  init_meshoptimizer();
4339
+ init_asset_analysis();
4340
+ init_fit();
4341
+ init_material_normalise();
3948
4342
  init_model_registry();
3949
4343
  SILENT_LOGGER = {
3950
4344
  debug: () => {},
@@ -3964,6 +4358,9 @@ import { createRequire as createRequire2 } from "module";
3964
4358
  function deflateSync(data, opts) {
3965
4359
  return dopt(data, opts || {}, 0, 0);
3966
4360
  }
4361
+ function inflateSync(data, opts) {
4362
+ return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
4363
+ }
3967
4364
  function strToU8(str, latin1) {
3968
4365
  if (latin1) {
3969
4366
  var ar_1 = new u8(str.length);
@@ -3997,6 +4394,21 @@ function strToU8(str, latin1) {
3997
4394
  }
3998
4395
  return slc(ar, 0, ai);
3999
4396
  }
4397
+ function strFromU8(dat, latin1) {
4398
+ if (latin1) {
4399
+ var r = "";
4400
+ for (var i2 = 0;i2 < dat.length; i2 += 16384)
4401
+ r += String.fromCharCode.apply(null, dat.subarray(i2, i2 + 16384));
4402
+ return r;
4403
+ } else if (td) {
4404
+ return td.decode(dat);
4405
+ } else {
4406
+ var _a2 = dutf8(dat), s = _a2.s, r = _a2.r;
4407
+ if (r.length)
4408
+ err(8);
4409
+ return s;
4410
+ }
4411
+ }
4000
4412
  function zipSync(data, opts) {
4001
4413
  if (!opts)
4002
4414
  opts = {};
@@ -4040,6 +4452,46 @@ function zipSync(data, opts) {
4040
4452
  wzf(out, o, files.length, cdl, oe);
4041
4453
  return out;
4042
4454
  }
4455
+ function unzipSync(data, opts) {
4456
+ var files = {};
4457
+ var e = data.length - 22;
4458
+ for (;b4(data, e) != 101010256; --e) {
4459
+ if (!e || data.length - e > 65558)
4460
+ err(13);
4461
+ }
4462
+ var c = b2(data, e + 8);
4463
+ if (!c)
4464
+ return {};
4465
+ var o = b4(data, e + 16);
4466
+ var z = b4(data, e - 20) == 117853008;
4467
+ if (z) {
4468
+ var ze = b4(data, e - 12);
4469
+ z = b4(data, ze) == 101075792;
4470
+ if (z) {
4471
+ c = b4(data, ze + 32);
4472
+ o = b4(data, ze + 48);
4473
+ }
4474
+ }
4475
+ var fltr = opts && opts.filter;
4476
+ for (var i2 = 0;i2 < c; ++i2) {
4477
+ var _a2 = zh(data, o, z), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off);
4478
+ o = no;
4479
+ if (!fltr || fltr({
4480
+ name: fn,
4481
+ size: sc,
4482
+ originalSize: su,
4483
+ compression: c_2
4484
+ })) {
4485
+ if (!c_2)
4486
+ files[fn] = slc(data, b, b + sc);
4487
+ else if (c_2 == 8)
4488
+ files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
4489
+ else
4490
+ err(14, "unknown compression type " + c_2);
4491
+ }
4492
+ }
4493
+ return files;
4494
+ }
4043
4495
  var require2, _a, Worker2, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, clim, freb = function(eb, start) {
4044
4496
  var b = new u16(31);
4045
4497
  for (var i = 0;i < 31; ++i) {
@@ -4087,7 +4539,20 @@ var require2, _a, Worker2, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, c
4087
4539
  }
4088
4540
  }
4089
4541
  return co;
4090
- }, flt, i, i, i, i, fdt, i, flm, fdm, shft = function(p) {
4542
+ }, flt, i, i, i, i, fdt, i, flm, flrm, fdm, fdrm, max = function(a) {
4543
+ var m = a[0];
4544
+ for (var i2 = 1;i2 < a.length; ++i2) {
4545
+ if (a[i2] > m)
4546
+ m = a[i2];
4547
+ }
4548
+ return m;
4549
+ }, bits = function(d, p, m) {
4550
+ var o = p / 8 | 0;
4551
+ return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
4552
+ }, bits16 = function(d, p) {
4553
+ var o = p / 8 | 0;
4554
+ return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
4555
+ }, shft = function(p) {
4091
4556
  return (p + 7) / 8 | 0;
4092
4557
  }, slc = function(v, s, e) {
4093
4558
  if (s == null || s < 0)
@@ -4103,6 +4568,146 @@ var require2, _a, Worker2, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, c
4103
4568
  if (!nt)
4104
4569
  throw e;
4105
4570
  return e;
4571
+ }, inflt = function(dat, st, buf, dict) {
4572
+ var sl = dat.length, dl = dict ? dict.length : 0;
4573
+ if (!sl || st.f && !st.l)
4574
+ return buf || new u8(0);
4575
+ var noBuf = !buf;
4576
+ var resize = noBuf || st.i != 2;
4577
+ var noSt = st.i;
4578
+ if (noBuf)
4579
+ buf = new u8(sl * 3);
4580
+ var cbuf = function(l2) {
4581
+ var bl = buf.length;
4582
+ if (l2 > bl) {
4583
+ var nbuf = new u8(Math.max(bl * 2, l2));
4584
+ nbuf.set(buf);
4585
+ buf = nbuf;
4586
+ }
4587
+ };
4588
+ var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
4589
+ var tbts = sl * 8;
4590
+ do {
4591
+ if (!lm) {
4592
+ final = bits(dat, pos, 1);
4593
+ var type = bits(dat, pos + 1, 3);
4594
+ pos += 3;
4595
+ if (!type) {
4596
+ var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
4597
+ if (t > sl) {
4598
+ if (noSt)
4599
+ err(0);
4600
+ break;
4601
+ }
4602
+ if (resize)
4603
+ cbuf(bt + l);
4604
+ buf.set(dat.subarray(s, t), bt);
4605
+ st.b = bt += l, st.p = pos = t * 8, st.f = final;
4606
+ continue;
4607
+ } else if (type == 1)
4608
+ lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
4609
+ else if (type == 2) {
4610
+ var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
4611
+ var tl = hLit + bits(dat, pos + 5, 31) + 1;
4612
+ pos += 14;
4613
+ var ldt = new u8(tl);
4614
+ var clt = new u8(19);
4615
+ for (var i2 = 0;i2 < hcLen; ++i2) {
4616
+ clt[clim[i2]] = bits(dat, pos + i2 * 3, 7);
4617
+ }
4618
+ pos += hcLen * 3;
4619
+ var clb = max(clt), clbmsk = (1 << clb) - 1;
4620
+ var clm = hMap(clt, clb, 1);
4621
+ for (var i2 = 0;i2 < tl; ) {
4622
+ var r = clm[bits(dat, pos, clbmsk)];
4623
+ pos += r & 15;
4624
+ var s = r >> 4;
4625
+ if (s < 16) {
4626
+ ldt[i2++] = s;
4627
+ } else {
4628
+ var c = 0, n = 0;
4629
+ if (s == 16)
4630
+ n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i2 - 1];
4631
+ else if (s == 17)
4632
+ n = 3 + bits(dat, pos, 7), pos += 3;
4633
+ else if (s == 18)
4634
+ n = 11 + bits(dat, pos, 127), pos += 7;
4635
+ while (n--)
4636
+ ldt[i2++] = c;
4637
+ }
4638
+ }
4639
+ var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
4640
+ lbt = max(lt);
4641
+ dbt = max(dt);
4642
+ lm = hMap(lt, lbt, 1);
4643
+ dm = hMap(dt, dbt, 1);
4644
+ } else
4645
+ err(1);
4646
+ if (pos > tbts) {
4647
+ if (noSt)
4648
+ err(0);
4649
+ break;
4650
+ }
4651
+ }
4652
+ if (resize)
4653
+ cbuf(bt + 131072);
4654
+ var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
4655
+ var lpos = pos;
4656
+ for (;; lpos = pos) {
4657
+ var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
4658
+ pos += c & 15;
4659
+ if (pos > tbts) {
4660
+ if (noSt)
4661
+ err(0);
4662
+ break;
4663
+ }
4664
+ if (!c)
4665
+ err(2);
4666
+ if (sym < 256)
4667
+ buf[bt++] = sym;
4668
+ else if (sym == 256) {
4669
+ lpos = pos, lm = null;
4670
+ break;
4671
+ } else {
4672
+ var add = sym - 254;
4673
+ if (sym > 264) {
4674
+ var i2 = sym - 257, b = fleb[i2];
4675
+ add = bits(dat, pos, (1 << b) - 1) + fl[i2];
4676
+ pos += b;
4677
+ }
4678
+ var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
4679
+ if (!d)
4680
+ err(3);
4681
+ pos += d & 15;
4682
+ var dt = fd[dsym];
4683
+ if (dsym > 3) {
4684
+ var b = fdeb[dsym];
4685
+ dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
4686
+ }
4687
+ if (pos > tbts) {
4688
+ if (noSt)
4689
+ err(0);
4690
+ break;
4691
+ }
4692
+ if (resize)
4693
+ cbuf(bt + 131072);
4694
+ var end = bt + add;
4695
+ if (bt < dt) {
4696
+ var shift = dl - dt, dend = Math.min(dt, end);
4697
+ if (shift + bt < 0)
4698
+ err(3);
4699
+ for (;bt < dend; ++bt)
4700
+ buf[bt] = dict[shift + bt];
4701
+ }
4702
+ for (;bt < end; ++bt)
4703
+ buf[bt] = buf[bt - dt];
4704
+ }
4705
+ }
4706
+ st.l = lm, st.p = lpos, st.b = bt, st.f = final;
4707
+ if (lm)
4708
+ final = 1, st.m = lbt, st.d = dm, st.n = dbt;
4709
+ } while (!final);
4710
+ return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
4106
4711
  }, wbits = function(d, p, v) {
4107
4712
  v <<= p & 7;
4108
4713
  var o = p / 8 | 0;
@@ -4426,6 +5031,12 @@ var require2, _a, Worker2, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, c
4426
5031
  for (var k in b)
4427
5032
  o[k] = b[k];
4428
5033
  return o;
5034
+ }, b2 = function(d, b) {
5035
+ return d[b] | d[b + 1] << 8;
5036
+ }, b4 = function(d, b) {
5037
+ return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
5038
+ }, b8 = function(d, b) {
5039
+ return b4(d, b) + b4(d, b + 4) * 4294967296;
4429
5040
  }, wbytes = function(d, b, v) {
4430
5041
  for (;v; ++b)
4431
5042
  d[b] = v, v >>>= 8;
@@ -4441,7 +5052,46 @@ var require2, _a, Worker2, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, c
4441
5052
  fltn(val, n, t, o);
4442
5053
  }
4443
5054
  }
4444
- }, te, td, tds = 0, exfl = function(ex) {
5055
+ }, te, td, tds = 0, dutf8 = function(d) {
5056
+ for (var r = "", i2 = 0;; ) {
5057
+ var c = d[i2++];
5058
+ var eb = (c > 127) + (c > 223) + (c > 239);
5059
+ if (i2 + eb > d.length)
5060
+ return { s: r, r: slc(d, i2 - 1) };
5061
+ if (!eb)
5062
+ r += String.fromCharCode(c);
5063
+ else if (eb == 3) {
5064
+ c = ((c & 15) << 18 | (d[i2++] & 63) << 12 | (d[i2++] & 63) << 6 | d[i2++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023);
5065
+ } else if (eb & 1)
5066
+ r += String.fromCharCode((c & 31) << 6 | d[i2++] & 63);
5067
+ else
5068
+ r += String.fromCharCode((c & 15) << 12 | (d[i2++] & 63) << 6 | d[i2++] & 63);
5069
+ }
5070
+ }, slzh = function(d, b) {
5071
+ return b + 30 + b2(d, b + 26) + b2(d, b + 28);
5072
+ }, zh = function(d, b, z) {
5073
+ var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
5074
+ var _a2 = z64hs(d, es, efl, z, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a2[0], su = _a2[1], off = _a2[2];
5075
+ return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
5076
+ }, z64hs = function(d, b, l, z, sc, su, off) {
5077
+ var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
5078
+ var nf = nsc + nsu + noff;
5079
+ if (z && nf) {
5080
+ for (;b + 4 < e; b += 4 + b2(d, b + 2)) {
5081
+ if (b2(d, b) == 1) {
5082
+ return [
5083
+ nsc ? b8(d, b + 4 + 8 * nsu) : sc,
5084
+ nsu ? b8(d, b + 4) : su,
5085
+ noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
5086
+ 1
5087
+ ];
5088
+ }
5089
+ }
5090
+ if (z < 2)
5091
+ err(13);
5092
+ }
5093
+ return [sc, su, off, 0];
5094
+ }, exfl = function(ex) {
4445
5095
  var le = 0;
4446
5096
  if (ex) {
4447
5097
  for (var k in ex) {
@@ -4535,7 +5185,9 @@ var init_esm = __esm(() => {
4535
5185
  for (i = 0;i < 32; ++i)
4536
5186
  fdt[i] = 5;
4537
5187
  flm = /* @__PURE__ */ hMap(flt, 9, 0);
5188
+ flrm = /* @__PURE__ */ hMap(flt, 9, 1);
4538
5189
  fdm = /* @__PURE__ */ hMap(fdt, 5, 0);
5190
+ fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
4539
5191
  ec = [
4540
5192
  "unexpected EOF",
4541
5193
  "invalid block type",
@@ -9083,7 +9735,7 @@ function validateInputOption(opts) {
9083
9735
  }
9084
9736
  validateInputObject(opts.input);
9085
9737
  }
9086
- function requireImage(images, model) {
9738
+ function requireImage2(images, model) {
9087
9739
  const image = images[0];
9088
9740
  if (!image?.url) {
9089
9741
  throw new GenerateImageError(`fal returned no image for model ${model}`);
@@ -9425,7 +10077,7 @@ async function generateAndSave(deps, model, input, outPath) {
9425
10077
  if (result.data.images.length > 1) {
9426
10078
  deps.warn(`⚠️ fal returned ${result.data.images.length} images; only the first is saved.`);
9427
10079
  }
9428
- const image = requireImage(result.data.images, model);
10080
+ const image = requireImage2(result.data.images, model);
9429
10081
  const bytes = await downloadImage(deps, result.requestId, image.url);
9430
10082
  await saveImage(deps, outPath, bytes, result.requestId, image.url);
9431
10083
  return {
@@ -9870,6 +10522,109 @@ var init_deps2 = __esm(() => {
9870
10522
  GLB_MAGIC = [103, 108, 84, 70];
9871
10523
  });
9872
10524
 
10525
+ // src/commands/generate-3d/checks.ts
10526
+ function formatMean(mean) {
10527
+ return mean.toFixed(2);
10528
+ }
10529
+ function isWholeTextureMetal(material) {
10530
+ if (material.metal === null) {
10531
+ return !material.hasMetallicRoughnessTexture && material.metallicFactor >= CHECK_THRESHOLDS.wholeTextureMetalFraction;
10532
+ }
10533
+ return material.metallicFactor > 0 && material.metal.fractionAtLeastHalf >= CHECK_THRESHOLDS.wholeTextureMetalFraction;
10534
+ }
10535
+ function metalWholeTextureFor(material) {
10536
+ if (!isWholeTextureMetal(material))
10537
+ return null;
10538
+ return {
10539
+ id: "metal-whole-texture",
10540
+ severity: "warning",
10541
+ materialIndex: material.index,
10542
+ message: `Material ${material.index} looks metal across its whole surface; check that the surface really is metal.`
10543
+ };
10544
+ }
10545
+ function flatChecks(analysis) {
10546
+ const largest = Math.max(...analysis.extents);
10547
+ if (largest <= 0)
10548
+ return [];
10549
+ const ratio = Math.min(...analysis.extents) / largest;
10550
+ if (ratio >= CHECK_THRESHOLDS.flatRatio)
10551
+ return [];
10552
+ return [
10553
+ {
10554
+ id: "flat",
10555
+ severity: "warning",
10556
+ materialIndex: null,
10557
+ message: "Model is flat (thickness is under 5% of its width); a flat model may disappear from some angles."
10558
+ }
10559
+ ];
10560
+ }
10561
+ function feetChecks(analysis) {
10562
+ const height = analysis.bounds.max[1] - analysis.bounds.min[1];
10563
+ const offset = Math.abs(analysis.bounds.min[1]);
10564
+ if (offset <= CHECK_THRESHOLDS.feetShare * height)
10565
+ return [];
10566
+ return [
10567
+ {
10568
+ id: "not-on-feet",
10569
+ severity: "warning",
10570
+ materialIndex: null,
10571
+ message: `Model's base is not at the origin. Use --origin feet to move the base to the origin.`
10572
+ }
10573
+ ];
10574
+ }
10575
+ function hasChannels(material) {
10576
+ return material.metal !== null && material.roughness !== null;
10577
+ }
10578
+ function texturedGlossy(material) {
10579
+ if (!hasChannels(material))
10580
+ return null;
10581
+ if (material.metal.fractionAtLeastHalf >= NON_METAL_FRACTION_MAX)
10582
+ return null;
10583
+ const value = material.roughness.mean * material.roughnessFactor;
10584
+ return value < CHECK_THRESHOLDS.glossyRoughnessMean ? { value, label: "mean" } : null;
10585
+ }
10586
+ function untexturedGlossy(material) {
10587
+ if (material.metallicFactor >= NON_METAL_FACTOR_MAX)
10588
+ return null;
10589
+ return material.roughnessFactor < CHECK_THRESHOLDS.glossyRoughnessMean ? { value: material.roughnessFactor, label: "factor" } : null;
10590
+ }
10591
+ function glossyValue(material) {
10592
+ return material.hasMetallicRoughnessTexture ? texturedGlossy(material) : untexturedGlossy(material);
10593
+ }
10594
+ function glossyFor(material) {
10595
+ const glossy = glossyValue(material);
10596
+ if (glossy === null)
10597
+ return null;
10598
+ return {
10599
+ id: "glossy-non-metal",
10600
+ severity: "warning",
10601
+ materialIndex: material.index,
10602
+ message: `Material ${material.index} is non-metal with low roughness (${glossy.label} ${formatMean(glossy.value)}); it may look glossy or metallic under light.`
10603
+ };
10604
+ }
10605
+ function computeChecks(analysis) {
10606
+ const checks = [...flatChecks(analysis), ...feetChecks(analysis)];
10607
+ for (const material of analysis.materials) {
10608
+ const metal = metalWholeTextureFor(material);
10609
+ if (metal !== null)
10610
+ checks.push(metal);
10611
+ const glossy = glossyFor(material);
10612
+ if (glossy !== null)
10613
+ checks.push(glossy);
10614
+ }
10615
+ return checks;
10616
+ }
10617
+ var CHECK_THRESHOLDS, NON_METAL_FACTOR_MAX = 0.5;
10618
+ var init_checks = __esm(() => {
10619
+ init_material_normalise();
10620
+ CHECK_THRESHOLDS = {
10621
+ wholeTextureMetalFraction: 0.5,
10622
+ flatRatio: 0.05,
10623
+ feetShare: 0.01,
10624
+ glossyRoughnessMean: 0.35
10625
+ };
10626
+ });
10627
+
9873
10628
  // src/commands/generate-3d/package-name.ts
9874
10629
  function sanitizeNamePart(value) {
9875
10630
  return value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[^A-Za-z0-9]+/, "");
@@ -9910,10 +10665,59 @@ function formatCount(count) {
9910
10665
  function captionRow(label, value) {
9911
10666
  return ` <dt>${label}</dt><dd>${value}</dd>`;
9912
10667
  }
10668
+ function variantButton(variant, pressed, label) {
10669
+ return ` <button type="button" class="variant" data-variant="${variant}" aria-pressed="${pressed}">${label}</button>`;
10670
+ }
10671
+ function lightingButton(mode, pressed) {
10672
+ const label = mode === "studio" ? "Studio" : mode === "directional" ? "Directional" : "Unlit";
10673
+ return ` <button type="button" class="lighting-option" data-lighting="${mode}" aria-pressed="${pressed}">${label}</button>`;
10674
+ }
10675
+ function checksPanel(checks, note) {
10676
+ if (note !== undefined && note !== null) {
10677
+ return ` <p class="ok">${escapeHtml(note)}</p>`;
10678
+ }
10679
+ if (checks.length === 0) {
10680
+ return ` <p class="ok">No issues found.</p>`;
10681
+ }
10682
+ const items = checks.map((check) => ` <li data-check="${escapeHtml(check.id)}">${escapeHtml(check.message)}</li>`).join(`
10683
+ `);
10684
+ return ` <ul>
10685
+ ${items}
10686
+ </ul>`;
10687
+ }
10688
+ function glbDataUri(glb) {
10689
+ return `data:model/gltf-binary;base64,${Buffer.from(glb).toString("base64")}`;
10690
+ }
10691
+ function isSunTexel(x2, y) {
10692
+ const dx = x2 - Math.round(ENVIRONMENT_WIDTH * 0.75);
10693
+ const dy = y - Math.round(ENVIRONMENT_HEIGHT * 0.7);
10694
+ return dx * dx + dy * dy <= SUN_RADIUS_PX * SUN_RADIUS_PX;
10695
+ }
10696
+ async function directionalEnvironmentPng() {
10697
+ const { default: sharp } = await import("sharp");
10698
+ const pixels = new Uint8Array(ENVIRONMENT_WIDTH * ENVIRONMENT_HEIGHT * 4);
10699
+ for (let index = 0;index < pixels.length; index += 4) {
10700
+ pixels[index + 3] = 255;
10701
+ const texel = index / 4;
10702
+ const x2 = texel % ENVIRONMENT_WIDTH;
10703
+ const y = Math.floor(texel / ENVIRONMENT_WIDTH);
10704
+ if (!isSunTexel(x2, y))
10705
+ continue;
10706
+ pixels[index] = 255;
10707
+ pixels[index + 1] = 255;
10708
+ pixels[index + 2] = 255;
10709
+ }
10710
+ const png = await sharp(Buffer.from(pixels), {
10711
+ raw: { width: ENVIRONMENT_WIDTH, height: ENVIRONMENT_HEIGHT, channels: 4 }
10712
+ }).png().toBuffer();
10713
+ return new Uint8Array(png);
10714
+ }
9913
10715
  function renderPreviewHtml(input) {
9914
10716
  const title = escapeHtml(input.title);
9915
10717
  const prompt = input.prompt === null ? "Not specified." : escapeHtml(input.prompt);
9916
- const dataUri = `data:model/gltf-binary;base64,${Buffer.from(input.webGlb).toString("base64")}`;
10718
+ const masterDataUri = glbDataUri(input.masterGlb);
10719
+ const webDataUri = glbDataUri(input.webGlb);
10720
+ const directionalEnvironment = `data:image/png;base64,${Buffer.from(input.directionalEnvironmentPng).toString("base64")}`;
9917
10721
  const rows = [
9918
10722
  captionRow("Model id", escapeHtml(input.modelId)),
9919
10723
  captionRow("Prompt", prompt),
@@ -9924,6 +10728,20 @@ function renderPreviewHtml(input) {
9924
10728
  captionRow("Master file", `${escapeHtml(input.masterZipEntry)} (in ${escapeHtml(input.zipName)})`)
9925
10729
  ].join(`
9926
10730
  `);
10731
+ const masterLabel = `Full resolution (master, ${formatBytes(input.masterBytes)}, ${formatCount(input.masterTriangles)} triangles)`;
10732
+ const webLabel = `Scaled down (web, ${formatBytes(input.webBytes)}, ${formatCount(input.webTriangles)} triangles)`;
10733
+ const variantButtons = [
10734
+ variantButton("master", false, masterLabel),
10735
+ variantButton("web", true, webLabel)
10736
+ ].join(`
10737
+ `);
10738
+ const lightingButtons = [
10739
+ lightingButton("studio", true),
10740
+ lightingButton("directional", false),
10741
+ lightingButton("unlit", false)
10742
+ ].join(`
10743
+ `);
10744
+ const checks = checksPanel(input.checks, input.checksNote);
9927
10745
  return `<!DOCTYPE html>
9928
10746
  <html lang="en">
9929
10747
  <head>
@@ -9939,15 +10757,109 @@ function renderPreviewHtml(input) {
9939
10757
  ${rows}
9940
10758
  </dl>
9941
10759
  </div>
10760
+ <section class="checks">
10761
+ <h2>Checks</h2>
10762
+ ${checks}
10763
+ </section>
10764
+ <div class="variants" role="group" aria-label="Variant">
10765
+ ${variantButtons}
10766
+ </div>
10767
+ <div class="lighting" role="group" aria-label="Lighting">
10768
+ ${lightingButtons}
10769
+ </div>
9942
10770
  <model-viewer
9943
- src="${dataUri}"
10771
+ src="${webDataUri}"
10772
+ data-master-src="${masterDataUri}"
10773
+ data-directional-environment="${directionalEnvironment}"
9944
10774
  alt="${title} preview"
9945
10775
  camera-controls
9946
10776
  auto-rotate
9947
10777
  shadow-intensity="1"
9948
10778
  exposure="1"
9949
10779
  touch-action="pan-y"></model-viewer>
9950
- <p class="note">Preview of the web variant. Full-resolution master is in ${escapeHtml(input.zipName)}. Drag to orbit, scroll to zoom.</p>
10780
+ <p class="note">Switch between the full-resolution master and the scaled-down web variant, and between Studio, Directional, and Unlit lighting. Drag to orbit, scroll to zoom.</p>
10781
+ <script>
10782
+ const viewer = document.querySelector('model-viewer');
10783
+ const variants = {
10784
+ master: viewer.getAttribute('data-master-src'),
10785
+ web: viewer.getAttribute('src'),
10786
+ };
10787
+ const directionalEnvironment = viewer.getAttribute('data-directional-environment');
10788
+ let lighting = 'studio';
10789
+
10790
+ const unlitState = new WeakMap();
10791
+
10792
+ function materialsOf() {
10793
+ const model = viewer.model;
10794
+ return model && model.materials ? model.materials : [];
10795
+ }
10796
+
10797
+ function setUnlit(enabled) {
10798
+ for (const material of materialsOf()) {
10799
+ const pbr = material.pbrMetallicRoughness;
10800
+ if (!pbr || !material.emissiveTexture) continue;
10801
+ const saved = unlitState.get(material);
10802
+ if (enabled) {
10803
+ const state = saved || {
10804
+ baseColorFactor: [...pbr.baseColorFactor],
10805
+ metallicFactor: pbr.metallicFactor,
10806
+ roughnessFactor: pbr.roughnessFactor,
10807
+ emissiveFactor: [...material.emissiveFactor],
10808
+ emissiveStrength: material.emissiveStrength,
10809
+ emissiveTexture: material.emissiveTexture.texture,
10810
+ };
10811
+ unlitState.set(material, state);
10812
+ const base = pbr.baseColorTexture ? pbr.baseColorTexture.texture : null;
10813
+ pbr.setBaseColorFactor([0, 0, 0, 1]);
10814
+ pbr.setMetallicFactor(0);
10815
+ pbr.setRoughnessFactor(1);
10816
+ material.setEmissiveStrength(1);
10817
+ material.emissiveTexture.setTexture(base);
10818
+ material.setEmissiveFactor(state.baseColorFactor.slice(0, 3));
10819
+ } else if (saved) {
10820
+ pbr.setBaseColorFactor(saved.baseColorFactor);
10821
+ pbr.setMetallicFactor(saved.metallicFactor);
10822
+ pbr.setRoughnessFactor(saved.roughnessFactor);
10823
+ material.setEmissiveFactor(saved.emissiveFactor);
10824
+ material.setEmissiveStrength(saved.emissiveStrength);
10825
+ material.emissiveTexture.setTexture(saved.emissiveTexture);
10826
+ unlitState.delete(material);
10827
+ }
10828
+ }
10829
+ }
10830
+
10831
+ function applyLighting() {
10832
+ if (lighting === 'directional') {
10833
+ viewer.setAttribute('environment-image', directionalEnvironment);
10834
+ viewer.setAttribute('exposure', '20');
10835
+ } else {
10836
+ viewer.removeAttribute('environment-image');
10837
+ viewer.setAttribute('exposure', '1');
10838
+ }
10839
+ setUnlit(lighting === 'unlit');
10840
+ }
10841
+
10842
+ for (const button of document.querySelectorAll('button[data-variant]')) {
10843
+ button.addEventListener('click', () => {
10844
+ const name = button.getAttribute('data-variant');
10845
+ viewer.setAttribute('src', variants[name]);
10846
+ for (const other of document.querySelectorAll('button[data-variant]')) {
10847
+ other.setAttribute('aria-pressed', String(other === button));
10848
+ }
10849
+ });
10850
+ }
10851
+ for (const button of document.querySelectorAll('button[data-lighting]')) {
10852
+ button.addEventListener('click', () => {
10853
+ lighting = button.getAttribute('data-lighting');
10854
+ for (const other of document.querySelectorAll('button[data-lighting]')) {
10855
+ other.setAttribute('aria-pressed', String(other === button));
10856
+ }
10857
+ applyLighting();
10858
+ });
10859
+ }
10860
+ viewer.addEventListener('load', applyLighting);
10861
+ applyLighting();
10862
+ </script>
9951
10863
  <script
9952
10864
  type="module"
9953
10865
  src="${MODEL_VIEWER_URL}"
@@ -9957,18 +10869,31 @@ ${rows}
9957
10869
  </html>
9958
10870
  `;
9959
10871
  }
9960
- var MODEL_VIEWER_URL = "https://cdn.jsdelivr.net/npm/@google/model-viewer@3.5.0/dist/model-viewer.min.js", MODEL_VIEWER_SRI = "sha384-Ftcjj/GNLxPvzNDftO/oryXB9aGxsGZY9JGqsXG0uUKgQDl9RfDgsx9NJ/4IVNPe", PANEL_STYLE = `
10872
+ var MODEL_VIEWER_URL = "https://cdn.jsdelivr.net/npm/@google/model-viewer@3.5.0/dist/model-viewer.min.js", MODEL_VIEWER_SRI = "sha384-Ftcjj/GNLxPvzNDftO/oryXB9aGxsGZY9JGqsXG0uUKgQDl9RfDgsx9NJ/4IVNPe", ENVIRONMENT_WIDTH = 512, ENVIRONMENT_HEIGHT = 256, SUN_RADIUS_PX = 10, PANEL_STYLE = `
9961
10873
  :root { color-scheme: dark; }
9962
10874
  body { margin: 0; padding: 24px; background: #111318; color: #e6e8ec;
9963
10875
  font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif; }
9964
10876
  h1 { margin: 0 0 16px; font-size: 18px; font-weight: 600; }
9965
- .caption { margin: 0 0 16px; padding: 12px 16px; border: 1px solid #2a2f3a;
10877
+ .caption, .checks { margin: 0 0 16px; padding: 12px 16px; border: 1px solid #2a2f3a;
9966
10878
  border-radius: 8px; background: #171a21; }
9967
10879
  .caption dl { display: grid; grid-template-columns: max-content 1fr;
9968
10880
  gap: 4px 16px; margin: 0; }
9969
10881
  .caption dt { color: #9aa3b2; }
9970
10882
  .caption dd { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
9971
10883
  font-size: 12px; word-break: break-word; }
10884
+ .checks h2 { margin: 0 0 8px; font-size: 14px; font-weight: 600; }
10885
+ .checks ul { margin: 0; padding-left: 20px; }
10886
+ .checks li { color: #f0c674; }
10887
+ .checks .ok { margin: 0; color: #9aa3b2; }
10888
+ .variants, .lighting { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
10889
+ .variants { margin-bottom: 8px; }
10890
+ .variant, .lighting-option { padding: 8px 12px; border: 1px solid #2a2f3a;
10891
+ border-radius: 8px; background: #171a21; color: #e6e8ec;
10892
+ font: inherit; cursor: pointer; }
10893
+ .variant[aria-pressed="true"], .lighting-option[aria-pressed="true"] {
10894
+ border-color: #6b8afd; background: #1d2436; }
10895
+ .variant:focus-visible, .lighting-option:focus-visible {
10896
+ outline: 2px solid #6b8afd; outline-offset: 2px; }
9972
10897
  model-viewer { width: 100%; height: 70vh; min-height: 420px;
9973
10898
  background: #0b0d11; border: 1px solid #2a2f3a; border-radius: 8px; }
9974
10899
  .note { color: #9aa3b2; font-size: 12px; }
@@ -10095,6 +11020,8 @@ __export(exports_generate_3d, {
10095
11020
  });
10096
11021
  import { createHash as createHash3 } from "node:crypto";
10097
11022
  import { basename, dirname as dirname4, extname as extname2, join as join2, resolve as resolve2 } from "node:path";
11023
+ import { NodeIO as NodeIO2 } from "@gltf-transform/core";
11024
+ import { ALL_EXTENSIONS as ALL_EXTENSIONS2 } from "@gltf-transform/extensions";
10098
11025
  function outputStem(outPath) {
10099
11026
  return outPath.slice(0, -GLB_EXTENSION.length);
10100
11027
  }
@@ -10329,13 +11256,21 @@ function inputParts(ctx, capabilities) {
10329
11256
  function rigParts(ctx) {
10330
11257
  return passedFlags(ctx).filter((entry) => !INPUT_FLAG_NAMES.includes(entry.flag)).filter((entry) => isAllowed(entry.flag, ctx.model)).map(formatForCommand);
10331
11258
  }
11259
+ function fitParts(fit) {
11260
+ if (fit === undefined)
11261
+ return [];
11262
+ return [
11263
+ ...fit.targetHeight === undefined ? [] : [`--target-height ${fit.targetHeight}`],
11264
+ ...fit.origin === "as-is" ? [] : [`--origin ${fit.origin}`]
11265
+ ];
11266
+ }
10332
11267
  function outputParts(ctx) {
10333
- const parts = [`--out ${shellQuote(ctx.out)}`];
10334
- if (ctx.force === true)
10335
- parts.push("--force");
10336
- if (ctx.packageDir !== undefined)
10337
- parts.push(`--package-dir ${shellQuote(ctx.packageDir)}`);
10338
- return parts;
11268
+ return [
11269
+ `--out ${shellQuote(ctx.out)}`,
11270
+ ...ctx.force === true ? ["--force"] : [],
11271
+ ...ctx.packageDir === undefined ? [] : [`--package-dir ${shellQuote(ctx.packageDir)}`],
11272
+ ...fitParts(ctx.fit)
11273
+ ];
10339
11274
  }
10340
11275
  function repeatParts(ctx) {
10341
11276
  const dryRun = ctx.dryRun === true ? ["--dry-run"] : [];
@@ -10538,6 +11473,7 @@ function resolvePlanTarget(opts) {
10538
11473
  out: opts.out,
10539
11474
  force: opts.force,
10540
11475
  packageDir: opts.packageDir,
11476
+ fit: opts.fit,
10541
11477
  dryRun: opts.dryRun
10542
11478
  };
10543
11479
  validateRigging(flagContext);
@@ -10588,6 +11524,7 @@ async function planGenerate3d(opts, deps) {
10588
11524
  inputKeys: Object.keys(opts.extraInput ?? {}),
10589
11525
  modelWarning: modelWarningFor(model),
10590
11526
  confirmCost: opts.confirmCost === true,
11527
+ fit: opts.fit,
10591
11528
  ...naming
10592
11529
  };
10593
11530
  }
@@ -10649,6 +11586,12 @@ function inputNoteLines(plan) {
10649
11586
  function confirmationLines(plan) {
10650
11587
  return plan.riggingRequest !== null && !plan.confirmCost ? ["Add --confirm-cost to run."] : [];
10651
11588
  }
11589
+ function fitLines(plan) {
11590
+ if (plan.fit === undefined)
11591
+ return [];
11592
+ const height = plan.fit.targetHeight === undefined ? "no scale" : `target height ${plan.fit.targetHeight} m`;
11593
+ return [`Fit: ${height}, origin ${plan.fit.origin} (master, web, and rig outputs)`];
11594
+ }
10652
11595
  function formatGenerate3dPlan(plan) {
10653
11596
  return [
10654
11597
  `Would generate ${plan.outPath}`,
@@ -10657,6 +11600,7 @@ function formatGenerate3dPlan(plan) {
10657
11600
  `Preview ${plan.htmlPath}`,
10658
11601
  ...planModelLines(plan),
10659
11602
  ...modelWarningLines(plan),
11603
+ ...fitLines(plan),
10660
11604
  ...formatReferencePlan(plan),
10661
11605
  "Request:",
10662
11606
  JSON.stringify(plan.input, null, 2),
@@ -10702,6 +11646,7 @@ function buildManifest2(args) {
10702
11646
  output: {
10703
11647
  path: args.outPath,
10704
11648
  sha256: args.sha256,
11649
+ sourceSha256: args.sourceSha256,
10705
11650
  bytes: args.byteLength,
10706
11651
  contentType: GLB_CONTENT_TYPE
10707
11652
  },
@@ -10725,21 +11670,29 @@ function riggingFailure(args) {
10725
11670
  const request = args.requestId === null ? "" : ` for request ${args.requestId}`;
10726
11671
  return new Generate3dError(`Rigging failed${request} (${args.endpoint}): ${args.detail}. The generated mesh and manifest are kept: ${args.meshPath}.`);
10727
11672
  }
11673
+ async function prepareRigGlb(args) {
11674
+ return normaliseMasterGlb(args.downloaded, args.fit, args.sharedFit);
11675
+ }
10728
11676
  async function downloadAndSaveGlb(args) {
10729
- let bytes;
11677
+ let downloaded;
10730
11678
  try {
10731
- bytes = await args.deps.fetchGlb(args.url);
11679
+ downloaded = await args.deps.fetchGlb(args.url);
10732
11680
  } catch (err2) {
10733
11681
  throw args.fail(`Failed to download ${args.label} (${args.url}): ${describeError(err2)}`);
10734
11682
  }
11683
+ const prepared = await prepareRigGlb({
11684
+ downloaded,
11685
+ fit: args.fit,
11686
+ sharedFit: args.sharedFit
11687
+ });
10735
11688
  try {
10736
- await args.deps.writeFile(args.path, bytes);
11689
+ await args.deps.writeFile(args.path, prepared.bytes);
10737
11690
  } catch (err2) {
10738
11691
  throw args.fail(`Failed to save ${args.label} ${args.path}: ${describeError(err2)}`);
10739
11692
  }
10740
- return bytes;
11693
+ return prepared;
10741
11694
  }
10742
- async function downloadAnimations(plan, deps, output, fail) {
11695
+ async function downloadAnimations(plan, deps, output, fail, sharedFit) {
10743
11696
  const animations = [];
10744
11697
  const files = [];
10745
11698
  for (const [index, animation] of output.animations.entries()) {
@@ -10747,14 +11700,23 @@ async function downloadAnimations(plan, deps, output, fail) {
10747
11700
  if (planned === undefined || planned.actionId !== animation.actionId) {
10748
11701
  throw fail(`No planned output path for animation clip ${animation.actionId}.`);
10749
11702
  }
10750
- const bytes = await downloadAndSaveGlb({
11703
+ const prepared = await downloadAndSaveGlb({
10751
11704
  deps,
10752
11705
  url: animation.glbUrl,
10753
11706
  path: planned.path,
10754
11707
  label: `animation clip ${animation.actionId}`,
10755
- fail
11708
+ fail,
11709
+ fit: undefined,
11710
+ sharedFit
11711
+ });
11712
+ files.push({
11713
+ actionId: animation.actionId,
11714
+ path: planned.path,
11715
+ bytes: prepared.bytes,
11716
+ materials: prepared.materials,
11717
+ fit: prepared.fit,
11718
+ ...definedErrors(prepared.normaliseError, prepared.fitError)
10756
11719
  });
10757
- files.push({ actionId: animation.actionId, path: planned.path, bytes });
10758
11720
  animations.push({
10759
11721
  actionId: animation.actionId,
10760
11722
  path: planned.path,
@@ -10791,14 +11753,15 @@ async function runRigging(plan, deps, modelGlbUrl) {
10791
11753
  } catch (err2) {
10792
11754
  throw fail(describeError(err2));
10793
11755
  }
10794
- const riggedBytes = await downloadAndSaveGlb({
11756
+ const rigged = await downloadAndSaveGlb({
10795
11757
  deps,
10796
11758
  url: output.riggedGlbUrl,
10797
11759
  path: riggedGlbPath,
10798
11760
  label: "rigged GLB",
10799
- fail
11761
+ fail,
11762
+ fit: plan.fit
10800
11763
  });
10801
- const animations = await downloadAnimations(plan, deps, output, fail);
11764
+ const animations = await downloadAnimations(plan, deps, output, fail, rigged.fit);
10802
11765
  return {
10803
11766
  section: {
10804
11767
  endpoint: request.endpoint,
@@ -10812,7 +11775,10 @@ async function runRigging(plan, deps, modelGlbUrl) {
10812
11775
  files: [],
10813
11776
  costLines: riggingCostLines(plan.animationActionIds.length)
10814
11777
  },
10815
- riggedBytes,
11778
+ riggedBytes: rigged.bytes,
11779
+ riggedMaterials: rigged.materials,
11780
+ riggedFit: rigged.fit,
11781
+ ...riggedErrorFields(rigged),
10816
11782
  animations: animations.bytes
10817
11783
  };
10818
11784
  }
@@ -10828,7 +11794,10 @@ function rigEntryNames(stem, kind, actionId) {
10828
11794
  async function buildOneRigFile(plan, deps, file) {
10829
11795
  const names = rigEntryNames(plan.stem, file.kind, file.actionId);
10830
11796
  try {
10831
- const web = await deps.createWebVariant(file.bytes, WEB_VARIANT_OPTIONS);
11797
+ const web = await deps.createWebVariant(file.bytes, webVariantOptions(plan, {
11798
+ materials: file.materials,
11799
+ fit: file.fit
11800
+ }));
10832
11801
  return { packaged: { ...file, ...names, web, webError: null }, message: null };
10833
11802
  } catch (err2) {
10834
11803
  const message = describeError(err2);
@@ -10844,13 +11813,19 @@ function rigFileInputs(plan, rig) {
10844
11813
  kind: "rigged",
10845
11814
  actionId: null,
10846
11815
  path: plan.riggedOutPath ?? "",
10847
- bytes: rig.riggedBytes
11816
+ bytes: rig.riggedBytes,
11817
+ materials: rig.riggedMaterials,
11818
+ fit: rig.riggedFit,
11819
+ ...definedErrors(rig.riggedNormaliseError, rig.riggedFitError)
10848
11820
  },
10849
11821
  ...rig.animations.map((animation) => ({
10850
11822
  kind: "animation",
10851
11823
  actionId: animation.actionId,
10852
11824
  path: animation.path,
10853
- bytes: animation.bytes
11825
+ bytes: animation.bytes,
11826
+ materials: animation.materials,
11827
+ fit: animation.fit,
11828
+ ...definedErrors(animation.normaliseError, animation.fitError)
10854
11829
  }))
10855
11830
  ];
10856
11831
  }
@@ -10878,6 +11853,9 @@ function rigFileManifestEntry(file) {
10878
11853
  actionId: file.actionId,
10879
11854
  path: file.path,
10880
11855
  bytes: file.bytes.byteLength,
11856
+ materials: file.materials,
11857
+ fit: file.fit,
11858
+ ...definedErrors(file.normaliseError, file.fitError),
10881
11859
  webError: file.webError
10882
11860
  };
10883
11861
  if (file.web === null) {
@@ -10929,6 +11907,12 @@ function buildMasterVariant(args) {
10929
11907
  file: `${args.stem}.glb`,
10930
11908
  bytes: args.masterBytes.byteLength,
10931
11909
  sha256: args.masterSha256,
11910
+ sourceSha256: args.sourceSha256,
11911
+ materials: args.materials,
11912
+ fit: args.fit,
11913
+ ...args.normaliseError === undefined ? {} : { normaliseError: args.normaliseError },
11914
+ ...args.fitError === undefined ? {} : { fitError: args.fitError },
11915
+ checks: args.checks,
10932
11916
  triangles: args.source.triangles,
10933
11917
  textures: args.source.textures,
10934
11918
  extensionsUsed: args.source.extensionsUsed
@@ -10937,7 +11921,7 @@ function buildMasterVariant(args) {
10937
11921
  function rigWebFailures(files) {
10938
11922
  return files.filter((file) => file.webError !== null).map((file) => ({ file: file.entry, message: file.webError ?? "" }));
10939
11923
  }
10940
- function webSettings(web) {
11924
+ function webSettings(web, checks) {
10941
11925
  return {
10942
11926
  maxTexturePx: WEB_VARIANT_MAX_TEXTURE_PX,
10943
11927
  textureFormat: "jpeg",
@@ -10945,7 +11929,10 @@ function webSettings(web) {
10945
11929
  dataQuality: WEB_VARIANT_DATA_TEXTURE_QUALITY,
10946
11930
  triangleTarget: WEB_VARIANT_TRIANGLE_TARGET,
10947
11931
  simplified: web.simplified,
10948
- skinnedPath: web.skinnedPath
11932
+ skinnedPath: web.skinnedPath,
11933
+ materials: web.materials,
11934
+ fit: web.fit,
11935
+ ...checks === undefined ? {} : { checks }
10949
11936
  };
10950
11937
  }
10951
11938
  function variantEntry(args) {
@@ -10962,14 +11949,14 @@ function variantEntry(args) {
10962
11949
  ...args.settings === undefined ? {} : { settings: args.settings }
10963
11950
  };
10964
11951
  }
10965
- function buildWebVariantEntry(stem, web, webSha256) {
11952
+ function buildWebVariantEntry(stem, web, webSha256, checks) {
10966
11953
  return {
10967
11954
  ...variantEntry({
10968
11955
  name: "web",
10969
11956
  file: `web/${stem}.web.glb`,
10970
11957
  bytes: web.bytes,
10971
11958
  stats: web,
10972
- settings: webSettings(web)
11959
+ settings: webSettings(web, checks)
10973
11960
  }),
10974
11961
  sha256: webSha256
10975
11962
  };
@@ -10997,9 +11984,14 @@ function buildPackageManifest(args) {
10997
11984
  stem: args.stem,
10998
11985
  masterBytes: args.masterBytes,
10999
11986
  masterSha256: args.masterSha256,
11987
+ sourceSha256: args.masterSourceSha256,
11988
+ materials: args.masterMaterials,
11989
+ fit: args.masterFit,
11990
+ ...args.masterErrors,
11991
+ checks: args.masterChecks,
11000
11992
  source: args.web.source
11001
11993
  }),
11002
- buildWebVariantEntry(args.stem, args.web, args.webSha256),
11994
+ buildWebVariantEntry(args.stem, args.web, args.webSha256, args.webChecks),
11003
11995
  ...args.rigVariants
11004
11996
  ],
11005
11997
  referenceImage: args.reference,
@@ -11027,9 +12019,16 @@ function buildPackageEntries(args) {
11027
12019
  function packageFailure(plan, step, err2) {
11028
12020
  return new Generate3dError(`Failed to ${step}: ${describeError(err2)}. The master mesh and manifest are kept: ${plan.outPath}.`);
11029
12021
  }
11030
- async function buildWebVariant(plan, deps, masterBytes) {
12022
+ function webVariantOptions(plan, processed) {
12023
+ return {
12024
+ ...WEB_VARIANT_OPTIONS,
12025
+ ...plan.fit === undefined ? {} : { fit: plan.fit },
12026
+ processed
12027
+ };
12028
+ }
12029
+ async function buildWebVariant(plan, deps, masterBytes, processed) {
11031
12030
  try {
11032
- return await deps.createWebVariant(masterBytes, WEB_VARIANT_OPTIONS);
12031
+ return await deps.createWebVariant(masterBytes, webVariantOptions(plan, processed));
11033
12032
  } catch (err2) {
11034
12033
  throw packageFailure(plan, "build the web variant", err2);
11035
12034
  }
@@ -11077,8 +12076,13 @@ async function writeZipAndPreview(plan, deps, files) {
11077
12076
  }
11078
12077
  async function writePackage(args) {
11079
12078
  const { plan, deps, master, paths, reference } = args;
11080
- const web = await buildWebVariant(plan, deps, master.bytes);
12079
+ const web = await buildWebVariant(plan, deps, master.bytes, {
12080
+ materials: master.materials,
12081
+ fit: master.fit
12082
+ });
11081
12083
  const webSha256 = createHash3("sha256").update(web.bytes).digest("hex");
12084
+ const webChecks = await checksForGlb(web.bytes);
12085
+ const directionalEnvironment = await directionalEnvironmentPng();
11082
12086
  const packageManifest = buildPackageManifest({
11083
12087
  packageName: args.packageName,
11084
12088
  createdAt: deps.now().toISOString(),
@@ -11088,6 +12092,12 @@ async function writePackage(args) {
11088
12092
  inputImage: master.inputImage,
11089
12093
  masterBytes: master.bytes,
11090
12094
  masterSha256: master.sha256,
12095
+ masterSourceSha256: master.sourceSha256,
12096
+ masterMaterials: master.materials,
12097
+ masterFit: master.fit,
12098
+ masterErrors: definedErrors(master.normaliseError, master.fitError),
12099
+ masterChecks: args.masterChecks,
12100
+ webChecks,
11091
12101
  web,
11092
12102
  webSha256,
11093
12103
  stem: plan.stem,
@@ -11108,6 +12118,7 @@ async function writePackage(args) {
11108
12118
  modelId: plan.model.id,
11109
12119
  prompt: args.prompt?.trim() ?? null,
11110
12120
  requestId: master.requestId,
12121
+ masterGlb: master.bytes,
11111
12122
  webGlb: web.bytes,
11112
12123
  webSha256,
11113
12124
  webTriangles: web.triangles,
@@ -11115,7 +12126,9 @@ async function writePackage(args) {
11115
12126
  masterBytes: master.bytes.byteLength,
11116
12127
  webBytes: web.bytes.byteLength,
11117
12128
  masterZipEntry: `${plan.stem}.glb`,
11118
- zipName: `${args.packageName}.zip`
12129
+ zipName: `${args.packageName}.zip`,
12130
+ checks: args.masterChecks,
12131
+ directionalEnvironmentPng: directionalEnvironment
11119
12132
  });
11120
12133
  await writeZipAndPreview(plan, deps, { ...paths, zipBytes, html, force: args.force });
11121
12134
  return paths;
@@ -11168,6 +12181,112 @@ async function ensureOutputDirs(plan, deps) {
11168
12181
  await deps.ensureParentDir(plan.outPath);
11169
12182
  await deps.ensureParentDir(plan.zipPath);
11170
12183
  }
12184
+ function glbIo() {
12185
+ return new NodeIO2().registerExtensions(ALL_EXTENSIONS2).registerDependencies({ "meshopt.decoder": MeshoptDecoder });
12186
+ }
12187
+ async function analyseGlb(glb) {
12188
+ return analyseDocument(await glbIo().readBinary(glb));
12189
+ }
12190
+ async function checksForGlb(glb) {
12191
+ try {
12192
+ return computeChecks(await analyseGlb(glb));
12193
+ } catch {
12194
+ return [];
12195
+ }
12196
+ }
12197
+ function sourceFallback(glb, errors) {
12198
+ return { bytes: glb, materials: [], fit: null, ...errors };
12199
+ }
12200
+ function dropMeshoptExtension(document2) {
12201
+ document2.getRoot().listExtensionsUsed().find((extension) => extension.extensionName === "EXT_meshopt_compression")?.dispose();
12202
+ }
12203
+ function definedErrors(normaliseError, fitError) {
12204
+ return {
12205
+ ...normaliseError === undefined ? {} : { normaliseError },
12206
+ ...fitError === undefined ? {} : { fitError }
12207
+ };
12208
+ }
12209
+ function riggedErrorFields(prepared) {
12210
+ return {
12211
+ ...prepared.normaliseError === undefined ? {} : { riggedNormaliseError: prepared.normaliseError },
12212
+ ...prepared.fitError === undefined ? {} : { riggedFitError: prepared.fitError }
12213
+ };
12214
+ }
12215
+ async function readGlbDocument(glb) {
12216
+ try {
12217
+ return { document: await glbIo().readBinary(glb) };
12218
+ } catch (err2) {
12219
+ return { normaliseError: `Normalisation failed: ${describeError(err2)}` };
12220
+ }
12221
+ }
12222
+ function defaultFitResult(analysis) {
12223
+ return {
12224
+ origin: "as-is",
12225
+ targetHeight: null,
12226
+ scale: 1,
12227
+ translation: [0, 0, 0],
12228
+ bounds: analysis.bounds
12229
+ };
12230
+ }
12231
+ function applySharedFit(document2, sharedFit) {
12232
+ if (sharedFit !== null)
12233
+ applyFitTransform(document2, sharedFit);
12234
+ return { fit: sharedFit };
12235
+ }
12236
+ async function fitByOptions(document2, analysis, fit) {
12237
+ if (fit === undefined)
12238
+ return { fit: defaultFitResult(analysis) };
12239
+ try {
12240
+ return { fit: await applyFit(document2, analysis, fit) };
12241
+ } catch (err2) {
12242
+ return { fitError: `Fit failed: ${describeError(err2)}` };
12243
+ }
12244
+ }
12245
+ async function prepareFit(document2, analysis, fit, sharedFit) {
12246
+ if (sharedFit !== undefined)
12247
+ return applySharedFit(document2, sharedFit);
12248
+ return fitByOptions(document2, analysis, fit);
12249
+ }
12250
+ async function prepareDocument(document2, fit, sharedFit) {
12251
+ let analysis;
12252
+ let materials;
12253
+ try {
12254
+ analysis = await analyseDocument(document2);
12255
+ materials = await normaliseMaterials(document2, analysis);
12256
+ } catch (err2) {
12257
+ return { normaliseError: `Normalisation failed: ${describeError(err2)}` };
12258
+ }
12259
+ const preparedFit = await prepareFit(document2, analysis, fit, sharedFit);
12260
+ if (!("fit" in preparedFit))
12261
+ return preparedFit;
12262
+ return { materials, fit: preparedFit.fit };
12263
+ }
12264
+ async function writePreparedDocument(io, document2) {
12265
+ try {
12266
+ return await io.writeBinary(document2);
12267
+ } catch {
12268
+ return null;
12269
+ }
12270
+ }
12271
+ async function normaliseMasterGlb(glb, fit, sharedFit) {
12272
+ const read = await readGlbDocument(glb);
12273
+ if ("normaliseError" in read)
12274
+ return sourceFallback(glb, read);
12275
+ dropMeshoptExtension(read.document);
12276
+ const prepared = await prepareDocument(read.document, fit, sharedFit);
12277
+ if (!("materials" in prepared))
12278
+ return sourceFallback(glb, prepared);
12279
+ const { prune } = await import("@gltf-transform/functions");
12280
+ read.document.setLogger(SILENT_LOGGER2);
12281
+ await read.document.transform(prune({ propertyTypes: ["Texture"], keepSolidTextures: true }));
12282
+ const bytes = await writePreparedDocument(glbIo(), read.document);
12283
+ if (bytes === null) {
12284
+ return sourceFallback(glb, {
12285
+ normaliseError: "Normalisation failed: the GLB could not be written back."
12286
+ });
12287
+ }
12288
+ return { bytes, materials: prepared.materials, fit: prepared.fit };
12289
+ }
11171
12290
  async function generateMaster(plan, deps, opts, reference = null) {
11172
12291
  const inputImage = reference?.inputImage ?? await uploadInputImage2(deps, plan.inputTarget);
11173
12292
  const input = plan.model.buildInput({
@@ -11178,9 +12297,11 @@ async function generateMaster(plan, deps, opts, reference = null) {
11178
12297
  const result = await deps.falSubscribe(plan.model.id, input);
11179
12298
  const output = readModelOutput(plan.model, result.data, result.requestId);
11180
12299
  const recovery = { requestId: result.requestId, url: output.glbUrl };
11181
- const bytes = await withRecovery("download", recovery, () => deps.fetchGlb(output.glbUrl));
11182
- await withRecovery("save", recovery, () => deps.writeFile(plan.outPath, bytes));
11183
- const sha2562 = createHash3("sha256").update(bytes).digest("hex");
12300
+ const sourceBytes = await withRecovery("download", recovery, () => deps.fetchGlb(output.glbUrl));
12301
+ const normalised = await normaliseMasterGlb(sourceBytes, plan.fit);
12302
+ await withRecovery("save", recovery, () => deps.writeFile(plan.outPath, normalised.bytes));
12303
+ const sourceSha256 = createHash3("sha256").update(sourceBytes).digest("hex");
12304
+ const sha2562 = createHash3("sha256").update(normalised.bytes).digest("hex");
11184
12305
  const manifest = buildManifest2({
11185
12306
  model: plan.model,
11186
12307
  input,
@@ -11188,7 +12309,8 @@ async function generateMaster(plan, deps, opts, reference = null) {
11188
12309
  requestId: result.requestId,
11189
12310
  outPath: plan.outPath,
11190
12311
  sha256: sha2562,
11191
- byteLength: bytes.byteLength,
12312
+ sourceSha256,
12313
+ byteLength: normalised.bytes.byteLength,
11192
12314
  textureUrls: output.textureUrls,
11193
12315
  referenceImage: buildReferenceManifest(plan, reference, inputImage),
11194
12316
  generatedAt: deps.now().toISOString(),
@@ -11196,8 +12318,12 @@ async function generateMaster(plan, deps, opts, reference = null) {
11196
12318
  });
11197
12319
  await deps.writeJson(plan.manifestPath, manifest);
11198
12320
  return {
11199
- bytes,
12321
+ bytes: normalised.bytes,
11200
12322
  sha256: sha2562,
12323
+ sourceSha256,
12324
+ materials: normalised.materials,
12325
+ fit: normalised.fit,
12326
+ ...definedErrors(normalised.normaliseError, normalised.fitError),
11201
12327
  requestId: result.requestId,
11202
12328
  inputImage,
11203
12329
  glbUrl: output.glbUrl,
@@ -11249,7 +12375,8 @@ async function packageOrError(args) {
11249
12375
  prompt: args.prompt,
11250
12376
  force: args.force,
11251
12377
  reference: args.reference,
11252
- rigFiles: args.rigFiles
12378
+ rigFiles: args.rigFiles,
12379
+ masterChecks: args.masterChecks
11253
12380
  });
11254
12381
  return null;
11255
12382
  } catch (err2) {
@@ -11269,7 +12396,8 @@ async function packageRun(args) {
11269
12396
  prompt: args.opts.prompt,
11270
12397
  force: args.opts.force === true,
11271
12398
  reference: args.reference,
11272
- rigFiles: args.rigging.files
12399
+ rigFiles: args.rigging.files,
12400
+ masterChecks: args.masterChecks
11273
12401
  });
11274
12402
  if (packageError === null) {
11275
12403
  await args.deps.writeJson(args.plan.manifestPath, packagedManifest(args.master, args.rigging, args.plan));
@@ -11286,6 +12414,8 @@ async function generate3d(opts, deps) {
11286
12414
  const reference = await generateReferenceFile(plan, deps);
11287
12415
  const bundledReference = await bundleReference(plan, deps, reference);
11288
12416
  const master = await generateMaster(plan, deps, opts, reference);
12417
+ const masterChecks = await checksForGlb(master.bytes);
12418
+ const warnings = [master.normaliseError, master.fitError].filter((warning) => warning !== undefined);
11289
12419
  const rigging = await rigOrError(plan, deps, master);
11290
12420
  const built = await buildRigFilesOrEmpty(plan, deps, rigging);
11291
12421
  rigging.files = built.files;
@@ -11293,19 +12423,34 @@ async function generate3d(opts, deps) {
11293
12423
  const rigged = rigManifest(master, rigging);
11294
12424
  if (rigged !== null)
11295
12425
  await deps.writeJson(plan.manifestPath, rigged);
11296
- await packageRun({ plan, deps, master, opts, reference: bundledReference, rigging });
12426
+ await packageRun({
12427
+ plan,
12428
+ deps,
12429
+ master,
12430
+ opts,
12431
+ reference: bundledReference,
12432
+ rigging,
12433
+ masterChecks
12434
+ });
11297
12435
  return {
11298
12436
  outPath: plan.outPath,
11299
12437
  manifestPath: plan.manifestPath,
11300
12438
  zipPath: plan.zipPath,
11301
12439
  htmlPath: plan.htmlPath,
11302
12440
  sha256: master.sha256,
12441
+ checks: masterChecks,
12442
+ warnings,
11303
12443
  costLines: formatGenerate3dCost(plan.costLines, plan.model.id, plan.generationCostKnown)
11304
12444
  };
11305
12445
  }
11306
- var GLB_CONTENT_TYPE = "model/gltf-binary", GLB_EXTENSION = ".glb", PLANNED_MODEL_URL = "<generated mesh GLB URL>", RIGGING_HUMANOID_WARNING = "Rigging (Meshy) expects a humanoid mesh; non-humanoid meshes fail on fal.", WEB_VARIANT_TRIANGLE_TARGET = 30000, WEB_VARIANT_MAX_TEXTURE_PX = 1024, WEB_VARIANT_TEXTURE_QUALITY = 80, WEB_VARIANT_DATA_TEXTURE_QUALITY = 95, PACKAGE_MANIFEST_NAME = "manifest.json", PACKAGE_PRICING_NOTE = "list-price estimate, not a billed amount", HTTP_URL_PATTERN2, INPUT_IMAGE_CONTENT_TYPES2, INPUT_REQUIREMENTS, PACKAGE_COLLISION_HINT = "Another run used this name in the same second; wait a second, use a different --package-dir, or pass --force.", FLAG_CAPABILITY, INPUT_FLAG_NAMES, INPUT_PLACEHOLDERS, INPUT_CAPABILITY, RESERVED_INPUT_FLAGS, GENERATED_REFERENCE_URL = "<generated reference image URL>", WEB_VARIANT_OPTIONS;
12446
+ var GLB_CONTENT_TYPE = "model/gltf-binary", GLB_EXTENSION = ".glb", PLANNED_MODEL_URL = "<generated mesh GLB URL>", RIGGING_HUMANOID_WARNING = "Rigging (Meshy) expects a humanoid mesh; non-humanoid meshes fail on fal.", WEB_VARIANT_TRIANGLE_TARGET = 30000, WEB_VARIANT_MAX_TEXTURE_PX = 1024, WEB_VARIANT_TEXTURE_QUALITY = 80, WEB_VARIANT_DATA_TEXTURE_QUALITY = 95, PACKAGE_MANIFEST_NAME = "manifest.json", PACKAGE_PRICING_NOTE = "list-price estimate, not a billed amount", HTTP_URL_PATTERN2, INPUT_IMAGE_CONTENT_TYPES2, INPUT_REQUIREMENTS, PACKAGE_COLLISION_HINT = "Another run used this name in the same second; wait a second, use a different --package-dir, or pass --force.", FLAG_CAPABILITY, INPUT_FLAG_NAMES, INPUT_PLACEHOLDERS, INPUT_CAPABILITY, RESERVED_INPUT_FLAGS, GENERATED_REFERENCE_URL = "<generated reference image URL>", WEB_VARIANT_OPTIONS, SILENT_LOGGER2;
11307
12447
  var init_generate_3d = __esm(() => {
12448
+ init_meshoptimizer();
12449
+ init_asset_analysis();
12450
+ init_checks();
11308
12451
  init_deps2();
12452
+ init_fit();
12453
+ init_material_normalise();
11309
12454
  init_model_registry();
11310
12455
  init_package_name();
11311
12456
  init_rigging();
@@ -11345,6 +12490,192 @@ var init_generate_3d = __esm(() => {
11345
12490
  quality: WEB_VARIANT_TEXTURE_QUALITY,
11346
12491
  dataQuality: WEB_VARIANT_DATA_TEXTURE_QUALITY
11347
12492
  };
12493
+ SILENT_LOGGER2 = {
12494
+ debug: () => {},
12495
+ info: () => {},
12496
+ warn: () => {},
12497
+ error: () => {}
12498
+ };
12499
+ });
12500
+
12501
+ // src/commands/generate-3d/rebuild-preview.ts
12502
+ var exports_rebuild_preview = {};
12503
+ __export(exports_rebuild_preview, {
12504
+ rebuildPreviewFromZip: () => rebuildPreviewFromZip
12505
+ });
12506
+ import { readFile as readFile3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
12507
+ import { basename as basename2, dirname as dirname5, join as join3 } from "node:path";
12508
+ import { NodeIO as NodeIO3 } from "@gltf-transform/core";
12509
+ import { ALL_EXTENSIONS as ALL_EXTENSIONS3 } from "@gltf-transform/extensions";
12510
+ function isRecord5(value) {
12511
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12512
+ }
12513
+ function invalidPackage(zipPath, detail) {
12514
+ return new Error(`Cannot rebuild the preview from ${zipPath}: ${detail}`);
12515
+ }
12516
+ function requireString(record, field, zipPath) {
12517
+ const value = record[field];
12518
+ if (typeof value !== "string" || value.length === 0) {
12519
+ throw invalidPackage(zipPath, `manifest.json has no ${field}.`);
12520
+ }
12521
+ return value;
12522
+ }
12523
+ function requireCount(record, field, zipPath) {
12524
+ const value = record[field];
12525
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
12526
+ throw invalidPackage(zipPath, `manifest.json has no valid ${field}.`);
12527
+ }
12528
+ return value;
12529
+ }
12530
+ function requireRecord(record, field, zipPath) {
12531
+ const value = record[field];
12532
+ if (!isRecord5(value)) {
12533
+ throw invalidPackage(zipPath, `manifest.json has no ${field} object.`);
12534
+ }
12535
+ return value;
12536
+ }
12537
+ function readPrompt(generator, zipPath) {
12538
+ const prompt = generator.prompt;
12539
+ if (prompt === null || typeof prompt === "string")
12540
+ return prompt;
12541
+ throw invalidPackage(zipPath, "manifest.json has no valid generator.prompt.");
12542
+ }
12543
+ function requireVariant(variants, name, zipPath) {
12544
+ const match = variants.find((variant) => isRecord5(variant) && variant.name === name);
12545
+ if (!isRecord5(match)) {
12546
+ throw invalidPackage(zipPath, `manifest.json has no "${name}" variant.`);
12547
+ }
12548
+ return {
12549
+ name,
12550
+ file: requireString(match, "file", zipPath),
12551
+ bytes: requireCount(match, "bytes", zipPath),
12552
+ triangles: requireCount(match, "triangles", zipPath),
12553
+ sha256: requireString(match, "sha256", zipPath)
12554
+ };
12555
+ }
12556
+ function parseManifest(bytes, zipPath) {
12557
+ let raw;
12558
+ try {
12559
+ raw = JSON.parse(new TextDecoder().decode(bytes));
12560
+ } catch {
12561
+ throw invalidPackage(zipPath, "manifest.json is not valid JSON.");
12562
+ }
12563
+ if (!isRecord5(raw)) {
12564
+ throw invalidPackage(zipPath, "manifest.json is not an object.");
12565
+ }
12566
+ const generator = requireRecord(raw, "generator", zipPath);
12567
+ const variants = raw.variants;
12568
+ if (!Array.isArray(variants)) {
12569
+ throw invalidPackage(zipPath, "manifest.json has no variants array.");
12570
+ }
12571
+ return {
12572
+ model: requireString(generator, "model", zipPath),
12573
+ requestId: requireString(generator, "requestId", zipPath),
12574
+ prompt: readPrompt(generator, zipPath),
12575
+ master: requireVariant(variants, "master", zipPath),
12576
+ web: requireVariant(variants, "web", zipPath)
12577
+ };
12578
+ }
12579
+ function requireEntry(files, variant, role, zipPath) {
12580
+ const bytes = files[variant.file];
12581
+ if (bytes === undefined) {
12582
+ throw invalidPackage(zipPath, `the ${role} GLB ${variant.file} is missing from the zip.`);
12583
+ }
12584
+ if (bytes.byteLength !== variant.bytes) {
12585
+ throw invalidPackage(zipPath, `${variant.file} is ${bytes.byteLength} bytes in the zip, but manifest.json says ${variant.bytes}.`);
12586
+ }
12587
+ return bytes;
12588
+ }
12589
+ async function readArchive(zipPath) {
12590
+ const archive = await readFile3(zipPath).catch((err2) => {
12591
+ throw new Error(`Cannot read the package ${zipPath}: ${err2 instanceof Error ? err2.message : String(err2)}`);
12592
+ });
12593
+ try {
12594
+ return unzipSync(archive);
12595
+ } catch (err2) {
12596
+ throw invalidPackage(zipPath, `not a readable zip archive (${err2 instanceof Error ? err2.message : String(err2)}).`);
12597
+ }
12598
+ }
12599
+ function loadPackage(zipPath, files) {
12600
+ const manifestBytes = files[PACKAGE_MANIFEST_NAME2];
12601
+ if (manifestBytes === undefined) {
12602
+ throw invalidPackage(zipPath, `${PACKAGE_MANIFEST_NAME2} is missing from the zip.`);
12603
+ }
12604
+ const manifest = parseManifest(manifestBytes, zipPath);
12605
+ return {
12606
+ manifest,
12607
+ masterGlb: requireEntry(files, manifest.master, "master", zipPath),
12608
+ webGlb: requireEntry(files, manifest.web, "web", zipPath)
12609
+ };
12610
+ }
12611
+ function packageBase(zipName, zipPath) {
12612
+ if (!zipName.endsWith(".zip") || zipName.length === ".zip".length) {
12613
+ throw invalidPackage(zipPath, "the package must be a non-empty .zip file.");
12614
+ }
12615
+ return zipName.slice(0, -".zip".length);
12616
+ }
12617
+ async function requireWritablePreview(htmlPath, force) {
12618
+ const stats = await stat2(htmlPath).catch(() => null);
12619
+ if (stats === null)
12620
+ return;
12621
+ if (stats.isDirectory()) {
12622
+ throw new Error(`Preview is a directory: ${htmlPath}. Pass a file path.`);
12623
+ }
12624
+ if (!force) {
12625
+ throw new Error(`Preview already exists: ${htmlPath}. Pass --force to overwrite.`);
12626
+ }
12627
+ }
12628
+ async function writePreview(htmlPath, html, force) {
12629
+ await requireWritablePreview(htmlPath, force);
12630
+ try {
12631
+ await writeFile2(htmlPath, new TextEncoder().encode(html), { flag: force ? "w" : "wx" });
12632
+ } catch (err2) {
12633
+ throw new Error(`Cannot write the preview ${htmlPath}: ${err2 instanceof Error ? err2.message : String(err2)}`);
12634
+ }
12635
+ }
12636
+ async function rebuildPreviewFromZip(zipPath, options) {
12637
+ const zipName = basename2(zipPath);
12638
+ const base = packageBase(zipName, zipPath);
12639
+ const htmlPath = join3(dirname5(zipPath), `${base}.html`);
12640
+ await requireWritablePreview(htmlPath, options.force);
12641
+ const { manifest, masterGlb, webGlb } = loadPackage(zipPath, await readArchive(zipPath));
12642
+ let checks = [];
12643
+ let checksNote = null;
12644
+ try {
12645
+ const io = new NodeIO3().registerExtensions(ALL_EXTENSIONS3).registerDependencies({ "meshopt.decoder": MeshoptDecoder });
12646
+ checks = computeChecks(await analyseDocument(await io.readBinary(masterGlb)));
12647
+ } catch (err2) {
12648
+ const detail = err2 instanceof Error ? err2.message : String(err2);
12649
+ checksNote = `Checks unavailable: ${detail}`;
12650
+ }
12651
+ const directionalEnvironment = await directionalEnvironmentPng();
12652
+ const html = renderPreviewHtml({
12653
+ title: base,
12654
+ modelId: manifest.model,
12655
+ prompt: manifest.prompt,
12656
+ requestId: manifest.requestId,
12657
+ masterGlb,
12658
+ webGlb,
12659
+ webSha256: manifest.web.sha256,
12660
+ webTriangles: manifest.web.triangles,
12661
+ masterTriangles: manifest.master.triangles,
12662
+ masterBytes: manifest.master.bytes,
12663
+ webBytes: manifest.web.bytes,
12664
+ masterZipEntry: manifest.master.file,
12665
+ zipName,
12666
+ checks,
12667
+ checksNote,
12668
+ directionalEnvironmentPng: directionalEnvironment
12669
+ });
12670
+ await writePreview(htmlPath, html, options.force);
12671
+ return htmlPath;
12672
+ }
12673
+ var PACKAGE_MANIFEST_NAME2 = "manifest.json";
12674
+ var init_rebuild_preview = __esm(() => {
12675
+ init_esm();
12676
+ init_meshoptimizer();
12677
+ init_asset_analysis();
12678
+ init_checks();
11348
12679
  });
11349
12680
 
11350
12681
  // ../../node_modules/.pnpm/pngjs@6.0.0/node_modules/pngjs/lib/chunkstream.js
@@ -12156,17 +13487,17 @@ var require_bitmapper = __commonJS((exports) => {
12156
13487
  }
12157
13488
  return rawPos;
12158
13489
  }
12159
- function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) {
13490
+ function mapImageCustomBit(image, pxData, getPxPos, bpp, bits2, maxBit) {
12160
13491
  let imageWidth = image.width;
12161
13492
  let imageHeight = image.height;
12162
13493
  let imagePass = image.index;
12163
13494
  for (let y = 0;y < imageHeight; y++) {
12164
13495
  for (let x2 = 0;x2 < imageWidth; x2++) {
12165
- let pixelData = bits.get(bpp);
13496
+ let pixelData = bits2.get(bpp);
12166
13497
  let pxPos = getPxPos(x2, y, imagePass);
12167
13498
  pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit);
12168
13499
  }
12169
- bits.resetAfterLine();
13500
+ bits2.resetAfterLine();
12170
13501
  }
12171
13502
  }
12172
13503
  exports.dataToBitMap = function(data, bitmapInfo) {
@@ -12175,9 +13506,9 @@ var require_bitmapper = __commonJS((exports) => {
12175
13506
  let depth = bitmapInfo.depth;
12176
13507
  let bpp = bitmapInfo.bpp;
12177
13508
  let interlace = bitmapInfo.interlace;
12178
- let bits;
13509
+ let bits2;
12179
13510
  if (depth !== 8) {
12180
- bits = bitRetriever(data, depth);
13511
+ bits2 = bitRetriever(data, depth);
12181
13512
  }
12182
13513
  let pxData;
12183
13514
  if (depth <= 8) {
@@ -12205,7 +13536,7 @@ var require_bitmapper = __commonJS((exports) => {
12205
13536
  if (depth === 8) {
12206
13537
  rawPos = mapImage8Bit(images[imageIndex], pxData, getPxPos, bpp, data, rawPos);
12207
13538
  } else {
12208
- mapImageCustomBit(images[imageIndex], pxData, getPxPos, bpp, bits, maxBit);
13539
+ mapImageCustomBit(images[imageIndex], pxData, getPxPos, bpp, bits2, maxBit);
12209
13540
  }
12210
13541
  }
12211
13542
  if (depth === 8) {
@@ -12213,7 +13544,7 @@ var require_bitmapper = __commonJS((exports) => {
12213
13544
  throw new Error("extra data found");
12214
13545
  }
12215
13546
  } else {
12216
- bits.end();
13547
+ bits2.end();
12217
13548
  }
12218
13549
  return pxData;
12219
13550
  };
@@ -12916,13 +14247,13 @@ var require_sync_inflate = __commonJS((exports, module) => {
12916
14247
  }
12917
14248
  return engine._processChunk(buffer, flushFlag);
12918
14249
  }
12919
- function inflateSync(buffer, opts) {
14250
+ function inflateSync2(buffer, opts) {
12920
14251
  return zlibBufferSync(new Inflate(opts), buffer);
12921
14252
  }
12922
- module.exports = exports = inflateSync;
14253
+ module.exports = exports = inflateSync2;
12923
14254
  exports.Inflate = Inflate;
12924
14255
  exports.createInflate = createInflate;
12925
- exports.inflateSync = inflateSync;
14256
+ exports.inflateSync = inflateSync2;
12926
14257
  });
12927
14258
 
12928
14259
  // ../../node_modules/.pnpm/pngjs@6.0.0/node_modules/pngjs/lib/sync-reader.js
@@ -12983,7 +14314,7 @@ var require_filter_parse_sync = __commonJS((exports) => {
12983
14314
  var require_parser_sync = __commonJS((exports, module) => {
12984
14315
  var hasSyncZlib = true;
12985
14316
  var zlib = __require("zlib");
12986
- var inflateSync = require_sync_inflate();
14317
+ var inflateSync2 = require_sync_inflate();
12987
14318
  if (!zlib.deflateSync) {
12988
14319
  hasSyncZlib = false;
12989
14320
  }
@@ -13045,7 +14376,7 @@ var require_parser_sync = __commonJS((exports, module) => {
13045
14376
  } else {
13046
14377
  let rowSize = (metaData.width * metaData.bpp * metaData.depth + 7 >> 3) + 1;
13047
14378
  let imageSize = rowSize * metaData.height;
13048
- inflatedData = inflateSync(inflateData, {
14379
+ inflatedData = inflateSync2(inflateData, {
13049
14380
  chunkSize: imageSize,
13050
14381
  maxLength: imageSize
13051
14382
  });
@@ -13239,7 +14570,7 @@ var exports_slice_deps = {};
13239
14570
  __export(exports_slice_deps, {
13240
14571
  createDefaultSliceImageDeps: () => createDefaultSliceImageDeps
13241
14572
  });
13242
- import { lstat, mkdir as mkdir3, readFile as readFile5, stat as stat4 } from "node:fs/promises";
14573
+ import { lstat, mkdir as mkdir3, readFile as readFile6, stat as stat5 } from "node:fs/promises";
13243
14574
  function decodePng(bytes) {
13244
14575
  const png = PNG.sync.read(Buffer.from(bytes));
13245
14576
  return { width: png.width, height: png.height, data: png.data };
@@ -13258,7 +14589,7 @@ function classifyStat(stats) {
13258
14589
  }
13259
14590
  async function statInput(path) {
13260
14591
  try {
13261
- return classifyStat(await stat4(path));
14592
+ return classifyStat(await stat5(path));
13262
14593
  } catch (err2) {
13263
14594
  if (err2.code === "ENOENT") {
13264
14595
  return { kind: "missing", size: 0 };
@@ -13269,7 +14600,7 @@ async function statInput(path) {
13269
14600
  async function readJsonIfExists(path) {
13270
14601
  let text;
13271
14602
  try {
13272
- text = await readFile5(path, "utf8");
14603
+ text = await readFile6(path, "utf8");
13273
14604
  } catch (err2) {
13274
14605
  if (err2.code === "ENOENT") {
13275
14606
  return;
@@ -13280,7 +14611,7 @@ async function readJsonIfExists(path) {
13280
14611
  }
13281
14612
  async function fileIdentity(path) {
13282
14613
  try {
13283
- const stats = await stat4(path);
14614
+ const stats = await stat5(path);
13284
14615
  return { dev: stats.dev, ino: stats.ino };
13285
14616
  } catch (err2) {
13286
14617
  if (err2.code === "ENOENT") {
@@ -13306,7 +14637,7 @@ function createDefaultSliceImageDeps() {
13306
14637
  return {
13307
14638
  pathStatus,
13308
14639
  statInput,
13309
- readFile: readFile5,
14640
+ readFile: readFile6,
13310
14641
  readJsonIfExists,
13311
14642
  decodePng,
13312
14643
  encodePng,
@@ -13344,7 +14675,7 @@ var init_remove_image_background_deps = __esm(() => {
13344
14675
  });
13345
14676
 
13346
14677
  // src/commands/remove-image-background/remove-image-background-fal.ts
13347
- import { basename as basename3, resolve as resolve4 } from "node:path";
14678
+ import { basename as basename4, resolve as resolve4 } from "node:path";
13348
14679
  function validateBackgroundResult(raw, model) {
13349
14680
  const unexpected = new RemoveBackgroundFalError(`fal returned no image for ${model}`);
13350
14681
  if (!isRecord2(raw))
@@ -13499,7 +14830,7 @@ async function removeBackgroundFal(options, deps) {
13499
14830
  model: spec.model,
13500
14831
  input: {
13501
14832
  ...engine === "birefnet" ? { mode: "Matting", output_format: "png" } : {},
13502
- source: basename3(inPath),
14833
+ source: basename4(inPath),
13503
14834
  sourceBytes: source.bytes.byteLength,
13504
14835
  sourceSha256: sha256(source.bytes),
13505
14836
  width: source.width,
@@ -13568,7 +14899,7 @@ var exports_remove_image_background_fal_deps = {};
13568
14899
  __export(exports_remove_image_background_fal_deps, {
13569
14900
  createDefaultRemoveBackgroundFalDeps: () => createDefaultRemoveBackgroundFalDeps
13570
14901
  });
13571
- import { dirname as dirname6 } from "node:path";
14902
+ import { dirname as dirname7 } from "node:path";
13572
14903
  function mebibytes(bytes) {
13573
14904
  return Math.round(bytes / (1024 * 1024));
13574
14905
  }
@@ -13608,7 +14939,7 @@ function createDefaultRemoveBackgroundFalDeps() {
13608
14939
  pathStatus: slice.pathStatus,
13609
14940
  writeFile: slice.writeFile,
13610
14941
  writeJson: slice.writeJson,
13611
- ensureParentDir: (path) => slice.ensureDir(dirname6(path)),
14942
+ ensureParentDir: (path) => slice.ensureDir(dirname7(path)),
13612
14943
  now: () => new Date,
13613
14944
  sdkVersion: readInstalledPackageVersion("@fal-ai/client"),
13614
14945
  log: (message) => console.log(message)
@@ -13711,15 +15042,15 @@ var init_stdin_heredoc = __esm(() => {
13711
15042
 
13712
15043
  // src/version.ts
13713
15044
  import { readFileSync as readFileSync3 } from "node:fs";
13714
- import { dirname as dirname7, join as join5 } from "node:path";
15045
+ import { dirname as dirname8, join as join6 } from "node:path";
13715
15046
  import { fileURLToPath } from "node:url";
13716
15047
  function getVersion() {
13717
15048
  try {
13718
15049
  const __filename2 = fileURLToPath(import.meta.url);
13719
- const __dirname2 = dirname7(__filename2);
15050
+ const __dirname2 = dirname8(__filename2);
13720
15051
  const possiblePaths = [
13721
- join5(__dirname2, "..", "package.json"),
13722
- join5(__dirname2, "..", "..", "package.json")
15052
+ join6(__dirname2, "..", "package.json"),
15053
+ join6(__dirname2, "..", "..", "package.json")
13723
15054
  ];
13724
15055
  for (const packagePath of possiblePaths) {
13725
15056
  try {
@@ -21221,12 +22552,12 @@ __export(exports_storage, {
21221
22552
  });
21222
22553
  import * as fs from "node:fs/promises";
21223
22554
  import { homedir, hostname } from "node:os";
21224
- import { join as join6 } from "node:path";
22555
+ import { join as join7 } from "node:path";
21225
22556
  async function ensureConfigDir() {
21226
22557
  await fs.mkdir(CHATROOM_DIR, { recursive: true, mode: 448 });
21227
22558
  }
21228
22559
  function getAuthFilePath() {
21229
- return join6(CHATROOM_DIR, AUTH_FILE);
22560
+ return join7(CHATROOM_DIR, AUTH_FILE);
21230
22561
  }
21231
22562
  function parseJsonc(content) {
21232
22563
  const jsonContent = content.split(`
@@ -21394,7 +22725,7 @@ var CHATROOM_DIR, AUTH_FILE = "auth.jsonc", PRODUCTION_CONVEX_URL = "https://cha
21394
22725
  var init_storage = __esm(() => {
21395
22726
  init_client2();
21396
22727
  init_version();
21397
- CHATROOM_DIR = join6(homedir(), ".chatroom");
22728
+ CHATROOM_DIR = join7(homedir(), ".chatroom");
21398
22729
  });
21399
22730
 
21400
22731
  // ../../node_modules/.pnpm/convex@1.45.0_react@19.2.7/node_modules/convex/dist/esm/server/pagination.js
@@ -22845,7 +24176,7 @@ function make(combine2) {
22845
24176
  function min(order) {
22846
24177
  return make((self2, that) => order(self2, that) === -1 ? self2 : that);
22847
24178
  }
22848
- function max(order) {
24179
+ function max2(order) {
22849
24180
  return make((self2, that) => order(self2, that) === 1 ? self2 : that);
22850
24181
  }
22851
24182
 
@@ -23038,7 +24369,7 @@ var init_result = __esm(() => {
23038
24369
  function make4(compare) {
23039
24370
  return (self2, that) => self2 === that ? 0 : compare(self2, that);
23040
24371
  }
23041
- var Number2, mapInput, isLessThan = (O) => dual(2, (self2, that) => O(self2, that) === -1), isGreaterThan = (O) => dual(2, (self2, that) => O(self2, that) === 1), isLessThanOrEqualTo = (O) => dual(2, (self2, that) => O(self2, that) !== 1), isGreaterThanOrEqualTo = (O) => dual(2, (self2, that) => O(self2, that) !== -1), min2 = (O) => dual(2, (self2, that) => self2 === that || O(self2, that) < 1 ? self2 : that), max2 = (O) => dual(2, (self2, that) => self2 === that || O(self2, that) > -1 ? self2 : that), clamp = (O) => dual(2, (self2, options) => min2(O)(options.maximum, max2(O)(options.minimum, self2))), isBetween = (O) => dual(2, (self2, options) => !isLessThan(O)(self2, options.minimum) && !isGreaterThan(O)(self2, options.maximum));
24372
+ var Number2, mapInput, isLessThan = (O) => dual(2, (self2, that) => O(self2, that) === -1), isGreaterThan = (O) => dual(2, (self2, that) => O(self2, that) === 1), isLessThanOrEqualTo = (O) => dual(2, (self2, that) => O(self2, that) !== 1), isGreaterThanOrEqualTo = (O) => dual(2, (self2, that) => O(self2, that) !== -1), min2 = (O) => dual(2, (self2, that) => self2 === that || O(self2, that) < 1 ? self2 : that), max3 = (O) => dual(2, (self2, that) => self2 === that || O(self2, that) > -1 ? self2 : that), clamp = (O) => dual(2, (self2, options) => min2(O)(options.maximum, max3(O)(options.minimum, self2))), isBetween = (O) => dual(2, (self2, options) => !isLessThan(O)(self2, options.minimum) && !isGreaterThan(O)(self2, options.maximum));
23042
24373
  var init_Order = __esm(() => {
23043
24374
  init_Function();
23044
24375
  Number2 = /* @__PURE__ */ make4((self2, that) => {
@@ -23050,7 +24381,7 @@ var init_Order = __esm(() => {
23050
24381
  return 1;
23051
24382
  return self2 < that ? -1 : 1;
23052
24383
  });
23053
- mapInput = /* @__PURE__ */ dual(2, (self2, f) => make4((b1, b2) => self2(f(b1), f(b2))));
24384
+ mapInput = /* @__PURE__ */ dual(2, (self2, f) => make4((b1, b22) => self2(f(b1), f(b22))));
23054
24385
  });
23055
24386
 
23056
24387
  // ../../node_modules/.pnpm/effect@4.0.1/node_modules/effect/dist/Option.js
@@ -23462,7 +24793,7 @@ __export(exports_Duration, {
23462
24793
  min: () => min3,
23463
24794
  millis: () => millis,
23464
24795
  micros: () => micros,
23465
- max: () => max3,
24796
+ max: () => max4,
23466
24797
  matchPair: () => matchPair,
23467
24798
  match: () => match2,
23468
24799
  isZero: () => isZero,
@@ -23731,7 +25062,7 @@ var TypeId4 = "~effect/Duration", bigint0, bigint1, bigint2, bigint10, bigint24,
23731
25062
  onMillis: (self3, that2) => self3 === that2,
23732
25063
  onNanos: (self3, that2) => self3 === that2,
23733
25064
  onInfinity: (self3, that2) => self3.value._tag === that2.value._tag
23734
- }), min3, max3, clamp2, divide, divideUnsafe, times, subtract, sum, isLessThan2, isLessThanOrEqualTo2, isGreaterThan2, isGreaterThanOrEqualTo2, equals2, parts = (self2) => {
25065
+ }), min3, max4, clamp2, divide, divideUnsafe, times, subtract, sum, isLessThan2, isLessThanOrEqualTo2, isGreaterThan2, isGreaterThanOrEqualTo2, equals2, parts = (self2) => {
23735
25066
  if (self2.value._tag === "Infinity") {
23736
25067
  return {
23737
25068
  days: Infinity,
@@ -23938,7 +25269,7 @@ var init_Duration = __esm(() => {
23938
25269
  }));
23939
25270
  between = /* @__PURE__ */ isBetween(Order);
23940
25271
  min3 = /* @__PURE__ */ min2(Order);
23941
- max3 = /* @__PURE__ */ max2(Order);
25272
+ max4 = /* @__PURE__ */ max3(Order);
23942
25273
  clamp2 = /* @__PURE__ */ clamp(Order);
23943
25274
  divide = /* @__PURE__ */ dual(2, (self2, by) => {
23944
25275
  if (!Number.isFinite(by))
@@ -24023,7 +25354,7 @@ var init_Duration = __esm(() => {
24023
25354
  isGreaterThanOrEqualTo2 = /* @__PURE__ */ isGreaterThanOrEqualTo(Order);
24024
25355
  equals2 = /* @__PURE__ */ dual(2, (self2, that) => Equivalence(self2, that));
24025
25356
  ReducerSum = /* @__PURE__ */ make2(sum, zero);
24026
- CombinerMax = /* @__PURE__ */ max(Order);
25357
+ CombinerMax = /* @__PURE__ */ max2(Order);
24027
25358
  CombinerMin = /* @__PURE__ */ min(Order);
24028
25359
  });
24029
25360
 
@@ -25120,7 +26451,7 @@ ${prefix}}`;
25120
26451
  let expiresAt = 0;
25121
26452
  let exit2;
25122
26453
  let current;
25123
- const join7 = (fiber2, run) => {
26454
+ const join8 = (fiber2, run) => {
25124
26455
  run.awaiters++;
25125
26456
  onExitUnsafe(fiber2, () => {
25126
26457
  if (--run.awaiters > 0 || current !== run)
@@ -25132,7 +26463,7 @@ ${prefix}}`;
25132
26463
  };
25133
26464
  return [withFiber((fiber2) => {
25134
26465
  if (current !== undefined)
25135
- return join7(fiber2, current);
26466
+ return join8(fiber2, current);
25136
26467
  if (exit2 !== undefined && (expiresAt === Infinity || fiber2.getRef(ClockRef).currentTimeMillisUnsafe() < expiresAt)) {
25137
26468
  return exit2;
25138
26469
  }
@@ -25153,7 +26484,7 @@ ${prefix}}`;
25153
26484
  expiresAt = duration === Infinity ? Infinity : clock.currentTimeMillisUnsafe() + duration;
25154
26485
  exit2 = exit_;
25155
26486
  }), true, true);
25156
- return run.fiber._exit ?? join7(fiber2, run);
26487
+ return run.fiber._exit ?? join8(fiber2, run);
25157
26488
  }), sync(() => {
25158
26489
  exit2 = undefined;
25159
26490
  })];
@@ -27863,11 +29194,11 @@ var TypeId10 = "~effect/Cron", CronProto, isCron = (u) => hasProperty(u, TypeId1
27863
29194
  weekday: lookup3.prev.weekday(weekdays)
27864
29195
  };
27865
29196
  return o;
27866
- }, makeRestrictions = (field, min4, max4, normalize2 = (value) => value) => (values) => {
29197
+ }, makeRestrictions = (field, min4, max5, normalize2 = (value) => value) => (values) => {
27867
29198
  const restrictions = [];
27868
29199
  for (const value of values) {
27869
- if (!Number.isInteger(value) || value < min4 || value > max4) {
27870
- throw new RangeError(`${field} must contain only integers between ${min4} and ${max4}`);
29200
+ if (!Number.isInteger(value) || value < min4 || value > max5) {
29201
+ throw new RangeError(`${field} must contain only integers between ${min4} and ${max5}`);
27871
29202
  }
27872
29203
  restrictions.push(normalize2(value));
27873
29204
  }
@@ -28386,7 +29717,7 @@ __export(exports_Schedule, {
28386
29717
  once: () => once,
28387
29718
  modifyDelay: () => modifyDelay,
28388
29719
  min: () => min4,
28389
- max: () => max4,
29720
+ max: () => max5,
28390
29721
  map: () => map6,
28391
29722
  jittered: () => jittered,
28392
29723
  isSchedule: () => isSchedule,
@@ -28442,7 +29773,7 @@ var TypeId11 = "~effect/Schedule", randomNext, CurrentMetadata2, ScheduleProto,
28442
29773
  return as(sleep(duration), meta);
28443
29774
  });
28444
29775
  });
28445
- })), toStepWithSleep = (schedule) => map3(toStepWithMetadata(schedule), (step) => (input) => map3(step(input), (meta) => meta.output)), addDelay, concat, concatResult, max4 = (schedules) => fromStep(map3(all2(schedules.map(toStep)), (steps) => (now, input) => flatMap(forEach(steps, (step) => matchEffect2(step(now, input), {
29776
+ })), toStepWithSleep = (schedule) => map3(toStepWithMetadata(schedule), (step) => (input) => map3(step(input), (meta) => meta.output)), addDelay, concat, concatResult, max5 = (schedules) => fromStep(map3(all2(schedules.map(toStep)), (steps) => (now, input) => flatMap(forEach(steps, (step) => matchEffect2(step(now, input), {
28446
29777
  onSuccess: (result2) => succeed3(result2[1]),
28447
29778
  onDone: () => undefined_,
28448
29779
  onFailure: failCause
@@ -28453,13 +29784,13 @@ var TypeId11 = "~effect/Schedule", randomNext, CurrentMetadata2, ScheduleProto,
28453
29784
  }
28454
29785
  return succeed3([duration, duration]);
28455
29786
  }))), maxDuration = (results2) => {
28456
- let max5 = results2[0];
29787
+ let max6 = results2[0];
28457
29788
  for (let i3 = 1;i3 < results2.length; i3++) {
28458
- max5 = results2[i3] && max5 && max3(max5, results2[i3]);
28459
- if (max5 === undefined)
29789
+ max6 = results2[i3] && max6 && max4(max6, results2[i3]);
29790
+ if (max6 === undefined)
28460
29791
  break;
28461
29792
  }
28462
- return max5;
29793
+ return max6;
28463
29794
  }, cron = (expression, tz) => {
28464
29795
  const parsed = isCron(expression) ? succeed2(expression) : parse(expression, tz);
28465
29796
  return fromStep(map3(fromResult(parsed), (cron2) => (now, _) => {
@@ -29213,7 +30544,7 @@ var init_Metric = __esm(() => {
29213
30544
  let count = 0;
29214
30545
  let sum2 = 0;
29215
30546
  let min5 = Number.MAX_VALUE;
29216
- let max5 = -Number.MAX_VALUE;
30547
+ let max6 = -Number.MAX_VALUE;
29217
30548
  map2(sort(bounds, Number2), (n, i3) => {
29218
30549
  boundaries[i3] = n;
29219
30550
  });
@@ -29242,8 +30573,8 @@ var init_Metric = __esm(() => {
29242
30573
  if (value < min5) {
29243
30574
  min5 = value;
29244
30575
  }
29245
- if (value > max5) {
29246
- max5 = value;
30576
+ if (value > max6) {
30577
+ max6 = value;
29247
30578
  }
29248
30579
  };
29249
30580
  const getBuckets = () => {
@@ -29261,7 +30592,7 @@ var init_Metric = __esm(() => {
29261
30592
  buckets: getBuckets(),
29262
30593
  count,
29263
30594
  min: min5,
29264
- max: max5,
30595
+ max: max6,
29265
30596
  sum: sum2
29266
30597
  }), update);
29267
30598
  }
@@ -29289,7 +30620,7 @@ var init_Metric = __esm(() => {
29289
30620
  let count = 0;
29290
30621
  let sum2 = 0;
29291
30622
  let min5 = Number.MAX_VALUE;
29292
- let max5 = -Number.MAX_VALUE;
30623
+ let max6 = -Number.MAX_VALUE;
29293
30624
  const snapshot = (now) => {
29294
30625
  const builder = [];
29295
30626
  let i3 = 0;
@@ -29329,8 +30660,8 @@ var init_Metric = __esm(() => {
29329
30660
  if (value < min5) {
29330
30661
  min5 = value;
29331
30662
  }
29332
- if (value > max5) {
29333
- max5 = value;
30663
+ if (value > max6) {
30664
+ max6 = value;
29334
30665
  }
29335
30666
  };
29336
30667
  const get2 = (context2) => {
@@ -29340,7 +30671,7 @@ var init_Metric = __esm(() => {
29340
30671
  quantiles,
29341
30672
  count,
29342
30673
  min: min5,
29343
- max: max5,
30674
+ max: max6,
29344
30675
  sum: sum2
29345
30676
  };
29346
30677
  };
@@ -29959,7 +31290,7 @@ var exports_Fiber = {};
29959
31290
  __export(exports_Fiber, {
29960
31291
  runIn: () => runIn,
29961
31292
  joinAll: () => joinAll,
29962
- join: () => join7,
31293
+ join: () => join8,
29963
31294
  isFiber: () => isFiber,
29964
31295
  interruptAs: () => interruptAs,
29965
31296
  interruptAllAs: () => interruptAllAs,
@@ -29969,13 +31300,13 @@ __export(exports_Fiber, {
29969
31300
  awaitAll: () => awaitAll,
29970
31301
  await: () => await_
29971
31302
  });
29972
- var await_, awaitAll, join7, joinAll, interrupt4, interruptAs, interruptAll, interruptAllAs, isFiber = (u) => hasProperty(u, FiberTypeId), getCurrent, runIn;
31303
+ var await_, awaitAll, join8, joinAll, interrupt4, interruptAs, interruptAll, interruptAllAs, isFiber = (u) => hasProperty(u, FiberTypeId), getCurrent, runIn;
29973
31304
  var init_Fiber = __esm(() => {
29974
31305
  init_effect();
29975
31306
  init_Predicate();
29976
31307
  await_ = fiberAwait;
29977
31308
  awaitAll = fiberAwaitAll;
29978
- join7 = fiberJoin;
31309
+ join8 = fiberJoin;
29979
31310
  joinAll = fiberJoinAll;
29980
31311
  interrupt4 = fiberInterrupt;
29981
31312
  interruptAs = fiberInterruptAs;
@@ -33161,7 +34492,7 @@ function handleCheckPropertyResult(result3, payload, property) {
33161
34492
  }
33162
34493
  }
33163
34494
  var $ZodCheck, numericOriginMap, $ZodCheckLessThan, $ZodCheckGreaterThan, $ZodCheckMultipleOf, $ZodCheckNumberFormat, $ZodCheckBigIntFormat, $ZodCheckMaxSize, $ZodCheckMinSize, $ZodCheckSizeEquals, $ZodCheckMaxLength, $ZodCheckMinLength, $ZodCheckLengthEquals, $ZodCheckStringFormat, $ZodCheckRegex, $ZodCheckLowerCase, $ZodCheckUpperCase, $ZodCheckIncludes, $ZodCheckStartsWith, $ZodCheckEndsWith, $ZodCheckProperty, $ZodCheckMimeType, $ZodCheckOverwrite;
33164
- var init_checks = __esm(() => {
34495
+ var init_checks2 = __esm(() => {
33165
34496
  init_core2();
33166
34497
  init_regexes();
33167
34498
  init_util();
@@ -34158,7 +35489,7 @@ function handleRefineResult(result3, payload, input, inst) {
34158
35489
  }
34159
35490
  var $ZodType, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, $ZodIPv6, $ZodMAC, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodCustomStringFormat, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodBigInt, $ZodBigIntFormat, $ZodSymbol, $ZodUndefined, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, $ZodVoid, $ZodDate, $ZodArray, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodXor, $ZodDiscriminatedUnion, $ZodIntersection, $ZodTuple, $ZodRecord, $ZodMap, $ZodSet, $ZodEnum, $ZodLiteral, $ZodFile, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodSuccess, $ZodCatch, $ZodNaN, $ZodPipe, $ZodCodec, $ZodPreprocess, $ZodReadonly, $ZodTemplateLiteral, $ZodFunction, $ZodPromise, $ZodLazy, $ZodCustom;
34160
35491
  var init_schemas = __esm(() => {
34161
- init_checks();
35492
+ init_checks2();
34162
35493
  init_core2();
34163
35494
  init_parse();
34164
35495
  init_regexes();
@@ -42977,7 +44308,7 @@ function _stringFormat(Class2, format3, fnOrRegex, _params = {}) {
42977
44308
  }
42978
44309
  var TimePrecision;
42979
44310
  var init_api4 = __esm(() => {
42980
- init_checks();
44311
+ init_checks2();
42981
44312
  init_registries();
42982
44313
  init_schemas();
42983
44314
  init_util();
@@ -44207,7 +45538,7 @@ var init_core3 = __esm(() => {
44207
45538
  init_parse();
44208
45539
  init_errors2();
44209
45540
  init_schemas();
44210
- init_checks();
45541
+ init_checks2();
44211
45542
  init_versions();
44212
45543
  init_registries();
44213
45544
  init_api4();
@@ -44247,7 +45578,7 @@ __export(exports_checks2, {
44247
45578
  gt: () => _gt,
44248
45579
  endsWith: () => _endsWith
44249
45580
  });
44250
- var init_checks2 = __esm(() => {
45581
+ var init_checks3 = __esm(() => {
44251
45582
  init_core3();
44252
45583
  });
44253
45584
 
@@ -45046,7 +46377,7 @@ var init_schemas2 = __esm(() => {
45046
46377
  init_core3();
45047
46378
  init_json_schema_processors();
45048
46379
  init_to_json_schema();
45049
- init_checks2();
46380
+ init_checks3();
45050
46381
  init_iso();
45051
46382
  init_parse2();
45052
46383
  _installedGroups = /* @__PURE__ */ new WeakMap;
@@ -46256,7 +47587,7 @@ function fromJSONSchema(schema, params) {
46256
47587
  var z, RECOGNIZED_KEYS;
46257
47588
  var init_from_json_schema = __esm(() => {
46258
47589
  init_registries();
46259
- init_checks2();
47590
+ init_checks3();
46260
47591
  init_iso();
46261
47592
  init_schemas2();
46262
47593
  z = {
@@ -46608,7 +47939,7 @@ var init_external = __esm(() => {
46608
47939
  init_iso();
46609
47940
  init_coerce();
46610
47941
  init_schemas2();
46611
- init_checks2();
47942
+ init_checks3();
46612
47943
  init_errors3();
46613
47944
  init_parse2();
46614
47945
  init_compat();
@@ -47853,7 +49184,7 @@ var init_conversation_mode = __esm(() => {
47853
49184
  });
47854
49185
 
47855
49186
  // ../shared/src/domain/task-envelope.ts
47856
- function isRecord5(value) {
49187
+ function isRecord6(value) {
47857
49188
  return typeof value === "object" && value !== null && !Array.isArray(value);
47858
49189
  }
47859
49190
  function isConversationMode(value) {
@@ -47899,10 +49230,10 @@ function copyTaskEnvelope(envelope) {
47899
49230
  };
47900
49231
  }
47901
49232
  function isEnvelopeRecordAndVersion(value) {
47902
- return isRecord5(value) && value.version === TASK_ENVELOPE_VERSION;
49233
+ return isRecord6(value) && value.version === TASK_ENVELOPE_VERSION;
47903
49234
  }
47904
49235
  function isWorkflowShape(workflow) {
47905
- return isRecord5(workflow) && isHandoffPreset(workflow.preset) && isHandoffPhase(workflow.phase);
49236
+ return isRecord6(workflow) && isHandoffPreset(workflow.preset) && isHandoffPhase(workflow.phase);
47906
49237
  }
47907
49238
  function isPresetChoice(workflow, mode) {
47908
49239
  return workflow.preset === workflowPresetForMode(mode);
@@ -49792,17 +51123,17 @@ import {
49792
51123
  writeFileSync
49793
51124
  } from "node:fs";
49794
51125
  import { homedir as homedir2 } from "node:os";
49795
- import { join as join8 } from "node:path";
51126
+ import { join as join9 } from "node:path";
49796
51127
  function getUrlHash() {
49797
51128
  const url2 = getConvexUrl();
49798
51129
  return createHash4("sha256").update(url2).digest("hex").substring(0, 8);
49799
51130
  }
49800
51131
  function getChildPidsFilePath() {
49801
- const dir = join8(homedir2(), ".chatroom");
49802
- return join8(dir, `daemon-children-${getUrlHash()}.pids`);
51132
+ const dir = join9(homedir2(), ".chatroom");
51133
+ return join9(dir, `daemon-children-${getUrlHash()}.pids`);
49803
51134
  }
49804
51135
  function ensureChatroomDir() {
49805
- const dir = join8(homedir2(), ".chatroom");
51136
+ const dir = join9(homedir2(), ".chatroom");
49806
51137
  if (!existsSync(dir)) {
49807
51138
  mkdirSync(dir, { recursive: true, mode: 448 });
49808
51139
  }
@@ -53131,7 +54462,7 @@ var init_task_service = __esm(() => {
53131
54462
 
53132
54463
  // src/daemon/services/task-service/infrastructure/repository/task-handoff-repository.ts
53133
54464
  import { mkdirSync as mkdirSync2 } from "node:fs";
53134
- import { dirname as dirname8 } from "node:path";
54465
+ import { dirname as dirname9 } from "node:path";
53135
54466
  import { DatabaseSync } from "node:sqlite";
53136
54467
  function normalizeRole(role) {
53137
54468
  return role.toLowerCase();
@@ -53151,7 +54482,7 @@ function mapRow(row) {
53151
54482
  };
53152
54483
  }
53153
54484
  function createTaskHandoffRepository(dbPath) {
53154
- mkdirSync2(dirname8(dbPath), { recursive: true });
54485
+ mkdirSync2(dirname9(dbPath), { recursive: true });
53155
54486
  const db = new DatabaseSync(dbPath);
53156
54487
  db.exec(`
53157
54488
  CREATE TABLE IF NOT EXISTS task_handoffs (
@@ -55709,7 +57040,7 @@ function createAgentLogWriter(prefix, options) {
55709
57040
  var BASH_TOOL_KIND = "tool: bash", BASH_RUNNING_PREFIX = "running:";
55710
57041
 
55711
57042
  // src/daemon/infrastructure/local/harness/services/pi/pi-agent-service.ts
55712
- import { join as join9 } from "node:path";
57043
+ import { join as join10 } from "node:path";
55713
57044
  function isExplicitToolError(result3) {
55714
57045
  if (result3 === null || typeof result3 !== "object")
55715
57046
  return false;
@@ -55719,7 +57050,7 @@ function isExplicitToolError(result3) {
55719
57050
  return "error" in obj && obj.error !== undefined;
55720
57051
  }
55721
57052
  function getPiSessionDir(workingDir) {
55722
- return join9(workingDir, ".chatroom", "pi-sessions");
57053
+ return join10(workingDir, ".chatroom", "pi-sessions");
55723
57054
  }
55724
57055
  var PI_COMMAND = "pi", GET_STATE_TIMEOUT_MS = 5000, PiAgentService;
55725
57056
  var init_pi_agent_service = __esm(() => {
@@ -56176,25 +57507,25 @@ var init_model_provider = () => {};
56176
57507
  // src/daemon/infrastructure/local/harness/services/cursor-sdk/cursor-sdk-package.ts
56177
57508
  import { existsSync as existsSync2, readFileSync as readFileSync5 } from "node:fs";
56178
57509
  import { createRequire as createRequire5 } from "node:module";
56179
- import { dirname as dirname9, join as join10 } from "node:path";
57510
+ import { dirname as dirname10, join as join11 } from "node:path";
56180
57511
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
56181
57512
  function resolveChatroomCliRoot(moduleRef = import.meta.url) {
56182
57513
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath2(moduleRef) : moduleRef;
56183
- let dir = dirname9(filePath);
56184
- while (dir !== dirname9(dir)) {
56185
- const packageJsonPath = join10(dir, "package.json");
57514
+ let dir = dirname10(filePath);
57515
+ while (dir !== dirname10(dir)) {
57516
+ const packageJsonPath = join11(dir, "package.json");
56186
57517
  if (existsSync2(packageJsonPath)) {
56187
57518
  const pkg = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
56188
57519
  if (pkg.name === "chatroom-cli") {
56189
57520
  return dir;
56190
57521
  }
56191
57522
  }
56192
- dir = dirname9(dir);
57523
+ dir = dirname10(dir);
56193
57524
  }
56194
57525
  throw new CursorSdkPackageError(`Could not locate chatroom-cli package root while resolving @cursor/sdk. ${REINSTALL_HINT}`);
56195
57526
  }
56196
57527
  function readPinnedSdkVersion(chatroomCliRoot) {
56197
- const pkg = JSON.parse(readFileSync5(join10(chatroomCliRoot, "package.json"), "utf8"));
57528
+ const pkg = JSON.parse(readFileSync5(join11(chatroomCliRoot, "package.json"), "utf8"));
56198
57529
  const specifier = pkg.dependencies?.["@cursor/sdk"];
56199
57530
  const pinned = specifier?.replace(/^[\^~>=<]+/, "").trim() ?? "";
56200
57531
  const match5 = pinned.match(/^(\d+\.\d+\.\d+)/);
@@ -56204,7 +57535,7 @@ function readPinnedSdkVersion(chatroomCliRoot) {
56204
57535
  return match5[1];
56205
57536
  }
56206
57537
  function readInstalledSdkVersion(entryPath) {
56207
- const packageJsonPath = join10(dirname9(entryPath), "..", "..", "package.json");
57538
+ const packageJsonPath = join11(dirname10(entryPath), "..", "..", "package.json");
56208
57539
  const pkg = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
56209
57540
  return pkg.version;
56210
57541
  }
@@ -56217,7 +57548,7 @@ function resolveSdkEsmImportPath(cjsEntryPath) {
56217
57548
  }
56218
57549
  async function importBundledCursorSdk(moduleRef = import.meta.url) {
56219
57550
  const chatroomCliRoot = resolveChatroomCliRoot(moduleRef);
56220
- const require4 = createRequire5(join10(chatroomCliRoot, "package.json"));
57551
+ const require4 = createRequire5(join11(chatroomCliRoot, "package.json"));
56221
57552
  const pinnedVersion = readPinnedSdkVersion(chatroomCliRoot);
56222
57553
  const entryPath = require4.resolve("@cursor/sdk", { paths: [chatroomCliRoot] });
56223
57554
  const installedVersion = readInstalledSdkVersion(entryPath);
@@ -56233,7 +57564,7 @@ async function importBundledCursorSdk(moduleRef = import.meta.url) {
56233
57564
  }
56234
57565
  function getBundledCursorSdkVersion(moduleRef = import.meta.url) {
56235
57566
  const chatroomCliRoot = resolveChatroomCliRoot(moduleRef);
56236
- const require4 = createRequire5(join10(chatroomCliRoot, "package.json"));
57567
+ const require4 = createRequire5(join11(chatroomCliRoot, "package.json"));
56237
57568
  const entryPath = require4.resolve("@cursor/sdk", { paths: [chatroomCliRoot] });
56238
57569
  return readInstalledSdkVersion(entryPath);
56239
57570
  }
@@ -57125,25 +58456,25 @@ import {
57125
58456
  } from "node:fs";
57126
58457
  import { createRequire as createRequire6 } from "node:module";
57127
58458
  import { homedir as homedir3 } from "node:os";
57128
- import { basename as basename5, dirname as dirname10, join as join11 } from "node:path";
58459
+ import { basename as basename6, dirname as dirname11, join as join12 } from "node:path";
57129
58460
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
57130
58461
  function resolveChatroomCliRoot2(moduleRef = import.meta.url) {
57131
58462
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath3(moduleRef) : moduleRef;
57132
- let dir = dirname10(filePath);
57133
- while (dir !== dirname10(dir)) {
57134
- const packageJsonPath = join11(dir, "package.json");
58463
+ let dir = dirname11(filePath);
58464
+ while (dir !== dirname11(dir)) {
58465
+ const packageJsonPath = join12(dir, "package.json");
57135
58466
  if (existsSync3(packageJsonPath)) {
57136
58467
  const pkg = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
57137
58468
  if (pkg.name === "chatroom-cli") {
57138
58469
  return realpathSync(dir);
57139
58470
  }
57140
58471
  }
57141
- dir = dirname10(dir);
58472
+ dir = dirname11(dir);
57142
58473
  }
57143
58474
  throw new ClaudeSdkPackageError(`Could not locate chatroom-cli package root while resolving ${CLAUDE_AGENT_SDK_PKG}. ${REINSTALL_HINT2}`);
57144
58475
  }
57145
58476
  function readPinnedSdkVersion2(chatroomCliRoot) {
57146
- const pkg = JSON.parse(readFileSync6(join11(chatroomCliRoot, "package.json"), "utf8"));
58477
+ const pkg = JSON.parse(readFileSync6(join12(chatroomCliRoot, "package.json"), "utf8"));
57147
58478
  const specifier = pkg.dependencies?.[CLAUDE_AGENT_SDK_PKG];
57148
58479
  const pinned = specifier?.replace(/^[\^~>=<]+/, "").trim() ?? "";
57149
58480
  const match5 = pinned.match(/^(\d+\.\d+\.\d+)/);
@@ -57153,7 +58484,7 @@ function readPinnedSdkVersion2(chatroomCliRoot) {
57153
58484
  return match5[1];
57154
58485
  }
57155
58486
  function readInstalledSdkVersion2(entryPath) {
57156
- const packageJsonPath = join11(dirname10(entryPath), "package.json");
58487
+ const packageJsonPath = join12(dirname11(entryPath), "package.json");
57157
58488
  const pkg = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
57158
58489
  return pkg.version;
57159
58490
  }
@@ -57179,19 +58510,19 @@ function resolveNativeCliBinaryPath(sdkEntryPath) {
57179
58510
  const binaryName = process.platform === "win32" ? "claude.exe" : "claude";
57180
58511
  let pkgDir;
57181
58512
  try {
57182
- pkgDir = dirname10(require4.resolve(`${platformPkg}/package.json`));
58513
+ pkgDir = dirname11(require4.resolve(`${platformPkg}/package.json`));
57183
58514
  } catch {
57184
58515
  throw new ClaudeSdkPackageError(`Native CLI binary package ${platformPkg} is not installed. ${REINSTALL_HINT2}`);
57185
58516
  }
57186
- const binaryPath = join11(pkgDir, binaryName);
58517
+ const binaryPath = join12(pkgDir, binaryName);
57187
58518
  if (!existsSync3(binaryPath)) {
57188
58519
  throw new ClaudeSdkPackageError(`Native CLI binary is missing: ${binaryPath}. ${REINSTALL_HINT2}`);
57189
58520
  }
57190
58521
  return binaryPath;
57191
58522
  }
57192
58523
  function pinNativeBinary(sourcePath, sdkVersion, pinRoot) {
57193
- const dir = join11(pinRoot, sdkVersion);
57194
- const pinnedPath = join11(dir, basename5(sourcePath));
58524
+ const dir = join12(pinRoot, sdkVersion);
58525
+ const pinnedPath = join12(dir, basename6(sourcePath));
57195
58526
  if (existsSync3(pinnedPath))
57196
58527
  return pinnedPath;
57197
58528
  mkdirSync3(dir, { recursive: true, mode: 448 });
@@ -57205,7 +58536,7 @@ async function resolvePathToClaudeCodeExecutable(moduleRef = import.meta.url) {
57205
58536
  if (cachedExecutablePath)
57206
58537
  return cachedExecutablePath;
57207
58538
  const chatroomCliRoot = resolveChatroomCliRoot2(moduleRef);
57208
- const require4 = createRequire6(join11(chatroomCliRoot, "package.json"));
58539
+ const require4 = createRequire6(join12(chatroomCliRoot, "package.json"));
57209
58540
  const sdkEntryPath = require4.resolve(CLAUDE_AGENT_SDK_PKG, { paths: [chatroomCliRoot] });
57210
58541
  const embeddedPath = resolveNativeCliBinaryPath(sdkEntryPath);
57211
58542
  const { extractFromBunfs } = await import("@anthropic-ai/claude-agent-sdk/extract");
@@ -57215,7 +58546,7 @@ async function resolvePathToClaudeCodeExecutable(moduleRef = import.meta.url) {
57215
58546
  }
57216
58547
  async function importBundledClaudeSdk(moduleRef = import.meta.url) {
57217
58548
  const chatroomCliRoot = resolveChatroomCliRoot2(moduleRef);
57218
- const require4 = createRequire6(join11(chatroomCliRoot, "package.json"));
58549
+ const require4 = createRequire6(join12(chatroomCliRoot, "package.json"));
57219
58550
  const pinnedVersion = readPinnedSdkVersion2(chatroomCliRoot);
57220
58551
  const entryPath = require4.resolve(CLAUDE_AGENT_SDK_PKG, { paths: [chatroomCliRoot] });
57221
58552
  const installedVersion = readInstalledSdkVersion2(entryPath);
@@ -57229,7 +58560,7 @@ async function importBundledClaudeSdk(moduleRef = import.meta.url) {
57229
58560
  }
57230
58561
  function getBundledClaudeSdkVersion(moduleRef = import.meta.url) {
57231
58562
  const chatroomCliRoot = resolveChatroomCliRoot2(moduleRef);
57232
- const require4 = createRequire6(join11(chatroomCliRoot, "package.json"));
58563
+ const require4 = createRequire6(join12(chatroomCliRoot, "package.json"));
57233
58564
  const entryPath = require4.resolve(CLAUDE_AGENT_SDK_PKG, { paths: [chatroomCliRoot] });
57234
58565
  return readInstalledSdkVersion2(entryPath);
57235
58566
  }
@@ -57252,7 +58583,7 @@ var init_claude_sdk_package = __esm(() => {
57252
58583
  this.name = "ClaudeSdkPackageError";
57253
58584
  }
57254
58585
  };
57255
- PINNED_BINARY_ROOT = join11(homedir3(), ".chatroom", "claude-native");
58586
+ PINNED_BINARY_ROOT = join12(homedir3(), ".chatroom", "claude-native");
57256
58587
  });
57257
58588
 
57258
58589
  // src/daemon/infrastructure/local/harness/services/assistant-text-capture.ts
@@ -58113,25 +59444,25 @@ var init_codex_sdk_model_variants = __esm(() => {
58113
59444
  // src/daemon/infrastructure/local/harness/services/codex-sdk/codex-sdk-package.ts
58114
59445
  import { existsSync as existsSync4, readFileSync as readFileSync7, realpathSync as realpathSync2, statSync } from "node:fs";
58115
59446
  import { createRequire as createRequire7 } from "node:module";
58116
- import { dirname as dirname11, join as join12 } from "node:path";
59447
+ import { dirname as dirname12, join as join13 } from "node:path";
58117
59448
  import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
58118
59449
  function resolveChatroomCliRoot3(moduleRef = import.meta.url) {
58119
59450
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath4(moduleRef) : moduleRef;
58120
- let dir = dirname11(filePath);
58121
- while (dir !== dirname11(dir)) {
58122
- const packageJsonPath = join12(dir, "package.json");
59451
+ let dir = dirname12(filePath);
59452
+ while (dir !== dirname12(dir)) {
59453
+ const packageJsonPath = join13(dir, "package.json");
58123
59454
  if (existsSync4(packageJsonPath)) {
58124
59455
  const pkg = JSON.parse(readFileSync7(packageJsonPath, "utf8"));
58125
59456
  if (pkg.name === "chatroom-cli") {
58126
59457
  return dir;
58127
59458
  }
58128
59459
  }
58129
- dir = dirname11(dir);
59460
+ dir = dirname12(dir);
58130
59461
  }
58131
59462
  throw new CodexSdkPackageError(`Could not locate chatroom-cli package root while resolving @openai/codex-sdk. ${REINSTALL_HINT3}`);
58132
59463
  }
58133
59464
  function readPinnedSdkVersion3(chatroomCliRoot) {
58134
- const pkg = JSON.parse(readFileSync7(join12(chatroomCliRoot, "package.json"), "utf8"));
59465
+ const pkg = JSON.parse(readFileSync7(join13(chatroomCliRoot, "package.json"), "utf8"));
58135
59466
  const specifier = pkg.dependencies?.["@openai/codex-sdk"];
58136
59467
  const pinned = specifier?.replace(/^[\^~>=<]+/, "").trim() ?? "";
58137
59468
  const match5 = pinned.match(/^(\d+\.\d+\.\d+)/);
@@ -58145,11 +59476,11 @@ function readInstalledSdkVersion3(packageJsonPath) {
58145
59476
  return pkg.version;
58146
59477
  }
58147
59478
  function resolveCodexSdkPackageJson(chatroomCliRoot) {
58148
- const require4 = createRequire7(join12(chatroomCliRoot, "package.json"));
59479
+ const require4 = createRequire7(join13(chatroomCliRoot, "package.json"));
58149
59480
  try {
58150
59481
  return require4.resolve("@openai/codex-sdk/package.json", { paths: [chatroomCliRoot] });
58151
59482
  } catch {
58152
- const candidate = join12(chatroomCliRoot, "node_modules", "@openai", "codex-sdk", "package.json");
59483
+ const candidate = join13(chatroomCliRoot, "node_modules", "@openai", "codex-sdk", "package.json");
58153
59484
  if (existsSync4(candidate)) {
58154
59485
  return candidate;
58155
59486
  }
@@ -58193,7 +59524,7 @@ function addPlatformPackageJsonCandidate(candidates, candidate) {
58193
59524
  }
58194
59525
  function resolvePlatformPackageJsonCandidates(platformPkg, chatroomCliRoot, codexPackageJson, chatroomRequire, codexRequire) {
58195
59526
  const candidates = [];
58196
- const codexPackageDir = realpathSync2(dirname11(codexPackageJson));
59527
+ const codexPackageDir = realpathSync2(dirname12(codexPackageJson));
58197
59528
  const platformPkgBaseName = platformPkg.includes("/") ? platformPkg.slice(platformPkg.indexOf("/") + 1) : platformPkg;
58198
59529
  try {
58199
59530
  addPlatformPackageJsonCandidate(candidates, codexRequire.resolve(`${platformPkg}/package.json`));
@@ -58201,17 +59532,17 @@ function resolvePlatformPackageJsonCandidates(platformPkg, chatroomCliRoot, code
58201
59532
  try {
58202
59533
  addPlatformPackageJsonCandidate(candidates, chatroomRequire.resolve(`${platformPkg}/package.json`, { paths: [chatroomCliRoot] }));
58203
59534
  } catch {}
58204
- addPlatformPackageJsonCandidate(candidates, join12(codexPackageDir, "..", platformPkgBaseName, "package.json"));
58205
- addPlatformPackageJsonCandidate(candidates, join12(codexPackageDir, "node_modules", platformPkg, "package.json"));
59535
+ addPlatformPackageJsonCandidate(candidates, join13(codexPackageDir, "..", platformPkgBaseName, "package.json"));
59536
+ addPlatformPackageJsonCandidate(candidates, join13(codexPackageDir, "node_modules", platformPkg, "package.json"));
58206
59537
  return candidates;
58207
59538
  }
58208
59539
  function resolveNativeCodexBinary(vendorRoot, targetTriple, codexBinaryName) {
58209
- const packageRoot = join12(vendorRoot, targetTriple);
58210
- const packageBinaryPath = join12(packageRoot, "bin", codexBinaryName);
58211
- if (isRegularFile(packageBinaryPath) && isRegularFile(join12(packageRoot, "codex-package.json"))) {
59540
+ const packageRoot = join13(vendorRoot, targetTriple);
59541
+ const packageBinaryPath = join13(packageRoot, "bin", codexBinaryName);
59542
+ if (isRegularFile(packageBinaryPath) && isRegularFile(join13(packageRoot, "codex-package.json"))) {
58212
59543
  return packageBinaryPath;
58213
59544
  }
58214
- const legacyBinaryPath = join12(packageRoot, "codex", codexBinaryName);
59545
+ const legacyBinaryPath = join13(packageRoot, "codex", codexBinaryName);
58215
59546
  if (isRegularFile(legacyBinaryPath)) {
58216
59547
  return legacyBinaryPath;
58217
59548
  }
@@ -58219,7 +59550,7 @@ function resolveNativeCodexBinary(vendorRoot, targetTriple, codexBinaryName) {
58219
59550
  }
58220
59551
  function resolveCodexExecutablePath(moduleRef = import.meta.url) {
58221
59552
  const chatroomCliRoot = resolveChatroomCliRoot3(moduleRef);
58222
- const require4 = createRequire7(join12(chatroomCliRoot, "package.json"));
59553
+ const require4 = createRequire7(join13(chatroomCliRoot, "package.json"));
58223
59554
  let codexPackageJson;
58224
59555
  try {
58225
59556
  codexPackageJson = require4.resolve(`${CODEX_NPM_NAME}/package.json`, {
@@ -58237,7 +59568,7 @@ function resolveCodexExecutablePath(moduleRef = import.meta.url) {
58237
59568
  throw new CodexSdkPackageError(`Native Codex CLI package ${platformPkg} is not installed. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies. ${REINSTALL_HINT3}`);
58238
59569
  }
58239
59570
  for (const platformPackageJson of platformPackageJsonCandidates) {
58240
- const vendorRoot = join12(dirname11(platformPackageJson), "vendor");
59571
+ const vendorRoot = join13(dirname12(platformPackageJson), "vendor");
58241
59572
  const binaryPath = resolveNativeCodexBinary(vendorRoot, targetTriple, codexBinaryName);
58242
59573
  if (binaryPath) {
58243
59574
  return binaryPath;
@@ -58253,7 +59584,7 @@ async function importBundledCodexSdk(moduleRef = import.meta.url) {
58253
59584
  if (installedVersion !== pinnedVersion) {
58254
59585
  throw new CodexSdkPackageError(`@openai/codex-sdk@${installedVersion} does not match chatroom-cli pin (${pinnedVersion}). ${REINSTALL_HINT3}`);
58255
59586
  }
58256
- const distEntryPath = join12(dirname11(packageJsonPath), "dist", "index.js");
59587
+ const distEntryPath = join13(dirname12(packageJsonPath), "dist", "index.js");
58257
59588
  if (!existsSync4(distEntryPath)) {
58258
59589
  throw new CodexSdkPackageError(`@openai/codex-sdk entry file is missing: ${distEntryPath}. ${REINSTALL_HINT3}`);
58259
59590
  }
@@ -60346,12 +61677,12 @@ var init_session_event_forwarder = __esm(() => {
60346
61677
  // src/daemon/infrastructure/local/harness/services/opencode-sdk/session-metadata-store.ts
60347
61678
  import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
60348
61679
  import { homedir as homedir4 } from "node:os";
60349
- import { dirname as dirname12, join as join13 } from "node:path";
61680
+ import { dirname as dirname13, join as join14 } from "node:path";
60350
61681
 
60351
61682
  class FileSessionMetadataStore {
60352
61683
  filePath;
60353
61684
  constructor(filePath) {
60354
- this.filePath = filePath ?? join13(homedir4(), ".chatroom", "opencode-sdk-sessions.json");
61685
+ this.filePath = filePath ?? join14(homedir4(), ".chatroom", "opencode-sdk-sessions.json");
60355
61686
  }
60356
61687
  load() {
60357
61688
  try {
@@ -60363,7 +61694,7 @@ class FileSessionMetadataStore {
60363
61694
  }
60364
61695
  save(sessions) {
60365
61696
  try {
60366
- const dir = dirname12(this.filePath);
61697
+ const dir = dirname13(this.filePath);
60367
61698
  if (!existsSync5(dir)) {
60368
61699
  mkdirSync4(dir, { recursive: true, mode: 448 });
60369
61700
  }
@@ -60946,25 +62277,25 @@ var init_opencode_sdk = __esm(() => {
60946
62277
 
60947
62278
  // src/daemon/infrastructure/local/harness/services/pi-sdk/pi-sdk-package.ts
60948
62279
  import { existsSync as existsSync6, readFileSync as readFileSync9 } from "node:fs";
60949
- import { dirname as dirname13, join as join14 } from "node:path";
62280
+ import { dirname as dirname14, join as join15 } from "node:path";
60950
62281
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL4 } from "node:url";
60951
62282
  function resolveChatroomCliRoot4(moduleRef = import.meta.url) {
60952
62283
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath5(moduleRef) : moduleRef;
60953
- let dir = dirname13(filePath);
60954
- while (dir !== dirname13(dir)) {
60955
- const packageJsonPath = join14(dir, "package.json");
62284
+ let dir = dirname14(filePath);
62285
+ while (dir !== dirname14(dir)) {
62286
+ const packageJsonPath = join15(dir, "package.json");
60956
62287
  if (existsSync6(packageJsonPath)) {
60957
62288
  const pkg = JSON.parse(readFileSync9(packageJsonPath, "utf8"));
60958
62289
  if (pkg.name === "chatroom-cli") {
60959
62290
  return dir;
60960
62291
  }
60961
62292
  }
60962
- dir = dirname13(dir);
62293
+ dir = dirname14(dir);
60963
62294
  }
60964
62295
  throw new PiSdkPackageError(`Could not locate chatroom-cli package root while resolving ${PI_CODING_AGENT_PKG}. ${REINSTALL_HINT4}`);
60965
62296
  }
60966
62297
  function readPinnedSdkVersion4(chatroomCliRoot) {
60967
- const pkg = JSON.parse(readFileSync9(join14(chatroomCliRoot, "package.json"), "utf8"));
62298
+ const pkg = JSON.parse(readFileSync9(join15(chatroomCliRoot, "package.json"), "utf8"));
60968
62299
  const specifier = pkg.dependencies?.[PI_CODING_AGENT_PKG];
60969
62300
  const pinned = specifier?.replace(/^[\^~>=<]+/, "").trim() ?? "";
60970
62301
  const match5 = pinned.match(/^(\d+\.\d+\.\d+)/);
@@ -60974,18 +62305,18 @@ function readPinnedSdkVersion4(chatroomCliRoot) {
60974
62305
  return match5[1];
60975
62306
  }
60976
62307
  function readInstalledSdkVersion4(entryPath) {
60977
- const packageJsonPath = join14(dirname13(entryPath), "..", "package.json");
62308
+ const packageJsonPath = join15(dirname14(entryPath), "..", "package.json");
60978
62309
  const pkg = JSON.parse(readFileSync9(packageJsonPath, "utf8"));
60979
62310
  return pkg.version;
60980
62311
  }
60981
62312
  function resolvePiSdkEntryPathFromNodeModules(chatroomCliRoot) {
60982
62313
  let dir = chatroomCliRoot;
60983
- while (dir !== dirname13(dir)) {
60984
- const entryPath = join14(dir, "node_modules", PI_CODING_AGENT_PKG, "dist", "index.js");
62314
+ while (dir !== dirname14(dir)) {
62315
+ const entryPath = join15(dir, "node_modules", PI_CODING_AGENT_PKG, "dist", "index.js");
60985
62316
  if (existsSync6(entryPath)) {
60986
62317
  return entryPath;
60987
62318
  }
60988
- dir = dirname13(dir);
62319
+ dir = dirname14(dir);
60989
62320
  }
60990
62321
  return;
60991
62322
  }
@@ -60993,7 +62324,7 @@ function resolvePiSdkEntryPath(chatroomCliRoot) {
60993
62324
  const resolveMeta = import.meta.resolve;
60994
62325
  if (typeof resolveMeta === "function") {
60995
62326
  try {
60996
- const parentUrl = pathToFileURL4(join14(chatroomCliRoot, "package.json")).href;
62327
+ const parentUrl = pathToFileURL4(join15(chatroomCliRoot, "package.json")).href;
60997
62328
  return fileURLToPath5(resolveMeta(PI_CODING_AGENT_PKG, parentUrl));
60998
62329
  } catch {}
60999
62330
  }
@@ -61720,7 +63051,7 @@ var MACHINE_CONFIG_VERSION = "1";
61720
63051
  import { randomBytes, randomUUID as randomUUID7 } from "node:crypto";
61721
63052
  import * as fs4 from "node:fs/promises";
61722
63053
  import { homedir as homedir5, hostname as hostname4 } from "node:os";
61723
- import { join as join15 } from "node:path";
63054
+ import { join as join16 } from "node:path";
61724
63055
  function enqueueMachineConfigSave(task) {
61725
63056
  const run = saveChain.then(task, task);
61726
63057
  saveChain = run.catch(() => {
@@ -61729,14 +63060,14 @@ function enqueueMachineConfigSave(task) {
61729
63060
  return run;
61730
63061
  }
61731
63062
  function chatroomConfigDir() {
61732
- return join15(homedir5(), ".chatroom");
63063
+ return join16(homedir5(), ".chatroom");
61733
63064
  }
61734
63065
  async function ensureConfigDir2() {
61735
63066
  const dir = chatroomConfigDir();
61736
63067
  await fs4.mkdir(dir, { recursive: true, mode: 448 });
61737
63068
  }
61738
63069
  function getMachineConfigPath() {
61739
- return join15(chatroomConfigDir(), MACHINE_FILE);
63070
+ return join16(chatroomConfigDir(), MACHINE_FILE);
61740
63071
  }
61741
63072
  async function loadConfigFile() {
61742
63073
  const configPath = getMachineConfigPath();
@@ -61838,7 +63169,7 @@ var init_storage2 = __esm(() => {
61838
63169
  // src/infrastructure/machine/daemon-state.ts
61839
63170
  import * as fs5 from "node:fs/promises";
61840
63171
  import { homedir as homedir6 } from "node:os";
61841
- import { join as join16 } from "node:path";
63172
+ import { join as join17 } from "node:path";
61842
63173
  function agentKey3(chatroomId, role, workingDir) {
61843
63174
  return workingDir ? `${chatroomId}/${role}/${encodeURIComponent(workingDir)}` : `${chatroomId}/${role}`;
61844
63175
  }
@@ -61846,7 +63177,7 @@ async function ensureStateDir() {
61846
63177
  await fs5.mkdir(STATE_DIR, { recursive: true, mode: 448 });
61847
63178
  }
61848
63179
  function stateFilePath(machineId) {
61849
- return join16(STATE_DIR, `${machineId}.json`);
63180
+ return join17(STATE_DIR, `${machineId}.json`);
61850
63181
  }
61851
63182
  async function loadDaemonState(machineId) {
61852
63183
  const filePath = stateFilePath(machineId);
@@ -61947,8 +63278,8 @@ async function loadEventCursor(machineId) {
61947
63278
  }
61948
63279
  var CHATROOM_DIR2, STATE_DIR, STATE_VERSION = "1";
61949
63280
  var init_daemon_state = __esm(() => {
61950
- CHATROOM_DIR2 = join16(homedir6(), ".chatroom");
61951
- STATE_DIR = join16(CHATROOM_DIR2, "machines", "state");
63281
+ CHATROOM_DIR2 = join17(homedir6(), ".chatroom");
63282
+ STATE_DIR = join17(CHATROOM_DIR2, "machines", "state");
61952
63283
  });
61953
63284
 
61954
63285
  // src/infrastructure/machine/index.ts
@@ -68791,8 +70122,8 @@ var init_backo2 = __esm(() => {
68791
70122
  Backoff.prototype.setMin = function(min5) {
68792
70123
  this.ms = min5;
68793
70124
  };
68794
- Backoff.prototype.setMax = function(max5) {
68795
- this.max = max5;
70125
+ Backoff.prototype.setMax = function(max6) {
70126
+ this.max = max6;
68796
70127
  };
68797
70128
  Backoff.prototype.setJitter = function(jitter) {
68798
70129
  this.jitter = jitter;
@@ -69351,9 +70682,9 @@ function resolveEnvSlug(convexUrl) {
69351
70682
 
69352
70683
  // src/infrastructure/log-server/resolve-logs-db-path.ts
69353
70684
  import { homedir as homedir7 } from "node:os";
69354
- import { join as join17 } from "node:path";
70685
+ import { join as join18 } from "node:path";
69355
70686
  function resolveLogsDbPath(convexUrl) {
69356
- return join17(homedir7(), ".chatroom", resolveEnvSlug(convexUrl ?? getConvexUrl()), "logs.sqlite");
70687
+ return join18(homedir7(), ".chatroom", resolveEnvSlug(convexUrl ?? getConvexUrl()), "logs.sqlite");
69357
70688
  }
69358
70689
  var init_resolve_logs_db_path = __esm(() => {
69359
70690
  init_client2();
@@ -69387,10 +70718,10 @@ var init_log_schema = __esm(() => {
69387
70718
 
69388
70719
  // src/infrastructure/log-server/open-log-database.ts
69389
70720
  import { mkdirSync as mkdirSync5 } from "node:fs";
69390
- import { dirname as dirname14 } from "node:path";
70721
+ import { dirname as dirname15 } from "node:path";
69391
70722
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
69392
70723
  function openLogDatabase(dbPath) {
69393
- mkdirSync5(dirname14(dbPath), { recursive: true });
70724
+ mkdirSync5(dirname15(dbPath), { recursive: true });
69394
70725
  const db = new DatabaseSync2(dbPath);
69395
70726
  db.exec("PRAGMA journal_mode = WAL");
69396
70727
  db.exec("PRAGMA busy_timeout = 5000");
@@ -69499,9 +70830,9 @@ function parsePsPidCommandLines(stdout) {
69499
70830
  function readPpid(pid) {
69500
70831
  if (process.platform === "linux") {
69501
70832
  try {
69502
- const stat6 = readFileSync10(`/proc/${pid}/stat`, "utf-8");
69503
- const closeParen = stat6.lastIndexOf(")");
69504
- const rest = closeParen >= 0 ? stat6.slice(closeParen + 2).split(" ") : stat6.split(" ");
70833
+ const stat7 = readFileSync10(`/proc/${pid}/stat`, "utf-8");
70834
+ const closeParen = stat7.lastIndexOf(")");
70835
+ const rest = closeParen >= 0 ? stat7.slice(closeParen + 2).split(" ") : stat7.split(" ");
69505
70836
  const ppid = Number.parseInt(rest[1] ?? "", 10);
69506
70837
  return Number.isFinite(ppid) && ppid >= 0 ? ppid : null;
69507
70838
  } catch {
@@ -69582,7 +70913,7 @@ var init_daemon_process_scan = __esm(() => {
69582
70913
  import { createHash as createHash5 } from "node:crypto";
69583
70914
  import { existsSync as existsSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync6 } from "node:fs";
69584
70915
  import { homedir as homedir8 } from "node:os";
69585
- import { join as join18 } from "node:path";
70916
+ import { join as join19 } from "node:path";
69586
70917
  function getUrlHash2() {
69587
70918
  const url3 = getConvexUrl();
69588
70919
  return createHash5("sha256").update(url3).digest("hex").substring(0, 8);
@@ -69596,7 +70927,7 @@ function ensureChatroomDir2() {
69596
70927
  }
69597
70928
  }
69598
70929
  function getPidFilePath() {
69599
- return join18(CHATROOM_DIR3, getPidFileName());
70930
+ return join19(CHATROOM_DIR3, getPidFileName());
69600
70931
  }
69601
70932
  function isProcessRunning(pid) {
69602
70933
  try {
@@ -69742,7 +71073,7 @@ var CHATROOM_DIR3, LOCK_RETRY_INTERVAL_MS = 500, LOCK_RETRY_MAX_WAIT_MS = 15000,
69742
71073
  var init_pid = __esm(() => {
69743
71074
  init_daemon_process_scan();
69744
71075
  init_client2();
69745
- CHATROOM_DIR3 = join18(homedir8(), ".chatroom");
71076
+ CHATROOM_DIR3 = join19(homedir8(), ".chatroom");
69746
71077
  });
69747
71078
 
69748
71079
  // src/commands/diagnostics/index.ts
@@ -69751,7 +71082,7 @@ __export(exports_diagnostics, {
69751
71082
  logs: () => logs,
69752
71083
  debugDaemon: () => debugDaemon
69753
71084
  });
69754
- import { stat as stat6, writeFile as writeFile5 } from "node:fs/promises";
71085
+ import { stat as stat7, writeFile as writeFile6 } from "node:fs/promises";
69755
71086
  function parseDuration(value2) {
69756
71087
  const match5 = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/.exec(value2.trim().toLowerCase());
69757
71088
  if (!match5)
@@ -69770,7 +71101,7 @@ function parseTimestamp(value2, flag) {
69770
71101
  async function writeOrPrint(value2, output) {
69771
71102
  const rendered = JSON.stringify(value2, null, 2);
69772
71103
  if (output) {
69773
- await writeFile5(output, `${rendered}
71104
+ await writeFile6(output, `${rendered}
69774
71105
  `, "utf8");
69775
71106
  console.log(`Wrote diagnostic dump to ${output}`);
69776
71107
  return;
@@ -69813,7 +71144,7 @@ async function debugDaemon(options) {
69813
71144
  async function readAllLogs(options, fromTimestamp, toTimestamp) {
69814
71145
  const dbPath = resolveLogsDbPath();
69815
71146
  try {
69816
- await stat6(dbPath);
71147
+ await stat7(dbPath);
69817
71148
  } catch {
69818
71149
  return { dbPath, entries: [] };
69819
71150
  }
@@ -69869,7 +71200,7 @@ async function logs(options) {
69869
71200
  return;
69870
71201
  }
69871
71202
  if (options.output) {
69872
- await writeFile5(options.output, result3.entries.map((entry) => `${new Date(entry.timestamp).toISOString()} [${entry.level}] ${entry.source} ${entry.message}`).join(`
71203
+ await writeFile6(options.output, result3.entries.map((entry) => `${new Date(entry.timestamp).toISOString()} [${entry.level}] ${entry.source} ${entry.message}`).join(`
69873
71204
  `) + (result3.entries.length ? `
69874
71205
  ` : ""), "utf8");
69875
71206
  console.log(`Wrote ${result3.entries.length} log entries to ${options.output}`);
@@ -70088,9 +71419,9 @@ var init_init = __esm(() => {
70088
71419
  });
70089
71420
 
70090
71421
  // src/tools/output.ts
70091
- import { join as join19 } from "node:path";
71422
+ import { join as join20 } from "node:path";
70092
71423
  function resolveChatroomDir(workingDir) {
70093
- return join19(workingDir, CHATROOM_DIR4);
71424
+ return join20(workingDir, CHATROOM_DIR4);
70094
71425
  }
70095
71426
  async function ensureChatroomDir3(deps, workingDir) {
70096
71427
  const dir = resolveChatroomDir(workingDir);
@@ -70098,7 +71429,7 @@ async function ensureChatroomDir3(deps, workingDir) {
70098
71429
  return dir;
70099
71430
  }
70100
71431
  async function ensureGitignore(deps, workingDir) {
70101
- const gitignorePath = join19(workingDir, ".gitignore");
71432
+ const gitignorePath = join20(workingDir, ".gitignore");
70102
71433
  const entry = CHATROOM_DIR4;
70103
71434
  let content = "";
70104
71435
  try {
@@ -70128,7 +71459,7 @@ function formatOutputTimestamp(date5 = new Date) {
70128
71459
  function generateOutputPath(workingDir, toolName, extension2, date5) {
70129
71460
  const timestamp = formatOutputTimestamp(date5);
70130
71461
  const filename = `${toolName}-${timestamp}.${extension2}`;
70131
- return join19(resolveChatroomDir(workingDir), filename);
71462
+ return join20(resolveChatroomDir(workingDir), filename);
70132
71463
  }
70133
71464
  var CHATROOM_DIR4 = ".chatroom";
70134
71465
  var init_output = () => {};
@@ -70138,7 +71469,7 @@ var exports_parse_pdf = {};
70138
71469
  __export(exports_parse_pdf, {
70139
71470
  parsePdf: () => parsePdf
70140
71471
  });
70141
- import { access, readFile as readFile9, writeFile as writeFile6, mkdir as mkdir7, appendFile } from "node:fs/promises";
71472
+ import { access, readFile as readFile10, writeFile as writeFile7, mkdir as mkdir7, appendFile } from "node:fs/promises";
70142
71473
  import { resolve as resolve7 } from "node:path";
70143
71474
  function isUrl(input) {
70144
71475
  return input.startsWith("http://") || input.startsWith("https://");
@@ -70148,10 +71479,10 @@ function createDefaultDeps6() {
70148
71479
  fs: {
70149
71480
  access: (p) => access(p),
70150
71481
  mkdir: (p, opts) => mkdir7(p, opts).then(() => {}),
70151
- readFile: (p, enc) => readFile9(p, { encoding: enc }).then((b) => b.toString()),
71482
+ readFile: (p, enc) => readFile10(p, { encoding: enc }).then((b) => b.toString()),
70152
71483
  appendFile: (p, content) => appendFile(p, content),
70153
- readFileAsBuffer: (p) => readFile9(p),
70154
- writeFile: (p, content, enc) => writeFile6(p, content, enc)
71484
+ readFileAsBuffer: (p) => readFile10(p),
71485
+ writeFile: (p, content, enc) => writeFile7(p, content, enc)
70155
71486
  },
70156
71487
  parser: {
70157
71488
  parse: async (input) => {
@@ -82903,9 +84234,9 @@ var init_parse_skill_file = __esm(() => {
82903
84234
  });
82904
84235
 
82905
84236
  // src/infrastructure/external-skills/discover-external-skills.ts
82906
- import { lstat as lstat2, readdir as readdir2, readFile as readFile11, realpath, stat as stat7 } from "node:fs/promises";
84237
+ import { lstat as lstat2, readdir as readdir2, readFile as readFile12, realpath, stat as stat8 } from "node:fs/promises";
82907
84238
  import os from "node:os";
82908
- import { join as join21 } from "node:path";
84239
+ import { join as join22 } from "node:path";
82909
84240
  function errorCode(err2) {
82910
84241
  return err2?.code ?? "unknown";
82911
84242
  }
@@ -82914,7 +84245,7 @@ function issue2(path2, reason, detail) {
82914
84245
  }
82915
84246
  function getExternalSkillRoots(homeDir) {
82916
84247
  return EXTERNAL_SKILL_ROOT_SUFFIXES.map((suffix) => ({
82917
- path: join21(homeDir, ...suffix.split("/"))
84248
+ path: join22(homeDir, ...suffix.split("/"))
82918
84249
  }));
82919
84250
  }
82920
84251
  async function discoverExternalSkills(options) {
@@ -82936,7 +84267,7 @@ async function scanRoot(rootPath, cap, state) {
82936
84267
  state.issues.push(issue2(rootPath, "root-truncated", String(candidates.length)));
82937
84268
  }
82938
84269
  for (const entry of candidates.slice(0, cap)) {
82939
- const sourcePath = join21(rootPath, entry);
84270
+ const sourcePath = join22(rootPath, entry);
82940
84271
  try {
82941
84272
  await scanEntry(entry, sourcePath, state);
82942
84273
  } catch (err2) {
@@ -82978,15 +84309,15 @@ async function scanEntry(entry, sourcePath, state) {
82978
84309
  }
82979
84310
  async function isSkillCandidate(sourcePath, issues) {
82980
84311
  try {
82981
- if (!(await stat7(sourcePath)).isDirectory())
84312
+ if (!(await stat8(sourcePath)).isDirectory())
82982
84313
  return false;
82983
84314
  } catch (err2) {
82984
84315
  issues.push(issue2(sourcePath, "unreadable", errorCode(err2)));
82985
84316
  return false;
82986
84317
  }
82987
- const skillPath = join21(sourcePath, SKILL_FILE_NAME);
84318
+ const skillPath = join22(sourcePath, SKILL_FILE_NAME);
82988
84319
  try {
82989
- const skillStat = await stat7(skillPath);
84320
+ const skillStat = await stat8(skillPath);
82990
84321
  if (!skillStat.isFile())
82991
84322
  return false;
82992
84323
  if (skillStat.size > MAX_SKILL_FILE_BYTES) {
@@ -83011,7 +84342,7 @@ async function isSymlink2(path2) {
83011
84342
  }
83012
84343
  async function readSkillFile(sourcePath, issues) {
83013
84344
  try {
83014
- return await readFile11(join21(sourcePath, SKILL_FILE_NAME), "utf8");
84345
+ return await readFile12(join22(sourcePath, SKILL_FILE_NAME), "utf8");
83015
84346
  } catch (err2) {
83016
84347
  issues.push(issue2(sourcePath, "unreadable", errorCode(err2)));
83017
84348
  return;
@@ -84871,10 +86202,10 @@ var init_durable_fifo_queue_schema = __esm(() => {
84871
86202
 
84872
86203
  // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-store.ts
84873
86204
  import { mkdirSync as mkdirSync7 } from "node:fs";
84874
- import { dirname as dirname15 } from "node:path";
86205
+ import { dirname as dirname16 } from "node:path";
84875
86206
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
84876
86207
  function openDurableFifoQueueStore(dbPath) {
84877
- mkdirSync7(dirname15(dbPath), { recursive: true });
86208
+ mkdirSync7(dirname16(dbPath), { recursive: true });
84878
86209
  const db = new DatabaseSync3(dbPath);
84879
86210
  for (const sql of DURABLE_FIFO_QUEUE_MIGRATIONS)
84880
86211
  db.exec(sql);
@@ -85169,9 +86500,9 @@ var init_keyed_fifo_batched_outbox_registry = __esm(() => {
85169
86500
 
85170
86501
  // src/daemon/infrastructure/outbox/outbox-db-path.ts
85171
86502
  import { homedir as homedir9 } from "node:os";
85172
- import { join as join23 } from "node:path";
86503
+ import { join as join24 } from "node:path";
85173
86504
  function resolveOutboxDbPath(machineId, kind) {
85174
- return join23(homedir9(), ".chatroom", "daemon", machineId, `outbox-${kind}.sqlite`);
86505
+ return join24(homedir9(), ".chatroom", "daemon", machineId, `outbox-${kind}.sqlite`);
85175
86506
  }
85176
86507
  var init_outbox_db_path = () => {};
85177
86508
 
@@ -85502,9 +86833,9 @@ var init_orphan_tracker = __esm(() => {
85502
86833
  });
85503
86834
 
85504
86835
  // src/daemon/entry/handlers/process/output-store.ts
85505
- import { appendFile as appendFile2, mkdir as mkdir10, readFile as readFile12, rm as rm3 } from "node:fs/promises";
86836
+ import { appendFile as appendFile2, mkdir as mkdir10, readFile as readFile13, rm as rm3 } from "node:fs/promises";
85506
86837
  import { tmpdir } from "node:os";
85507
- import { join as join24 } from "node:path";
86838
+ import { join as join25 } from "node:path";
85508
86839
 
85509
86840
  class TempFileOutputStore {
85510
86841
  state;
@@ -85535,7 +86866,7 @@ class TempFileOutputStore {
85535
86866
  async getLastNLines(maxLines) {
85536
86867
  let source;
85537
86868
  try {
85538
- source = await readFile12(this.state.filePath, "utf-8");
86869
+ source = await readFile13(this.state.filePath, "utf-8");
85539
86870
  } catch {
85540
86871
  source = this.state.inMemory;
85541
86872
  }
@@ -85552,7 +86883,7 @@ class TempFileOutputStore {
85552
86883
  }
85553
86884
  async getFullOutput() {
85554
86885
  try {
85555
- return await readFile12(this.state.filePath, "utf-8");
86886
+ return await readFile13(this.state.filePath, "utf-8");
85556
86887
  } catch {
85557
86888
  return this.state.inMemory;
85558
86889
  }
@@ -85567,7 +86898,7 @@ function createOutputStore(runId) {
85567
86898
  if (!RUN_ID_RE.test(runId)) {
85568
86899
  throw new Error(`Invalid runId: ${runId}`);
85569
86900
  }
85570
- const filePath = join24(TEMP_DIR, `${runId}.log`);
86901
+ const filePath = join25(TEMP_DIR, `${runId}.log`);
85571
86902
  return new TempFileOutputStore(filePath);
85572
86903
  }
85573
86904
  async function ensureTempDir() {
@@ -85581,15 +86912,15 @@ async function cleanOrphanTempFiles() {
85581
86912
  var TAIL_WINDOW_BYTES, TEMP_DIR, RUN_ID_RE, MAX_TAIL_LINES_V2 = 50;
85582
86913
  var init_output_store = __esm(() => {
85583
86914
  TAIL_WINDOW_BYTES = 32 * 1024;
85584
- TEMP_DIR = join24(tmpdir(), "chatroom-cli", "runs");
86915
+ TEMP_DIR = join25(tmpdir(), "chatroom-cli", "runs");
85585
86916
  RUN_ID_RE = /^[a-z0-9]+$/i;
85586
86917
  });
85587
86918
 
85588
86919
  // src/daemon/entry/task-handoff-repository-path.ts
85589
86920
  import { homedir as homedir10 } from "node:os";
85590
- import { join as join25 } from "node:path";
86921
+ import { join as join26 } from "node:path";
85591
86922
  function resolveTaskHandoffRepositoryPath(machineId) {
85592
- return join25(homedir10(), ".chatroom", "daemon", machineId, "task-handoffs.sqlite");
86923
+ return join26(homedir10(), ".chatroom", "daemon", machineId, "task-handoffs.sqlite");
85593
86924
  }
85594
86925
  var init_task_handoff_repository_path = () => {};
85595
86926
 
@@ -85705,10 +87036,10 @@ var init_durable_coalescing_state_schema = __esm(() => {
85705
87036
 
85706
87037
  // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-store.ts
85707
87038
  import { mkdirSync as mkdirSync8 } from "node:fs";
85708
- import { dirname as dirname16 } from "node:path";
87039
+ import { dirname as dirname17 } from "node:path";
85709
87040
  import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
85710
87041
  function openDurableCoalescingStateStore(path2) {
85711
- mkdirSync8(dirname16(path2), { recursive: true });
87042
+ mkdirSync8(dirname17(path2), { recursive: true });
85712
87043
  const db = new DatabaseSync4(path2);
85713
87044
  for (const sql of DURABLE_COALESCING_STATE_MIGRATIONS)
85714
87045
  db.exec(sql);
@@ -87182,7 +88513,7 @@ var init_workspace_visibility_policy = __esm(() => {
87182
88513
 
87183
88514
  // src/infrastructure/services/workspace/workspace-ignore.ts
87184
88515
  import { promises as fsPromises } from "node:fs";
87185
- import { readFile as readFile13 } from "node:fs/promises";
88516
+ import { readFile as readFile14 } from "node:fs/promises";
87186
88517
  import { homedir as homedir11 } from "node:os";
87187
88518
  import path2 from "node:path";
87188
88519
  function normalizeRelativePath(relativePath) {
@@ -87247,7 +88578,7 @@ async function loadGitExtraExcludeRuleSets(rootDir) {
87247
88578
  }
87248
88579
  async function readIgnoreFile(filePath) {
87249
88580
  try {
87250
- return await readFile13(filePath, "utf-8");
88581
+ return await readFile14(filePath, "utf-8");
87251
88582
  } catch {
87252
88583
  return null;
87253
88584
  }
@@ -88219,7 +89550,7 @@ var init_git_workspace_hierarchy = __esm(() => {
88219
89550
  });
88220
89551
 
88221
89552
  // ../../node_modules/.pnpm/readdirp@5.1.1/node_modules/readdirp/index.js
88222
- import { lstat as lstat3, readdir as readdir3, realpath as realpath2, stat as stat8 } from "node:fs/promises";
89553
+ import { lstat as lstat3, readdir as readdir3, realpath as realpath2, stat as stat9 } from "node:fs/promises";
88223
89554
  import { join as pjoin, resolve as presolve, sep as psep } from "node:path";
88224
89555
  import { Readable } from "node:stream";
88225
89556
  function readdirp(root, options = {}) {
@@ -88316,7 +89647,7 @@ var init_readdirp = __esm(() => {
88316
89647
  const type = opts.type ?? defaultOptions.type;
88317
89648
  this._fileFilter = normalizeFilter(opts.fileFilter);
88318
89649
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
88319
- const statMethod = opts.lstat ? lstat3 : stat8;
89650
+ const statMethod = opts.lstat ? lstat3 : stat9;
88320
89651
  if (wantBigintFsStats) {
88321
89652
  this._stat = (path5) => statMethod(path5, { bigint: true });
88322
89653
  } else {
@@ -88404,9 +89735,9 @@ var init_readdirp = __esm(() => {
88404
89735
  return { files, depth, path: path5 };
88405
89736
  }
88406
89737
  _formatEntry(dirent, path5) {
88407
- const basename6 = this._isDirent ? dirent.name : dirent;
88408
- const fullPath = pjoin(path5, basename6);
88409
- const entry = { path: fullPath.slice(this._relStart), fullPath, basename: basename6 };
89738
+ const basename7 = this._isDirent ? dirent.name : dirent;
89739
+ const fullPath = pjoin(path5, basename7);
89740
+ const entry = { path: fullPath.slice(this._relStart), fullPath, basename: basename7 };
88410
89741
  if (this._isDirent) {
88411
89742
  entry.dirent = dirent;
88412
89743
  return entry;
@@ -88471,7 +89802,7 @@ var init_readdirp = __esm(() => {
88471
89802
 
88472
89803
  // ../../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/handler.js
88473
89804
  import { watch as fs_watch, unwatchFile, watchFile } from "node:fs";
88474
- import { realpath as fsrealpath, lstat as lstat4, open, stat as stat9 } from "node:fs/promises";
89805
+ import { realpath as fsrealpath, lstat as lstat4, open, stat as stat10 } from "node:fs/promises";
88475
89806
  import { type as osType } from "node:os";
88476
89807
  import * as sp from "node:path";
88477
89808
  function createFsWatchInstance(path5, options, listener, errHandler, emitRaw) {
@@ -88502,9 +89833,9 @@ class NodeFsHandler {
88502
89833
  _watchWithNodeFs(path5, listener) {
88503
89834
  const opts = this.fsw.options;
88504
89835
  const directory = sp.dirname(path5);
88505
- const basename7 = sp.basename(path5);
89836
+ const basename8 = sp.basename(path5);
88506
89837
  const parent = this.fsw._getWatchedDir(directory);
88507
- parent.add(basename7);
89838
+ parent.add(basename8);
88508
89839
  const absolutePath = sp.resolve(path5);
88509
89840
  const options = {
88510
89841
  persistent: opts.persistent
@@ -88514,7 +89845,7 @@ class NodeFsHandler {
88514
89845
  let closer;
88515
89846
  if (opts.usePolling) {
88516
89847
  const enableBin = opts.interval !== opts.binaryInterval;
88517
- options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
89848
+ options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
88518
89849
  closer = setFsWatchFileListener(path5, absolutePath, options, {
88519
89850
  listener,
88520
89851
  rawEmitter: this.fsw._emitRaw
@@ -88532,18 +89863,18 @@ class NodeFsHandler {
88532
89863
  if (this.fsw.closed) {
88533
89864
  return;
88534
89865
  }
88535
- const dirname18 = sp.dirname(file2);
88536
- const basename7 = sp.basename(file2);
88537
- const parent = this.fsw._getWatchedDir(dirname18);
89866
+ const dirname19 = sp.dirname(file2);
89867
+ const basename8 = sp.basename(file2);
89868
+ const parent = this.fsw._getWatchedDir(dirname19);
88538
89869
  let prevStats = stats;
88539
- if (parent.has(basename7))
89870
+ if (parent.has(basename8))
88540
89871
  return;
88541
89872
  const listener = async (path5, newStats) => {
88542
89873
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
88543
89874
  return;
88544
89875
  if (!newStats || newStats.mtimeMs === 0) {
88545
89876
  try {
88546
- const newStats2 = await stat9(file2);
89877
+ const newStats2 = await stat10(file2);
88547
89878
  if (this.fsw.closed)
88548
89879
  return;
88549
89880
  const at = newStats2.atimeMs;
@@ -88561,9 +89892,9 @@ class NodeFsHandler {
88561
89892
  prevStats = newStats2;
88562
89893
  }
88563
89894
  } catch (error51) {
88564
- this.fsw._remove(dirname18, basename7);
89895
+ this.fsw._remove(dirname19, basename8);
88565
89896
  }
88566
- } else if (parent.has(basename7)) {
89897
+ } else if (parent.has(basename8)) {
88567
89898
  const at = newStats.atimeMs;
88568
89899
  const mt = newStats.mtimeMs;
88569
89900
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -88899,7 +90230,7 @@ var init_handler = __esm(() => {
88899
90230
  ERROR: "error"
88900
90231
  };
88901
90232
  EV = EVENTS;
88902
- statMethods = { lstat: lstat4, stat: stat9 };
90233
+ statMethods = { lstat: lstat4, stat: stat10 };
88903
90234
  HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
88904
90235
  binaryExtensions = new Set([
88905
90236
  "3dm",
@@ -89171,7 +90502,7 @@ var init_handler = __esm(() => {
89171
90502
  // ../../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
89172
90503
  import { EventEmitter } from "node:events";
89173
90504
  import { stat as statcb, Stats } from "node:fs";
89174
- import { readdir as readdir4, stat as stat10 } from "node:fs/promises";
90505
+ import { readdir as readdir4, stat as stat11 } from "node:fs/promises";
89175
90506
  import * as sp2 from "node:path";
89176
90507
  function arrify(item) {
89177
90508
  return Array.isArray(item) ? item : [item];
@@ -89625,7 +90956,7 @@ var init_chokidar = __esm(() => {
89625
90956
  const fullPath = opts.cwd ? sp2.join(opts.cwd, path5) : path5;
89626
90957
  let stats2;
89627
90958
  try {
89628
- stats2 = await stat10(fullPath);
90959
+ stats2 = await stat11(fullPath);
89629
90960
  } catch (err2) {}
89630
90961
  if (!stats2 || this.closed)
89631
90962
  return;
@@ -89729,8 +91060,8 @@ var init_chokidar = __esm(() => {
89729
91060
  }
89730
91061
  return this._userIgnored(path5, stats);
89731
91062
  }
89732
- _isntIgnored(path5, stat11) {
89733
- return !this._isIgnored(path5, stat11);
91063
+ _isntIgnored(path5, stat12) {
91064
+ return !this._isIgnored(path5, stat12);
89734
91065
  }
89735
91066
  _getWatchHelpers(path5) {
89736
91067
  return new WatchHelper(path5, this.options.followSymlinks, this);
@@ -89951,7 +91282,7 @@ function diffPathIndexes(previous, next2) {
89951
91282
  import { createHash as createHash8, randomUUID as randomUUID8 } from "node:crypto";
89952
91283
  import * as fs6 from "node:fs/promises";
89953
91284
  import { homedir as homedir12 } from "node:os";
89954
- import { join as join28 } from "node:path";
91285
+ import { join as join29 } from "node:path";
89955
91286
  function workspaceKeyFor(workingDir) {
89956
91287
  const normalized = normalizeWorkingDirForLookup(workingDir);
89957
91288
  return createHash8("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -89965,10 +91296,10 @@ function buildPathIndex(entries) {
89965
91296
  }
89966
91297
  function manifestPath(machineId, workingDir) {
89967
91298
  const key = workspaceKeyFor(workingDir);
89968
- return join28(SYNC_STATE_DIR, machineId, key, "manifest.json");
91299
+ return join29(SYNC_STATE_DIR, machineId, key, "manifest.json");
89969
91300
  }
89970
91301
  async function ensureDir2(filePath) {
89971
- await fs6.mkdir(join28(filePath, ".."), { recursive: true, mode: 448 });
91302
+ await fs6.mkdir(join29(filePath, ".."), { recursive: true, mode: 448 });
89972
91303
  }
89973
91304
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
89974
91305
  try {
@@ -90028,7 +91359,7 @@ function entriesFromPathIndex(paths) {
90028
91359
  }
90029
91360
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
90030
91361
  var init_workspace_sync_state = __esm(() => {
90031
- SYNC_STATE_DIR = join28(homedir12(), ".chatroom", "sync-state");
91362
+ SYNC_STATE_DIR = join29(homedir12(), ".chatroom", "sync-state");
90032
91363
  });
90033
91364
 
90034
91365
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -91842,17 +93173,17 @@ function parseMakefileTargets(content) {
91842
93173
  var MAX_NAME_LENGTH2 = 256;
91843
93174
 
91844
93175
  // src/infrastructure/services/workspace/workspace-resolver.ts
91845
- import { readFile as readFile15, readdir as readdir5, stat as stat11 } from "node:fs/promises";
91846
- import { join as join29, basename as basename8, resolve as resolve11 } from "node:path";
93176
+ import { readFile as readFile16, readdir as readdir5, stat as stat12 } from "node:fs/promises";
93177
+ import { join as join30, basename as basename9, resolve as resolve11 } from "node:path";
91847
93178
  async function resolveGlobPatternStar(rootDir, cleaned) {
91848
- const parentDir2 = join29(rootDir, cleaned.slice(0, -2));
93179
+ const parentDir2 = join30(rootDir, cleaned.slice(0, -2));
91849
93180
  try {
91850
93181
  const entries = await readdir5(parentDir2, { withFileTypes: true });
91851
93182
  const dirs = [];
91852
93183
  for (const entry of entries) {
91853
93184
  if (!entry.isDirectory())
91854
93185
  continue;
91855
- const dirPath = join29(parentDir2, entry.name);
93186
+ const dirPath = join30(parentDir2, entry.name);
91856
93187
  if (resolve11(dirPath).startsWith(resolve11(rootDir))) {
91857
93188
  dirs.push(dirPath);
91858
93189
  }
@@ -91863,11 +93194,11 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
91863
93194
  }
91864
93195
  }
91865
93196
  async function resolveLiteralPath(rootDir, cleaned) {
91866
- const dir = join29(rootDir, cleaned);
93197
+ const dir = join30(rootDir, cleaned);
91867
93198
  try {
91868
93199
  if (!resolve11(dir).startsWith(resolve11(rootDir)))
91869
93200
  return [];
91870
- const s = await stat11(dir);
93201
+ const s = await stat12(dir);
91871
93202
  if (s.isDirectory())
91872
93203
  return [dir];
91873
93204
  } catch {}
@@ -91883,10 +93214,10 @@ async function resolveGlobPattern(rootDir, pattern) {
91883
93214
  }
91884
93215
  async function readPackageJson(dir) {
91885
93216
  try {
91886
- const content = await readFile15(join29(dir, "package.json"), "utf-8");
93217
+ const content = await readFile16(join30(dir, "package.json"), "utf-8");
91887
93218
  const pkg = JSON.parse(content);
91888
93219
  return {
91889
- name: pkg.name || basename8(dir),
93220
+ name: pkg.name || basename9(dir),
91890
93221
  scripts: pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {}
91891
93222
  };
91892
93223
  } catch {
@@ -91895,7 +93226,7 @@ async function readPackageJson(dir) {
91895
93226
  }
91896
93227
  async function readPnpmWorkspacePatterns(rootDir) {
91897
93228
  try {
91898
- const content = await readFile15(join29(rootDir, "pnpm-workspace.yaml"), "utf-8");
93229
+ const content = await readFile16(join30(rootDir, "pnpm-workspace.yaml"), "utf-8");
91899
93230
  const patterns = [];
91900
93231
  let inPackages = false;
91901
93232
  for (const line of content.split(`
@@ -91922,7 +93253,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
91922
93253
  }
91923
93254
  async function readPackageJsonWorkspacePatterns(rootDir) {
91924
93255
  try {
91925
- const content = await readFile15(join29(rootDir, "package.json"), "utf-8");
93256
+ const content = await readFile16(join30(rootDir, "package.json"), "utf-8");
91926
93257
  const pkg = JSON.parse(content);
91927
93258
  if (!pkg.workspaces)
91928
93259
  return [];
@@ -91970,12 +93301,12 @@ async function resolveSubWorkspaces(rootDir, pm) {
91970
93301
  var init_workspace_resolver = () => {};
91971
93302
 
91972
93303
  // src/infrastructure/services/workspace/command-discovery.ts
91973
- import { access as access2, readFile as readFile16 } from "node:fs/promises";
91974
- import { join as join30, relative as relative3, basename as basename9 } from "node:path";
93304
+ import { access as access2, readFile as readFile17 } from "node:fs/promises";
93305
+ import { join as join31, relative as relative3, basename as basename10 } from "node:path";
91975
93306
  async function detectPackageManager(workingDir) {
91976
93307
  for (const { file: file2, manager } of LOCKFILE_MAP) {
91977
93308
  try {
91978
- await access2(join30(workingDir, file2));
93309
+ await access2(join31(workingDir, file2));
91979
93310
  return manager;
91980
93311
  } catch {}
91981
93312
  }
@@ -92019,7 +93350,7 @@ function isValidScriptEntry(name, script) {
92019
93350
  }
92020
93351
  async function readJsonFile(filePath, label) {
92021
93352
  try {
92022
- const content = await readFile16(filePath, "utf-8");
93353
+ const content = await readFile17(filePath, "utf-8");
92023
93354
  return parseJsonc2(content);
92024
93355
  } catch (error51) {
92025
93356
  if (error51.code !== "ENOENT") {
@@ -92043,8 +93374,8 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
92043
93374
  return commands;
92044
93375
  }
92045
93376
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
92046
- let rootPackageName = basename9(workingDir);
92047
- const pkg = await readJsonFile(join30(workingDir, "package.json"), "root package.json");
93377
+ let rootPackageName = basename10(workingDir);
93378
+ const pkg = await readJsonFile(join31(workingDir, "package.json"), "root package.json");
92048
93379
  if (!pkg)
92049
93380
  return { commands: [], rootPackageName };
92050
93381
  if (pkg.name)
@@ -92060,7 +93391,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
92060
93391
  }
92061
93392
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
92062
93393
  const turboTaskNames = [];
92063
- const turbo = await readJsonFile(join30(workingDir, "turbo.json"), "turbo.json");
93394
+ const turbo = await readJsonFile(join31(workingDir, "turbo.json"), "turbo.json");
92064
93395
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
92065
93396
  return turboTaskNames;
92066
93397
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -92073,10 +93404,10 @@ async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
92073
93404
  async function readDenoJson(workingDir) {
92074
93405
  const commands = [];
92075
93406
  for (const fileName of ["deno.json", "deno.jsonc"]) {
92076
- const deno = await readJsonFile(join30(workingDir, fileName), fileName);
93407
+ const deno = await readJsonFile(join31(workingDir, fileName), fileName);
92077
93408
  if (!deno?.tasks || typeof deno.tasks !== "object")
92078
93409
  continue;
92079
- const pkgName = deno.name ?? basename9(workingDir);
93410
+ const pkgName = deno.name ?? basename10(workingDir);
92080
93411
  const subWorkspace = { type: "deno", path: ".", name: pkgName };
92081
93412
  for (const [taskName, taskCommand] of Object.entries(deno.tasks)) {
92082
93413
  if (!isValidScriptEntry(taskName, taskCommand))
@@ -92096,14 +93427,14 @@ async function readMakefile(workingDir) {
92096
93427
  const commands = [];
92097
93428
  for (const fileName of ["Makefile", "makefile"]) {
92098
93429
  try {
92099
- const content = await readFile16(join30(workingDir, fileName), "utf-8");
93430
+ const content = await readFile17(join31(workingDir, fileName), "utf-8");
92100
93431
  const targets = parseMakefileTargets(content);
92101
93432
  if (targets.length === 0)
92102
93433
  continue;
92103
93434
  const subWorkspace = {
92104
93435
  type: "make",
92105
93436
  path: ".",
92106
- name: basename9(workingDir)
93437
+ name: basename10(workingDir)
92107
93438
  };
92108
93439
  for (const target of targets) {
92109
93440
  commands.push({
@@ -92234,8 +93565,8 @@ function createWorkspaceGitProbe(args2) {
92234
93565
  }
92235
93566
  async function statFingerprint(absPath) {
92236
93567
  try {
92237
- const stat12 = await fs7.stat(absPath);
92238
- return `${stat12.mtimeMs}:${stat12.size}`;
93568
+ const stat13 = await fs7.stat(absPath);
93569
+ return `${stat13.mtimeMs}:${stat13.size}`;
92239
93570
  } catch {
92240
93571
  return "missing";
92241
93572
  }
@@ -93261,12 +94592,12 @@ async function readCommandsUnits(workingDir) {
93261
94592
  return [{ kind: "commands", commands: commands.map(toCommandEntry) }];
93262
94593
  }
93263
94594
  async function readWorktreeUnits(workingDir) {
93264
- const [dirty, stat12] = await Promise.all([isDirty(workingDir), getDiffStat(workingDir)]);
94595
+ const [dirty, stat13] = await Promise.all([isDirty(workingDir), getDiffStat(workingDir)]);
93265
94596
  return [
93266
94597
  {
93267
94598
  kind: "worktree",
93268
94599
  isDirty: dirty,
93269
- diffStat: stat12.status === "available" ? { ...stat12.diffStat } : { filesChanged: 0, insertions: 0, deletions: 0 }
94600
+ diffStat: stat13.status === "available" ? { ...stat13.diffStat } : { filesChanged: 0, insertions: 0, deletions: 0 }
93270
94601
  }
93271
94602
  ];
93272
94603
  }
@@ -93643,7 +94974,7 @@ var init_workspace_service2 = __esm(() => {
93643
94974
  });
93644
94975
 
93645
94976
  // src/daemon/entry/init-daemon.ts
93646
- import { stat as stat12 } from "node:fs/promises";
94977
+ import { stat as stat13 } from "node:fs/promises";
93647
94978
  async function discoverModelsForHarness(harness, service4) {
93648
94979
  const installed2 = await service4.isInstalled();
93649
94980
  if (!installed2) {
@@ -93678,7 +95009,7 @@ function createDefaultDeps22() {
93678
95009
  kill: (pid, signal) => process.kill(pid, signal)
93679
95010
  },
93680
95011
  fs: {
93681
- stat: stat12
95012
+ stat: stat13
93682
95013
  },
93683
95014
  machine: {
93684
95015
  clearAgentPid,
@@ -97518,7 +98849,7 @@ var init_git_clone = __esm(() => {
97518
98849
 
97519
98850
  // src/infrastructure/local-actions/git-clone.ts
97520
98851
  import { spawn as spawn5 } from "node:child_process";
97521
- import { readdir as readdir6, stat as stat13 } from "node:fs/promises";
98852
+ import { readdir as readdir6, stat as stat14 } from "node:fs/promises";
97522
98853
  function parseGitCloneProgressLine(line) {
97523
98854
  const match5 = PROGRESS_LINE_PATTERN.exec(line.trim());
97524
98855
  if (!match5?.[1] || !match5[2])
@@ -97532,7 +98863,7 @@ function classifyGitCloneFailure(stderrTail, spawnErrorCode) {
97532
98863
  }
97533
98864
  async function statOrNull(path8) {
97534
98865
  try {
97535
- return await stat13(path8);
98866
+ return await stat14(path8);
97536
98867
  } catch (error51) {
97537
98868
  if (error51.code === "ENOENT")
97538
98869
  return null;
@@ -98111,7 +99442,7 @@ var init_assert_registered_working_dir = () => {};
98111
99442
 
98112
99443
  // src/infrastructure/services/workspace/workspace-path-security.ts
98113
99444
  import { realpath as realpath3 } from "node:fs/promises";
98114
- import { dirname as dirname19, isAbsolute as isAbsolute2, relative as relative4, resolve as resolve12, sep } from "node:path";
99445
+ import { dirname as dirname20, isAbsolute as isAbsolute2, relative as relative4, resolve as resolve12, sep } from "node:path";
98115
99446
  import { gunzipSync } from "node:zlib";
98116
99447
  function validateRelativePathSegments(filePath) {
98117
99448
  if (filePath.includes("\x00"))
@@ -98139,7 +99470,7 @@ function assertContained(workspaceRoot, targetPath) {
98139
99470
  return { ok: true };
98140
99471
  }
98141
99472
  async function resolveNonExistentTarget(workspaceRoot, candidate) {
98142
- let current = dirname19(candidate);
99473
+ let current = dirname20(candidate);
98143
99474
  for (;; ) {
98144
99475
  try {
98145
99476
  const ancestorReal = await realpath3(current);
@@ -98157,7 +99488,7 @@ async function resolveNonExistentTarget(workspaceRoot, candidate) {
98157
99488
  if (code2 !== "ENOENT") {
98158
99489
  return { ok: false, error: "Invalid file path" };
98159
99490
  }
98160
- const parent = dirname19(current);
99491
+ const parent = dirname20(current);
98161
99492
  if (parent === current) {
98162
99493
  return { ok: false, error: "Path escapes workspace" };
98163
99494
  }
@@ -98216,7 +99547,7 @@ function gunzipBase64Payload(base643, maxBytes) {
98216
99547
  var init_workspace_path_security = () => {};
98217
99548
 
98218
99549
  // src/daemon/entry/files/file-content-fulfillment.ts
98219
- import { readFile as readFile17 } from "node:fs/promises";
99550
+ import { readFile as readFile18 } from "node:fs/promises";
98220
99551
  import { gzipSync as gzipSync3 } from "node:zlib";
98221
99552
  function getErrorCause(error51) {
98222
99553
  if (typeof error51 === "object" && error51 !== null && "cause" in error51 && error51.cause !== undefined) {
@@ -98291,7 +99622,7 @@ var init_file_content_fulfillment = __esm(() => {
98291
99622
  continue;
98292
99623
  }
98293
99624
  let fileNotFound = false;
98294
- const buffer = yield* exports_Effect.catch(exports_Effect.tryPromise(() => readFile17(absolutePath)), (error51) => {
99625
+ const buffer = yield* exports_Effect.catch(exports_Effect.tryPromise(() => readFile18(absolutePath)), (error51) => {
98295
99626
  if (isENOENT(error51)) {
98296
99627
  fileNotFound = true;
98297
99628
  return exports_Effect.succeed(Buffer.alloc(0));
@@ -98536,10 +99867,10 @@ function parseAttachmentFileName(remainder) {
98536
99867
  if (underscore <= 0)
98537
99868
  return false;
98538
99869
  const uuid3 = remainder.slice(0, underscore);
98539
- const basename10 = remainder.slice(underscore + 1);
98540
- if (!UUID_V4_RE.test(uuid3) || !basename10)
99870
+ const basename11 = remainder.slice(underscore + 1);
99871
+ if (!UUID_V4_RE.test(uuid3) || !basename11)
98541
99872
  return false;
98542
- return sanitizeChatAttachmentFileName(basename10) === basename10;
99873
+ return sanitizeChatAttachmentFileName(basename11) === basename11;
98543
99874
  }
98544
99875
  function getInvalidChatAttachmentUploadPathReason(relativePath) {
98545
99876
  if (relativePath.includes("\\"))
@@ -98568,14 +99899,14 @@ function normalizeSegments(relativePath) {
98568
99899
  function hasBlockedSegment(lowerSegments) {
98569
99900
  return lowerSegments.some((segment) => BLOCKED_SEGMENT_NAMES.has(segment));
98570
99901
  }
98571
- function hasBlockedBasename(basename10) {
98572
- return /^\.env(?:\.|$)/.test(basename10) || /\.(?:pem|key)$/.test(basename10) || basename10 === "id_rsa" || basename10 === "credentials.json";
99902
+ function hasBlockedBasename(basename11) {
99903
+ return /^\.env(?:\.|$)/.test(basename11) || /\.(?:pem|key)$/.test(basename11) || basename11 === "id_rsa" || basename11 === "credentials.json";
98573
99904
  }
98574
99905
  function isBlockedUploadTargetPath(relativePath) {
98575
99906
  if (!relativePath)
98576
99907
  return false;
98577
- const { lowerSegments, basename: basename10 } = normalizeSegments(relativePath);
98578
- return hasBlockedSegment(lowerSegments) || hasBlockedBasename(basename10);
99908
+ const { lowerSegments, basename: basename11 } = normalizeSegments(relativePath);
99909
+ return hasBlockedSegment(lowerSegments) || hasBlockedBasename(basename11);
98579
99910
  }
98580
99911
  function getBlockedUploadTargetReason(relativePath) {
98581
99912
  if (!isBlockedUploadTargetPath(relativePath))
@@ -98625,8 +99956,8 @@ var init_file_write_request_priority = __esm(() => {
98625
99956
  });
98626
99957
 
98627
99958
  // src/daemon/entry/files/file-write-fulfillment.ts
98628
- import { access as access4, mkdir as mkdir12, rename as rename4, rm as rm5, writeFile as writeFile10 } from "node:fs/promises";
98629
- import { dirname as dirname20 } from "node:path";
99959
+ import { access as access4, mkdir as mkdir12, rename as rename4, rm as rm5, writeFile as writeFile11 } from "node:fs/promises";
99960
+ import { dirname as dirname21 } from "node:path";
98630
99961
  function isTerminalFileWriteError(errorMessage3) {
98631
99962
  const terminalMessages = new Set([
98632
99963
  "Invalid file path",
@@ -98725,11 +100056,11 @@ async function validateWriteOperation(operation, absolutePath) {
98725
100056
  }
98726
100057
  async function writePayloadToDisk(absolutePath, operation, content) {
98727
100058
  if (operation === "create") {
98728
- await mkdir12(dirname20(absolutePath), { recursive: true });
98729
- await writeFile10(absolutePath, content, { flag: "wx" });
100059
+ await mkdir12(dirname21(absolutePath), { recursive: true });
100060
+ await writeFile11(absolutePath, content, { flag: "wx" });
98730
100061
  return;
98731
100062
  }
98732
- await writeFile10(absolutePath, content);
100063
+ await writeFile11(absolutePath, content);
98733
100064
  }
98734
100065
  async function fulfillOneFileWriteRequest(session2, request3) {
98735
100066
  const claim = await claimWriteRequest(session2, request3);
@@ -98803,7 +100134,7 @@ async function fulfillOneFileWriteRequest(session2, request3) {
98803
100134
  });
98804
100135
  return;
98805
100136
  }
98806
- await mkdir12(dirname20(targetResolved.absolutePath), { recursive: true });
100137
+ await mkdir12(dirname21(targetResolved.absolutePath), { recursive: true });
98807
100138
  await rename4(resolved.absolutePath, targetResolved.absolutePath);
98808
100139
  await completeWriteRequest(session2, claimed._id, claimed.revision, { status: "done" });
98809
100140
  const elapsed2 = Date.now() - startTime;
@@ -100534,9 +101865,9 @@ var init_execute_chatroom_stop_command = __esm(() => {
100534
101865
 
100535
101866
  // src/daemon/entry/persistence-path.ts
100536
101867
  import { homedir as homedir13 } from "node:os";
100537
- import { join as join31 } from "node:path";
101868
+ import { join as join32 } from "node:path";
100538
101869
  function resolvePersistenceDbPath(machineId) {
100539
- return join31(homedir13(), ".chatroom", "daemon", machineId, "events.sqlite");
101870
+ return join32(homedir13(), ".chatroom", "daemon", machineId, "events.sqlite");
100540
101871
  }
100541
101872
  var init_persistence_path = () => {};
100542
101873
 
@@ -101095,10 +102426,10 @@ var init_schema = __esm(() => {
101095
102426
 
101096
102427
  // src/daemon/infrastructure/persistence/open-database.ts
101097
102428
  import { mkdirSync as mkdirSync9 } from "node:fs";
101098
- import { dirname as dirname21 } from "node:path";
102429
+ import { dirname as dirname22 } from "node:path";
101099
102430
  import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
101100
102431
  function openDatabase(dbPath) {
101101
- mkdirSync9(dirname21(dbPath), { recursive: true });
102432
+ mkdirSync9(dirname22(dbPath), { recursive: true });
101102
102433
  const db = new DatabaseSync5(dbPath);
101103
102434
  for (const sql of MIGRATIONS2) {
101104
102435
  db.exec(sql);
@@ -112072,13 +113403,13 @@ var require_cookie = __commonJS((exports) => {
112072
113403
  } while (index < len2);
112073
113404
  return obj;
112074
113405
  }
112075
- function startIndex(str, index, max5) {
113406
+ function startIndex(str, index, max6) {
112076
113407
  do {
112077
113408
  var code2 = str.charCodeAt(index);
112078
113409
  if (code2 !== 32 && code2 !== 9)
112079
113410
  return index;
112080
- } while (++index < max5);
112081
- return max5;
113411
+ } while (++index < max6);
113412
+ return max6;
112082
113413
  }
112083
113414
  function endIndex(str, index, min5) {
112084
113415
  while (index > min5) {
@@ -117211,19 +118542,19 @@ function routeRequest(req, res, deps) {
117211
118542
 
117212
118543
  // src/daemon/local-web/server/serve-static.ts
117213
118544
  import { existsSync as existsSync10, readFileSync as readFileSync12 } from "node:fs";
117214
- import { dirname as dirname22, join as join32 } from "node:path";
118545
+ import { dirname as dirname23, join as join33 } from "node:path";
117215
118546
  import { fileURLToPath as fileURLToPath6 } from "node:url";
117216
118547
  function clientDistCandidates(here) {
117217
118548
  return [
117218
- join32(here, "client/build"),
117219
- join32(here, "../client/build"),
117220
- join32(here, "../src/daemon/local-web/client/build")
118549
+ join33(here, "client/build"),
118550
+ join33(here, "../client/build"),
118551
+ join33(here, "../src/daemon/local-web/client/build")
117221
118552
  ];
117222
118553
  }
117223
- function resolveClientDistDir(here = dirname22(fileURLToPath6(import.meta.url))) {
118554
+ function resolveClientDistDir(here = dirname23(fileURLToPath6(import.meta.url))) {
117224
118555
  const candidates = clientDistCandidates(here);
117225
118556
  for (const dir of candidates) {
117226
- if (existsSync10(join32(dir, "index.html")))
118557
+ if (existsSync10(join33(dir, "index.html")))
117227
118558
  return dir;
117228
118559
  }
117229
118560
  return candidates[0];
@@ -117236,7 +118567,7 @@ function tryServeStatic(req, res, distDir) {
117236
118567
  return false;
117237
118568
  }
117238
118569
  const safePath = pathname === "/" ? "/index.html" : pathname;
117239
- const filePath = join32(distDir, safePath);
118570
+ const filePath = join33(distDir, safePath);
117240
118571
  if (!filePath.startsWith(distDir)) {
117241
118572
  res.writeHead(403);
117242
118573
  res.end();
@@ -117253,7 +118584,7 @@ function tryServeStatic(req, res, distDir) {
117253
118584
  }
117254
118585
  return true;
117255
118586
  }
117256
- const indexPath = join32(distDir, "index.html");
118587
+ const indexPath = join33(distDir, "index.html");
117257
118588
  if (existsSync10(indexPath)) {
117258
118589
  res.writeHead(200, { "Content-Type": "text/html" });
117259
118590
  if (req.method === "HEAD") {
@@ -118617,7 +119948,7 @@ var {
118617
119948
  } = import__.default;
118618
119949
 
118619
119950
  // src/commands/generate-3d/index.ts
118620
- import { readFile as readFile3, stat as stat2 } from "node:fs/promises";
119951
+ import { readFile as readFile4, stat as stat3 } from "node:fs/promises";
118621
119952
  init_model_registry();
118622
119953
  var GENERATE_3D_PLATFORMS = ["fal"];
118623
119954
  var DEFAULT_GENERATE_3D_PLATFORM = "fal";
@@ -118627,6 +119958,20 @@ function parsePlatform(value) {
118627
119958
  }
118628
119959
  return value;
118629
119960
  }
119961
+ var ORIGIN_MODES = ["feet", "center", "as-is"];
119962
+ function parseOrigin(value) {
119963
+ if (!ORIGIN_MODES.includes(value)) {
119964
+ throw new InvalidArgumentError(`must be one of ${ORIGIN_MODES.join(", ")}`);
119965
+ }
119966
+ return value;
119967
+ }
119968
+ function parseTargetHeight(value) {
119969
+ const meters = Number(value);
119970
+ if (!Number.isFinite(meters) || meters <= 0) {
119971
+ throw new InvalidArgumentError("must be a positive, finite number of metres");
119972
+ }
119973
+ return meters;
119974
+ }
118630
119975
  var CAPABILITY_FLAGS = [
118631
119976
  { capability: "prompt", flag: "--prompt" },
118632
119977
  { capability: "image", flag: "--input-image" },
@@ -118661,7 +120006,7 @@ function parseInputJson2(text) {
118661
120006
  return parsed;
118662
120007
  }
118663
120008
  async function readInputFile(path) {
118664
- const stats = await stat2(path).catch(() => null);
120009
+ const stats = await stat3(path).catch(() => null);
118665
120010
  if (stats === null)
118666
120011
  throw new Error(`--input file not found: ${path}`);
118667
120012
  if (!stats.isFile())
@@ -118669,7 +120014,7 @@ async function readInputFile(path) {
118669
120014
  if (stats.size > MAX_INPUT_BYTES) {
118670
120015
  throw new Error(`--input file is ${stats.size} bytes; the maximum is ${MAX_INPUT_BYTES} bytes (1 MiB).`);
118671
120016
  }
118672
- return parseInputJson2(await readFile3(path, "utf8"));
120017
+ return parseInputJson2(await readFile4(path, "utf8"));
118673
120018
  }
118674
120019
  async function readExtraInput(value) {
118675
120020
  if (value === undefined)
@@ -118702,7 +120047,16 @@ Package:
118702
120047
  <base>.zip holds the master GLB, web/<stem>.web.glb, and manifest.json; the
118703
120048
  reference image at reference/... when one was generated or supplied; and the
118704
120049
  rigged and animation GLBs with their web versions when rigging ran.
118705
- <base>.html is a standalone preview. <base> is <out-stem>-<timestamp>.
120050
+ <base>.html is a standalone preview of both variants: the full-resolution
120051
+ master and the scaled-down web variant. <base> is <out-stem>-<timestamp>.
120052
+
120053
+ Fit:
120054
+ --target-height and --origin apply to the master, the web variant, and every
120055
+ rig output. With no flags the asset is written as the model returned it.
120056
+
120057
+ Preview:
120058
+ chatroom generate-3d preview <base>.zip rebuilds <base>.html from an existing
120059
+ package without contacting fal. Pass --force to replace an existing page.
118706
120060
 
118707
120061
  Cost:
118708
120062
  List prices as of ${GENERATE_3D_PRICES_AS_OF}, not billed amounts; fal returns no
@@ -118714,9 +120068,18 @@ function printResult(result) {
118714
120068
  console.log(`Manifest ${result.manifestPath}`);
118715
120069
  console.log(`Package ${result.zipPath}`);
118716
120070
  console.log(`Preview ${result.htmlPath}`);
120071
+ for (const check of result.checks)
120072
+ console.log(`Warning: ${check.message}`);
120073
+ for (const warning of result.warnings)
120074
+ console.log(`Warning: ${warning}`);
118717
120075
  for (const line of result.costLines)
118718
120076
  console.log(line);
118719
120077
  }
120078
+ function toFit(options) {
120079
+ if (options.origin === "as-is" && options.targetHeight === undefined)
120080
+ return;
120081
+ return { origin: options.origin, targetHeight: options.targetHeight };
120082
+ }
118720
120083
  function toGenerate3dOptions(options, extraInput) {
118721
120084
  const base = {
118722
120085
  prompt: options.prompt,
@@ -118724,6 +120087,7 @@ function toGenerate3dOptions(options, extraInput) {
118724
120087
  model: options.model,
118725
120088
  out: options.out,
118726
120089
  force: options.force,
120090
+ fit: toFit(options),
118727
120091
  rig: options.rig,
118728
120092
  packageDir: options.packageDir,
118729
120093
  extraInput,
@@ -118747,7 +120111,12 @@ async function executeGenerate3d(options) {
118747
120111
  const { generate3d: generate3d2 } = await Promise.resolve().then(() => (init_generate_3d(), exports_generate_3d));
118748
120112
  printResult(await generate3d2(generateOptions, deps));
118749
120113
  }
118750
- async function runGenerate3d(options) {
120114
+ async function runGenerate3d(options, command) {
120115
+ if (options.out === undefined) {
120116
+ command.error("error: required option '--out <path>' not specified", {
120117
+ code: "commander.missingMandatoryOptionValue"
120118
+ });
120119
+ }
118751
120120
  try {
118752
120121
  await executeGenerate3d(options);
118753
120122
  } catch (err2) {
@@ -118755,12 +120124,26 @@ async function runGenerate3d(options) {
118755
120124
  process.exitCode = 1;
118756
120125
  }
118757
120126
  }
120127
+ async function runGenerate3dPreview(zip, options, command) {
120128
+ try {
120129
+ const { rebuildPreviewFromZip: rebuildPreviewFromZip2 } = await Promise.resolve().then(() => (init_rebuild_preview(), exports_rebuild_preview));
120130
+ const parentForce = command.parent?.opts().force === true;
120131
+ const htmlPath = await rebuildPreviewFromZip2(zip, {
120132
+ force: options.force === true || parentForce
120133
+ });
120134
+ console.log(`Preview ${htmlPath}`);
120135
+ } catch (err2) {
120136
+ console.error(`❌ ${err2 instanceof Error ? err2.message : String(err2)}`);
120137
+ process.exitCode = 1;
120138
+ }
120139
+ }
118758
120140
  function registerGenerate3dCommand(program2) {
118759
- program2.command("generate-3d").description("Generate one 3D mesh (GLB) with a fal model, plus its JSON manifest and a zip/preview package").option("--model <id>", "fal endpoint id (default fal-ai/trellis-2); any id is accepted").requiredOption("--out <path>", "Output GLB path (manifest is written to <path>.json)").option("--platform <name>", "Generation platform: fal", parsePlatform, DEFAULT_GENERATE_3D_PLATFORM).option("--prompt <text>", "text prompt; with the default model, a reference image is generated first").option("--input-image <path-or-url>", "reference image (local file or URL); the image-to-3D input").option("--force", "replace existing output, reference, package, preview, rig, and clip files").option("--rig", "rig with Meshy 7.1 (requires --model meshy/v7.1/...); needs --confirm-cost on a real run").option("--animation-action-id <id>", "Meshy preset animation id (0-696), repeatable; requires --rig", collectRepeatable, []).option("--package-dir <dir>", "Directory for the package zip and preview (default: the directory of --out)").option("--input <json|@file>", "extra fal request fields as a JSON object, merged over the model defaults; reserved: prompt, image_url; @file reads JSON from a file").option("--confirm-cost", "confirm the priced plan for a real run that rigs or animates (Meshy 7.1 only), after the user confirms the priced dry run; not needed for --dry-run").option("--dry-run", "Validate and print the request and the static list-price estimate. No upload or fal generation call is made; FAL_API_KEY is not required").addHelpText("after", HELP_AFTER).action(runGenerate3d);
120141
+ const generate3d2 = program2.command("generate-3d").description("Generate one 3D mesh (GLB) with a fal model, plus its JSON manifest and a zip/preview package").option("--model <id>", "fal endpoint id (default fal-ai/trellis-2); any id is accepted").option("--out <path>", "Output GLB path (required; manifest is written to <path>.json)").option("--platform <name>", "Generation platform: fal", parsePlatform, DEFAULT_GENERATE_3D_PLATFORM).option("--prompt <text>", "text prompt; with the default model, a reference image is generated first").option("--input-image <path-or-url>", "reference image (local file or URL); the image-to-3D input").option("--force", "replace existing output, reference, package, preview, rig, and clip files").option("--target-height <meters>", "Scale the model so its height is this many metres (positive, finite)", parseTargetHeight).option("--origin <origin>", "Where the origin goes: feet (bottom centre), center, or as-is (default)", parseOrigin, "as-is").option("--rig", "rig with Meshy 7.1 (requires --model meshy/v7.1/...); needs --confirm-cost on a real run").option("--animation-action-id <id>", "Meshy preset animation id (0-696), repeatable; requires --rig", collectRepeatable, []).option("--package-dir <dir>", "Directory for the package zip and preview (default: the directory of --out)").option("--input <json|@file>", "extra fal request fields as a JSON object, merged over the model defaults; reserved: prompt, image_url; @file reads JSON from a file").option("--confirm-cost", "confirm the priced plan for a real run that rigs or animates (Meshy 7.1 only), after the user confirms the priced dry run; not needed for --dry-run").option("--dry-run", "Validate and print the request and the static list-price estimate. No upload or fal generation call is made; FAL_API_KEY is not required").addHelpText("after", HELP_AFTER).action(runGenerate3d);
120142
+ generate3d2.command("preview <zip>").description("Rebuild <base>.html from an existing package zip, offline").option("--force", "replace an existing <base>.html").action(runGenerate3dPreview);
118760
120143
  }
118761
120144
 
118762
120145
  // src/commands/generate-image/index.ts
118763
- import { readFile as readFile4, stat as stat3 } from "node:fs/promises";
120146
+ import { readFile as readFile5, stat as stat4 } from "node:fs/promises";
118764
120147
  init_image();
118765
120148
  init_model_capabilities();
118766
120149
  init_image_preflight();
@@ -118781,7 +120164,7 @@ async function readInputOption(value) {
118781
120164
  if (!value.startsWith("@"))
118782
120165
  return parseInputJson(value);
118783
120166
  const path = value.slice(1);
118784
- const stats = await stat3(path).catch(() => null);
120167
+ const stats = await stat4(path).catch(() => null);
118785
120168
  if (stats === null)
118786
120169
  throw new Error(`--input file not found: ${path}`);
118787
120170
  if (!stats.isFile())
@@ -118789,7 +120172,7 @@ async function readInputOption(value) {
118789
120172
  if (stats.size > MAX_SIDECAR_BYTES) {
118790
120173
  throw new Error(`--input file is ${stats.size} bytes; the maximum is ${MAX_SIDECAR_BYTES} bytes (1 MiB).`);
118791
120174
  }
118792
- return parseInputJson(await readFile4(path, "utf8"));
120175
+ return parseInputJson(await readFile5(path, "utf8"));
118793
120176
  }
118794
120177
  function parseSeed(value) {
118795
120178
  const seed = Number(value);
@@ -118852,7 +120235,7 @@ function registerChatroomRoleCommand(program2, config) {
118852
120235
  }
118853
120236
 
118854
120237
  // src/commands/remove-image-background/remove-image-background.ts
118855
- import { basename as basename2, dirname as dirname5, extname as extname3, join as join3, resolve as resolve3 } from "node:path";
120238
+ import { basename as basename3, dirname as dirname6, extname as extname3, join as join4, resolve as resolve3 } from "node:path";
118856
120239
 
118857
120240
  // src/commands/remove-image-background/background-key.ts
118858
120241
  init_image_preflight();
@@ -119217,7 +120600,7 @@ function resolveMode(opts) {
119217
120600
  throw new RemoveBackgroundError("--out must end in .png.");
119218
120601
  }
119219
120602
  const outPath = resolve3(opts.out);
119220
- return { kind: "single", inPath: resolve3(opts.in), outPath, outDir: dirname5(outPath) };
120603
+ return { kind: "single", inPath: resolve3(opts.in), outPath, outDir: dirname6(outPath) };
119221
120604
  }
119222
120605
  if (opts.inDir === undefined || opts.outDir === undefined) {
119223
120606
  throw new RemoveBackgroundError("--in-dir and --out-dir must be used together.");
@@ -119245,7 +120628,7 @@ function requireRanges(opts) {
119245
120628
  async function enumerateInputs(mode, deps) {
119246
120629
  if (mode.kind === "single") {
119247
120630
  return {
119248
- inputs: [{ path: mode.inPath, stem: basename2(mode.inPath, extname3(mode.inPath)) }],
120631
+ inputs: [{ path: mode.inPath, stem: basename3(mode.inPath, extname3(mode.inPath)) }],
119249
120632
  ignoredNonPng: 0
119250
120633
  };
119251
120634
  }
@@ -119258,7 +120641,7 @@ async function enumerateInputs(mode, deps) {
119258
120641
  throw new RemoveBackgroundError(`${mode.inDir} contains ${pngs.length} PNG files; the maximum is ${MAX_BATCH_FILES}.`);
119259
120642
  }
119260
120643
  return {
119261
- inputs: pngs.map((name) => ({ path: join3(mode.inDir, name), stem: basename2(name, ".png") })),
120644
+ inputs: pngs.map((name) => ({ path: join4(mode.inDir, name), stem: basename3(name, ".png") })),
119262
120645
  ignoredNonPng: entries.length - pngs.length
119263
120646
  };
119264
120647
  }
@@ -119313,7 +120696,7 @@ function requireSafeName(name, context) {
119313
120696
  }
119314
120697
  function assignNames(mode, opts, tiles) {
119315
120698
  if (mode.kind === "single") {
119316
- const name = requireSafeName(basename2(mode.outPath, ".png"), "--out");
120699
+ const name = requireSafeName(basename3(mode.outPath, ".png"), "--out");
119317
120700
  return [{ tile: tiles[0], assignedName: name, assignedBy: "out" }];
119318
120701
  }
119319
120702
  const stems = new Set(tiles.map((tile) => tile.stem));
@@ -119538,7 +120921,7 @@ async function prepareAssets(named, mode, options, deps) {
119538
120921
  throw new RemoveBackgroundError(`Cannot remove the background from ${entry.tile.path}: ${keyed.message}`);
119539
120922
  }
119540
120923
  const { encoded, outputWidth, outputHeight, trim } = encodeAsset(entry.tile.image, keyed, options, deps);
119541
- const path = mode.kind === "single" ? mode.outPath : join3(mode.outDir, `${entry.assignedName}.png`);
120924
+ const path = mode.kind === "single" ? mode.outPath : join4(mode.outDir, `${entry.assignedName}.png`);
119542
120925
  prepared.push({
119543
120926
  tile: namedTile,
119544
120927
  keyed,
@@ -119817,7 +121200,7 @@ function registerRemoveImageBackgroundFalCommand(program2) {
119817
121200
 
119818
121201
  // src/commands/slice-image/slice.ts
119819
121202
  init_image_preflight();
119820
- import { basename as basename4, extname as extname4, join as join4, resolve as resolve5 } from "node:path";
121203
+ import { basename as basename5, extname as extname4, join as join5, resolve as resolve5 } from "node:path";
119821
121204
  var SLICE_TOOL = "chatroom slice-image";
119822
121205
  var MAX_TILES = 256;
119823
121206
  var MAX_GRID_DIMENSION = 64;
@@ -119832,12 +121215,12 @@ class SliceImageError extends Error {
119832
121215
  function padWidth(tileCount) {
119833
121216
  return Math.max(2, String(tileCount).length);
119834
121217
  }
119835
- function requireInteger(value, label, min, max) {
121218
+ function requireInteger(value, label, min, max2) {
119836
121219
  if (!Number.isInteger(value)) {
119837
121220
  throw new SliceImageError(`${label} must be an integer.`);
119838
121221
  }
119839
- if (value < min || max !== undefined && value > max) {
119840
- const range = max === undefined ? `at least ${min}` : `between ${min} and ${max}`;
121222
+ if (value < min || max2 !== undefined && value > max2) {
121223
+ const range = max2 === undefined ? `at least ${min}` : `between ${min} and ${max2}`;
119841
121224
  throw new SliceImageError(`${label} must be ${range}.`);
119842
121225
  }
119843
121226
  return value;
@@ -120128,20 +121511,20 @@ async function sliceImage(opts, deps) {
120128
121511
  gutter,
120129
121512
  allowRemainder: opts.allowRemainder === true
120130
121513
  });
120131
- const sheetStem = basename4(inPath, extname4(inPath));
121514
+ const sheetStem = basename5(inPath, extname4(inPath));
120132
121515
  const names = resolveTileNames({
120133
121516
  cols,
120134
121517
  rows,
120135
121518
  names: opts.names,
120136
121519
  prefix: opts.prefix ?? sheetStem
120137
121520
  });
120138
- const indexPath = join4(outDir, `${sheetStem}.slices.json`);
121521
+ const indexPath = join5(outDir, `${sheetStem}.slices.json`);
120139
121522
  const sourceManifestPath = `${inPath}.json`;
120140
121523
  const outputs = grid.cells.map((cell) => {
120141
121524
  const name = names[cell.index] ?? "";
120142
121525
  if (name.length === 0)
120143
121526
  return null;
120144
- const path = join4(outDir, `${name}.png`);
121527
+ const path = join5(outDir, `${name}.png`);
120145
121528
  return { cell, name, path, manifestPath: `${path}.json` };
120146
121529
  }).filter((entry) => entry !== null);
120147
121530
  const skipped = grid.cells.filter((cell) => (names[cell.index] ?? "").length === 0).map((cell) => cell.index);
@@ -121048,4 +122431,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
121048
122431
  });
121049
122432
  program2.parse();
121050
122433
 
121051
- //# debugId=D6C1587E8A62204B64756E2164756E21
122434
+ //# debugId=0BF4A795FB05C49664756E2164756E21